Subaru SSM protocol over OBDII

Discussion and research on OBDII integration, especially on OEM specific PIDs from Ford, GM, Chevrolet, BMW, Porsche, Audi, Toyota, Volkswagen, Mazda, Honda, Subaru, Mitsubishi and others.
Post Reply
mikebr75
Posts: 9
Joined: Tue Dec 29, 2015 8:03 pm

Subaru SSM protocol over OBDII

Post by mikebr75 »

Diving into the world of logging from the Subaru ECU I found a good bit of information from the RomRaider site (a tool I've begun using because of the tuner who is working on my engine/map).

The Subaru ECU uses the SSM protocol to handle request/responses to the ECU (reading and writing):

http://romraider.com/RomRaider/SsmProtocol

so.... now to start reading up on Lua and figure out if the ECU can be queried this way.

MichiganMat
Posts: 9
Joined: Fri Nov 04, 2016 12:12 am
Location: San Jose, CA

Post by MichiganMat »

Sorry to hi-jack your thread, but I've got a question:
Im close to pulling the trigger on an MK3 system for my 914-WRX project here. Its got a 2002 EJ motor and Im going to be running a custom flash via RomRaider. Does the RaceCapture interface well enough with the Subaru ECU to pull RPM and speed #s? First and foremost, I need the RaceCapture to send the basic information over bluetooth to an android tablet. If there are compatibility issues, how extensive are they? Id like to know if there are compatibility issues before buying a whole bunch of expensive gear!
Thanks,
Mat

jlwall
Posts: 5
Joined: Sat Apr 15, 2017 9:19 pm
Location: San Jose, CA

Post by jlwall »

Yes, SSM over an ISOTP layer will get you what you want.
Doing a Single "Byte" or "U8" read is very easy

The layout goes [0xA8,0x00,ptra<<16,ptra<<8,ptra]. where prta is a 24 bit point to your reading object.
This takes 5 bytes in the ISOTP layer, then 6 bytes total on CAN Physical Layer

0x7E0 [DLC=7], data{ (PCI_SINGLE<<4) + (tx->size&0x07), 0xA8,0x00,ptra<<16,ptra<<8,ptra}

This is effectively sending a "Single ISO" Frame, where the payload of the "SSM" message is 7 bytes or less.

What is complicated is when you want to ask for multiple U24 points to read larger datatypes inside the ECU.

[0xA8,0x00,ptra<<16,ptra<<8,ptra,ptrb<<16,ptrb<<8,ptrb]
lets say you want to SSM request ptra, then ptrs ( which would be 1 byte away as an example to get a U16)

This is 8 items, and thus ISOtp needs to include sending a First Frame, wait for the FLOW, then send the consecutive frames.


I ending up making an external bare metal device that does this all for me, polling for response @ 200uSec / 5khz. This then pumps up straight up CAN messages to RCP directly. Lua was going to be too slow, and requires tight coupling of the ISOtp layer.

I could consider porting and placing all of this inside a branch build of RCP, but it might be skew the product a bit and make it too specific for Subarus'.






Code: Select all

void sendIsoSingleFrame&#40;IsoTpMessage* tx&#41;
&#123;
    uint8_t t = 0;
    if&#40;tx->size <= 7&#41;
    &#123;
        isotp_can_tx.id      = tx->id;    /* standard frame */
        isotp_can_tx.dlc  = 8;/* Length = 8 */
        isotp_can_tx.data8&#91;0&#93; = &#40;PCI_SINGLE<<4&#41; + &#40;tx->size&0x07&#41;;
        while&#40;t<tx->size&#41;
        &#123;
            isotp_can_tx.data8&#91;1+t&#93; = tx->payload&#91;t&#93;;
            t++;
        &#125;
                CAN_Write &#40; CAN_BUS1, &isotp_can_tx&#41;;
    &#125;
&#125;

void sendIsoFlowFrame&#40;uint32_t id, uint8_t iso_flow, uint8_t iso_block, uint8_t times&#41;
&#123;
      isotp_can_tx.id      =  id;    /* standard frame */
      isotp_can_tx.data8&#91;0&#93; = &#40;PCI_FLOW_CONTROL_FRAME<<4&#41; + &#40;iso_flow&0x07&#41;;
      isotp_can_tx.data8&#91;1&#93; = iso_block;
      isotp_can_tx.data8&#91;2&#93; = times;
      isotp_can_tx.data8&#91;3&#93; = 0x00;
      isotp_can_tx.data8&#91;4&#93; = 0x00;
      isotp_can_tx.data8&#91;5&#93; = 0x00;
      isotp_can_tx.data8&#91;6&#93; = 0x00;
      isotp_can_tx.data8&#91;7&#93; = 0x00;
            CAN_Write &#40; CAN_BUS1, &isotp_can_tx&#41;;
&#125;

void sendIsoFirstFrame&#40;IsoTpMessage* tx &#41;
&#123;
    uint8_t t = 0;
    isotp_can_tx.id      = tx->id;    /* standard frame */
    isotp_can_tx.data8&#91;0&#93; = &#40;PCI_FIRST_FRAME<<4&#41; + &#40;&#40;tx->size>>8&#41;&0x0F&#41;;
    isotp_can_tx.data8&#91;1&#93; = &#40;&#40;tx->size&#41;&0xFF&#41;;
    tx->index = 1;
    while&#40;t<6&#41;
    &#123;
        isotp_can_tx.data8&#91;2+t&#93; = tx->payload&#91;t&#93;;
        t++;
    &#125;
    tx->dataIndex += t;
        CAN_Write &#40; CAN_BUS1, &isotp_can_tx&#41;;
&#125;

void sendIsoConsecutiveFrame&#40;IsoTpMessage* tx &#41;
&#123;
    uint8_t t = 0;
    isotp_can_tx.id      = tx->id;    /* standard frame */
    isotp_can_tx.data8&#91;0&#93; = &#40;PCI_CONSECUTIVE_FRAME<<4&#41; + &#40;&#40;tx->index&#41;&0x0F&#41;;
    tx->index++;
    if&#40;tx->index>15&#41;
    &#123;
        tx->index = 0;
    &#125;

    while&#40;&#40;t < 7&#41; && &#40;&#40;t+tx->dataIndex&#41; < tx->size&#41;&#41;
    &#123;
        isotp_can_tx.data8&#91;1+t&#93; = tx->payload&#91;t + tx->dataIndex&#93;;
        t++;
    &#125;
    tx->dataIndex += t;

        CAN_Write &#40; CAN_BUS1, &isotp_can_tx&#41;;

&#125;


mikebr75
Posts: 9
Joined: Tue Dec 29, 2015 8:03 pm

Post by mikebr75 »

jlwall, huge thank you for the info; the explanation is very thorough. I just need to figure out what I actually need from the ECU - I may be able to get away with a single channel... and unpack the info you provided!
MichiganMat wrote:Does the RaceCapture interface well enough with the Subaru ECU to pull RPM and speed #s? First and foremost, I need the RaceCapture to send the basic information over bluetooth to an android tablet.
In my install I've been able to use alternate means to get the RPM and speed. For RPM I tapped the RPM signal wire to the combination display and ran that to one of the RaceCapture/Pro RPM signal inputs. Works fine for me. Also, the built in GPS functions accurately display speed (my LCD display now blocks the speed on combo display, but I verified speed vs speed).

I imagine you could pull the RPM signal from the ECU this way without the OEM display, or if not use the coil-X module to read directly from the ignition pulses?

I can't verify yet, but I expect these methods give a higher sample rate than polling the ECU.

jlwall
Posts: 5
Joined: Sat Apr 15, 2017 9:19 pm
Location: San Jose, CA

Post by jlwall »

There are also items in the wild, this is what I pull off the main bus, which is also the bus that is on the orbit/ssm 0x7e0, This is for a '13 still

Code: Select all

void can_decode_22b_ids&#40;CANRxMsg_t *canMsg&#41;
&#123;

  switch&#40;canMsg->id&#41;
        &#123;
        case 0x070&#58;
            mDC_yaw = &#40;float&#41;&#40;canMsg->data8&#91;0&#93;    + canMsg->data8&#91;1&#93;*256&#41;*&#40;float&#41;0.005-&#40;float&#41;163.84;
            mDC_yaccel = &#40;float&#41;&#40;canMsg->data8&#91;4&#93;    + canMsg->data8&#91;5&#93;*256&#41;*&#40;float&#41;0.00012742 - &#40;float&#41;4.1768;
            break;
        case 0x410&#58;
            mDC_transTorque = &#40;float&#41;canMsg->data8&#91;1&#93;*&#40;float&#41;1.6;
            mDC_engTorque = &#40;float&#41;canMsg->data8&#91;2&#93;*&#40;float&#41;1.6;     //USED
            mDC_lossTorque = &#40;float&#41;canMsg->data8&#91;3&#93;*&#40;float&#41;1.6;
            mDC_accel = &#40;float&#41;canMsg->data8&#91;4&#93;*100/255;     //USED
            mDC_engineSpeed = canMsg->data8&#91;5&#93;    + canMsg->data8&#91;6&#93;*256 ;     //USED
            break;
        case 0x411&#58;
            mDC_gear = canMsg->data8&#91;4&#93;;     //USED
            mDC_cruise = canMsg->data8&#91;5&#93;;
            mDC_bMIL = canMsg->data8&#91;7&#93;&0x01;     //USED
            mDC_SImode = &#40;&#40;canMsg->data8&#91;7&#93;&0x18&#41;>>3&#41;*0x03;     //USED
            break;
        case 0x501&#58;
            mDC_ABS_MReduction = &#40;float&#41;canMsg->data8&#91;2&#93;*&#40;float&#41;1.6;
            mDC_ABS_MAllowed = &#40;float&#41;canMsg->data8&#91;3&#93;*&#40;float&#41;1.6;
            mDC_bBABS = &#40;canMsg->data8&#91;4&#93;>>2&#41;&0x01;
            mDC_bBABSReduce = &#40;canMsg->data8&#91;4&#93;>>0&#41;&0x01;
             break;
        case 0x511&#58;
            mDC_steerAngle = &#40;int16_t&#41;&#40;canMsg->data8&#91;0&#93;    + canMsg->data8&#91;1&#93;*256&#41;;     //USED
            mDC_Msteering = &#40;int16_t&#41;&#40;canMsg->data8&#91;2&#93;    + canMsg->data8&#91;3&#93;*256&#41;;     //USED
            mDC_brakePercent = canMsg->data8&#91;4&#93;*100/255;     //USED
            break;
        case 0x512&#58;
            mDC_vCar = &#40;float&#41;&#40;canMsg->data8&#91;2&#93;    + canMsg->data8&#91;3&#93;*256&#41;*&#40;float&#41;0.05625;     //USED
            mDC_bVDC = &#40;canMsg->data8&#91;0&#93;>>4&#41;&0x01;
            mDC_bBrake = &#40;canMsg->data8&#91;4&#93;>>4&#41;&0x01;     //USED
            mDC_FaultCode = &#40;uint16_t&#41;&#40;canMsg->data16&#91;3&#93;&#41;;
            break;
        case 0x513&#58;
            mDC_vCarFL = &#40;float&#41;&#40;canMsg->data8&#91;0&#93;    + canMsg->data8&#91;1&#93;*256&#41;*&#40;float&#41;0.05625f;     //USED
            mDC_vCarFR = &#40;float&#41;&#40;canMsg->data8&#91;2&#93;    + canMsg->data8&#91;3&#93;*256&#41;*&#40;float&#41;0.05625f;     //USED
            mDC_vCarRL = &#40;float&#41;&#40;canMsg->data8&#91;4&#93;    + canMsg->data8&#91;5&#93;*256&#41;*&#40;float&#41;0.05625f;     //USED
            mDC_vCarRR = &#40;float&#41;&#40;canMsg->data8&#91;6&#93;    + canMsg->data8&#91;7&#93;*256&#41;*&#40;float&#41;0.05625f;     //USED
            break;
        case 0x514&#58;
            mDC_bReverse = &#40;canMsg->data8&#91;0&#93;>>2&#41;&0x01;
            mDC_tAmbient = &#40;float&#41;canMsg->data8&#91;2&#93;/2-40;     //USED
            mDC_bWiper = &#40;canMsg->data8&#91;3&#93;>>6&#41;&0x01;
            mDC_bLightHigh = &#40;canMsg->data8&#91;3&#93;>>3&#41;&0x01;
            mDC_bLightLow = &#40;canMsg->data8&#91;3&#93;>>2&#41;&0x01;
            mDC_bLightSwitch = &#40;canMsg->data8&#91;3&#93;>>1&#41;&0x01;
            mDC_bDefogger = &#40;canMsg->data8&#91;3&#93;>>0&#41;&0x01;
            mDC_rFuelLevel = &#40;canMsg->data8&#91;4&#93;>>6&#41;*0x03    + canMsg->data8&#91;5&#93;*4 ;
            mDC_bParkingBrake = &#40;canMsg->data8&#91;7&#93;>>7&#41;&0x01;
            break;
        case 0x600&#58;
            mDC_rFuelFlow =  canMsg->data8&#91;1&#93;    + canMsg->data8&#91;2&#93;*256 ;     //USED
            mDC_tWater =  canMsg->data8&#91;3&#93;-40;     //USED
            mDC_bClutch = &#40;canMsg->data8&#91;6&#93;>>2&#41;&0x01;     //USED
            break;
        default&#58;
        //No Message Passed
    break;
    &#125;
&#125;

mikebr75
Posts: 9
Joined: Tue Dec 29, 2015 8:03 pm

Post by mikebr75 »

I finally have some time to work on this, and my results so far have been unsatisfactory.

Ignoring Lua scripting for now (failure after failure to even get a response from the ECU), I looked into the available documentation the Subaru Select Monitor protocol uses an ISO9141 interface which I thought was supported by the Legacy OBD-II cable. After trying all available options in the OBD-II channel page no response is ever seen in RaceCapture.

I would be interested to know if there is some way I can test the Legacy OBD-II cable -but I don't have any cars that are old enough to use it except the one that has produced nothing but failures.

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

Post by brentp »

Nice that you have an external box to decode the subaru data and broadcast it over CAN; that's a perfect application of the technology!

If you're broadcasting the data in a straightforward manner, then you should be able to use the newer direct CAN mapping instead of Lua it runs at native speed and has an easy to use mapping interface:
https://wiki.autosportlabs.com/CAN_Bus_ ... AN_mapping
Brent Picasso
CEO and Founder, Autosport Labs
Facebook | Twitter

Post Reply