Subaru SSM3 via Can

Discussions on CAN mapping for ECUs, sensors, PDMs, and more.
Post Reply
boggie1688
Posts: 138
Joined: Fri Apr 07, 2017 3:47 pm
Location: Oakland, CA

Subaru SSM3 via Can

Post by boggie1688 »

Hello all!

I just purchased my second race capture unit for my 2015 WRX. I'll start by saying, I'm not a programmer or engineer, so if I'm asking something totally incorrect, please just stop me.

First and foremost, I thought I'd share this Can Library. I'm not 100% sure how accurate all the information is, but I'll test it when my RCP Track arrives. https://subdiesel.wordpress.com/ecu-ana ... -messages/

I found out subaru has a specific protocol called SSM. Newer generations of subarus communicate on this diagnostic protocol via CAN. This is much faster than serial. It requires you send request commands to the ECU. There is much more vehicle information on SSM. A lot of this is defined by Romraider. Knock sensors, AFR, AF learning, AF correction, fuel pressure, etc.

Moving on. From this page, https://subdiesel.wordpress.com/generic ... 2-via-can/
READ SSM2 ADDRESS(ES)
<CAN ID Tester> A8 00 <Address1[3]> <Address2[3]> ...
Note: Mode byte (after A8) must be 00 – fast poll not supported unlike SSM via Serial.
Request single address, 0x000008 for example:
00 00 07 E0 A8 00 00 00 08
Note: the zero byte after A8 is a padding byte and probably has no effect. Same as in serial SSM2. Just like with serial SSM2 you can request multiple addresses at once by adding more SSM addresses (3 bytes each).

Example: DPF Regeneration Count
Need to request two bytes, addresses 0x00029D and 0x00029E, which make up this Diesel parameter.
x [-] = A*256+B like SSM2 0x00029D; tracks fully completed regenerations only
x: response payload data as 16 bit unsigned integer (uint16), big endian; alternative view: A = high (first) byte, B = low (second) byte
00 00 07 E0 A8 00 00 02 9D 00 02 9E
Example respose:
00 00 07 E8 E8 01 23
&#8594; SSM data bytes are "01 23"
&#8594; x = 0x0123 = decimal 291; &#8594; result = 291 regenerations
Alternative view by handling individual bytes: &#8594; A = 0x01; B = 0x23 &#8594; result = 0x01 * 0x100 + 0x23 = decimal 1 * 256 + 35 = 291
Ecu ID = 000007E0 or 2016 (dec)

I assume this would be my transmit Can ID to communicate with the ecu.

A8 00 (hex)

A8 = 168 (dec)
00 = 0 (dec)

I'm not sure what this is for, but it appears required. The quote above states that 00 is required as padding, I assume this would be another byte in the payload.

Given the above request of:
00 00 07 E0 A8 00 00 02 9D 00 02 9E

00029D = 669
00029E = 670

Would it be correct to guess that this would be the correct txCAN command?
channel = 0 (assumed I'm connected to CAN 0)
id = 2016
ext = 0
data = {168,0,669,670}
res = txCAN(channel, id, ext, data)


I don't think I can request a dec value greater than 255 in one byte. Is there a way around this? Or am I wrong?

Moving on and breaking down the response:
00 00 07 E8 E8 01 23

000007E8 = 2024

This would be the can ID I look for. Then data would be {E8,01,23} or {232,1,35}. I'm arbitrarily assuming this is how the racecapture software would capture the ecu's response. I know there is a can frame format and that race capture parses this in the background. I could be totally wrong, does anyone have any insight?

There other issue here is that I can only send 8 bytes and I know I will likely need to send more, but I'll start here before going too far down the road.

boggie1688
Posts: 138
Joined: Fri Apr 07, 2017 3:47 pm
Location: Oakland, CA

Post by boggie1688 »

Rom Raider's latest definitions are here: http://www.romraider.com/forum/viewtopic.php?f=8&t=1642
2015 WRX definitions are here: http://www.romraider.com/forum/viewtopi ... =8&t=10701
The xml files contain address and formulas.

boggie1688
Posts: 138
Joined: Fri Apr 07, 2017 3:47 pm
Location: Oakland, CA

Post by boggie1688 »

Made some progress on this, and I hit a wall and need some help.

Here is my arduino script:

Code: Select all

// MCP2515 Mask and Filter example for standard CAN message frames.
// Written by Cory J. Fowler &#40;20140717&#41;

/***********************************************************************************
If you send the following standard IDs below to an Arduino loaded with this sketch
you will find that 0x102 and 0x105 will not get in.

ID in Hex  -   Two Data Bytes!   -  Filter/Mask in HEX
   0x100   + 0000 0000 0000 0000 =   0x01000000
   0x101   + 0000 0000 0000 0000 =   0x01010000
   0x102   + 0000 0000 0000 0000 =   0x01020000  This example will NOT be receiving this ID
   0x103   + 0000 0000 0000 0000 =   0x01030000
   0x104   + 0000 0000 0000 0000 =   0x01040000
   0x105   + 0000 0000 0000 0000 =   0x01050000  This example will NOT be receiving this ID
   0x106   + 0000 0000 0000 0000 =   0x01060000
   0x107   + 0000 0000 0000 0000 =   0x01070000

   This mask will check the filters against ID bit 8 and ID bits 3-0.   
    MASK   + 0000 0000 0000 0000 =   0x010F0000
   
   If there is an explicit filter match to those bits, the message will be passed to the
   receive buffer and the interrupt pin will be set.
   This example will NOT be exclusive to ONLY the above frame IDs, for that a mask such
   as the below would be used&#58; 
    MASK   + 0000 0000 0000 0000 = 0x07FF0000
    
   This mask will check the filters against all ID bits and the first data byte&#58;
    MASK   + 1111 1111 0000 0000 = 0x07FFFF00
   If you use this mask and do not touch the filters below, you will find that your first
   data byte must be 0x00 for the message to enter the receive buffer.
   
   At the moment, to disable a filter or mask, copy the value of a used filter or mask.
   
   Data bytes are ONLY checked when the MCP2515 is in 'MCP_STDEXT' mode via the begin
   function, otherwise &#40;'MCP_STD'&#41; only the ID is checked.
***********************************************************************************/


#include <mcp_can.h>
#include <SPI.h>

long unsigned int rxId;
unsigned char len = 0;
unsigned char rxBuf&#91;8&#93;;

MCP_CAN CAN0&#40;9&#41;;                          // Set CS to pin 10

void setup&#40;&#41;
&#123;
  Serial.begin&#40;1000000&#41;;
  if&#40;CAN0.begin&#40;MCP_STD, CAN_500KBPS, MCP_16MHZ&#41; == CAN_OK&#41; Serial.print&#40;"MCP2515 Init Okay!!\r\n"&#41;;
  else Serial.print&#40;"MCP2515 Init Failed!!\r\n"&#41;;
  pinMode&#40;2, INPUT&#41;;                       // Setting pin 2 for /INT input

  CAN0.init_Mask&#40;0,0,0x7FF0000&#41;;
  CAN0.init_Mask&#40;1,0,0x7FF0000&#41;;
  CAN0.init_Filt&#40;0,0,0x7E80000&#41;;                // Init first filter...
  CAN0.init_Filt&#40;1,0,0x7E80000&#41;;
  CAN0.init_Filt&#40;2,0,0x7E80000&#41;;
  CAN0.init_Filt&#40;3,0,0x7E80000&#41;;
  CAN0.init_Filt&#40;4,0,0x7E80000&#41;;
  CAN0.init_Filt&#40;5,0,0x7E80000&#41;;
 

  Serial.println&#40;"MCP2515 Library Mask & Filter Example..."&#41;;
  CAN0.setMode&#40;MCP_NORMAL&#41;;                // Change to normal mode to allow messages to be transmitted

  byte goldenp&#91;8&#93; = &#123;0x02,0x10,0x03,0x00,0x00,0x00,0x00,0x00&#125;;
  byte sndStat = CAN0.sendMsgBuf&#40;0x7DF, 0, 8, goldenp&#41;;
&#125;

void loop&#40;&#41;
&#123;
  coolt&#40;&#41;;
&#125;

void sendtoECU&#40;byte data&#91;&#93;&#41;
&#123;

  // send data&#58;  ID = 0x100, Standard CAN Frame, Data length = 8 bytes, 'data' = array of data bytes to send
  byte sndStat = CAN0.sendMsgBuf&#40;0x7E0,0,8,data&#41;;
  if&#40;sndStat == CAN_OK&#41;&#123;
    Serial.println&#40;"ECU Request Sent"&#41;;
  &#125; else &#123;
    Serial.println&#40;"Error"&#41;;
  &#125;
&#125;

void sendtoRCP&#40;byte data&#91;&#93;&#41;
&#123;
  // send data&#58;  ID = 0x100, Standard CAN Frame, Data length = 8 bytes, 'data' = array of data bytes to send
  byte sndStat = CAN0.sendMsgBuf&#40;0x7E9, 0, 8, data&#41;;
  if&#40;sndStat == CAN_OK&#41;&#123;
      Serial.print&#40;"ID&#58; "&#41;;
      Serial.print&#40;0x7E9, HEX&#41;;
      Serial.print&#40;" Data&#58; "&#41;;
      for&#40;int i = 0; i<len; i++&#41;           // Print each byte of the data
      &#123;
        if&#40;data&#91;i&#93; < 0x10&#41;                // If data byte is less than 0x10, add a leading zero
        &#123;
          Serial.print&#40;"0"&#41;;
        &#125;
        Serial.print&#40;data&#91;i&#93;, HEX&#41;;
        Serial.print&#40;" "&#41;;
      &#125;
      Serial.println&#40;&#41;;
  &#125;
&#125;

void coolt&#40;&#41;
&#123;
  byte coolt1&#91;8&#93; = &#123;0x10,0x0B,0xA8,0x00,0x00,0x00,0x08,0xF8&#125;;
  byte coolt2&#91;8&#93; = &#123;0x21,0xCD,0x64,0xF8,0xCD,0x65,0x00,0x00&#125;;
  sendtoECU&#40;coolt1&#41;;
  sendtoECU&#40;coolt2&#41;;
  int get = 2;    //rxBuf array starts at 0
  int send = 1;
  read&#40;get, send&#41;;
&#125;

void read&#40;int get,int send&#41;
&#123;
    if&#40;!digitalRead&#40;2&#41;&#41;                    // If pin 2 is low, read receive buffer
    &#123;
      CAN0.readMsgBuf&#40;&rxId, &len, rxBuf&#41;; // Read data&#58; len = data length, buf = data byte&#40;s&#41;
      if &#40;rxBuf&#91;1&#93; == 0xE8 && rxBuf&#91;0&#93; == 0x04&#41;;
      &#123;
        Serial.println&#40;rxBuf&#91;0&#93;&#41;;
        Serial.println&#40;rxBuf&#91;1&#93;&#41;;
        Serial.println&#40;rxBuf&#91;2&#93;&#41;;
        Serial.println&#40;rxBuf&#91;get&#93;&#41;;
        byte rcpBuf&#91;send&#93; = &#123;rxBuf&#91;get&#93;&#125;;
        Serial.println&#40;rcpBuf&#91;1&#93;&#41;;
        Serial.println&#40;rcpBuf&#91;0&#93;&#41;;
        Serial.print&#40;"ID&#58; "&#41;;
        Serial.print&#40;rxId, HEX&#41;;
        Serial.print&#40;" Data&#58; "&#41;;
        for&#40;int i = 0; i<len; i++&#41;           // Print each byte of the data
        &#123;
          if&#40;rxBuf&#91;i&#93; < 0x10&#41;                // If data byte is less than 0x10, add a leading zero
          &#123;
            Serial.print&#40;"0"&#41;;
          &#125;
          Serial.print&#40;rxBuf&#91;i&#93;, HEX&#41;;
          Serial.print&#40;" "&#41;;
        &#125;
        Serial.println&#40;&#41;;
        sendtoRCP&#40;rcpBuf&#41;;
      &#125;
    &#125;
&#125;
The script is design to:
1. Send a start diagnostic packet in the setup function.
2. Runs through the loop.
3. A function is called in the loop, IE coolT().
4. Within coolT(), two can packets are defined, then passed to function sendtoECU().
5. Within coolT(), two integers are sent to the read() function. These integers define which byte to pull from the packet response from the ECU, and secondly where two put the byte when sent to the function sendtoRCP().
6. The function call to send the can packet to the RCP is at the end of read().

I thought this was some nifty programming but apparently not. Mind you I have a lot of prints inside of this code because I keep coming up with an issue here. Everything here is working, I connect both the arduino and the RC Track, sorry I keep saying RCP. Using the sniffer script for the RC, I can see the 2016 packets going to the ecu, the 2024 packets coming back from the ecu, then the 2025 packet being sent out to the RCP.

The first packet is:
0x04 0xE8 0x6F 0x13 0xF7 0x00 0x00 0x00 (coolT only cares about the 3 byte)
The second packet:
0x30 0x00 0x00 0x00 0x00 0x00 0x00 0x00

Because this is iso TP, and I'm implementing iso tp, I think the second packet tells the arduino that this is the end of the multi frame message.

Ok everything works great when the arduino see the first packet, but every once in awhile the second packet makes it in. Because the third byte is 0x00, the arduino takes that and sends it to the RCP. On the dashboard, I'll get the right value but every once in awhile it will zero out because of the 0x00 byte.

I tried to put an if statement to check the 2nd byte of the packet to make sure it is 0xE8. This one byte will always be in the same position, and is never found in the 2nd packet. However, my if statement doesn't seem to work.

Here is an example of the output when I run the above code:

Code: Select all

ECU Request Sent
ECU Request Sent
48
0
0
0
63
0
ID&#58; 7E8 Data&#58; 30 00 00 00 00 00 00 00 
ID&#58; 7E9 Data&#58; 00 3F 03 00 61 00 88 02 
ECU Request Sent
ECU Request Sent
4
232
111
111
63
111
ID&#58; 7E8 Data&#58; 04 E8 6F 13 FD 00 00 00 
ID&#58; 7E9 Data&#58; 6F 3F 03 00 61 00 88 02 
ECU Request Sent
ECU Request Sent
On a brighter not, I'm about 80% done mapping the parameters I want from SSM. They are the following:
AF Correction
AF Learning, AFR
AVCS Exhaust L/R
AVCS Intake L/R
Closed Loop On or Off
Ignition Timing
Intake Temp
Map Voltage
Wastegate Duty

I need to confirm but I think I can get the following directly from the can bus:
Oil Temp
Coolant Temp
RPM
Throttle Position
Steering Angle
Brake

I'm having issues decoding:
Intake Manifold Temp
Boost
DAM
Feedback Knock
Fine Knock Learning

DAM, Feedback Knock, Fine Knock Learning are important to me, but the car is tuned so well that it never knocks. Its like trying to catch a ghost.

boggie1688
Posts: 138
Joined: Fri Apr 07, 2017 3:47 pm
Location: Oakland, CA

Post by boggie1688 »

Code: Select all

#include <mcp_can.h>
#include <SPI.h>

long unsigned int rxId;
unsigned char len = 0;
unsigned char rxBuf&#91;8&#93;;

MCP_CAN CAN0&#40;9&#41;;                          // Set CS to pin 10

void setup&#40;&#41;
&#123;
  Serial.begin&#40;1000000&#41;;
  if&#40;CAN0.begin&#40;MCP_STD, CAN_500KBPS, MCP_16MHZ&#41; == CAN_OK&#41; Serial.print&#40;"MCP2515 Init Okay!!\r\n"&#41;;
  else Serial.print&#40;"MCP2515 Init Failed!!\r\n"&#41;;
  pinMode&#40;2, INPUT&#41;;                       // Setting pin 2 for /INT input

  CAN0.init_Mask&#40;0,0,0x7FF0000&#41;;
  CAN0.init_Mask&#40;1,0,0x7FF0000&#41;;
  CAN0.init_Filt&#40;0,0,0x7E80000&#41;;                // Init first filter...
  CAN0.init_Filt&#40;1,0,0x7E80000&#41;;
  CAN0.init_Filt&#40;2,0,0x7E80000&#41;;
  CAN0.init_Filt&#40;3,0,0x7E80000&#41;;
  CAN0.init_Filt&#40;4,0,0x7E80000&#41;;
  CAN0.init_Filt&#40;5,0,0x7E80000&#41;;
 

  Serial.println&#40;"MCP2515 Library Mask & Filter Example..."&#41;;
  CAN0.setMode&#40;MCP_NORMAL&#41;;                // Change to normal mode to allow messages to be transmitted

  byte goldenp&#91;8&#93; = &#123;0x02,0x10,0x03,0x00,0x00,0x00,0x00,0x00&#125;;
  byte sndStat = CAN0.sendMsgBuf&#40;0x7DF, 0, 8, goldenp&#41;;
&#125;

void loop&#40;&#41;
&#123;
igntiming&#40;&#41;;
afc&#40;&#41;;
&#125;

void sendtoECU&#40;byte data&#91;&#93;&#41;
&#123;
  byte sndStat = CAN0.sendMsgBuf&#40;0x7E0,0,8,data&#41;;
&#125;

void sendtoRCP&#40;byte data&#91;&#93;, int id&#41;
&#123;
  byte sndStat = CAN0.sendMsgBuf&#40;id, 0, 8, data&#41;;
&#125;



void coolt&#40;&#41;
&#123;
  byte coolt1&#91;8&#93; = &#123;0x10,0x0B,0xA8,0x00,0x00,0x00,0x08,0xF8&#125;;
  byte coolt2&#91;8&#93; = &#123;0x21,0xCD,0x64,0xF8,0xCD,0x65,0x00,0x00&#125;;
  sendtoECU&#40;coolt1&#41;;
  sendtoECU&#40;coolt2&#41;;
  int get = 2;    //rxBuf array starts at 0
  int send = 1;
  int id = 0x7E9; //ID 2025
  read&#40;get, send, id&#41;;
&#125;

void avcsil&#40;&#41;
&#123;
  byte avcsil1&#91;8&#93; = &#123;0x10,0x0B,0xA8,0x00,0x00,0x00,0x3D,0xF8&#125;;
  byte avcsil2&#91;8&#93; = &#123;0x21,0xCD,0x64,0xF8,0xCD,0x65,0x00,0x00&#125;;
  sendtoECU&#40;avcsil1&#41;;
  sendtoECU&#40;avcsil2&#41;;
  int get = 2;    //rxBuf array starts at 0
  int send = 1;
  int id = 0x7EA; //ID 2026
  read&#40;get, send, id&#41;;
&#125;

void avcsir&#40;&#41;
&#123;
  byte avcsir1&#91;8&#93; = &#123;0x10,0x0B,0xA8,0x00,0x00,0x00,0x3D,0xF8&#125;;
  byte avcsir2&#91;8&#93; = &#123;0x21,0xCD,0x64,0xF8,0xCD,0x65,0x00,0x00&#125;;
  sendtoECU&#40;avcsir1&#41;;
  sendtoECU&#40;avcsir2&#41;;
  int get = 2;    //rxBuf array starts at 0
  int send = 1;
  int id = 0x7EB; //ID 2027
  read&#40;get, send, id&#41;;
&#125;

void avcsel&#40;&#41;
&#123;
  byte avcsell&#91;8&#93; = &#123;0x10,0x0B,0xA8,0x00,0x00,0x01,0x19,0xF8&#125;;
  byte avcsel2&#91;8&#93; = &#123;0x21,0xCD,0x64,0xF8,0xCD,0x65,0x00,0x00&#125;;
  sendtoECU&#40;avcsell&#41;;
  sendtoECU&#40;avcsel2&#41;;
  int get = 2;    //rxBuf array starts at 0
  int send = 1;
  int id = 0x7EC; //ID 2028
  read&#40;get, send, id&#41;;
&#125;

void avcser&#40;&#41;
&#123;
  byte avcser1&#91;8&#93; = &#123;0x10,0x0B,0xA8,0x00,0x00,0x01,0x18,0xF8&#125;;;
  byte avcser2&#91;8&#93; = &#123;0x21,0xCD,0x64,0xF8,0xCD,0x65,0x00,0x00&#125;;
  sendtoECU&#40;avcser1&#41;;
  sendtoECU&#40;avcser2&#41;;
  int get = 2;    //rxBuf array starts at 0
  int send = 1;
  int id = 0x7ED; //ID 2029
  read&#40;get, send, id&#41;;
&#125;
void igntiming&#40;&#41;
&#123;
  byte igntiming1&#91;8&#93; = &#123;0x10,0x0B,0xA8,0x00,0x00,0x00,0x11,0xF8&#125;;
  byte igntiming2&#91;8&#93; = &#123;0x21,0xCD,0x64,0xF8,0xCD,0x65,0x00,0x00&#125;;
  sendtoECU&#40;igntiming1&#41;;
  sendtoECU&#40;igntiming2&#41;;
  int get = 2;    //rxBuf array starts at 0
  int send = 1;
  int id = 0x7EE; //ID 2030
  read&#40;get, send, id&#41;;
&#125;

void afl&#40;&#41;
&#123;
  byte afl1&#91;8&#93; = &#123;0x10,0x0B,0xA8,0x00,0x00,0x00,0x0A,0xF8&#125;;
  byte afl2&#91;8&#93; = &#123;0x21,0x8A,0xF9,0xF8,0xCD,0x64,0xF8,0xCD&#125;;
  sendtoECU&#40;afl1&#41;;
  sendtoECU&#40;afl2&#41;;
  int get = 2;    //rxBuf array starts at 0
  int send = 1;
  int id = 0x7EF; //ID 2031
  read&#40;get, send, id&#41;;
&#125;

void afc&#40;&#41;
&#123;
  byte afc1&#91;8&#93; = &#123;0x10,0x0B,0xA8,0x00,0x00,0x00,0x09,0xF8&#125;;
  byte afc2&#91;8&#93; = &#123;0x21,0x8A,0xF9,0xF8,0xCD,0x64,0xF8,0xCD&#125;;
  sendtoECU&#40;afc1&#41;;
  sendtoECU&#40;afc2&#41;;
  int get = 2;    //rxBuf array starts at 0
  int send = 1;
  int id = 0x7F0; //ID 2032
  read&#40;get, send, id&#41;;
&#125;

void read&#40;int get,int send, int id&#41;
&#123;
    if&#40;!digitalRead&#40;2&#41;&#41;                    // If pin 2 is low, read receive buffer
    &#123;
      CAN0.readMsgBuf&#40;&rxId, &len, rxBuf&#41;; // Read data&#58; len = data length, buf = data byte&#40;s&#41;
      byte rcpBuf&#91;send&#93; = &#123;rxBuf&#91;get&#93;&#125;;
      int test = rxBuf&#91;1&#93;;
      if &#40;test == 0xE8&#41;
      &#123;
        sendtoRCP&#40;rcpBuf, id&#41;;
      &#125;
    &#125;
&#125;
There something fishy going on here and I can't put my finger on it.

So I change the buffer test slightly, which worked. Then I started creating functions for all the different parameters. I can only enable one, or two before shit goes screwy. The values start to jump around a bit on the dashboard. I'm not entirely sure where to start with the diagnosis. IE: Am I overloading the arduino? Overloading the canbus? Overloading the RCP?

If anyone has insight, it would be appreciated.

boggie1688
Posts: 138
Joined: Fri Apr 07, 2017 3:47 pm
Location: Oakland, CA

Post by boggie1688 »

I think my issue here is making multiple single calls for each parameter.

I'm forced to figure out this ISO TP stuff.

Except, I'm going to do it in the same mimic way I did above. Now that I've decoded so many of the different individual requests, the byproduct is I can figure out what the multi frame request and receive should be.

I might be able to salvage this project.

boggie1688
Posts: 138
Joined: Fri Apr 07, 2017 3:47 pm
Location: Oakland, CA

Post by boggie1688 »

Code: Select all

#include <mcp_can.h>
#include <SPI.h>

long unsigned int rxId;
unsigned char len = 0;
unsigned char rxBuf&#91;8&#93;;
int it = 3;
int mv = 4;
int cl = 5;
int afr1 = 6;
int afr2 = 7;
int afc = 1;
int afl = 2;

MCP_CAN CAN0&#40;9&#41;;                          // Set CS to pin 10

void setup&#40;&#41;
&#123;
  Serial.begin&#40;1000000&#41;;
  if&#40;CAN0.begin&#40;MCP_STD, CAN_500KBPS, MCP_16MHZ&#41; == CAN_OK&#41; Serial.print&#40;"MCP2515 Init Okay!!\r\n"&#41;;
  else Serial.print&#40;"MCP2515 Init Failed!!\r\n"&#41;;
  pinMode&#40;2, INPUT&#41;;                       // Setting pin 2 for /INT input

  CAN0.init_Mask&#40;0,0,0x7FF0000&#41;;
  CAN0.init_Mask&#40;1,0,0x7FF0000&#41;;
  CAN0.init_Filt&#40;0,0,0x7E80000&#41;;                // Init first filter...
  CAN0.init_Filt&#40;1,0,0x7E80000&#41;;
  CAN0.init_Filt&#40;2,0,0x7E80000&#41;;
  CAN0.init_Filt&#40;3,0,0x7E80000&#41;;
  CAN0.init_Filt&#40;4,0,0x7E80000&#41;;
  CAN0.init_Filt&#40;5,0,0x7E80000&#41;;
 

  Serial.println&#40;"MCP2515 Library Mask & Filter Example..."&#41;;
  CAN0.setMode&#40;MCP_NORMAL&#41;;                // Change to normal mode to allow messages to be transmitted

  byte goldenp1&#91;8&#93; = &#123;0x02,0x10,0x03,0x00,0x00,0x00,0x00,0x00&#125;;
  byte sndStat1 = CAN0.sendMsgBuf&#40;0x7DF, 0, 8, goldenp1&#41;;
  byte goldenp2&#91;8&#93; = &#123;0x02,0x27,0x03,0x00,0x00,0x00,0x00,0x00&#125;;
  byte sndStat2 = CAN0.sendMsgBuf&#40;0x7E0, 0, 8, goldenp2&#41;;
&#125;

void loop&#40;&#41;
&#123;
mimic&#40;&#41;;
&#125;

void sendtoECU&#40;byte data&#91;&#93;&#41;
&#123;
  byte sndStat = CAN0.sendMsgBuf&#40;0x7E0,0,8,data&#41;;
  delay&#40;.0005&#41;;
&#125;

void sendtoRCP&#40;byte data&#91;&#93;, int id&#41;
&#123;
  byte sndStat = CAN0.sendMsgBuf&#40;id, 0, 8, data&#41;;
&#125;



void mimic&#40;&#41;
&#123;
  byte mimic1&#91;8&#93; = &#123;0x10,0x1D,0xA8,0x00,0x00,0x00,0x12,0x00&#125;;
  byte mimic2&#91;8&#93; = &#123;0x21,0x00,0x1D,0xF8,0xAB,0x7F,0xF8,0x8A&#125;;
  byte mimic3&#91;8&#93; = &#123;0x22,0xF8,0xF8,0x8A,0xF9,0x00,0x00,0x09&#125;;
  byte mimic4&#91;8&#93; = &#123;0x23,0x00,0x00,0x0A,0xF8,0xCD,0x64,0xF8&#125;;
  byte mimic5&#91;8&#93; = &#123;0x24,0xCD,0x65,0x00,0x00,0x00,0x00,0x00&#125;;
  byte mimic6&#91;8&#93; = &#123;0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00&#125;;
  read&#40;&#41;;
  read&#40;&#41;;
  sendtoECU&#40;mimic1&#41;;
  read&#40;&#41;;
  read&#40;&#41;;
  sendtoECU&#40;mimic2&#41;;
  read&#40;&#41;;
  read&#40;&#41;;
  sendtoECU&#40;mimic3&#41;;
  read&#40;&#41;;
  read&#40;&#41;;
  sendtoECU&#40;mimic4&#41;;
  read&#40;&#41;;
  read&#40;&#41;;
  sendtoECU&#40;mimic5&#41;;
  read&#40;&#41;;
  read&#40;&#41;;
  sendtoECU&#40;mimic6&#41;;
  read&#40;&#41;;
  read&#40;&#41;;
&#125;

void read&#40;&#41;
&#123;
    if&#40;!digitalRead&#40;2&#41;&#41;                    // If pin 2 is low, read receive buffer
    &#123;
      CAN0.readMsgBuf&#40;&rxId, &len, rxBuf&#41;; // Read data&#58; len = data length, buf = data byte&#40;s&#41;
      int test = rxBuf&#91;0&#93;;
      if &#40;test ==0x10&#41;
      &#123;
        byte rcpBuf&#91;&#93; = &#123;rxBuf&#91;it&#93;,rxBuf&#91;mv&#93;,rxBuf&#91;cl&#93;,rxBuf&#91;afr1&#93;,rxBuf&#91;afr2&#93;&#125;;
        sendtoRCP&#40;rcpBuf, 0x7E9&#41;;
      &#125;
        if &#40;test == 0x21&#41;
        &#123;
        byte rcpBuf&#91;&#93; = &#123;rxBuf&#91;afc&#93;,rxBuf&#91;afl&#93;&#125;;
        sendtoRCP&#40;rcpBuf, 0x7EA&#41;;
        &#125;
    &#125;
&#125;

Code change to mimic an iso to multi frame packet from the accessport. I then decoded the multi frame packet back, and adjust my read function to pick the bytes needed. It works. I can query 6 different parameters in one go, however the dashboard updates very slowly.

I'm not sure if I am overwhelming the ecu with request packets or not. I will fiddle with delays to see how it might help.

boggie1688
Posts: 138
Joined: Fri Apr 07, 2017 3:47 pm
Location: Oakland, CA

Post by boggie1688 »

The big reveal!

https://youtu.be/gqfbOfd2MUM

2015 WRX
Accessport Installed (not sure if these requests work on a stock rom ECU)

The SSM3 was mapped by using the AP to poll the ECU's diagnostic channel.
ECU response comes on CAN ID 2024 or 0x7E8.
AP/RC requests must be done sent on CAN ID 2016 or 0x7E0

I also think that a one time packet must be sent to the ECU on 2015 or 0x7DF.
txCAN(0,2015,0,{2,16,3,0,0,0,0,0});



**************
Requesting Data
**************

The accessport has multiple parameters it can requests, because of this multiple can packets are sent to the ECU. The number of packets changes depending on how many parameters you wish to log. I have not decoded every single parameter. Instead I selected the ones I wanted, used the AP to poll the ECU, and sniffed which packets were being send on 0x7E0.

For the following parameters:
Air/Fuel Correction
Air/Fuel Learning
Air/Fuel Ratio
Dynamic Advance Multiplier
Ethanol Raw
Feedback Knock Correction
Fine Knock Learning
Fuel Pressure
Gear Position
Ignition Timing
Intake Temp
Intake Manifold Temp
RPM

Each of the above parameters has a specific address it calls on. The sum of the address adds up to the overall request to the ecu. See below.

txCAN(0,2016,0,{16,53,168,0,0,0,9,0})
txCAN(0,2016,0,{33,0,10,248,138,248,248,138})
txCAN(0,2016,0,{34,249,248,17,174,248,60,62})
txCAN(0,2016,0,{35,248,60,63,248,167,170,248})
txCAN(0,2016,0,{36,167,183,248,136,132,248,136})
txCAN(0,2016,0,{37,133,248,166,49,0,0,17})
txCAN(0,2016,0,{38,0,0,18,248,136,212,248})
txCAN(0,2016,0,{39,205,100,248,205,101,0,0})
txCAN(0,2016,0,{48,0,0,0,0,0,0,0})

A few things to note here:
1. The second byte of the first packet tells the ECU the length of the request. From here it knows how many packets to expect.
2. The third byte of the first packet tells the ECU this is a read request. It will never change.
3. The first byte of each packet represents the packet number. There will always be a 16 or 0x10 and a 48 or 0x30. They represent the start and end packets. 33,34,35,36,37, / 0x21, 0x22, 0x23, 0x24, 0x25, etc represents the rest of the packets in the request and tell the ecu how to organize the total request.

Code: Select all


txCAN&#40;0,2015,0,&#123;2,16,3,0,0,0,0,0&#125;&#41;;
setTickRate&#40;25&#41;

function onTick&#40;&#41;
txCAN&#40;0,2016,0,&#123;16,53,168,0,0,0,9,0&#125;&#41;
txCAN&#40;0,2016,0,&#123;33,0,10,248,138,248,248,138&#125;&#41;
txCAN&#40;0,2016,0,&#123;34,249,248,17,174,248,60,62&#125;&#41;
txCAN&#40;0,2016,0,&#123;35,248,60,63,248,167,170,248&#125;&#41;
txCAN&#40;0,2016,0,&#123;36,167,183,248,136,132,248,136&#125;&#41;
txCAN&#40;0,2016,0,&#123;37,133,248,166,49,0,0,17&#125;&#41;
txCAN&#40;0,2016,0,&#123;38,0,0,18,248,136,212,248&#125;&#41;
txCAN&#40;0,2016,0,&#123;39,205,100,248,205,101,0,0&#125;&#41;
txCAN&#40;0,2016,0,&#123;48,0,0,0,0,0,0,0&#125;&#41;
end

********************
Decoding ECU Response
********************


The response from the ECU will be in multiple packets. Depending on how many parameters you log the number of packets required to respond will change. For the above parameters, the response will include 4 packets. Here is the ECU response:

0x10 0x0B 0xE8 0x7D 0x7B 0x20 0x07 0x10
0x21 0x20 0x4C 0x00 0x1A 0x96 0x00 0x00
0x22 0xA1 0x50 0x57 0x1A 0x84 0x00 0x00
0x30 0x00 0x00 0x00 0x00 0x00 0x00 0x00

A few things to note:
1. Again the first byte represents the position of the packet within the entire response.
2. Byte two of the first packet represents the length of the response.
3. Byte three of the first packet is the ECU's acknowledgement of the read request and is 0xA8+0x08.

Here is how you decode the response packets:

Air/Fuel Correction
Can ID: 2024 (0x7E8)
Sub ID: 16 (0x10)
Offset: 3
Length: 1
Formula: (x-128)*100/128

Air/Fuel Learning
Can ID: 2024 (0x7E8)
Sub ID: 16 (0x10)
Offset: 4
Length: 1
Formula: (x-128)*100/128

Air/Fuel Ratio
Can ID: 2024 (0x7E8)
Sub ID: 16 (0x10)
Offset: 5
Length: 2
Big Endian
Formula: (x**0.00179443359375)

Dynamic Advance Multiplier
Can ID: 2024 (0x7E8)
Sub ID: 16 (0x10)
Offset: 7
Length: 1
Formula: (x/16)

Ethanol Raw
Can ID: 2024 (0x7E8)
Sub ID: 33 (0x21)
Offset: 1
Length: 2
Big Endian
Formula: (x/656)

Feedback Knock Correction (Formula is still under testing)
Can ID: 2024 (0x7E8)
Sub ID: 33 / 0x21
Offset: 3
Length: 1
Formula: ??
Under normal running conditions this should be 0. The AP reports knock in increments of -1.41. (-1.41, -2.81, -4.2, etc) There is only one byte that returns a result. Thus far I've seen -1.41 and a byte value of 255 / 0xFF. Still working on this, but I know that anything greater than 0 equals knock.

Fine Knock Learning (Formula is still under testing)
Can ID: 2024 (0x7E8)
Sub ID: 33 / 0x21
Offset: 4
Length: 1
Formula: ?? (see my answer for Feedback Knock Correction)

Fuel Pressure
Can ID: 2024 (0x7E8)
Sub ID: 33 (0x21)
Offset: 5
Length: 2
Formula: (x/10)

Gear Position
Can ID: 2024 (0x7E8)
Sub ID: 33 (0x21)
Offset: 7
Length: 1
Formula: X

Ignition Timing
Can ID: 2024 (0x7E8)
Sub ID: 34 (0x22)
Offset: 1
Length: 1
Formula: (x-128)/2

Intake Temp
Can ID: 2024 (0x7E8)
Sub ID: 34 (0x22)
Offset: 2
Length: 1
Formula: 32+9(x-40)/5
(formula includes C->F conversion)

Intake Manifold Temp
Can ID: 2024 (0x7E8)
Sub ID: 34 (0x22)
Offset: 3
Length: 1
Formula: 32+9(x-50)/5
(formula includes C->F conversion)

RPM
Can ID: 2024 (0x7E8)
Sub ID: 34 (0x22)
Offset: 4
Length: 2
Big Endian
Formula: (x/5)


Please note, if you change the request packets, the responses will SHIFT. That means the above mappings will no longer work. For now, you will absolutely need to use the requests packets I provided, to ensure the mappings are correct.


***********************************************
Additional can maps that don't require requests to the ECU
***********************************************

Throttle
Can ID: 320
Offset: 0
Length: 1

Brake (sweep not PSI)
Can ID: 209
Offset: 2
Length: 1

Steering
Can ID: 2
Offset: 0
Length: 2
Big Endian
Source Type: Sign

Coolant Temp
Can ID: 864
Offset: 3
Length: 1
Formula: 32+9(x-40)/5
(Formula includes C->F conversion)

Oil Temp
Can ID: 864
Offset: 2
Length: 1
Formula: 32+9(x-40)/5
(Formula includes C->F conversion)


****************************
Extra Credit - A request to help me
****************************

I'm working to confirm my formula for Ethanol Raw for a Flex Fuel setup. I need more data points and will test more. The same applies to feedback knock correction and fine knock learning.

However, I need help with Boost!

Here is the response packet from the ECU in Hex (Engine Off)
0x06 0xE8 0x55 0xD1 0x65 0x00 0x00 0x00
For the above, boost is at -0.26 ish.

Here is the reponse packet from the ECU in Hex. (Engine Idle)
0x06 0xE8 0x0A 0x9F 0x65 0x1E 0x46 0x00
For the above, boost is at -11.1 ish

For payload equals byte 1-8, I think the bytes of interests are 3 (0x55), 4 (0xD1), 6 (0x00) and 7 (0x00). 6 and 7 are of interest because they change when the engine is on. But really I think it is 3 or 4.

I also have a set of packets for 26+ parameter requests. For some reason, when I send this set of packets, the ecu ignores my requests. If someone wants to help me understand what is happening, it would be awesome. I think it has something to do with the timing of how the packets are sent because if I set the can mappings, then use my Accessport to request the data, everything works fine. The send packets are below:

0x10 0x74 0xA8 0x00 0x00 0x00 0x09 0x00
0x20 0x00 0x15 0x00 0x00 0x30 0x00 0x00
0x21 0x00 0x0A 0xF8 0x8A 0xF8 0xF8 0x8A
0x22 0xF9 0x00 0x01 0x19 0x00 0x01 0x18
0x23 0x00 0x00 0x3D 0x00 0x00 0x3C 0xF8
0x24 0x92 0x64 0xF8 0x92 0x65 0x00 0x00
0x25 0x23 0xF8 0xA6 0x26 0xF8 0xAB 0x7F
0x26 0x00 0x00 0x08 0xF8 0x11 0xAE 0xF8
0x27 0x3C 0x3E 0xF8 0x3C 0x3F 0xF8 0xA7
0x28 0xAA 0xF8 0xA7 0xB7 0xF8 0x88 0x84
0x29 0xF8 0x88 0x85 0xF8 0xA6 0x31 0x00
0x2A 0x00 0x11 0x00 0x00 0x20 0x00 0x00
0x2B 0x0E 0x00 0x00 0x0F 0x00 0x00 0x12
0x2C 0xF8 0x88 0xD4 0x00 0x00 0x1D 0xF8
0x2D 0xCD 0x64 0xF8 0xCD 0x65 0xF8 0xAE
0x2E 0xDA 0xF8 0xAE 0xDB 0xF8 0xB3 0xC0
0x2F 0xF8 0xB3 0xC1 0x00 0x00 0x23 0x00
0x30 0x00 0x00 0x00 0x00 0x00 0x00 0x00


Parameters requested:
Air/Fuel Correction
Air/Fuel Learning
Air/Fuel Ration
AVCS Exhaust Left
AVCS Exhaust Right
AVCS Intake Left
AVCS Intake Right
Boost
Calculated Load
Cloosed Loop
Coolant T
Dynamic Advance Multiplier
Ethanol Raw
Feedback Knock Correction
Fine Knock Learning
Fuel Pressure
Gear Pos
Ignition Timing
Injector Duty
Intake Temp
Intake Manifold
MAF Voltage
RPM
Requested Torque
Target Boost
Throttle Position
Wastegate Duty

brentp
Site Admin
Posts: 6282
Joined: Wed Jan 24, 2007 6:36 am

Post by brentp »

This is great progress!

For help on the formula, I'd rope in some other subaru enthusiasts that might know more. Point them here and they may be able to unlock some secrets!
Brent Picasso
CEO and Founder, Autosport Labs
Facebook | Twitter

boggie1688
Posts: 138
Joined: Fri Apr 07, 2017 3:47 pm
Location: Oakland, CA

Post by boggie1688 »

Made some changes and thought I'd share

Code: Select all

txCAN&#40;0,2016,0,&#123;16,68,168,0,248,17,174,0&#125;&#41;
txCAN&#40;0,2016,0,&#123;33,0,9,0,0,10,248,138&#125;&#41;
txCAN&#40;0,2016,0,&#123;34,248,248,138,249,0,1,25&#125;&#41;
txCAN&#40;0,2016,0,&#123;35,0,1,24,0,0,61,0&#125;&#41;
txCAN&#40;0,2016,0,&#123;36,0,60,0,0,35,248,60&#125;&#41;
txCAN&#40;0,2016,0,&#123;37,62,248,60,63,248,136,132&#125;&#41;
txCAN&#40;0,2016,0,&#123;38,248,136,133,248,166,49,0&#125;&#41;
txCAN&#40;0,2016,0,&#123;39,0,17,0,0,18,248,136&#125;&#41;
txCAN&#40;0,2016,0,&#123;40,212,248,146,100,248,146,101&#125;&#41;
txCAN&#40;0,2016,0,&#123;41,248,205,100,248,205,101,0&#125;&#41;
txCAN&#40;0,2016,0,&#123;48,0,0,0,0,0,0,0&#125;&#41;
This set of commands requests:
AF Correction
AF Learning
AF Ratio
AVCS Exhaust Left
AVCS Exhaust Right
AVCS Intake Left
AVCS Intake Right
Barometer Pressure
DAM
Ethanol Raw
Fuel Pressure
Gear Position
Ignition Timing
Intake Temp
Intake Manifold Temp
Manifold Pressure
RPM

New additions are all the AVCS Cam information. Needed to log cam activation, compare its sync and also watch its affects on oil pressure. Also added Barometer pressure and Manifold pressure to calculate Boost!!

Note, barometer and manifold are in kpa. Convert to PSI by multiplying by .145038. Boost is manifold pressure - barometer pressure + 2. Why the +2, I don't know. But doing so results in a matching value to the cars boost gauge.

Formula for AVCS is (x-50)

The rest of the formulas are the same as above.

The ECU will reply with:

Code: Select all

ID	1	2	3	4	5	6	7	8
2024	16	23	232	16	128	128	32	0
2024	33	50	50	50	50	102	44	144
2024	34	2	208	0	128	51	62	87
2024	35	228	0	0	0	0	0	0
2024	48	0	0	0	0	0	0	0
ID Sub ID Byte
AFC 2024 16 5
AFL 2024 16 6
AFR 2024 16 7,8
AVCS E L 2024 33 2
AVCS E R 2024 33 3
AVCS I L 2024 33 4
AVCS I R 2024 33 5
Baro 2024 33 6
DAM 2024 16 4
Ethanol Raw 2024 33 7,8
Fuel Pressure 2024 34 2,3
Gear Pos 2024 34 4
Ignition Timing 2024 34 5
Intake Temp 2024 34 6
Intake Manifold 2024 34 7
Manifold Pressure 2024 34 8
RPM 2024 35 2,3

NOTE: In my spreadsheets the byte start at 1 and run to 8. For Racecapture they start at 0 and run to 7. Adjust accordingly.

Have fun!

boggie1688
Posts: 138
Joined: Fri Apr 07, 2017 3:47 pm
Location: Oakland, CA

Post by boggie1688 »

Quick addition here.

These are wheel speed sensors directly from the network. No active request is needed to the ECU. This should work on all cars using the Hitachi ecu. Non EJ motors, FA20 engines.

Can ID:
212

FL
Byte: 0,1
Length: 2
FR
Byte 2,3
Length: 2
RL
Byte 4,5
Length: 2
RR
Byte 6,7
Length: 2

All are unsigned.
Formula: X * 0.05625 = km/h

Also working on determining Traction control state, but it looks like the bit value position varies year to year.

brentp
Site Admin
Posts: 6282
Joined: Wed Jan 24, 2007 6:36 am

Post by brentp »

Nice progress. Do you have this in github? or is the script you posted here the latest?

Is the hardware basically an arduino with a CAN shield?
Brent Picasso
CEO and Founder, Autosport Labs
Facebook | Twitter

boggie1688
Posts: 138
Joined: Fri Apr 07, 2017 3:47 pm
Location: Oakland, CA

Post by boggie1688 »

Script 2-3 posts above is the latest.

This is all done on the racecapture track. That script sends all the requests to the ecu, and then I just use the can mapping to decode the replies. :D

The only problem is if you don't copy the script exactly, say you omit a request parameter, it shifts the replies entirely. Each time you want to compile a new set of requests, you gotta figure out the reply sub ids, offset, and lengths. At some point, I'll make a sheet that has all the info and you can just select and it auto generates a set of request and tells you what the decode sub id, offset and lengths are.

I think I got most of the TCS stuff, but I kinda want to confirm it before I post it.

boggie1688
Posts: 138
Joined: Fri Apr 07, 2017 3:47 pm
Location: Oakland, CA

Post by boggie1688 »

TCS Mapping is done.

Can ID:
211
Set to Bit Mode

"Traction Control" - does not have a light on the dash. However, trac mode or traction control is completely disable, this value goes to 0. A value of 1 indicates the system is full active.
Offset: 31
Length: 1

"Trac Mode" - a value of 0 indicates the Trac Mode light is disabled, meaning the car is not in trac mode. A value of 1 indicates the Trac Mode light is illuminated, and the car is in Trac Mode.
Offset: 4
Length: 1

"Traction Off Light" - a value of 0 indicates the Traction Off Light is disabled, meaning the traction control system is ON. A value of 1 for Traction Off Light, means the Traction Off Light is illuminated and Traction Control is off.
Offset: 12
Length: 1

"Stability Light" - a value of 0 indicates the Stability system is not intervening. A value of 1 indicates the Stability light is on, and the stability system is intervening. This value will flip from 0 to 1 rapidly when the system is intervening.
Offset: 25
Length: 1


Demo of everything working can be viewed here:
https://youtu.be/VQ9gC-fSx3I

boggie1688
Posts: 138
Joined: Fri Apr 07, 2017 3:47 pm
Location: Oakland, CA

Post by boggie1688 »

Most recent config.

There is shiftx3 script in the file. There is also a series of active transmits to the ECU in the script. Responses from the ECU are received on 2024. I also have an analogX, so DiffT and OilPressure are coming from the AnalogX.
Attachments
15+ WRX.zip
(4.84 KiB) Downloaded 747 times

boggie1688
Posts: 138
Joined: Fri Apr 07, 2017 3:47 pm
Location: Oakland, CA

Post by boggie1688 »

Got a lead on brake pressure, not just sweep. Going to test to day and add it if it looks right.

Edit: no go on pressure... Thought it would be quick but I that doesn't seem to be the case. Pressure might have to wait for when I have more time.

Post Reply