Aperture Controls

The 2015 Subaru WRX electric steering rack conversion

Logical

Here's where we're at: Everything works mechanically, you can drive the car if you have decent arm strength. The computer is using power so it's doing *something*. The CANbus lines from the computer are spitting out data... but what is it?


Protocol Analysis

This is one of two status bytes that the control units transmits on the CANbus (hi speed.)

Address 0x370: Electric power steering status:
I think this packet is analogous to Subaru Diesel Crew's packet 0x550.
Byte 0: Changes when I turn the wheel. 0xFF when not working
Byte 1 & 2: Zero when working 0xFF when not working.
Byte 3 & 4: Current in Amps?
Byte 5: Message Counter
Byte 6 & 7: Zero

Address 0x371: Electric power steering something else
Byte 0: 0xFF when not working, unknown data when working
Byte 1: 0xFF when not working, unknown data when working
Byte 2: 0x00 when not working, unknown data when working
Byte 3: 0x00 when not working, unknown data when working
Byte 4: 0x80 when not working, unknown data when working
Byte 5 & 6: Steering position
Byte 7: Zero

At this point I was stuck. And when I get stuck, I take things apart.




Inside the steering computer

Lifting the cover off the control unit. Things to notice here:
  • Connector A (lower left) has two pins that are labeled on the silkscreen, but not in the documentation. "SSM" and "WL"
  • Connector C (lower middle) has a pin labeled "MODE" that isn't in the documentation.
  • The pins going across in a single row all go down to the power board for the individual phases of the AC motor drivers.
  • The main CPU is the big square on the right (more info below.)



  • Bottom side of the controller board. Mostly power supply and monitoring


    Electric steering power board

    I unsoldered the controller board and took a look at the power board. You can see the power FETs (P2N0403) in pairs for each phase of the motor.

    Some more information before I close up the unit:
    • IC1 is the main CPU, a Renesas SH7047 Flash Version.
    • IC3 is an Infineon TLE 6280GP 3-phase bridge driver
    • IC5 is an ST 95040WR 4k EEProm
    • IC7 is a TI 2142Q Op Amp
    • IC9 is an NXP 82C250Y CANBus controller

    Speaking of CANbus...


    Throw random bytes at it until something happens

    I decided I was going to hammer the CANbus interface to see if anything happens. The idea was to set up a logger that would watch the current draw of the control unit while I sent random garbage to CANbus addresses in order to see what it's listening for. And bingo!

    Address 0x140: Throttle and RPM (used on the BRZ and posted by Acree on Romraider.com)
    Byte 0,4,5: TPS
    Byte 2,3: RPM

    I had the scanner running while I was playing a video game and hear a loud relay click. I reran the scan slower until I centered in on the address that made it click. When I found Acree's post , I knew I had my address. I re-installed the control module in the car, and I hooked up my Arduino to the CANBus using my homemade tap cable (so I can still use my Cobb Accessport at the same time.)




    Steering computer running with Arduino simulating CANbus data

    I programmed the Arduino to take the TPS/RPM packet that the 2010 puts out on address 0x410, and retransmit a reformatted packet to 0x140 whenever it comes in. I moved all the equipment into the car and took it out into the driveway.... voila! Power steering! I'll have to design a proper CANbus translator with proper watchdogs on a real MCU, but at least I can finally drive the car! Some extra information: Here's a couple of short packet dumps from my work.
    Steering not working:
    
    WRX EPS Something FF FF 0 0 80 32 CE 0
    WRX EPS Current FF FF 20 C1 80 7 0 0
    WRX EPS Something FF FF 0 0 80 33 CE 0
    WRX EPS Something FF FF 0 0 80 39 CE 0
    WRX EPS Something FF FF 0 0 80 44 CE 0
    WRX EPS Current FF FF 20 C1 80 8 0 0
    WRX EPS Something FF FF 0 0 80 55 CE 0
    WRX EPS Something FF FF 0 0 80 6D CE 0
    WRX EPS Current FF FF 20 C1 80 9 0 0
    WRX EPS Something FF FF 0 0 80 8B CE 0
    
    
    Steering working (with packet injection):
    
    Crafted Packet 0x140 0 0 8C 3 0 0 0 0
    ECU Engine 0x410 E 0 1B A 0 8C 3 C1
    WRX EPS Current 0x370 1 0 0 C2 FC 1 0 0
    Crafted Packet 0x140 0 0 8C 3 0 0 0 0
    ECU Engine 0x410 E 0 1B A 0 8C 3 C1
    WRX EPS Something 0x371 1 0 0 20 FD 76 CE 0
    Crafted Packet 0x140 0 0 80 3 0 0 0 0
    ECU Engine 0x410 E 0 1B A 0 80 3 C1
    Crafted Packet 0x140 0 0 80 3 0 0 0 0
    ECU Engine 0x410 E 0 1B A 0 80 3 C1
    WRX EPS Something 0x371 1 0 0 20 FD 76 CE 0
    WRX EPS Current 0x370 0 0 0 C2 FC 2 0 0
    Crafted Packet 0x140 0 0 84 3 0 0 0 0
    ECU Engine 0x410 E 0 1B A 0 84 3 C1
    Crafted Packet 0x140 0 0 84 3 0 0 0 0
    ECU Engine 0x410 E 0 1B A 0 84 3 C1
    Crafted Packet 0x140 0 0 7F 3 0 0 0 0
    ECU Engine 0x410 E 0 1B A 0 7F 3 C1
    Crafted Packet 0x140 0 0 7F 3 0 0 0 0
    
    And lastly, the Arduino code that makes the power steering work:
    		#include 
    		#include "mcp_can.h"
    
    		const int SPI_CS_PIN = 9;
    		unsigned char msg[8] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
    		bool wantPacket = false;
    		MCP_CAN CAN(SPI_CS_PIN); // Set CS pin
    
    		void setup()
    		{
    			Serial.begin(115200);
    
    			START_INIT:
    			if(CAN_OK == CAN.begin(CAN_500KBPS)) // init can bus : baudrate = 500k
    			{
    				Serial.println("CAN BUS Shield init ok!");
    			}
    			else
    			{
    				Serial.println("CAN BUS Shield init fail");
    				Serial.println("Init CAN BUS Shield again");
    				delay(100);
    				goto START_INIT;
    			}
    		}
    
    		void loop()
    		{
    			unsigned char len = 0;
    			unsigned char buf[8];
    			
    			//wait for CAN data to be available
    			if(CAN_MSGAVAIL == CAN.checkReceive())
    			{
    				CAN.readMsgBuf(&len, buf);
    				INT32U canId = CAN.getCanId();
    				
    				//filter for 2010 sti packet
    				if (canId == 0x410)
    				{
    					//dis/reassemble packet
    					msg[2] = buf[5]; //rpm 1
    					msg[3] = buf[6]; //rpm 2
    					
    					//send translation to power steering
    					CAN.sendMsgBuf(0x140, 0, 8, msg);
    					Serial.print(millis());
    					Serial.print(", Crafted Packet 0x140, ");
    					
    					for(int i = 0; i<len; i++) // print the data
    					{
    						Serial.print(msg[i], HEX);Serial.print(", ");
    					}
    				
    					Serial.println();
    				}
    				
    				switch (canId)
    				{
    					case 0x370:
    						Serial.print(millis());
    						Serial.print(", WRX EPS Current 0x370");
    						wantPacket = true;
    						break;
    					
    					case 0x371:
    						Serial.print(millis());
    						Serial.print(", WRX EPS Something 0x371");
    						wantPacket = true;
    						break;
    						
    					case 0x410:
    						Serial.print(millis());
    						Serial.print(", ECU Engine 0x410");
    						wantPacket = true;
    						break;
    				}
    
    				if (wantPacket)
    				{
    					Serial.print(", ");
    					
    					for(int i = 0; i<len; i++) // print the data
    					{
    						Serial.print(buf[i], HEX);
    						Serial.print(", ");
    					}
    					Serial.println();
    					wantPacket = false;
    				}
    			}
    			delay(1);
    
    		}
    		



Prev: Electrical