RX8 Project – Part 22, Engine Rebuild

To preface this I am not an engine builder, this is just the approximate process I followed more to give you all some handy reference photos and some info that might be helpful to someone. Please don’t take any of this as gospel! I still don’t even know if the engine will actually work!

Anyone reading this blog may have noticed that the mechanical work on the car seemed to stop a very long time ago. While there has been a bit of a gap due to other aspects of life getting in the way there was more progress than it may have appeared. Back in 2018, some 3 years ago as I type this, I took my recently stripped engine parts (ostensibly the block and crank) along with the custom flywheel and spacer to be checked and relevant parts balanced with a view to rebuilding the engine.

With little surprise I got a call back shortly after to tell me the pistons were not serviceable and would need replacing. Following my earlier research (mentioned in a previous post) into the Noble M12 and later cars I decided that for the power point I was aiming for (300 bhp approximately) the stock cast type pistons should be up to the job with Noble moving over to forged parts with the M400 model and target power above 400 bhp. Online I’ve seen many people reporting the cast pistons hitting their limits up around 500bhp but what their life expectancy is at that point who knows. With some luck I manged to find a set of +0.5mm oversize stock replacement pistons online ( part H663CP ) so sent these up to be checked for balance with the engine. The thing Noble did upgrade was to upgrade the factory rods with forged ones so I had already sourced some suitable rods although even that was a bit of a challenge. The ones I bought were from XPOWER Engines in Essex and are still listed on Ebay as I type this as ST220 3.0 H-section EN24 steel rods and come with ARP 2000 bolts. I’d not come across this company until this point but some research showed they are quite well known so I felt pretty confident they’d be ok.

So now I’ve got these parts all shipped up to MJA Automotive in Bromsgrove who would rebore and hone the block to match the new oversize pistons as well as giving everything a proper clean, checked and completely balanced. They’re a small firm but attention to detail was great, they even sand blasted and repainted the original crank pully because it was a bit rusty.

So now I had a pile of goodies to put together :

S-type engine parts for rebuild

This picture is another good indication of how small this engine block actually is for the potential power output. You can see all the goodies here and basically everything that should be replaced was so new pistons, rings, rods, clutch, upgraded engine bearings (more on this later) and then obviously the custom flywheel and cleaned crank. This means only one thing, I had to build up the engine. Now when most people rebuild their own engine for the first time they start with something like a lawnmower engine but not me…In retrospect I probably should’ve just paid MJA to build it but I like a challenge!

First things first I decided to assemble all the pistons on the rods to have a quick win. This is as simple as taking the ring clip and pin out of the piston, putting the rod in place, sliding the pin back through and putting the ring clip back in. Add a dab of lubricant inside the small bearing before you put it together. The pin should be a slip fit on this because its a fully floating arrangement and so the rod bushing may need reaming to fit correctly if this isn’t the case. It’s also with noting these rods are not handed because in this engine they all have individual locations on the crank and do not touch each other whereas some engines have paired locations leading to the rod big end bearing having a flat side and a curved/chamfered side on the outer faces. If you see this the flat faces of the rods should be oriented to touch each other in the pair.

Comparison of used stock piston and rod and new piston and forged rod

Here you can see the state of the old pistons and the massive difference in the size of the rods. The keen eyed amongst you may notice this piston has the rings installed, what I actually did was installed all the oil rings at the bottom of the pistons but left the compression rings of so they could be correctly trimmed and fitted to the engine later. The ones show I’d slotted on for my own curiosity about how it all went together and removed shortly afterwards.

Box of assembled pistons and forged rods for S-type v6

That all looks rather shiny, I’m not used to car parts being this clean!

This was around the time I tried to find a manual for how to actually rebuild this engine with all the tolerances allowed for all the various parts and after a brief search found the S-type workshop manual located on jagrepair.com . This manual is massive at some 3300 pages and covers basically every aspect of the car but obviously since I don’t have the rest of the car I concentrated on the engine section which for this engine starts on page 635. I can’t add much on the instruction in it – it really is step by step so that’s the place to go for the detail!

So back to the things I did differently and some gratuitous photos of shiny stuff. while We’re still on the subject of piston rods I actually found and ordered some Mahle motorsport “high performance” racing bearings but curiously I found out shortly afterwards that Mahle Motorsport don’t sell a kit for this engine. After a concerning period waiting to see what would turn up and if I’d just been conned what actually arrived was the following :

Racing rod bearing for S-type

Checking the Mahle racing bearings catalogue I’d already found told me this was actually a bearing for a 2.3L Duratec which is a 4 cylinder engine and I’d been shipped one complete 4 cyl bearing kit ( kit number VC1013) and half a second one to make up a complete set for the V6. Checking it all out sure enough they do seem to be the correct dimensions for this engine.

Rod bearing installation detail for S-type V6

They fit well but the only bit of strangeness is racing bearings don’t have the location notch usually found on rod bearings so a lot of care must be taken to make sure they’re correctly centred in the rod when it’s assembled. Apparently this is because the notch reduces the bearing area adjacent to the notch. More info about this can be found from Mahle themselves here . Contrary to common belief the notch isn’t there to prevent the bearing from spinning and is purely to centre it on assembly. Once the rod is assembled the the hoop stress in the bearing produces so much friction it will stay in place with no issues. Spun bearings are caused when the bearing seizes onto the crank, this is usually caused by insufficient lubrication and if this happens the bearing will spin whether or not you have the notch.

So back to the block, once it’s mounted upside down on the stand go ahead and drop the block side halves of the crank bearings in place. The parts I used here are King bearings kit number MB4056SI :

Crank bearing installation photo for S-type V6

Crank bearing photo at an angle, S-type v6

Also don’t forget to add the thrust bearing on the flywheel end. It’s a bit hard to see in the photo because I haven’t got a photo without assembly lubricant but it’s there.

Lubricated bearings, S-type V6 assembly

Apply assembly lubricant to all the bearing faces

Now drop in the crank, carefully! then go ahead and apply assembly lubricant to all the running surfaces. In the photo the middle two rod locations aren’t lubricated yet because they’re at the back and needs rotating for access.

S-type crank installed

Next take the lower block housing and install the other bearing halves into the appropriate locations. As above apply assembly lubricant on the bearing faces.

S-type v6 lower block

Check the bearings are holding on as the next step involves dropping this section downward onto the upper block so make sure they don’t fall out. If they wont stay put you can lift the crank back out, drop it on this section then use it to hold the bearing shells in place when you flip it over and put it on the upper block. Run a bead of RTV along the mating face of one the two block halves before you put it together. Read the instructions on the RTV – usually you need to let it partly cure before pushing the parts together. Remember the RTV goes to the inside of the bolt holes otherwise oil will weep past the bolts. This is also why the flange is wider on the inside.

S-type v6 lower block assembled

Hopefully you should have something that looks like this. Note the locations of the bolts with the M6 thread on the reverse side – these are the ones the the windage tray bolts onto so they have to be in the right positions. Torque all the bolts down following the workshop manual.

Now for the top side. We need to set the piston ring gaps which will involve working out what your gap should be (there are various online calculators now which make this easy). We need to be looking at larger gaps due to running a turbo and I wanted to make sure I had some headroom to run higher boost later without issues so worked on the side of going a touch larger. I ended up with a number of 0.57mm on the top ring and 0.72mm on the second ring but I think this is probably overly cautious. Who knows, maybe one day I’ll run nitrous. The rings are measured by inserting them into the bore, making sure they’re totally parallel to the block deck using some sort of depth tool, this can be done with a vernier caliper or a variety of other methods. You then measure the gap with feeler gauges when in this position and file back the ends of the ring as necessary to get the required gap. The filed ends need to be totally flat and parallel to each other. I put a flat file in a vice and carefully filed it down. Be careful, you can’t put it back if you go too far. Also piston rings are very brittle. Don’t mix up your top and second rings of install them wrong. I suggest buying a cheap piston ring installer plier to get them on easily.

It’s quite common to lubricate the cylinder walls prior to installing the pistons but some ring manufacturers actually specify not to do this now. Check the instructions on your rings. I used a light coat of some slightly thicker engine oil I had lying about and wiped it off with a rag. The general guidance here is assembly lubricant shouldn’t be used on cylinder walls as it prevents the rings bedding in correctly.

Next up you need a piston ring compressor to tighten up the rings to fit into the cylinder bore. Make sure you get this tight enough because why you try to tap the piston into place if the ring is sticking out relative to the bore it’s possible to break the ring. Traditionally people drive the piston in with the wooden handle of a hammer to avoid damaging the piston face. I came up with a different solution tapping a section of silicone hose to avoid damage. Be careful to line the piston up with the bore. If you can rotate you engine stand such that the piston you are trying to put in is vertical then do so, this way you are less likely to scratch the bore with the rod as you lower it in.

Using a section of hose to install pistons

The pistons should have an indication mark on it which shows which side should point to the front of the engine. In this case this is the drilled mark but on other pistons it can be an arrow on the piston face or other mark, make sure you get this right!

Once the piston is fully in the bore go underneath and carefully guide the rod onto the crank then bolt the end of the rod back on (with its bearing inside) and do up the bolts. At this point they only need to be tight enough to stop it all falling apart so even finger tight is probably enough or a little over.

S-type V6 with new pistons installed

Now that’s quite shiny!

One question that comes up all the time is the correct socket for ARP rod bolts. after a lot of searching I’ve found according to their catalogue they do them with two common sizes of head, either a 3/8″ AF or a 7/16″ AF, both of which are of 12 point type so standard 6 point sockets will not fit. I’ve seen numerous reports online where people are saying it’s a 10mm metric. It isn’t, a 12 point 10mm will fit over the 3/8″ head but it’s a very sloppy fit you’d be only contacting the bolt on the very top of the points making the risk of stripping the head quite high. The correct socket should be a very nice slip fit.

ARP2000 con rod bolt

I’m not sure exactly which kit the rod bolts are from (or even if they are from one) because they came assembled into the rods to keep them together. ARP themselves don’t seem to to a specific kit for this engine so I would assume much like the rod bearings (which I bought from the same company) they’re actually repurposed parts from the Duratec 2.3 kits or something similar. With the forged rods they could be almost be anything just selected to fit the rod so I suggest either buying them with the rods or you can buy ARP bolts by thread and length to suit whatever you have.

Once you’ve put all your pistons in and torqued all the bolts up we move on to new head gaskets. First of check where the location sleeves are – as you look at the mating faces of the block two of the holes are larger, these are intended to have steel sleeves in which locate the head relative to the block. I installed these in the block but if you do the next few steps the same as me you might find it easier to install these into the head to make the assembly easier. The head gaskets I used were genuine Ford originals parts (actually badged FoMoCo) but sold as Jaguar parts they are specific to this version of the engine because the Ford version of the engine has different water flow routes open/blocked to make the coolant flow differently. The parts I used are as follows :

RH Head Gasket 2.5 Jaguar – C2S44649
LH Head Gasket 2.5 Jaguar – XR857984
Head Bolts (Single) – XR85387

You might want to order an ARP head stud kit at this point rather than the standard head bolts. I didn’t as at the time it was an expensive add-on (around £300) for what was supposed to be a budget project but in retrospect it might have been a safer option. I don’t have the part number for the kit noted anywhere.

S-type v6 with gasket in place

Bolt on the water pipe pipe on the top front of the block at this point. It’s much easier than doing it later! Don’t forget to install the O-ring on it and for belt and braces it might be best to add some RTV round it because fixing it if it leaks is a big job involving removing at least one head.

S-type v6 front of block water fitting

Now the next bit is something people will probably hate me for but whatever, as I’ve said before this was supposed to be a budget build with the potential for later upgrade if it ever worked. What I did was get a decompression plate cut to space out each cylinder head a little because these engines are 10.3:1 as standard and I wanted to run a not insignificant amount of boost through it. Because my plan was to have the best response I could from the engine I still wanted to keep the CR as high as I could while having a safe enough margin after a discussion with Mike at Ferriday Engineering. While I write this in 2021 his website is giving me a security warning so I don’t know what’s going on there but his email is mike@ferriday.co.uk, I can only assume (and hope) he’s still operating because he’s a very nice and knowledgeable guy. He told me that standard 1.5mm plate would give a compression ratio of 9.1:1 which should be fine. We started under the assumption the mating face would be the same as the Mondeo V6 he already had on file but that turned out to not be the case and he ended up taking my old gaskets as a template and then during a couple revisions by email I highlighted some holes that didn’t exist in the head so could safely be taken out of the decompression plate.

The decompression plate gets bonded to the face of the heads and effectively forms an extension of it, there are a few sealers used for this but the most widely regarded of them seems to be Stag Wellseal which is a form of high temperature non setting sealer resistant to fuels and oils. It is initially quite liquid but goes very sticky rather quickly and after that its quite challenging to remove. Get a suitable plastic spreader and move quickly! Despite various tales on the internet of people using the plate with two head gaskets (one each side) that’s not how these are supposed to be used generally. The idea is the face of the head is freshly refinished and so is totally smooth and flat and the decomp plate will be the same so the actual thickness of sealer will be negligible. Add to this the plate, head and block are all aluminium and so there shouldn’t be any differential thermal expansion issues. So yes it’s technically a bit of a bodge, but it’s done in the best way we can and by all accounts should hold up to my use without issue. Plus I always have the option to get custom forged pistons made later if I want to throw lots of money at it. At the end of the day this is still a cheap engine so if it does all go wrong I’ll do something else!

S-type v6 with decompression plate test fitted


Here you can see the decomp plate in position for a trial fit before being bonded onto the head. If you look carefully you’ll notice the cylinder bores in the plate aren’t round, this is because they’re not actually round in the head gasket to provide clearance for the valves.

Its probably worth highlighting here that on this engine the head bolts are under the cams so you have to assemble the head after bolting it in place. The head bolts are M10 with 6 point hex heads but with a reduced size hex. They are recessed in narrow deep bores, I used a standard 15mm deep impact socket but it was very close to not fitting so worth checking this though if yours doesn’t fit you probably found out when you took it apart!

tolerance on S-type V6 head bolts

The head bolt tightening sequence and procedure are detailed in the workshop manual but long story short I suggest getting an angle gauge for this as they’re specified as a torque + angle. These are torque to yield bolts and so you get one shot to get it right since they’re single use.

Once the head is bolted down install the cams. At this point is doesn’t matter where in their rotation they are as we will set that later but try to put all the cam retainer pack in the same positions they came out of. Make sure to coat all bearing/contact surfaces with assembly grease.

S-type v6 reassembled head

Next if you are re-using the S-Type water fitting on this engine (though I think this applies to others as well) you will want to install this now if you haven’t already, if you don’t you won’t be able to with both heads bolted on so this is your last chance!

Now just rinse and repeat for the other head…

S-type v6 head on decomp plate

Once you’ve done that slip the oil pump onto the crank and bolt it in place. Hopefully at this point you should have an engine that looks a bit like this:

S-type v6 front of engine (no cover) with both heads in place

If you’ve got to this point I suggest going and having a break. This assembly will be continued in my next post…

RX8 Project – Part 21, Canbus #6, Working Code

**EDIT**

I’ve had a couple of questions around the CAN library I use. It seems I’ve probably been using a very out of date version of the Seeed Studios library for some time now but selecting “Seeed Library” in the declarations in this code as it is set above makes it compatible with the latest (tested on Seeed CAN-BUS Shield Library 2.3.1). I suggest using the most recent version available which can be installed from the Arduino library manager or from GitHub. If you’re having problems with this code but didn’t previously with my older code it’s likely this is your problem. The code should still work with the older library with the “MCP_Library” option selected but there’s not much point.

I’ve also just noticed the code on this page seems to have some issue being copied/pasted from this page so here’s a link to the Arduino INO file:


The original post starts below:

Following a couple requests recently from people I’ve decided to post my code as it currently stands. I’ve been meaning to tidy it up and crop out all the extraneous bits but I’ve just not had time so here we go. I describe this as “working code” simply because it’s the one I’m still working on!

There’s a lot going on here so don’t expect it to be an immediate plug and play and additionally there are extra variables and things that I’ve used for testing with no purpose otherwise so don’t be surprised if you can’t work out what all of it is for. One trick bit I’ve added is if a specified digital input is tied to 0V when the Arduino powers up it starts in a listen mode where if the ECU is still connected it logs the exchange between it an the immobiliser and stores the data to the internal EEPROM memory. If you then disconnect the ECU and remove the 0V jumper it will wait for the immobiliser to try to initialise by matching its code to the one logged and sent the stored response. I don’t know if this will work correctly on all cars but it should. As per one of my previous posts you can actually just write random data in this exchange as long as the packet structure is right and it’ll work.

Similarly the code also includes the update for the ODO and trip meters based on ABS speed data so that should all work ok hopefully.

The latest section I was working on when other things started taking all my time again is to decode CAN packets from a Megasquirt ECU to control. Generally this should work but you might want to modify this to either not overwrite certain if you are getting them from elsewhere such as temperature for the cluster reading from an analogue input rather than CAN. There is an enable boolean for this (MSCAN) at the top of variable declaration but it’s defaulted to false to stop it messing with anything normally.

As ever if anyone wants to know any more about what’s going on just post a comment at the bottom. Sometimes it takes me a while to respond but I try to answer everyone.

My only other request is if you link to this page when sharing this elsewhere, mostly because I find it really interesting to see how it’s being used!

// Code modified by Jonathan Coe (www.chamberofunderstanding.co.uk) 2021 with the following:
//
// Fixed variable rollover issue with speeds over 163
// Added new definitions to allow switching to Leonardo CAN hardware
// Added new definitions to allow use of Seeed CAN library rather than MCP_CAN clones
// Added startup LED blink to confirm unit powered
// Added two short LED blinks when can chip started successfully
// Added slow LED blinking when CAN chip failed to start
// Added function to pull immobiliser challenge/response packets from existing vehicle
// Added EEPROM functions to store config data
// Added Code to check immobiliser requests against data from previous scans (retained through power cycle) and respond with stored answer
// Added Code to increment the Odometer/Trip based on live speed from ABS system
// Added Code to decode Megasquirt CAN data for engine.
//
// This code is a development from the work done by Dave Blackhurst (details below) which in itself was based 
// on earlier work from this website which in itself included research done by others before on the ID's
//

//  **************************************************************************************

// Arduino Leonardo code for replacing the PCM within a Mark 1 RX8
// This code allows you to leave the CANBUS in place, just removing the PCM
// There are plenty of ID's on the CANBUS I do not understand, and any use of this code is strictly at your own risk
//
// Features turned on, possibly working - at least they do not complain on the dashboard,
//    ABS / DSC
//    Traction Control
//    Immobiliser Deactivated
//    Power Steering Enabled
//    RPM Controllable
//    Vehicle Speed set by Wheel Sensors
//    Warning lights turned off (you can turn them on in the code if you wish)
//
//    Written by David Blackhurst, dave@blackhurst.co.uk 06/10/2019
//
//    Some parts of this code have been dissected from other sources - in researching this project I copied a lot of code to play with
//    Sorry if some of this is yours - just let me know and I will attribute it to you.
//
//    I have throttle pedal code in here to translate the output of the primary throttle signal to the range my controller required. This is unlikely to be helpful to anyone else 
//    unless you happen to have the dodgy chinese controller I have.
//
//    Again use AT YOUR OWN RISK

#include <Arduino.h>


/// ********************* Option Selection *********************
// JC 21/01/20 - Updates to select hardware version to allow support for Leonardo CAN
//  and preferred CAN library (either the standard MCP-CAN versions or SEEED version) for compiler

// Comment out to select correct hardware
#define LEO_CAN         
// #define Seeed_CAN

// Comment out to select correct CAN library
#define Seeed_Library
//#define MCP_Library


#ifdef Seeed_CAN      // Configure Pins for Seeed CAN Shield
  #define CANint          2
  #define LED             13
  #define CAN_CS          10
  #define Set_Immobiliser 1
#endif

#ifdef LEO_CAN        // Configure Pins for Leonardo CAN
  #define CANint          7
  #define LED             23
  #define CAN_CS          17
  #define Set_Immobiliser 4
#endif

#ifdef Seeed_Library
  #include "mcp2515_can.h"
  mcp2515_can CAN0(CAN_CS); // Configure CAN SPI Chip Select
#endif

#ifdef MCP_Library
  #include <mcp_can.h>
  #include <mcp_can_dfs.h>
  MCP_CAN CAN0(CAN_CS); // Configure CAN SPI Chip Select
#endif

#include <EEPROM.h>       // Load EEPROM library to save configuration data

/// ********************* End of Option Selection *********************

// Enable MS_CAN Decode
bool MSCAN = false;

// Variables for Throttle Pedal
int analogPin = A1;
int outputPin = 5;

int val = 0;
int lowPedal = 0;
int highPedal = 0;
int convertThrottle = 0;
int base = 0;
int output = 0;

// Declarations for loop delays
long lastRefreshTime = 0;
long ODORefreshTime = 0;

// Variables for PCM, Only overrideable if PCM removed from CAN
bool checkEngineMIL;
bool checkEngineBL;
byte engTemp;
byte odo;
bool oilPressure;
bool lowWaterMIL;
bool batChargeMIL;
bool oilPressureMIL;

// Variables for PCM, Only overrideable if PCM removed from CAN
int engineRPM;
int vehicleSpeed;
byte throttlePedal;

// Variables for ABS/DSC, Only overrideable if ABS/DSC removed from CAN
bool dscOff;
bool absMIL;
bool brakeFailMIL;
bool etcActiveBL;
bool etcDisabled;

// Variables for Wheel Speed 
// JC 21/01/20 - changed from int to long as variable rollover was causing speeds over 163 to go negative
long frontLeft;
long frontRight;
long rearLeft;
long rearRight;

//Variables for reading in from the CANBUS
unsigned char len = 0;
unsigned char buf[8];
unsigned long ID = 0;

//Setup Array's to store bytes to send to CAN on Various ID's
byte send201[8]  = {0, 0, 255, 255, 0, 0, 0, 255};
byte send420[7]  = {0, 0, 0, 0, 0, 0, 0};
byte send212[7]  = {0, 0, 0, 0, 0, 0, 0};

//Setup PCM Status's required to fool all other CAN devices that everything is OK, just send these out continuously
byte send203[7]  = {19,19,19,19,175,3,00};                // {19,19,19,19,175,3,19} data to do with traction control
byte send215[8]  = {2,45,2,45,2,42,6,129};                // {2,45,2,45,2,42,6,129}, experimented with {2,0,2,0,2,0,0,0} but no idea
byte send231[5]  = {15,0,255,255,0};                      // {15,0,255,255,0} or {255,0,255,255,0}
byte send240[8]  = {4,0,40,0,2,55,6,129};                 // No idea what this is for
byte send620[7]  = {0,0,0,0,0,0,4}; //needed for abs light to go off, byte 7 is different on different cars, sometimes 2,3 or 4 {0,0,0,0,16,0,4}
byte send630[8]  = {8,0,0,0,0,0,106,106}; //needed for abs light to go off, AT/MT and Wheel Size
byte send650[1]  = {0};  //Cruise Light, 0 = Off, Bit 6 = Green "Cruise", Bit 7 = Yellow "Cruise Main"


//  Declarations for testing 4B0/4B1 VSS Rx on Megasquirt.
//  180 mph
//byte send4b1[8]  = {113, 40, 113, 40, 113, 40, 113, 40};
//  180 mph rear, 160mph front
//byte send4b1[8]  = {100, 149, 100, 149, 113, 40, 113, 40};
//  100 mph
//byte send4b1[8]  = {62, 221, 62, 221, 62, 221, 62, 221};
//  10 mph
//byte send4b1[8]  = {6, 73, 6, 73, 6, 73, 6, 73};

//KCM / Immobiliser replies for Dave Blackhurst
//byte send41a[8] = {7,12,48,242,23,0,0,0};                      // Reply to 47 first  : 0x 07 0C 30 F2 17 00 00 00
//byte send41b[8] = {129,127,0,0,0,0,0,0};                       // Reply to 47 second : 0x 81 7F

// Immobiliser replies for Jon Coe
byte send41a[8] = {7,120,192,226,94,0,0,0};                      // Reply to 47 first  : 0x 07 78 C0 E2 5E 00 00 00 
                                                                 // Bytes 0 is the same, bytes 3 & 4 dont seem to matter, 5,6,7 are zero
byte send41b[8] = {129,127,0,0,0,0,0,0};                         // Reply to 47 second : 0x 81 7F

// Immobiliser Blank
byte response_a[8] = {7,0,0,0,0,0,0,0};
byte response_b[8] = {129,127,0,0,0,0,0,0};                      // This always seems to be this value (0x 81 7F) so used as default
byte request_a[8] = {6,127,0,0,0,0,0,0};
byte request_b[8] = {8,0,0,0,1,0,0,0};

// Time delay for Odo
long ODOus = 4500000;                                           // Set to max 4,500,000 to keep dash MIL warning lights awake at 0 speed

void printhex( byte [], int );                                  // JC - Prototype for function to send byte array to serial monitor as HEX pairs
                                                                // Actual function is later in code - prototypes not technically required in Arduino IDE

void setup() {
  Serial.begin(115200);
  Serial.println("Start Setup");

  // Give a Wakeup Blink - Disabled to speed up boot
  /*
  pinMode(LED, OUTPUT);
  digitalWrite(LED, HIGH);
  delay(500);
  digitalWrite(LED, LOW);
  */
    
  pinMode(CANint, INPUT);                           // Set CAN interrupt pin as input
  pinMode(Set_Immobiliser, INPUT_PULLUP);           // Configure Button to setup immobiliser as input with pullup enabled
  
  if (CAN0.begin(CAN_500KBPS) == CAN_OK) {          // Connect to CAN chip
    Serial.println("Found High Speed CAN");
      
      // JC 21/01/20 - Added two short blinks of LED to identify CAN chip started
      // Disabled to improve speed
      /*
      digitalWrite(LED, HIGH);
      delay(200);
      digitalWrite(LED, LOW);
      delay(100);
      digitalWrite(LED, HIGH);
      delay(200);
      digitalWrite(LED, LOW);
      */
      
  } else {
    Serial.println("Failed to find High Speed CAN");
    while (1) {
      Serial.println("Loop Forever");
      
      // JC 21/01/20 - Added long blinking of LED to identify CAN chip fault
      digitalWrite(LED, HIGH);
      delay(1000);
      digitalWrite(LED, LOW);
      delay(1000);
      
    }
  }
  
  // Populates CAN buffers with meaningful initial data
  // for live use this will prevent dash lights defaulting to ON etc
  
  setDefaults(); // JC - Sets up some default values to fill CAN registers with sensible data in case nothing else is written later


//******* JC 25/01/21 Immobiliser Compatibility Mods *******

//    Check if First Run Immobiliser Code Scanning is Enabled
    if (digitalRead(Set_Immobiliser) == 0){
        immobiliserCodeSet();
    }

//    Check if stored values exist in EEPROM and if not setup from program defaults

    if (EEPROM.read(0) == 0 && EEPROM.read(1) == 0){
      writeByteArrayIntoEEPROM(0, request_a, 16);
      writeByteArrayIntoEEPROM(16, response_a, 16);
      writeByteArrayIntoEEPROM(32, request_b, 16);
    }

//    Pull Immobiliser codes from EEPROM 

      //delay(5000); delay to allow serial startup - disabled for speed
      readByteArrayFromEEPROM(0, request_a, 8);
      Serial.print("Request A from EEPROM : ");
      printhex(request_a,8);
      
      readByteArrayFromEEPROM(16, response_a, 8);
      Serial.print("Response A from EEPROM : ");
      printhex(response_a,8);
      
      readByteArrayFromEEPROM(32, request_b, 8); 
      Serial.print("Request B from EEPROM : ");
      printhex(request_b,8);
}

// ******* JC 25/01/21 Code to write Hex array to Serial *******

void printhex(byte b[], int sizeOfArray){
  Serial.print("0x ");
    for (int i=0;i<sizeOfArray;i++){
      if(b[i]<10){
        Serial.print("0");
      }
      Serial.print(b[i],HEX);
      Serial.print(" ");
      }
  Serial.println("");
}

//////////////////////////////////////////////////////////////////////////////////////
//
// ******* JC 25/01/21 Code to Scan Immobiliser codes from a working system ******* 
//          Set_Immobiliser is a digital input which is configured as 
//          INPUT_PULLUP and tied to ground to enable this mode.
//
//////////////////////////////////////////////////////////////////////////////////////


void immobiliserCodeSet(){
  
      digitalWrite(LED, HIGH);
      delay(3000);
      digitalWrite(LED, LOW);
      Serial.println("Immobiliser Code Read Mode");
    
    //immobiliserCodeSet;
      int immSet1 = 0;
      do
      if(CAN_MSGAVAIL == CAN0.checkReceive()) { // Check to see whether data is read
          CAN0.readMsgBufID(&ID, &len, buf);    // Read data
          
          if(ID == 0x47) { //71 Dec is 47 Hex - Keyless Chat
              if(buf[0] == 0x6 && buf[1] == 0x7F){
                  memcpy(request_a, buf, 8);
                  immSet1++;
                  Serial.print("Request 1 Found! - ");
                  printhex(buf,8);
                  
              }
              if (buf[0] == 0x8){
                  memcpy(request_b, buf, 8);
                  immSet1++;
                  Serial.print("Request 2 Found! - ");
                  printhex(buf,8);
              }
          }
               
          if(ID == 0x41) {
              if(buf[0] == 0x07){
                memcpy(response_a, buf, 8);
                immSet1++;
                Serial.print("Response 1 Found! - ");
                printhex(buf,8);
              }
          }
      Serial.print("CAN Data Received - Found ");
      Serial.println(immSet1);
   } while (immSet1 < 3);
   
      digitalWrite(LED, HIGH);
      delay(200);
      digitalWrite(LED, LOW);
      delay(100);
      digitalWrite(LED, HIGH);
      delay(200);
      digitalWrite(LED, LOW);
      delay(100);
      digitalWrite(LED, HIGH);
      delay(200);
      digitalWrite(LED, LOW);

      Serial.print("All Codes Found - Saving...");
      
      writeByteArrayIntoEEPROM(0, request_a, 16);
      writeByteArrayIntoEEPROM(16, response_a, 16);
      writeByteArrayIntoEEPROM(32, request_b, 16);
      
      Serial.println("Ok!");
      Serial.println("");
      Serial.println(" Turn off ignition, reset to normal mode and reboot");
 
      while (digitalRead(Set_Immobiliser) == 0);            //    Halt here as long as input held low
  }


// ******* End of Immobiliser Scan Routine *******


void setDefaults() {
  Serial.println("Setup Started");
  // StatusMIL
  engTemp         = 145; //Roughly in the middle
  odo             = 0;
  oilPressure     = 1;   // For the gauge, 1 is OK, 0 is L
  checkEngineMIL  = 0;
  checkEngineBL   = 0;
  lowWaterMIL     = 0;
  batChargeMIL    = 0;
  oilPressureMIL  = 0;
  
  // StatusPCM
  engineRPM       = 1000;   // RPM
  vehicleSpeed    = 0;      // km/h + 10000
  throttlePedal   = 0;      // %
  
  // StatusDSC
  dscOff          = 0;
  absMIL          = 0;
  etcActiveBL     = 0;
  etcDisabled     = 0;
  brakeFailMIL    = 0;

  /*
  Serial.println("Start wait to ensure Throttle Pedal is on");
  delay(500);
  lowPedal = 341;  //analogRead(analogPin) - 40;  Temporary fixed value //read the throttle pedal, should be around 1.7v minus 40 to ensure no small throttle inputs
  highPedal = 803; //4v
  
  // Voltage to read from Pedal 1.64v - 4.04v
  // Going to use a safe range 1.7v to 4v
  // Low of 1.7v has been read above as can fluctuate
  // 1.7v = INT 341
  // 4v = INT 803
  // (highPedal - lowPedal) = RANGE FROM RX8 PEDAL
  // out for 1024 (5v max), controller wants 4.5v max = 920 (adding 40 to help stabilise)
  
  convertThrottle = 960 / (highPedal - lowPedal);
  Serial.print("Low Pedal ");
  Serial.print(lowPedal);
  Serial.print(", High Pedal ");
  Serial.println(highPedal);
  Serial.println("Setup Complete");

  */
}

//    ********** JC 25/01/21 - Add Functionality to read/write arrays from EEPROM **********
//    Taken from www.roboticsbackend.com and expanded for byte arrays

void writeIntArrayIntoEEPROM(int address, int numbers[], int arraySize)
{
  int addressIndex = address;
  for (int i = 0; i < arraySize; i++) 
  {
    EEPROM.write(addressIndex, numbers[i] >> 8);
    EEPROM.write(addressIndex + 1, numbers[i] & 0xFF);
    addressIndex += 2;
  }
}
void readIntArrayFromEEPROM(int address, int numbers[], int arraySize)
{
  int addressIndex = address;
  for (int i = 0; i < arraySize; i++)
  {
    numbers[i] = (EEPROM.read(addressIndex) << 8) + EEPROM.read(addressIndex + 1);
    addressIndex += 2;
  }
}

void writeByteArrayIntoEEPROM(int address, byte numbers[], int arraySize)
{
  int addressIndex = address;
  for (int i = 0; i < arraySize; i++) 
  {
    EEPROM.write(addressIndex, numbers[i] >> 8);
    EEPROM.write(addressIndex + 1, numbers[i] & 0xFF);
    addressIndex += 2;
  }
}
void readByteArrayFromEEPROM(int address, byte numbers[], int arraySize)
{
  int addressIndex = address;
  for (int i = 0; i < arraySize; i++)
  {
    numbers[i] = (EEPROM.read(addressIndex) << 8) + EEPROM.read(addressIndex + 1);
    addressIndex += 2;
  }
}

// ********** End of EEPROM Section **********


void updateMIL() {
  send420[0] = engTemp;
  //send420[1] = odo;     
  send420[4] = oilPressure;

  if (checkEngineMIL == 1) {
    send420[5] = send420[5] | 0b01000000;
  } else {
    send420[5] = send420[5] & 0b10111111;
  }

  if (checkEngineBL == 1) {
    send420[5] = send420[5] | 0b10000000;
  } else {
    send420[5] = send420[5] & 0b01111111;
  }

  if (lowWaterMIL == 1) {
    send420[6] = send420[6] | 0b00000010;
  } else {
    send420[6] = send420[6] & 0b11111101;
  }

  if (batChargeMIL == 1) {
    send420[6] = send420[6] | 0b01000000;
  } else {
    send420[6] = send420[6] & 0b10111111;
  }

  if (oilPressureMIL == 1) {
    send420[6] = send420[6] | 0b10000000;
  } else {
    send420[6] = send420[6] & 0b01111111;
  }
}

void updatePCM() {
  int tempEngineRPM = engineRPM * 3.85;
  int tempVehicleSpeed = (vehicleSpeed * 100) + 10000;
  
  send201[0] = highByte(tempEngineRPM);       
  send201[1] = lowByte(tempEngineRPM);        

  send201[4] = highByte(tempVehicleSpeed);    
  send201[5] = lowByte(tempVehicleSpeed);     

  send201[6] = (200 / 100) * throttlePedal;   //Pedal information is in 0.5% increments 
}

void updateDSC() {
  if (dscOff == 1) {
    send212[3] = send212[3] | 0b00000100;
  } else {
    send212[3] = send212[3] & 0b01111011;
  }

  if (absMIL == 1) {
    send212[4] = send212[4] | 0b00001000;
  } else {
    send212[4] = send212[4] & 0b11110111;
  }

  if (brakeFailMIL == 1) {
    send212[4] = send212[4] | 0b01000000;
  } else {
    send212[4] = send212[4] & 0b10111111;
  }

  if (etcActiveBL == 1) {
    send212[5] = send212[5] | 0b00100000;
  } else {
    send212[5] = send212[5] & 0b11011111;
  }

  if (etcDisabled == 1) {
    send212[5] = send212[5] | 0b00010000;
  } else {
    send212[5] = send212[5] & 0b11101111;
  }
}


long calcMicrosecODO(float speedKMH){
  long uS;
  float freq;
  float speedMPH;
   
  Serial.print("Speed = ");
  Serial.print(speedKMH/100);
  Serial.println(" km/h");
  speedMPH = speedKMH / 160.934;
  // Required frequency for timer 1 ISR
  //  1.15 is 4140 (Pulse per Mile) / 3600 (1hr in seconds)
  //  0.7146 is 2572.5 (pulse per KM) / 3600
  freq = speedMPH * 1.15; 
  Serial.print("Freq = ");
  Serial.print(freq);
  Serial.println(" Hz");
  uS = 1000000/freq;
  if(uS < 4500000 && uS > 0){
    return (uS);}
  else {
    return (4500000);
  }
  
 
}

void sendOnClock(){
  // Do not increment ODO byte when step is = 4.5s
  // slower than this updateMIL must still be called so 
  // warning lights don't turn on but speed may be zero!
  if ( ODOus < 4500000){
    send420[1]++;   
  }
  updateMIL();
  CAN0.sendMsgBuf(0x420, 0, 7, send420);
}

void sendOnTenth() {
  //PCM Status's to mimic the PCM being there, these may be different for different cars, and not all are always required, better safe and include them all.
  CAN0.sendMsgBuf(0x203, 0, 7, send203);
  CAN0.sendMsgBuf(0x215, 0, 8, send215);
  CAN0.sendMsgBuf(0x231, 0, 8, send231);
  CAN0.sendMsgBuf(0x240, 0, 8, send240);
  CAN0.sendMsgBuf(0x620, 0, 7, send620);
  CAN0.sendMsgBuf(0x630, 0, 8, send630);
  CAN0.sendMsgBuf(0x650, 0, 1, send650);
  
  updateMIL();
  CAN0.sendMsgBuf(0x420, 0, 7, send420);    //Moved to sendOnClock to update at timer ISR frequency for ODO

  updatePCM();
  CAN0.sendMsgBuf(0x201, 0, 8, send201);

  // Send to Megasquirt VSS Sim - For testing Megasquirt ABS decode
  //  CAN0.sendMsgBuf(0x4b1, 0, 8, send4b1);
  
  /* Add this section back in if you want to take control of ABS / DSC Lights.
  updateDSC();
  CAN0.sendMsgBuf(0x212, 0, 7, send212);
  */
}

void loop() {
  //Send information on the CanBus every 100ms to avoid spamming the system.
  if(micros() - lastRefreshTime >= 100000) {
		lastRefreshTime += 100000;
    sendOnTenth();
	}
  // Call function to updateMIL on variable timebase
   if(micros() - ODORefreshTime >= ODOus) {
   ODORefreshTime += ODOus;
    sendOnClock();
  }
  
  //Read the CAN and Respond if necessary or use data
  if(CAN_MSGAVAIL == CAN0.checkReceive()) { // Check to see whether data is read
    CAN0.readMsgBufID(&ID, &len, buf);    // Read data

    //digitalWrite(LED, HIGH);
    //delay(1);
    //digitalWrite(LED, LOW);
    
    if(ID == 0x212) {           // 0x212 = 530
      for(int i = 0; i<len; i++) { // Output 8 Bytes of data in Dec
        Serial.print(buf[i]);
        Serial.print("\t");
      }
      
     //Serial.print(time);   // Timestamp
      Serial.println("");
     //Serial.println(line); // Line Number
    }
    
    //Keyless Control Module and Immobiliser want to have a chat with the PCM, this deals with the conversation
    if(ID == 0x47) { //71 Dec is 47 Hex - Keyless Chat
      /*
      //***** Fixed Coding for Dave Blackhurst's Car *******
      if(buf[1] == 127 && buf[2] == 2) {                        // 0x 06 7F 02 00 00 00 00 00
        CAN0.sendMsgBuf(0x041, 0, 8, send41a);                  // 0x041 = 65
      }
      if(buf[1] == 92 && buf[2] == 244) {                       // 0x 08 5C F4 65 22 01 00 00
        CAN0.sendMsgBuf(0x041, 0, 8, send41b);                  // 0x 81 7F 00 00 00 00 00 00
      }

      //***** Fixed Coding for Jon Coe's Car *******
      // Some experimentation showed that on the initial request for my car byte 2 was a 01 not a 02
      // however all codes so far begin 06 7F for either car so this was used.
      // Similarly in the second message from the immobiliser bytes 1-4 change but byte 5 is always 01 on either vehicle
      // The negotiation happens during every start but the codes only seem to cycle whenever the battery 
      // is disconnected  

      if(buf[0] == 0x6 && buf[1] == 0x7F ) {                      // 0x 06 7F 01 00 00 00 00 32
        //printhex(buf,8);                                        // Transmit out received request on debug serial port - breaks timings on vehicle.
        CAN0.sendMsgBuf(0x041, 0, 8, send41a);                    // 0x041 = 65
      }
      if(buf[0] == 0x8 && buf[5] == 0x1 ) {                        // 0x 08 94 29 BC 91 01 00 32
        CAN0.sendMsgBuf(0x041, 0, 8, send41b);                     // 0x 81 7F 00 00 00 00 00 00  
      }
   */
     
//    ********** JC 25/01/21 - Add Functionality to use immobiliser responses stored in EEPROM **********

      //if(memcmp(buf, request_a, 8) == 0){                          // Check first request matches stored pattern (difference = 0)
      if(buf[0] == request_a[0] && buf[1] == request_a[1] ) {
        CAN0.sendMsgBuf(0x041, 0, 8, response_a);                  // Send stored response to 0x041 = 65
      }
      //if(memcmp(buf, request_b, 8) == 0) {                         // Check second request starts with "08"
      if(buf[0] == request_b[0] && buf[5] == request_b[5] ) {
        CAN0.sendMsgBuf(0x041, 0, 8, response_b);                  // Send second response - seems to always be 0x 81 7F 00 00 00 00 00 00
      }

      
    }


    
    //Read wheel speeds to update Dash
    //if(ID == 1200) { //1201 Dec is 4b1 Hex - Wheel Speeds ----> check this address. Wheel speeds for dash 4B1 -> 201

    if(ID == 0x4B0) {
      frontLeft = (buf[0] * 256) + buf[1] - 10000;
      frontRight = (buf[2] * 256) + buf[3] - 10000;
      rearLeft = (buf[4] * 256) + buf[5] - 10000;
      rearRight = (buf[6] * 256) + buf[7] - 10000;
      
      //Going to check front wheel speeds for any issues, ignoring the rears due to problems created by wheelspin
      if (frontLeft - frontRight > 500 || frontLeft - frontRight < -500) { //more than 5kph difference in wheel speed
        checkEngineMIL = 1; //light up engine warning light and set speed to zero
        vehicleSpeed = 0;
      } else {
        vehicleSpeed = (((frontLeft + frontRight) / 2) / 100); //Get average of front two wheels.
      }
      //Update timer count value with live speed for ODO
      //OCR1A = calcTimer1Count((frontLeft + frontRight) / 2);
      // delay in MS for ODO
      ODOus = calcMicrosecODO((frontLeft + frontRight) / 2);
      Serial.print("ODO Step : ");
      Serial.print(ODOus);
      Serial.println("us");
      // Dump speed to serial for debug - this is just a cropped int.
      //Serial.println(vehicleSpeed);
      
    }

    
    // Decode for Megasquirt - JC
    /* This section matches the various fields used by Megasquirt
     *  CAN send to decode for the RX8 cluster. Not all fields are
     *  converted from the Megasquirt as most are not required.
     *  The CAN start address for Megasquirt is the default 0x5F2 (1520)
     */
    
  if(MSCAN == true){
    if(ID == 0x5F0) { //1520 Dec is 5F0 Hex - Megasquirt Block 0
      // Block 0 - Seconds, PW2, PW2, RPM, 2 bytes each

      engineRPM = (buf[6] * 256) + buf[7];
    }
       
    if(ID == 0x5F2) { //1522 Dec is 5F2 Hex - Megasquirt Block 2
      // Block 2 - Baro(kPa*10), MAP(kPa*10), MAT(degF*10), CLT(degF*10)
      // Edit map to set "normal" range on cluster (in degF) where needle stays centred
      // outside of the normal range needle will rapidly increase or decrease.

      int normMin = 140;    //  60 degC
      int normMax = 220;    // 104 degC

      engTemp = map((buf[6] * 256) + buf[7],normMin*10,normMax*10,110,150);
    }
    
    if(ID == 0x5F3) { //1523 Dec is 5F3 Hex - Megasquirt Block 3
      // Block 3 - TPS (%*10), Batt (V*10), EGO1(Depricated on MS3), 
      // EGO2(Depricated on MS3), 2 bytes each

      throttlePedal = ((buf[0] * 256) + buf[1]) / 10;
    }

    if(ID == 0x624) { //1572 Dec is 624 Hex - Megasquirt Block 52
      // Block 54 - CANin_1(?),CANout_1, CANout_2, 
      // Knock_ret (deg*10, 1 byte), Fuel flow (cc/min*10, 2 byte), 
      // Fuel Consumption(l/km, 2 byte)
      // First 3 bytes appear wrong in the Megasquirt CAN documentation
      // testing shows byte 1 is CANout_1 not CANin_2
      // byte 2 is CANout_2

      byte CANout_1   = buf[1];
      byte CANout_2   = buf[2];
     
      // Read Check engine light from Megasquirt
      checkEngineMIL  = bitRead(CANout_1,0);

      // Blink traction control light - Disabled due to ABS unit
      //etcActiveBL     = bitRead(CANout_1,1);    

      // Read Oil Pressure light from Megasquirt
      oilPressureMIL     = bitRead(CANout_1,2);
      
      // Also set cluster "gauge" to match warning light
      if(oilPressureMIL == 1){
        oilPressure = 0;
      }
      else{
        oilPressure = 1;
      }
            
    }

   }  // Close MSCAN mode check
   
  } // Close CAN message receive processing

  /*  Reading throttle sensor for electric drive control - 
  
  //Throttle Pedal Work
  val = analogRead(analogPin);  // read the input pin
  
  //See Set Defaults Method for Calculations
  if (val < 110 || val > 960) { // If input is less than 0.5v or higher than 4.5v something is wrong so NO THROTTLE
    val = 0;
  }
  base = val - lowPedal;
  if (base < 0) {
    base = 0;
  }
  output = base * convertThrottle;
  if (output > 960) {
    output = 960;
  }
  throttlePedal = (100 / 960) * output;
  analogWrite(outputPin,(output/4));

  */
}

Also for completeness here is the alternate section which handles the immobiliser with random data in response_a but otherwise is much the same as the other version. The immobiliser module hashes this with the RFID code off the key and passes it back and request_b just gives the immobiliser the OK. The only issue you might get is if other versions of the car use a different packet format because both my methods rely on matching the packet structure to pick it out of the data stream.

//******* JC 25/01/21 Immobiliser Compatibility Mods *******

//    Check if First Run Immobiliser Code Scanning is Enabled
    if (digitalRead(Set_Immobiliser) == 0){
        immobiliserCodeSet();
    }

//    Check if stored values exist in EEPROM and if not setup from program defaults

    if (EEPROM.read(0) == 0 && EEPROM.read(1) == 0){
      writeByteArrayIntoEEPROM(0, request_a, 16);
      writeByteArrayIntoEEPROM(16, response_a, 16);
      writeByteArrayIntoEEPROM(32, request_b, 16);
    }

//    Pull Immobiliser codes from EEPROM 

      //delay(5000);                                          // Delay used for serial diagnostics to give the monitor time to connect before sending data
      readByteArrayFromEEPROM(0, request_a, 8);
      Serial.print("Request A from EEPROM : ");
      printhex(request_a,8);
      
      //readByteArrayFromEEPROM(16, response_a, 8);
      response_a[1] = random(255);
      response_a[2] = random(255);
      response_a[3] = random(255);
      response_a[4] = random(255);
      Serial.print("Response A - Random Filler : ");
      printhex(response_a,8);
      
      readByteArrayFromEEPROM(32, request_b, 8); 
      Serial.print("Request B from EEPROM : ");
      printhex(request_b,8);
}

The only other thing you need to do for the random data is generate some random data, I did this by using a floating input as a random seed. The data doesn’t actually need to be random and you can just set fixed values for response_a bytes 1-4 or whatever as long as the rest of the response structure is correct.

void setup() {

  //pinMode(A1, INPUT);
  randomSeed(analogRead(A1));                                   // A5 Not actually connected on Leonardo CAN - Used as seed value

As ever, your mileage may vary and you get no guarantee from me!

RX8 Project – Part 20, Canbus #5, RX8 Odometer

So again for this post I’m going to concentrate on one specific aspect of the RX8 CANbus system which a few people have asked me about and that’s making the ODOmeter on the cluster tick up correctly without the factory PCM (ECU) being fitted to the car. This also allows the trip A and B meters to work correctly as they are all linked to the same register.

I’ve mentioned in previously to people that the odo requires a state change to update and I think this is why people haven’t been able to identify what controls it – simply writing a value doesn’t do anything. It is controlled via byte 1 of what I defined as statusMIL, this is CAN ID 0x420 which is the same ID which also controls things like the temperature and oil pressure displays on the dash.

I originally identified how this was controlled during some of my early reverse engineering work on the cluster when I was just sending blocks of values (initially cycling between 00 and FF for all bytes in the array) to CAN ID’s to see if I could make anything happen and I noticed at some stage the trip had increased. Clearly this meant one of the ID’s was doing something and because the same ID also controls various warning lights they also flashed on and off at the same time as the odo incremented which made the identification of the ID rather simple.

Next up I edited the program to count each byte in the CAN data up in order, so for example start with an array with only 00 in each byte then starting at byte 0 increment the value by 1, send the whole array, wait briefly and increment again and so on. When the top value of FF is reached set it back to 00 step on to byte 1 and so on. This allowed me to isolate the specific byte that was controlling the odo changes. Anyway the story goes on and after a lot of trial and error with values and timings I got to this bit of fairly horrible test code:

#include <mcp_can.h>
//#include <mcp_can_dfs.h>      //Declaration for Standard Can
#include "mcp2515_can.h"        //Declaration for Seeedstudio Can Library

#define CANint 7  // Normally 2
#define LED2 23   // Normally 8
#define LED3 0    // Normally 7

#define NOP __asm__ ("nop\n\t")
const int SPI_CS_PIN = 17;
mcp2515_can CAN0(SPI_CS_PIN);   // Declaration for Seeedstudio library
//MCP_CAN CAN0(SPI_CS_PIN);     // Set CS to pin 10 - Older Library version


void setup() {
  Serial.begin(115200);

    delay(1000);    //delay to allow for monitor
    Serial.println("Init…");
    Serial.println("Setup pins");
    pinMode(LED2, OUTPUT);
    pinMode(SS, OUTPUT);
    pinMode(LED3, OUTPUT);
    pinMode(CANint, INPUT);

    Serial.println("CAN init:");
    
    if (CAN0.begin(CAN_500KBPS) == CAN_OK) 
    {
        Serial.println("OK!"); 
    } 
    else 
    {
        Serial.println("fail :-(");
        while (1) 
        {
            Serial.println("Zzz… ");
            delay(1000);
        }
     }

Serial.println("Good to go!");
}

unsigned char statusPCM[8]  = {125,0,0,0,156,0,0,0};                            // Write to 201
unsigned char statusMIL[8]  = {140,0,0,0,1,0,0,0};                              // Write to 420

void loop() 
{
    
    unsigned int i=0;
    unsigned int j=0;
    unsigned int k=0;
    float mile=0;
    unsigned char num;
    
    
   
    //Warning Lights - 0=Error, 1=OK

        for(k=0;k<200;k++)
        {
          
          for(i=0;i<=206;i++)
          {
          
          statusPCM[0] = 50;           //RPM  Value*67 gives 8500 RPM Reading Redline is 127
          statusPCM[1] = 0;           
          statusPCM[2] = 0;
          statusPCM[3] = 0;
          statusPCM[4] = 93;           //Speed  Value=0.63*(Speed)+38.5
          statusPCM[5] = 0;
          statusPCM[6] = 0;
          statusPCM[7] = 0;

          statusMIL[0] = 145;         // Temp 91-0%; 96-10%; 107-25%; 152=50%; 158-75%; 164=100%    
          statusMIL[1] = i;           // Odo / Trip     
          statusMIL[2] = 0;
          statusMIL[3] = 0;
          statusMIL[4] = 1;           // Oil Pressure (0=Off, >=1 is OK)         
          statusMIL[5] = 0;           // Check Engine Light
          statusMIL[6] = 0;           // Battery Charge Light
          statusMIL[7] = 0;
          
            CAN0.sendMsgBuf(0x420, 0, 8, statusMIL);
            delay(20);
            CAN0.sendMsgBuf(0x201, 0, 8, statusPCM);
            delay(20);
          }
          
        Serial.println("Miles : ");
          mile=(k+1)*0.05;
          Serial.println(mile);
      
        } 
while(1){}
}

Basically much of it is the includes and setup which is common on all CAN code but the more interesting bit is the main loop. In essence it just uses two nested loops to count up to a certain value and transmit the new value each time. All the other CAN is just static values to turn off warning lights on the cluster – the the ID 201 isn’t actually required here but it stops extra things blinking at you! Additionally ignore the comments on the statusPCM, while these sort of work I realised later that the speed and RPM use 2 byte blocks rather than a single byte to display all values.

So in the code there are 2 loops, the first is counter “i” which in this counts up to 206 (which is 207 steps including 0) and the second outer loop “k” which counts up to 200 and for each increment of “i” the new byte value is sent over CAN. Now through experimenting with various values for counters in earlier versions of this code I’d come up with the values you see here which seem to be consistently accurate for me. Basically 207 changes of the byte sent to the dash = 0.05 Miles counted so the extra loop “k” multiplies this up to a useful mileage change we can measure on the cluster. every time “k” increments it also sends and update to the serial monitor so we can keep track of it. We can see that 200 loops of 0.05 miles should result in a count of 10 miles and that’s exactly what we get on the trip meter.

The very last line is one you don’t see in many Arduino programs, “while(1){};” makes the program hang at this step. Basically what it’s doing is a normal while loop except using a static 1 as the condition makes it always true but there’s no code in the curly brackets so it will just sit there endlessly doing nothing rather than starting the main loop again as it would normally. This was simply so I could leave the code running to completion (at this stage I was sending CAN data with much wider time delays than needed to make sure none got missed) without it looping back round without me noticing.

So now we can count it up which is interesting but not especially useful as it is but since my car didn’t move I left it here for a long time with the plan to sort it out later. Anyway following my recent posts resurrecting this project there’s been some interest in how to sort it out so I started working on it within my already butchered about derivative of Dave Blackhursts code, which in itself was a further development of much of my earlier work. I’m aiming to post a version of my finalised code for the complete system once I’m happy I’ve got all the features I want but this should give you enough info to understand how this bit works.

Updates by Timer1 Interrupt

First off lets just say this didn’t work quite right for this task so I moved away from timer interrupts but it does include some interesting bits of timer manipulation so I’m including it anyway.

The best idea I had initially was to use the data from the ABS system which gave an accurate speed for all the wheels individually but the problem was I would need to vary the frequency at which the ODO byte was changed such that the ODO rate matched the speed. My first idea here was to use a hardware timer on the Arduino and use a hardware interrupt to increment and send the CAN packet. This would require the timer count value to be calculated on the fly from the ABS data. First I built and Excel spreadsheet which firstly calculated the frequency we need to update and send CAN data at to give the right ODO reading for a given wheel speed.

excel mph to freq conversion

So all this does is some basic calculation,

Pulses per mile * mph = pulses per hour

pulses per hour / 3600 = Hz (updates per second)

1000 / Hz = Millisecond interval per update (used to cross reference from the CAN logger)

From this we can fiddle about with values to see what gives enough range on timer1 (because it’s 16 bit which gives us a much wider range of intervals, timer 0 is only 8 bit and usually used elsewhere in the Arduino IDE) to make this work, or for that matter if it even is possible without dynamically changing the pre-scaler value.

Timer 1 is controlled primarily by register OCR1A which is the value it counts to before the timer resets so we need to work out the correct value for the register to do what we want. Rearranging the standard equation gives us this:

OCR1A = (( Clock_Freq / Required_Freq ) / Prescale ) – 1

Now because the count is a 16 bit integer there will be some error created by rounding on the count value so I added another section which updates to show the error vs the required value.

Excel OCR1A count value calculation

The top two lines are just the standard calculation the middle two lines are where the clock and prescaler values are entered and the OCR1A value is calculated. The 230 and 1.15 come from the previous step automatically. The bottom two lines then return the degree of error resulting.

It looks like we can cover the range we want with a prescaler of 1024 however as the speed goes down below 1mph the period between CAN sends becomes excessively long and beyond the range of the timer (max value of OCR1A is 65535), the limit is approximately 0.21 mph which seems reasonable. This means only 1 update over CAN every 4.1s which it turns out is about the limit anyway because if you make the updates longer than about 4.5s for this ID all the warning lights turn on again between updates but we can handle this case seperately.

Now to get the timer working we need to set up a few other things

TCCR1B options

We want to use CTC mode resets the count value at the top of the count to make it loop round constantly so WGM12(CTC1) in register TCCR1B should be set to 1.

TCCR1B options

Next to get the prescaler value of 1024 we need we set CS12 and CS10 to 1. Lastly OCIE1A in TIMSK1 needs to be set to 1 to trigger the interrupt every time the count reaches the count value set bring it all together and we get something like this :

void setupTimer1() {
  noInterrupts();
  // Clear registers
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;

  // Freq (Hz) (Clock (Hz) /((OCR1A + 1) * Prescaler))
  OCR1A = 64700;
  // Set CTC mode - trigger interrupt on TOP
  TCCR1B |= (1 << WGM12);
  // Prescaler 1024
  TCCR1B |= (1 << CS12) | (1 << CS10);
  // Output Compare Match A Interrupt Enable
  TIMSK1 |= (1 << OCIE1A);
  // Enable Interrupts
  interrupts();
}

// Send update to cluster on interrupt
ISR(TIMER1_COMPA_vect) {
    // Increment ODO byte each time function is called
  // this makes ODO update based on ISR frequency call.
  // Check speed is > 0.21 mph ( (left+right/2) * 0.337962 * 100
  if ( (frontLeft + frontRight) >= 68){
    send420[1]++;   
  }
   
  CAN0.sendMsgBuf(0x420, 0, 7, send420);
 
  return;
}

Next up to convert from a speed to a timer count value we need to do the same thing on the Arduino that we did in Excel earlier so here’s a quick function which just calculates count.

int calcTimer1Count(float speedKMH){
  float freq;
  int count;
  float speedMPH;
  
  //Serial.print("speed = ");
  //Serial.println(speedKMH);
  speedMPH = speedKMH / 160.934;
  // Required frequency for timer 1 ISR
  //  1.15 is 4140 (Pulse per Mile) / 3600 (1hr in seconds)
  //  0.7146 is 2572.5 (pulse per KM) / 3600
  freq = speedMPH * 1.15; 
  //Serial.print("freq = ");
  //Serial.println(freq);
  // Required OCR1A value based on 16Mhz clock, works best with 1024x prescaler giving minimum 0.21mph
  count = ((16000000 / freq) / 1024) - 1;
  //Serial.print("count = ");
  //Serial.println(count);
  return count;
}

The input here is km/h because that’s what the ABS values are given in so we might as well convert that here as well.

Now in the main loop we’re scanning for incoming data on CAN and checking what the ID is already for the bits to manage the immobiliser exchange and handily in this Dave Blackhurst has already added code to read the ABS data which we know is coming in on ID 4B0 in two byte blocks so we can just update OCR1A with a function call to our new calculation function with the average of the front wheel speeds whenever new ABS data is received.

    if(ID == 0x4B0) {
      frontLeft = (buf[0] * 256) + buf[1] - 10000;
      frontRight = (buf[2] * 256) + buf[3] - 10000;
      rearLeft = (buf[4] * 256) + buf[5] - 10000;
      rearRight = (buf[6] * 256) + buf[7] - 10000;
      
      //Going to check front wheel speeds for any issues, ignoring the rears due to problems created by wheelspin
      if (frontLeft - frontRight > 500 || frontLeft - frontRight < -500) { //more than 5kph difference in wheel speed
        checkEngineMIL = 1; //light up engine warning light and set speed to zero
        vehicleSpeed = 0;
      } else {
        vehicleSpeed = (((frontLeft + frontRight) / 2) / 100); //Get average of front two wheels.
      }
      //Update timer count value with live speed for ODO
      OCR1A = calcTimer1Count((frontLeft + frontRight) / 2);
      
      // Dump speed to serial for debug - this is just a cropped int.
      //Serial.println(vehicleSpeed);
      
    }

Now when I ran the whole program I hit a minor issue, it worked initially and the timer speed changed correctly but after a small number of can packets the Arduino stopped sending any other CAN data. I don’t know what caused it – best guess is when the interrupt was called it was blocking something and causing a buffer overrun somewhere else making the Arduino crash but either way I felt a different approach would probably be quicker than solving that so I moved on.

Updates by Delay

So the alternate approach relies on the main loop running quite quickly and simply counts how much time has elapsed since a function was last called and if over the set value calls the function. This basically allows functions to be called in the loop without needing to slow the loop itself down. The down side here vs interrupts is the accuracy of this is entirely based on the speed of the main loop so if the loop slows down the events will move about in time. Also obviously if the loop is only doing something like 10ms per iteration you will never get something to go faster than that!

void loop() {
  //Send information on the CanBus every 100ms to avoid spamming the system.
  if(millis() - lastRefreshTime >= 100) {
    lastRefreshTime += 100;
    sendOnTenth();
  }

So this is the initial version Dave used in his code to keep the scanning at a decent pace for the CAN reads. The function millis() just returns a counter value in milliseconds since startup. Unfortunately scanning at millisecond rates isn’t going to cut it for the ODO because at say 200mph the update period is 4.35ms so if we work in ms the best we could do assuming the loop ran fast enough is 5ms which means the ODO would be going 15% slower than the vehicle speed. Luckily Arduino also has a similar built in function ‘micros()’ which does the same thing but counts in microseconds. Some of you familiar with binary might be thinking an integer counting up in microseconds will hit its limit very quickly and then it is likely to cause a problem with the calculation however we get round this because the returned value is of type ‘unsigned long’ which gives us a range of up to about 70 minutes before the count resets. Interestingly because of the way data types wrap round like this the variable “lastRefreshTime” can actually be either signed or unsigned and it works the same.

long calcMicrosecODO(float speedKMH){
  long uS;
  float freq;
  float speedMPH;
   
  Serial.print("Speed = ");
  Serial.print(speedKMH/100);
  Serial.println(" km/h");
  speedMPH = speedKMH / 160.934;
  // Required frequency for timer 1 ISR
  //  1.15 is 4140 (Pulse per Mile) / 3600 (1hr in seconds)
  //  0.7146 is 2572.5 (pulse per KM) / 3600
  freq = speedMPH * 1.15; 
  Serial.print("Freq = ");
  Serial.print(freq);
  Serial.println(" Hz");
  uS = 1000000/freq;
  if(uS < 4500000 && uS > 0){
    return (uS);}
  else {
    return (4500000);
  }

Similarly to the previous interrupt version we have a function which calculates the correct delay required to keep the ODO at the right frequency, there are some minor differences to work in microseconds and if the speed is too slow cap the output at 4500000us (4.5s). Otherwise it’s much the same.

    if(ID == 0x4B0) {
      frontLeft = (buf[0] * 256) + buf[1] - 10000;
      frontRight = (buf[2] * 256) + buf[3] - 10000;
      rearLeft = (buf[4] * 256) + buf[5] - 10000;
      rearRight = (buf[6] * 256) + buf[7] - 10000;
      
      //Going to check front wheel speeds for any issues, ignoring the rears due to problems created by wheelspin
      if (frontLeft - frontRight > 500 || frontLeft - frontRight < -500) { //more than 5kph difference in wheel speed
        checkEngineMIL = 1; //light up engine warning light and set speed to zero
        vehicleSpeed = 0;
      } else {
        vehicleSpeed = (((frontLeft + frontRight) / 2) / 100); //Get average of front two wheels.
      }
      //Update timer count value with live speed for ODO
      ODOus = calcMicrosecODO((frontLeft + frontRight) / 2);
      
      Serial.print("ODO Step : ");
      Serial.print(ODOus);
      Serial.println("us");
      // Dump speed to serial for debug - this is just a cropped int.
      //Serial.println(vehicleSpeed);
      
    }

Above we see the call for ‘calcMicrosecODO’ which is basically the same as the interrupt version but gives an actual time delay in microseconds rather than a timer count. It is called in this way with a calculation in the parameter field because if it is stored in a variable in between the number gets cropped off at the decimal point even if the variable is a float – I believe this is due to the way Arduino processes floats.

void loop() {
  //Send information on the CanBus every 100ms to avoid spamming the system.
  if(micros() - lastRefreshTime >= 100000) {
    lastRefreshTime += 100000;
    sendOnTenth();
  }
  // Call function to updateMIL on variable timebase
   if(micros() - ODORefreshTime >= ODOus) {
   ODORefreshTime += ODOus;
    sendOnClock();
  }

So our extra call for the variable timed refresh is above, it varies a bit from the first one because we have a varying time delay depending on the frequency required which here is a long we are calling ‘ODOus’ which we calculated previously. Obviously because we are using two comparisons we need another refresh time variable to keep them distinct, this is also a long. Also the second one calls a specific function for the variable time refreshes ‘sendOnClock()’

void sendOnClock(){
  // Do not increment ODO byte when step is = 4.5s
  // slower than this updateMIL must still be called so 
  // warning lights don't turn on but speed may be zero!
  if ( ODOus < 4500000){
    send420[1]++;   
  }
  updateMIL();
  CAN0.sendMsgBuf(0x420, 0, 7, send420);
}

This function is pretty basic, all it’s doing is checking the resulting ODOus value isn’t 4.5s because if it is this value is means the calculation is at it’s upper limit and the car isn’t moving (specifically the speed is below 0.21 mph). In this state it still updates the warning light registers and sends the CAN packet to keep the warning lights from blinking on but it doesn’t increment the ODO byte value so the ODO doesn’t continue to tick on.

// Time delay for Odo
long ODOus = 4500000;      

// Set to max 4,500,000 to keep dash 
// MIL warning lights awake at 0 speed

The declaration for ODOus is set as 4,500,000 at start up so it correctly. The only downside to this is it takes a few seconds for the dash lights to go out initially and the oil pressure gauge goes to normal in two steps. This could be dramatically improved either by setting a higher minimum speed at which the ODO ticks because even increasing to 0.5 mph would improve the maximum duration to 1.7 seconds. Another option to avoid this is just to send an update to 0x420 in between the ODO calls. If we keep the one to the other loop ‘sendOnTenth’ without incrementing the ODO value it will just display any changes to the warning lights almost instantly while keeping the ODO working correctly.

void sendOnTenth() {
  //  PCM Status's to mimic the PCM being there, these may be different for different cars, 
  //  and not all are always required, better safe and include them all.
  CAN0.sendMsgBuf(0x203, 0, 7, send203);
  CAN0.sendMsgBuf(0x215, 0, 8, send215);
  CAN0.sendMsgBuf(0x231, 0, 8, send231);
  CAN0.sendMsgBuf(0x240, 0, 8, send240);
  CAN0.sendMsgBuf(0x620, 0, 7, send620);
  CAN0.sendMsgBuf(0x630, 0, 8, send630);
  CAN0.sendMsgBuf(0x650, 0, 1, send650);
  
  updateMIL();
  CAN0.sendMsgBuf(0x420, 0, 7, send420);    // Duplicated in sendOnClock to update ODO

  updatePCM();
  CAN0.sendMsgBuf(0x201, 0, 8, send201);

So that’s it but I should probably mention that I’ve not tested this on a live car however I have a program which works out the correct CAN byte values and then builds a correctly formatted string which can be pasted into USBtinViewer and sent to simulate an update from the ABS and the Arduino and cluster all seem to respond correctly:

Labview speed to CAN bytes converter  front panel
Labview speed to CAN bytes converter  front panel code

In this code you will see a scaling factor of 100 which should be correct but on my cluster I seem to get a speedometer reading 1 mph higher than intended from a calculated 5-185mph I actually see 6-186 mph on the cluster. I’m not sure why this is but it may be on purpose due to the common requirement for speedometers to never read lower than true speed. Indicated speed is required to be within -0% / +10% + 4km/h of actual speed in the EU. Technically we could correct this by doing two vehicle speed calculations on the Arduino, one for the odometer calculation using the proper scaling factors to keep it accurate and one for the displayed vehicle speed using a scaling factor which corrects this error. I do not intend to do this but I have found a scaling factor of 99 rather than 100 makes the speedometer match up if required.

As ever your mileage may vary, but hopefully it’ll closer than it was before! As I said earlier this is part of another major overhaul of the code and I’m intending to publish the final version as a complete code when I’m happy I’ve got everything working but that might take some time.

RX8 Project – Part 19, Canbus #4, The Comeback!

Lets be clear before we start – This will not allow you to start a car with a factory ECU with the wrong key or by bypassing the immobiliser. This is to do with making things like engine swaps work fully in the car without the factory ECU connected.

I’m going to apologise right now, there’s quite a lot of background about how I investigated the process of the ECU talking to the immobiliser in this one, if you don’t care and just want the CAN info scroll down a bit. I’ll write a complete update of all the codes when this phase of work is all done.

As some of you are probably aware my RX8 engine swap has been going on some time for a variety of reasons but specifically due to not having cover and so not easily being able to hook the car CANbus up to my computer to do live diagnostics I had hit something of a problem. I couldn’t generate data fast enough to fake all the devices on the bus reliably to test things on my desk away from the car but had no-where undercover to work with it connected to a PC so largely I’d planned to concentrate on organising a garage – something that should have happened more than a year ago now but due to the pandemic and related 2020 problems that not gone as well as hoped!

Anyway a few months ago on trying to find a CAN diagram for the car to aid answering a comment someone posted on here (yes I do try to answer them but I know sometimes it takes a while – I’m hoping to keep a better eye on it this year!) I came across a video from a guy called Dave Blackhurst (this one) which was rather interesting as he apparently had my two main issues from my previous code sorted, specifically the immobiliser system (the thing I want to look at here) and the power steering.

So to see how he was doing it I went to the linked Github and downloaded his Arduino code and on opening it I was largely very familiar code, specifically a whole set of variable declarations for the various dash instruments and a large section of code related to using a boolean variable to set the correct bit in the corresponding CAN packet so individual warning lights can be turned on or off and the speed/RPM etc to all be set easily on the cluster. So this was basically a development of my previous work

Now just to be clear this isn’t a complaint. This is just the first time I’d see my work go full circle and someone else actually develop it further which was quite exciting. If I hadn’t wanted it used and developed I wouldn’t have posted it online! Even better was it gave me a kick to have another go at it and at the very least try this new and improved code which said it fixed all the issues because if true Dave might have just solved my remaining problems!

So back to the immobiliser. I’d never managed to get the immobiliser light to go out despite trying all sorts of combinations of data from the logs I had but after seeing what Dave had done I realised I’d been missing a critical bit of information. The CAN logs I’d previously worked from were predominantly provided but someone else who contacted me via the blog and so were from two other cars and not my own. Additionally you can see in the early blog posts I actually built my own CANbus board several years ago which worked but had some speed issues with all the work it was doing. This was because when I started this project several years ago there was little else available for sensible money so it was the only real option. Long story short the immobiliser relies on a very short data exchange which occurs when the ignition key is first turned on and is never repeated and basically it had just been missed by the loggers that I had data from previously so I may never have figured it out!

Excited at the prospect of this just being a load the code and go I wondered if in the intervening years better hardware was available for this job than my old Arduino Nano based DIY job and there are now various Arduino expansion shields for CAN but to me these were all rather clunky solutions so I started looking at the “Feather” range which has a couple options but again these were a board and a piggback interface which isn’t what I wanted and there’s a new one which is the Adafruid Feather M4 CAN which looked pretty good but doesn’t seem to have ever actually been in stock anywhere so moving on.

Adafruit Feather M4 CAN

Following this I had a realisation – if if was going on the car it really didn’t need an integrated battery. Previously I’d started using Leonardo based modules for projects and these proved very quick for most tasks and have integrated USB meaning data throughput can be faster so I tried to find a Leonardo based CAN module. Eventually I found a company who I’ve used before for various neat modules do basically exactly what I was after for a reasonable price, enter Hobbytronics and their L-CANBUS board.

Leonardo CAN BUS board
Hobbytronics Leonardo Canbus

Looks just the job and even comes with the headers and 9 way connector unsoldered. and the board itself has pads for screw terminals to be soldered in place of the 9 way connector for bare wire connections. This time round I decided I wasn’t going to mess about with screw terminals while testing so I splashed out and bought a £10 OBD2 – 9 Way D-Sub CAN cable off eBay to go with it (unfortunately Hobbytronics were out of stock of the cable). This also had the advantage of including 12V and ground connections which are routed to the board regulator so the module is powered directly off the OBD2 port on the car making testing really easy.

I also decided being powered off the car that rather than risk shorting the module/car it needed a case and since I recently was given what I believe is still the cheapest 3d printer on eBay by a friend who got so annoyed with it he bought one that was actually good, so I designed a 3d printable one :

It’s pretty basic, the holes should be undersize for an M2.5 laptop screw so they basically thread cut it when you first put them in. Not ideal but it’s what I had. M2.5 sized plastic screws with a coarser thread would be better but either way it held fine. This case also leaves the USB accessible.

Anyway, back to the point we now have some nice neat hardware so I tweaked the code to run on this module (different CAN pins to the normal Adafruit CAN shields) and flashed it. After plugging it into the car…nothing happened. Once I’d reflashed the Leonardo with CLK_OUT enabled as per the instructions following a conversation with Hobbytronics who were very helpful (in their defence it says to do it right on the product page but I’d not read that bit!) I loaded it again and when hooked up to the car what I got was the basic warning lights went off, but the immobiliser and power steering that I’d hoped to resolve were still there. Time to delve a bit deeper!

So looking at Dave’s code here’s the bit to resolve the immobiliser:

 if(CAN_MSGAVAIL == CAN0.checkReceive()) { 
  // Check to see whether data is read
 }
    CAN0.readMsgBufID(&ID, &len, buf);    // Read data

        
    //Keyless Control Module and Immobiliser want to have a chat with the PCM, this deals with the conversation
    
    if(ID == 0x47) { //71 Dec is 47 Hex - Keyless Chat
      
      //***** Fixed Coding for Dave Blackhurst's Car *******
      if(buf[1] == 127 && buf[2] == 2) {      
        // Look for 0x 06 7F 02 00 00 00 00 00
      }
        CAN0.sendMsgBuf(0x041, 0, 8, send41a);// 0x041 = 65
        // send41a = 0x 07 0C 30 F2 17 00 00 00
      }
      if(buf[1] == 92 && buf[2] == 244) {     
        // Look for 0x 08 5C F4 65 22 01 00 00
      }
        CAN0.sendMsgBuf(0x041, 0, 8, send41b);
        // send41b = 0x 81 7F 00 00 00 00 00 00
      }

I’ve added in the codes being looked for or sent in each case which Dave identified from scanning the bus on his RX8 just to make it easier to see what’s going on. Breaking this down ID 0x47 is the immobiliser module sending data out which generally seems to just keep repeating 0x06 01 FF 00 00 00 00 00 when in normal use with the car running as factory I have that from my previous logs. So this first code starting 0x 06 7F 02 is something from the immobiliser which triggers the exchange. The code basically just reads any incoming data then checks the ID is 0x47 (i.e. it’s coming from the immobiliser) and that two bytes match the what he knows the CAN data should be (simpler than checking the whole code) , specifically byte 1 being 127 (7F) and byte 2 being 2. He then sends the recorded response to this (send41a – 0x 07 0C 30…) back to the immobiliser which would normally be done by the PCM (ECU) in the car when present. Then we look for the response from the immobiliser matches what we expect (0x 08 5C F4….) and sends a second reply to the immobiliser (send41b – 0x 81 7F 00….). I started thinking the module wasn’t talking to the CANbus right but after some fault finding and adding a diagnostic LED blink at critical points I found it was on the bus but just wasn’t seeing the right data coming from the immobiliser to respond to. Now I knew this exchange worked on Dave’s car but not on mine so clearly the codes we have aren’t universal in some way but I needed to work out what was going on but at least I knew what to look for.

Back when I was trying to find the code to disable the power steering light from Labview I bought a device called the USBtin which is a neat little PCB which is basically just a USB to CAN adapter but it has a built in protocol to control it so you can read the data via software like Putty or relative easily develop custom applications to connect to it. Now facing this problem I decided to give it a go and see if it was actually fast enough to catch this exchange in the first few fractions of a second of the ignition being on. I blew the dust of the original ECU for the car and hooked it back up to the bus (there’s no engine but I hoped that wouldn’t matter for this bit) loaded the basic USBtinViewer onto a tabled and hooked it up.

Ok it’s a photo of a tablet screen but anyway, the point is the USBtin is clearly fast enough to catch all the data because the monitor mode shows it’s caught the exchange because it’s logged packets to ID 0x41 and 0x47 and the last message to 0x41 matches the last one from Dave’s car (send41b – 0x 81 7F…). So it’s got the data, unfortunately to see what was sent both ways I had to trawl through the trace mode which just lists every CAN packet on the bus but after a bit of searching I found this:

So going through it first and ignoring all the extra data (there will be more to follow on this) there’s the default message from the immobiliser sending 06 01 FF… highlighted in yellow, then shortly afterward we see what looks very similar to Dave’s first message of the exchange but where his was 0x 06 7F 02, mine critically is 0x 06 7F 01. Looking back to Dave’s code for this we find that he was specifically looking for byte 2 = 2 and mine is 1, which is probably why it never triggered on my car. Now because that first packet we need to match starts 0x 06 7F … on both cars I can just change the check to look for that combination instead but at this point I also realised the outgoing data from the ECU (0x 07…) and return from the immobiliser (0x 08…) are totally different for my car so rather than mess about I just swapped out both to match what I’d logged for my car (assuming the codes may be car specific to stop PCM’s (ECU’s) being swapped between vehicles they’re not coded to or something) and tried it again, but this time…

Yes this time it cleared the security light! Definitely progress!

But for me making it work isn’t really enough and I like to understand why something works and hopefully make it better!

First off I tried again but this time with the matching done using the updated positions for the consistent bits of the code common to both cars. Specifically getting rid of the check for the byte 2 being any value because this appeared to change from car to car. What we can reasonably assume is fixed from this data is byte 0 because they always seem to indicate the step in the exchange with the exception of the initialisation state and first request state which both start “06” however byte 1 gives us if the immobiliser is still starting (0x 06 01…) and making a request to the PCM (0x 06 7F) and these match on both cars. We then send back one of the first response packets (0x 07….) and wait for the next request which byte comparing the data both cars start with byte 0 = 08 and based on the rest it’s reasonable to assume again this is the sequence step and so universal. Then Dave looks for a value of byte 2 which differs between our cars, however I have noticed that byte 5 for both cars is always 01 so this would work for both. So we end up with this :

      
      byte send41a[8] = {7,120,192,226,94,0,0,0};                      
      // Reply to 47 first  : 0x 07 78 C0 E2 5E 00 00 00 
      // Bytes 0 is the same, bytes 3 & 4 dont seem to matter, 
      // 5,6,7 are zero
      
      byte send41b[8] = {129,127,0,0,0,0,0,0};                         
      // Reply to 47 second : 0x 81 7F
      
      //***** Fixed Coding for Jon's Car *******
      // Some experimentation showed that on the initial request
      // for my car byte 2 was a 01 not a 02
      // however all codes so far begin 06 7F 
      // for either car so this was used.
      // Similarly in the second message from the 
      // immobiliser bytes 1-4 change but byte 5 
      // is always 01 on either vehicle

      if(buf[0] == 0x6 && buf[1] == 0x7F ) {                      
        // 0x 06 7F 01 00 00 00 00 32
        
        // printhex(buf,8);                                        
        // Transmit out received request on debug serial port 
        // - breaks timings on vehicle.
        
        CAN0.sendMsgBuf(0x041, 0, 8, send41a);                    
        // 0x 07 78 C0 E2 5E 00 00 00 
      }
      if(buf[0] == 0x8 && buf[5] == 0x1 ) {                        
        // 0x 08 94 29 BC 91 01 00 32
        
        CAN0.sendMsgBuf(0x041, 0, 8, send41b);                     
        // 0x 81 7F 00 00 00 00 00 00  
      }

I’ve included the notes I made at the time – the printhex is a function I wrote to dump these arrays out to the serial port easily as two digit hex pairs for monitoring what’s going on at different times. As noted here it breaks the timings of the exchange between the ECU and immobiliser but if we want to check something we just add it in and at least we can see what the data was. I’ll do a separate post for that as it might be useful to others. Not unexpectedly matching in this way worked fine but more interestingly I tried my car with Dave’s codes matching in this new way and interestingly it actually worked with no security light…

Well that raises even more questions because in theory I’d just swapped my ECU for his (in terms of the code exchange at least) and it authenticated!

So I went back to my car, reconnected the original ECU, powered it up with the USB logger again and noticed that each time I turned my ignition on the code exchange was different so clearly the idea of the code being fixed for a pair of ECU and immobiliser was wrong. After a number of goes and recording all the results a few things started to become apparent.

Firstly the initial request from the immobiliser had some variations on bytes 7 and 8 which always matched the byte 7 and 8 in the second request but the bytes we look for (byte 0 and 1 shown in green) are always 06 7F in every try.

Second, the first response given by the ECU indeed always starts 07 (light blue), but the next 4 bytes change each time the ignition is turned on (bright blue).

Third, bytes 0 and 5 in the second request are always 08 and 01 respectively (orange) however the 4 bytes between change each time the ignition is turned on (yellow).

Fourth, the second response is always the same (pink).

So what I think is going on here is actually something slightly different. If we assume for a moment the ECU knows the code or processing going on in the immobiliser we get a proper challenge/response interaction. I think the first message sent by the immobiliser is basically a “ready” message telling the ECU to send a block of data. The ECU then responds with a 4 byte block of data which is randomised each start. The immobiliser then performs some function we don’t know and sends the result back to the ECU where it checks the result against what it should be and gives a pass or fail back to the immobiliser so it updates the dash light. The last packet is always the same because we’re using everything from one car so its a pass so we don’t know what a mismatch fail looks like yet but I’m going to look into this at some point.

Now the interesting result of this outcome is with our Arduino we’re not checking if the code response actually matches and if my theory is right the outbound 4 bytes can be absolutely anything as it’s basically just a seed value, then the immobiliser will will reply with a coded version which we just ignore and so long as we reply with a pass message at that point (0x 81 7F 00 00 00 00 00 00)the immobiliser turns the dash light off.

To prove this out I set up the Arduino to send different random numbers in each of bytes 1-4 to the immobiliser in the initial response and indeed while the response from the immobiliser bytes 1-4 change as in response the immobiliser light went out on the dash every time.

My final theory is that the immobiliser module is reading the data from the chip in the key, and using that to transform the initial data from the ECU to create the response and since the ECU knows the key code and whatever this transformation is it knows if it’s the right key or not.

Now hopefully that problems solved for all RX8’s, I’m aware I haven’t posted a full, complete code like I have done previously but that’s because it has all sorts of other features going in this currently and I’ve not finished and tidied the rest of it yet. I’ve also considered the situation where there might be an RX8 out there that doesn’t match the code pattern found in my current sample of two vehicles so I’ve actually added a feature where if you jumper a wire onto a digital input and connect the Arduino to a car with the original ECU in place it will read out the entire code exchange and store all the codes for that car in EEPROM on the Arduino. If you then take the jumper off and pull out the ECU, when you turn the ignition on again the Arduino will use the codes it just stored instead of the defaults. Again I’ll write this up properly when it’s all finished.

Finally thanks to Dave Blackhurst for your work on this. While your solution didn’t work for my immobiliser it gave me the drive to have another go and enough information to go in the right direction and hopefully we’re a step closer to a universal engine/motor swap solution for everyone.

More to follow!

Living with a Scirocco 1.4 TSI 160 (118kW) – Part 3, Supercharger Bypass Valve (P10A4 Fault)

Sometime last year I was having problems finding a spot in a carpark so ended up sat in the car with it idling for several minutes and when I went to pull away the engine warning light flashed on almost immediately – not something anyone wants to see!

For the rest of the drive the car would mostly work ok at low engine RPM but the engine all but refused to go above approximately 3000rpm which is around the RPM the turbo takes over from the supercharger on this engine and the supercharger is disengaged by its electronic clutch. The curious bit was that at hitting the cut off there was a sudden loss of power at that RPM rather than it staying consistent and just holding an RPM limit suggesting it wasn’t a limp mode as such. My initial suspicion was that the turbo wasn’t spooling up so whether the wastegate was stuck open or something I didn’t know.

Luckily I bought VCDS which is an excellent tool for diagnosing and performing adjustments and if you do a lot of work on VW’s well worth the investment so I hooked that up to see what fault the check engine light was actually reporting and saw the following :

A quick Google of the code P10A4 identifies specific air regulating flap in question but this could also be found by a brief search of the engine bay – there aren’t many flaps in the intake! VW refers to this part as J808.

Image of the TSI160 engine with intake flap highlighted

What this flap actually does is when the supercharger disengages the flap opens providing an unrestricted flow to the intake of the turbo. Obviously if this doesn’t open when the supercharger disengages suddenly the air supply to the engine is closed and you get a massive power loss. You can see what’s going on in the image below which is taken from VW’s manual for this engine – SSP-359. This isn’t a workshop manual but does explain the design and behaviour of this engine in a good level of detail.

There are quite a few guides on how to replace this part such as using the one at workshop-manuals.com . This is an excellent resource but I do recommend using an ad blocker before going there as there are so many ads it’s very annoying otherwise!

My approach to get at the flap was to start at the airbox end and disconnect all the lines going into the intake tract (there are many in all directions so carefully work along it). Eventually once everything is detached you can undo the three long screws which hold the intake pipes on either side tight against the flap and so hold it in place. In my case I ended up detaching the section of pipe after the flap as well so I could lift the whole intake pipe up to get access to the bottom screw nearest the engine as its very awkward to get at otherwise.

Anyway as usual I didn’t want to replace the part if I could avoid it and luckily I found some comments somewhere saying the problem was caused by oil weeping from the intake into the flap unit clogging up the motor so I wondered if it could be cleaned because fundamentally there’s very little that can go wrong inside as they are basically just a motor. Luckily at this point knowing this was likely the problem I figured I couldn’t make it any worse than dead so started trying to work out what was going on.

So looking at the casing of the valve I came to the conclusion there was a simple gearing mechanism inside but based on the space between the clear position of the motor under the curved housing the likelihood was that the gear on the motor itself was small and so I could not explain why there was a significant large area in the housing below it which would likely be a handy cavity with no obvious purpose. The problem being caused by oil makes sense – the motor sits in the bottom of the housing which is underneath the intake pipework so any oil leaking into the housing would pool around the motor. I think the leak is through the spindle bearing for the flap.

So I came up with a plan which may be apparent already because I neglected to take any “before” pictures. I didn’t want to cut the unit open because even though the two halves appeared to be screwed together it looked like the seam had been sealed in some way (presumably part of why oil gets trapped inside) and while nothing appeared to be spring return I really didn’t want to risk it. So I decided to drill a small hole into the extra casing I identified earlier by using the casing as a guide as to where the gear and so end of the motor was. I then carefully drilled a small hole approximate 4mm in the casing and indeed there’s clear space behind this area.

My plan was basically to just flush the inside out with brake cleaner to remove all the oil residue so the hole was just a little large than the straw on the spray can. Sure enough on blasting some into it immediately the predictable brown runoff started pouring out. Another interesting thing happened as well – some residue started to run out of the seam inside the intake passage suggesting this isn’t sealed from the internal workings and in fact the seal on the unit is only on the very outer edge if the housing which may well explain the leaking issue! anyway several passes with brake clean and sloshing it about inside to rinse out as much as possible (I recommend just keep replacing the brake clean until it runs out clear) and it should be ok again hopefully. At this stage you might want to spray a small amount of lithium grease through the hole onto the gears for longevity but if you do just keep it small so you don’t just end up fouling everything with that instead!

Next, make sure the thing is totally clear of brake clean! I held mine under a hot air hand drier for about 10 minute to make it hot enough to drive off all the vapour then left it out in the sun for about half and hour. The last thing you want is a it catching fire!

The final thing I wanted to do is cover up the hole, particularly now knowing the intake wasn’t sealed from the innards. To solve this I tapped the new hole out to M5 and carefully checking the clearance I had behind the cover I put a short button head in place that wouldn’t foul the motor drive gear. Hopefully this also means If I ever have this problem again cleaning it should be easier. You can see the new M5 button head below.

After putting it back into the car I used VCDS to clear the fault code and so reset the engine warning light and took it on a test drive to confirm everything was fine. I think in total this took me about three hours but I was figuring it out as I went so you could probably do it in less.

While this was done during 2020 and so I have been using the car less due to travel limitations and working from home I can confirm that now five months later the car still works well and I have had no further issues following this repair.

Living with a Scirocco 1.4 TSI 160 (118kW) – Part 2, Turbo Replacement

It’s been a little over a year since I posted the introduction blog article on this car and more specifically on the unusual twin charged engine it has. Unsurprisingly over the last year or so I’ve found a few things that need a little work but generally the car has been excellent, needing minimal thought but certainly has some aspects to be aware of for prospective owners to keep the the engine working correctly.

When I bought the car it was a bit lacking power compared to what I was expecting and when accelerated hard in one gear (which due to a seemingly large gap between the supercharger and turbo rev ranges required revving it high) and changed up it would randomly have no power at all. I managed to trace this to a couple problems both related to the turbo wastegate. Firstly the requirement to rev it high was caused by serious wear on the wastegate pivot meaning the wastegate didn’t fully close so the turbo wouldn’t spin up properly. A temporary bodge to get round this is to tighten up the actuator rod to take up the slack but while this sort of helps it actually wears the housing even faster but it can get you by while you wait for a replacement. The second problem of lacking power after a high RPM change was that the wastegate actuator rod was actually bent and touching the turbo housing so it was actually getting temporarily stuck when fully extended so going into the next gear the turbo was basically just dumping the exhaust out the wastegate rather than doing anything useful. So this definitely needed looking at!

First off let me just say I initially looked at the position of the turbo nicely sat at the top front of the engine and thought a couple hours and it’d be done. I was wrong, very wrong! It looks lovely and easily accessible but it just isn’t as easy as it looks for many reasons mostly relating to it not being a turbo mounted to a manifold. the entire exhaust side manifold and turbo are a single unit so you need sufficient clearance to pull the whole unit out.

There it is under the heat shield – looks simple right?

I used various guides to do this swap and generally was in a rush (that didn’t work out so well) so I have very few photos of this but the information is fairly widely available anyway (try searching for guides to the mk6 Golf with the same engine) this is to highlight a few points people may find useful. I suggest referring to workshop manuals for a handy guide with diagrams of each section you need but strongly recommend an ad blocker before you do.

  1. The hard plastic boost pipe which runs from the supercharger to the turbo inlet is retained at the turbo end by a single M6 torx bolt with the threads tapped into the aluminium casting. On the rebuilt unit I bought this thread turned out to be ruined to the point it was impossible to tighten. I suspect this is because undoing the captive fastener during disassembly tries to push a metal sleeve out of the plastic. This is fine in itself but I think it wears the aluminium, similarly tightening it back in will also be hard on it. The reality is the pipe should be pulled back a little at a time as the screw is undone to prevent the load on the threads but this is a bit awkward to achieve as the pipe has very little ‘give’ in it. I strongly recommend checking this before you start – I had to call in a favour because having spent a lot of time swapping the turbo it was rapidly approaching closing time for all the shops to get anything to repair this and without it the car shouldn’t be run. If you’re in any doubt just buy an M6 helicoil kit and put a shiny new insert in place in the aluminium casting because there is only the one screw and if it fails your car will not be happy! Helicoils in softer materials are actually stronger than directly tapping the material the right size because the insert is a stronger material than what it’s going into and because they’re fitted by screwing into a larger thread in the parent material than the desired final thread they have a larger contact surface area in that material.

2. To fully undo all the bolts of the manifold flange you have to undo the alternator mounting bolts and twist it out the way. To do this you have to take off the alternator belt by releasing the tensioner then remove the top mounting bolt for the alternator entirely and slacken the other. This requires removing the engine bay undertrays as well but if you’re doing this job save some time and just pull them all off now. The alternator can then be rotated down and away from the block to get at the bolt. Someone out there might have some creative way of getting at that bolt but I had nothing that would get at it from any angle and couldn’t see any other way if could be done because it’s in a recess with manifold one side, oil filter casting the other and alternator in front of it.

3. Remove the radiator fans. In the picture above you can see how tight this is relative to the turbo and so you need to do this to have enough clearance both to get tools in to undo the manifold nuts and also to remove the the turbo itself from the exhaust studs. Removing these is done from the underside and also involves removal of the pipe between the turbo outlet and intercooler to give sufficient space. You need this removed to change the turbo anyway so it’s no inconvenience.

You’re looking to remove pipe sections 15, 16 and 17 for clearance. Item 11 are two bolts holding the charge pipe to the engine. The radiator sits between the charge cooler and this charge pipe.

The fan module can be removed as a single unit downwards with both fans in place by simply removing the four bolts holding it to the radiator and unplugging it at its electrical connector (item 13 on the bottom edge in the image above).

4. Buy a fitting kit off eBay or somewhere – there are load available but this is the simplest way of making sure you have all the replacement seals and gaskets you might need. Get the most comprehensive one you can find if you can’t easily go to get more parts once this car is apart!

5. The oil drain hose from the turbo is an absolute pig to get at.

The part I’m referring to here is number 12 above and consists of a section of solid pipe at the turbo end with a very short section of hose crimped on. I used a socket on a series of extension bars to get the bolt out of the turbo end but the block end is very awkward to get at because you can’t see it from any angle and the access is tight because the bolt sits virtually under the downpipe. Good luck! When you’re struggling to put it back on after changing the turbo don’t forget the gasket. Also this pipe is apparently common for leaking because the bolt doesn’t get put in sufficiently tightly or the gasket gets damaged during reassembly. I’ve highlighted this below in red.

6. The coolant hard line on top of the turbo needs to be removed which leaves an open rubber hose end. An M8 bolt fits perfectly to block this and stop coolant pouring out over everything so have one to hand before you take it off.

7. Carefully check the boost control hoses – apparently these commonly crack and certainly in my case they were quite degraded around the turbo. You can buy the proper replacement VW part if you wish but it may be cheaper to just order some 5mm vacuum hose and put a run in. In my case I didn’t notice the damage until I started taking it apart and managed to get a random bit from a friend. His wasn’t the common stuff it was thin walled and reinforced so the standard clamps didn’t fit but luckily with some persuasion I managed to fit the thin hose into an offcut of the original one. When combined with a suitable hose clip it’s working fine and has been for ages – that said I do not recommend this option!

Damaged section of hose in red above. The image below shows the hose I replaced marked in red going from the turbo housing back to the boost control solenoid and then the second similar line from the solenoid to the wastegate actuator marked in green.

Unfortunately as I mentioned earlier I didn’t take extensive photos of this replacement but I hope these few points help someone out there!

Good luck!

RX8 Project – Part 18, Resurfacing Cylinder Heads the Cheap Way!

As ever do this at your own risk. For most people you’re better off just getting heads machined by a specialist but if you’re reading this blog you’re probably already aware that’s not always the way I do it!

So this idea came from me wondering how I could easily clean up the cylinder heads on the V6 without additional machining. The heads were generally in good condition so I just wanted a fresh surface for the new head gaskets to seal well on rather than trying to remove any surface damage or warping. If you have this sort of damage this method is not for you.

When I came up against this problem I decided to do some research and found quite a few people online saying you could just do it with suitable abrasive paper and a sanding block. Now I get the idea but the engineer in me sees a good possibility of some part getting ground back more than another actually increasing the issues with the head that we’re trying to remove in the first place. Around this time I spoke with a few different people who have experience with engines and they all said much the same thing – machine skimming is safest and easiest but with enough care it should be possible to do a perfectly good job by hand, the problem is getting the whole thing completely flat which is very difficult by hand. Most people who had done this seemed to have done it on engines with small cylinder heads such as single cylinder machine engines which being small are easier to get flat by hand.

The problem was absolutely one of getting something suitably flat that would cover the whole head to get the whole thing even so I started looking into what might work. I was already aware of engineers surface tables which are used for checking flatness but these are large, heavy and very expensive as they’re often made of stone or tool steel. I then looked at getting some surface ground steel plate (where a thick steel place is ground to a precision flatness) but again this seemed to be expensive and awkward. After a bit of thought I had an idea…

£4 eBay coffee table


Ok, so at first glance this seems like a daft idea but stick with me! The cheapest and most rigid precisely flat surface I could find was a piece of toughened float glass. Specifically toughened because it is created in such as way as to pre-stress the surface which makes it both stronger and stiffer but also much more brittle – this is the glass that breaks down into granules when broken rather than shards. Initially my plan was to just order a decent sized bit but that seemed rather wasteful so I thought about it and realised second hand furniture included quite sizeable bits of the glass. I began searching eBay and Facebook marketplace to see if I could find something suitable as cheaply as possible and after a week or so found this coffee table. It was nearby and listed as local collection only with some damage to the wood veneer and scratches on top surface of the glass (so the price was unlikely to go high) and 99p no reserve starting. Couple days later I was the proud owner of a £4 coffee table!

Now you may be wondering why we aren’t bothered about the surface scratches on the glass which is likely to be a problem with any similar furniture. The reason is twofold, firstly due to this being toughened glass any scratches are likely to only be very minor and secondly the glass has a whole other side which is unlikely to have any scratches anyway so we’re going to use the underside.

I took the table apart carefully removing the top and to give additional support I placed it flat on a 19mm (3/4″) thick bit of chip board carefully screwing an section of baton at each end to stop the glass sliding about while we’re working. This assembly when then placed on my carpet which is just very hard office carpet tiles on concrete so shouldn’t allow any appreciable movement so hopefully with that stack of support the glass should be perfectly flat even with a cylinder head on it! The abrasive I will be using is wet and dry paper in a range of coarseness, get a pack of each grade you plan to use, it will take a lot of it! I made sure the wet and dry would stay in place by spraying the back of it with spray mount adhesive and putting it in place. Spray mount should also peel off relatively easily when we need to change the paper. I used 3 sheets to create an area larger than the head face in both length and width.

Glass coffee table refinishing rig


So now we have our setup we need to prepare the cylinder head for this. When it came off the engine it was quite grimy as you might expect so this needed addressing.

Head with worst of gasket scraped off


So here you can see how it was when it was (almost) fresh off the engine but with the residual head gasket material scraped off. You can see the amount of grime isn’t too bad, I have already wiped some off the top half but the bottom is a bit more representative. You can also clearly see the outline marks from the head gasket that we’re looking to remove later. This step basically involved soaking the mating face in a de-greaser then wiping it all off carefully.

Head during degreasing
Head after degreasing but before refinishing


It’s not perfect but it’s a huge improvement on where we started. The combustion chambers are considerably better. There’s some residual on the face but its more staining than anything else and will be removed by the refacing. So now we’re ready to go.

First cover the wet and dry with your chosen lubricant – water should work but I found WD40 seemed to work better as it helped the head ‘glide’ more. A light oil like 3in1 would probably be even better as it’s a little thicker again and WD40 tends to dissolve the spray adhesive as you work making the wet&dry come loose. Take the head and place it on the wet and dry (I started at 120 grit) holding both sides lightly start to slide it across the surface. There are different approaches to this where you can angle the head first one way then the other to give a crosshatch pattern. In my case I generally moved it in a long oval and this seemed to give a nice even finish but as ever your mileage may vary!

Cylinder head sat on DIY refinishing setup

You will find that as you work the oil and metal shavings will spread so I suggest doing it somewhere you don’t mind the mess!

This will take some time and effort. If the wet and dry wears down replace it. when you’re happy with the initial surface being clean of all the minor marks and debris you can go up to increasingly fine levels of grit for a better finish.

On the subject of finishes when I was doing this work I found the following information which relates the abrasive rating with the achieved resulting surface roughness – if anyone knows where this is from please let me know as I can’t seem to find out.

US GritUK Grit Ra µmRa µinch
 P1203125
 P180285
80 1.6570
 P2401.550
 P3200.7530
180 0.6225
240 0.4518
 P5000.415
320 0.2510
Comparison of grits vs achieved surface finish

So in the context of this I’m working in UK grit. Unfortunately the only information I could find on the required surface finishes for head gaskets came from the US so is in Ra µinch (Ra being the roughness average of the surface) but luckily this table equates everything. Generally normal gaskets seem to need a surface finish of about 50-60 Ra µinch, modern multi layer steel head gaskets require 30 Ra µinch or smoother so we need to finish at a minimum of P320. I actually went up to P400 to be safe.

Comparison between not touched and work in progress cylinder head


This is the comparison of the untouched head and the one with the first couple of grits done and so not quite finished but you can see the massive improvement made here.

Head surface reflection

I know judging by eye isn’t accurate but it’s clearly doing something good!

Keep going until you do all the grits you need and when you’re done then you need to check the flatness. I did this with an engineers straight edge (as opposed to a builders straight edge which is a big ruler) which cost £25 off ebay. This is a bit of steel that has been precision ground to be completely straight in one plane so is often only a couple mm thick but 70mm wide or more. You check the flatness by putting the straight edge perpendicular to the head (so it sticks up) and trying to slide a feeler gauge under the mid point (or as close as possible) of the area you’re checking. You need to check the width, length and both diagonals but also check across all the bores. The head should have no more than 0.075 mm off flat over the longest span on an iron head, or 0.05 mm off flat on an aluminium head on a V6. In my case the smallest feeler I have is 0.04mm so that proved it was good enough but to check it further I got some thin foil, checked the thickness with a digital micrometer which came out as 0.01mm and placed the foil on one of the central bridges of the head and put the straight edge on and it rocked on the foil. I then redid the test with the foil at the ends and the same again in other directions. Each time the straight edge was clearly resting on the foil first so the head must be flat to <0.01mm across the whole head. Unfortunately I neglected to take any photos of this stage but there’s plenty of information online.

That’s about as good as it gets so our £4 coffee table looks like a success. Plus I still own a coffee table – albeit with a few new scratches!

RX8 Project – Part 17, Changing to a concentric clutch slave.

To preface this I’ve not actually run the car with this setup yet so please make your own decision if you give it a go. This was done on the gearbox from a 2006 year RX8 5 speed box so may not be applicable to others. It looks like it should work but that’s only my opinion – your mileage may vary!

So this is a bit of an odd problem which depending on the engine you’re swapping in may not be and issue but in my case I decided a V6 was a good idea and unfortunately the standard clutch slave on the RX8 gearbox is on the top offset to one side which lines up perfectly with one of the cylinder heads on my V6. Add to this if you made the better decision mentioned earlier and made the adapter thicker you may be able to avoid this as well. But since I’m largely making this up as I go along here we are!

Now I did look into whether anyone offered a concentric slave conversion for this car but it seems that was never a thing anyone did so I set to work building my own. Luckily there was one thing I knew which would help this process quite a bit – the input shaft on the gearbox is the same diameter/spline as most Ford patterns and so a Ford part should be exactly the right clearance. Add to that I’m actually using a Ford clutch if I get the depth right everything should just match up ok.

So that’s the good news, the bad news is the RX8 gearbox was never intended to be used in this way so mounting a cylinder could be an issue. Now on the RX8 there’s a flanged sleeve mounted which the original release bearing slides on the outside of. This is held on by four bolts into the back of the bellhousing and so this appeared to be essentially the only option. The tube itself can’t stay because the new concentric slave is the same ID and so clashes with it but I thought why don’t I just unbolt the tube at the flange and bolt a suitable adapter there and we’re good to go? Well it’s never that easy is it. Under that flange is a location lip which not only keeps it concentric to the gearbox input shaft but it turns out it also the height of the shoulder accurately holds the input bearing in place behind it so if I just remove that whole part the bearing will move out of position and that will very likely result in it not having enough support and rapidly removing itself from the gearbox in small pieces.

3D model of the RX8 gearbox input bearing retainer
Unfortunately I can’t find a photo of it but the part looked like this!

Ok so I can’t just remove the flanged tube and stick an adapter plate on but how about cutting the tube down to the flange to leave a flat face above the bearing retainer and just using some longer bolts to keep it all in place. After some very careful trimming I was left with this:

Modified RX8 bearing retainer

Next was picking a suitable concentric slave from the Ford range. After a bit of poking about and trying to find something I could make fit I found the Teckmarx TMCS00047 which is a 3rd party part number for a 2001-2007 Mk3 Mondeo/Cougar among others which as you might have read earlier was also available with this same V6 engine I am using and this model has a few advantages firstly that both in and out hydraulics are in one direction so if I make that line up with the original position of the clutch fork I should have easy access and also that they’re threaded the standard M10x1 brake fitting thread so I can direct connect hoses or hardline as I need to make it work. another major advantage is they’re used on loads of versions of the car so they’re widely available and very cheap at under £25 delivered. It also seems that the RX8 also has almost same clutch master cylinder bore as the Mondeo (18mm vs 19mm) which should mean pedal travel is still sensible.

Mondeo mk3 concentric clutch slave

3D model of Mk3 Mondeo Clutch slave

Now with the clutch slave accurately 3D modelled I could measure the 4 bolt flange from the gearbox bearing retainer and by overlaying the two bolt patterns aligned on the centre of the input shaft I could design an adapter which I could index the relative rotational angle of the bolt patterns in the software until the fluid connections where in the right place for the hole in the bellhousing. The resulting first version was this :

3D design of first adapter design

Initially I transferred this to a bit of scrap plastic to make sure I hadn’t made any stupid mistakes before spending much more time cutting a proper steel adapter plate.

Plastic prototype RX8 clutch slave adapter

So with all that checked out and nothing apparently an issue I moved onto the steel one. I did make a mistake here if you can spot it…

To make the adapter I did the same as I had done with the plastic where I printed out the design at 100% scale, stuck it to the steel and then used a centre punch to mark the centre of all the drill positions. I admit this isn’t the most accurate method but it seems to work quite well!

Clutch slave adapter Mk1 in steel

This is the initial adapter, the four larger holes are M8 clearance holes. on the original RX8 flanged retainer they’re 9.4mm but I think I did them 8.5mm as that’s the drill I had available and tightening up the tolerance was probably a good thing. This actually turned out to be less of a problem in the end but that’s another story. The centre hole is larger than the original design to allow for the location feature I’d overlooked on the new slave (which is 42mm OD) to sit within it.

Clutch slave adapter trial fitted to the gearbox

So it fits, I called this good progress but it should come as absolute no surprise that it wasn’t quite that simple…

Clutch slave unit fitted to adapter plate

As soon as I tried to add the clutch slave all the issues become apparent as it just clashed with everything. This told me that I’d need to change the adapter to countersunk bolts so the slave didn’t foul them. I could have changed the rotation but I wanted to avoid having lengths of pipe in the bell housing if I could. Plus I’d already made this steel adapter and didn’t want to do it again!

The other thing I noticed is that the cast webs off the original pivot point actually clashed with the adapter plate preventing it from quite sitting flat so I decided to remove some of the plate to correct this minor issue.

Now the adapter sits flat and at the same time I countersunk all the adapter bolt holes and replaced the bolts.

It all fits more or less where I wanted it but when I tried to bolt up the gearbox I saw another problem. With the bearing retainer plate, a sensible thickness for an adapter plate and the height of the concentric slave itself the slave was already almost fully pressed down so that which it may have worked initially as the clutch wore the slave would prevent the clutch from fully re-engaging. Clearly not ideal so we need to get more radical. First off the back of the clutch slave had a lip similar to the one on the RX8 flanged plate which initially I was just going to leave on and sit on top of the bearing retainer plate as it was slightly thicker than the adapter plate but that just wasn’t an option any more. Below you can the way the slave is totally compressed. Also note how close the hydraulic connection point is to the original pivot point casting.

Stack height issues in the new clutch assembly

This lip was adding a couple mm of stack height we needed to remove so I proceeded to carefully file the lip off down on the slave such that it would sit full within the adapter plate and ideally totally flush to the back of the plate.

On trying to refit this in its new position I realised I’d created another problem that I glossed over earlier – that I’d need to remove some of the original gearbox casting to make the new slave sit flat in the orientation I needed as the original clutch fork pivot point clashed with the location where I wanted the hydraulic connections on the new slave. The best method I found was a drill bit larger than the feature and just drill the top of it away until it clears the new slave.

Around this time I realised really I needed to remove the original flanged bearing retainer plate as it alone added about 4mm to the stack height so I engaged in the type of butchery that makes engineers wince. I took the flanged retainer and trimmed the flange off it. Yes I specifically mean that – if you cut through the bearing retainer ring it will reduce the height such that the bearing is no longer held tightly so you need to carefully trim off just the flange plate leaving a ring the right height fill the gap between the bearing and where the retaining plate face would be. because the new slave retaining face was now flush with the adapter plate this ring will now be held in place by that. Removing this plate now meant I had to drill yet more out of the pivot casting to prevent it clashing but that’s easy.

Final fitment of the concentric slave conversion from the original fork position

Now everything is in place and the hydraulics are accessible through the original clutch fork hole.

RX8 gearbox refitted to the car

And all back in the car…

For anyone who may want it here’s the PDF drawing for the adapter :

Start of 2020 Update

Apologies to anyone who’s been waiting for an update on any of the projects I’ve put on here – I’ve had a lot going on over the last year and writing up all of this information takes me a lot of time so has taken a bit of a back seat. None of that information has been lost and I hope to start catching up on all of this over the next few months.

Where we are right now :

V6 Mazda RX8

The V6 engine has been fully rebuilt and has been sat in my living room next to an engine crane for about 8 months now, Partly this is due to me not currently having a garage to work in and it being winter and partly due to being very busy last summer. But yes, the project is still ongoing and hopefully will make good progress this year.

Six million dollar welder

Is still on my workbench awaiting the last couple of bits. I’ve had to swap out 24V PSU’s a couple times as they couldn’t deal with the current of the new feed motor. It now works but needs the relays mounted to the panel so I need to remember to find 4″ of DIN rail.

DL180 Server

Is still working beautifully after the Arduino fan controller mod. It’s been hosting this blog ever since so something like 12 months now without a problem. It also provides the storage for my CCTV system.

Hikvision CCTV

I’ve got a couple of updates to write about expanding the system beyond the original one camera setup I wrote up last year. Using multiple cameras with network storage seems to be poorly documented and have a few interesting quirks but after some work I got it working fine. The biggest problem I’ve had since is when the local police asked for my footage following a nearby break-in I had to supply them on a 1 Tb hard drive! High resolution digital video takes up a lot of space!

Other Projects

I’ve had various other projects going on as well. I’ve started looking into using NodeMCU WiFi microcontrollers as sensor nodes with their own web servers around my house with NodeRed requesting the page and parsing the HTML to return the information I want. I can read the temperature sensor connected to the nodes and control the LED on the MCU from NodeRed. Currently the data is not stored because I ran out of time trying to get MySQL setup. I’ll write this all up at some point

Raspberry Pi’s – I seem to have collected a selection of Raspberry Pi’s somewhere along the way. I have a Zero, a ZeroW, 1B (which in a different life I turned into a PoE CCTV camera), 2B and 3B plus I think somewhere there’s another 2B. I really should do something cool with them!

Wooden storage box – I started restoring a large wooden box some time ago. The box used to be in my granddad’s workshop as a toolbox for many years and before that I gather it belonged to his uncle so it’s been around for a long time and has suffered a bit with use and age with some areas with woodworm damage and the rope handles badly degraded. It has also been painted brown at some stage so I’ll need to get that cleaned up as well. I intend to restore it to a ‘usable’ condition so rather than trying to make it as new just tidy it up, repair the damage and make it solid enough not to degrade further but still look like the well used item it is.

Subwoofer project – Has been in constant use for ages despite never being technically finished. I really need to actually finish it and write this up!

There will be more but I think that’s enough update for now – rest assured I’ve not stopped! Especially since this morning another turbo arrived in the post, the fourth I now have here….

Living with a Scirocco 1.4 TSI 160 (118kW)

So recently I finally decided it was time to retire my previous long suffering car – a 2003 1.4L Mk1 Seat Leon I’ve had for 10 years! When I bought the car in 2009 it had 62,000 miles on the clock, now it has 198,000 miles on it and needs to be run on 10W40 rather than the specified 5W30 just to stop the engine rattling. The Seat did well but it had a hard life including 3 years commuting 400 miles a week and had got to the point where I was fully expecting it to fail sooner or later and wanted something that wasn’t as underpowered.

So I started looking about for another car and the new style Scirocco caught my eye. After looking for a while I found a decent condition version with reasonable mileage, service history and not reaching a high bid. Detail on this car was a little lacking as it was just described as a 1.4 TSI but the car had no engine/spec badges (a factory option from VW) so I wasn’t sure which version it actually was but on the basis it wasn’t advertised as the higher power option it would be the lower power turbo only 122 bhp model. So I went for it and got it for a decent price. When I arrived to collect it having never actually seen it before I checked it and found the identifying sticker in the boot which showed the power as 118kW, this is 160bhp so I’d got the more powerful one.

Image of a 2010 Scirocco
Something like this one.

This is both a blessing and a curse because while obviously it goes better the 160bhp version also have a reputation for unexpectedly experiencing catastrophic engine failure.

That said always take forum posts on the internet with a pinch of salt – people rarely take to the internet as much when their car works perfectly.

By this point its too late to back out so I’m now the owner of a Scirocco with a 1.4L engine! So now I start looking into things I need to watch out for. The engine is the first interesting thing here as it’s both supercharged and turbocharged to give a much better low down grunt than expected from such a small engine with supercharger boost while still having a wider power curve by the turbo taking over at about 3000 rpm and working higher up. The engine peaks out at about 1.5 Bar of boost (22 PSI) from the factory. This system obviously adds complexity and potential points of failure with various valves and clutches to make it all work so a number of things to keep an eye on.

VW Technical guide to this engine available here

Clearly we’re playing with a fairly highly strung engine so my first thought is what the maintenance schedule on these was like. People tend to ignore their cars so long as they keep working and from my previous 1.4 VW engine in my Seat I’m aware they have some issues with oil consumption. On my first look at some of the reports of damage online most seemed to mention failures that could easily be a result of oil starvation. Again, something to keep an eye on.

Moving beyond the engine that car itself is fairly advanced as well. These cars come as standard with adaptive suspension designed to react to road conditions. It has four sports seats which are very comfortable and the boot is quite reasonable for this type of car. Internally the Scirocco is very similar (depending on model year) to either a mark 5 or mark 6 Golf but is a bit less practical due to the style of the vehicle and lower roof line. That said I’ve had four full grown adults in mine and while it’s not hugely roomy it’s comfortable enough.

Now for the the but – I think mine was cheap partly because it has none of the extras. It doesn’t have cruise control, it doesn’t have HID headlights, it doesn’t have the more common 18″ ‘turbine’ wheels (I have the 17″ shown above), no DAB radio and no bluetooth. Other than the twincharged engine its a basic model and for most people that would be all there is to it but that’s not how I work. I will improve it as I go along and hopefully record how I do it all on here!