2018 Ford Fiesta ST Boost and RPM gauge

I’m working on getting a boost gauge on my 2018 Ford Fiesta ST, and I have some progress but mainly still stuck. I found that some users are reporting these values work with the Torque app, but I’m not sure what the OBD auto header would be that they use, or what CAN ID to send this data with. I’ve tried sending via CAN ID 0x700 ~ 0x7f7 with no response seen above 0x700.

Torque PID settings

PID: 221440
Long Name: Boost (that's what I use)
Short Name: MAP
Min Value: 0.0
Max Value: 30.0
Scale Factor: x1
Unit Type: psi
Equation: (((A*256)+B*0.03625)-BARO()
OBD Header: Auto

My current WIP app

which just send data to the USB Serial output

#include "Particle.h"
#include <carloop.h>

/*
PID: 221440
Long Name: Boost (that's what I use)
Short Name: MAP
Min Value: 0.0
Max Value: 30.0
Scale Factor: x1
Unit Type: psi
Equation: (((A*256)+B*0.03625)-BARO()
OBD Header: Auto
*/

// Don't block the main program while connecting to WiFi/cellular.
// This way the main program runs on the Carloop even outside of WiFi range.
SYSTEM_THREAD(ENABLED);

// Tell the program which revision of Carloop you are using.
Carloop<CarloopRevision2> carloop;

unsigned int MSG_ID = 0x7df;
int rpm = 0;
float mph = 0.0f;

// Send a message at a regular time interval
void sendMessage() {
    static uint32_t lastTransmitTime = 0;
    uint32_t transmitInterval = 1000; /* ms */
    uint32_t now = millis();
    if (now - lastTransmitTime > transmitInterval) {
        CANMessage message;

        // A CAN message has an ID that identifies the content inside
        message.id = MSG_ID;

        // It can have from 0 to 8 data bytes
        message.len = 8;

        // Pass the data to be transmitted in the data array
        message.data[0] = 0x03;
        message.data[1] = 0x22;
        message.data[2] = 0x14; //boost
        message.data[3] = 0x40;
        // message.data[2] = 0x04; // transmission oil temp
        // message.data[3] = 0x61;
        
        // message.data[4] = 0x55;
        // message.data[5] = 0x55;
        // message.data[6] = 0x55;
        // message.data[7] = 0x55;

        // Send the message on the bus!
        carloop.can().transmit(message);

        lastTransmitTime = now;
        
        if (MSG_ID < 0x7e7) {
            MSG_ID++;
        } else {
            MSG_ID = 0x7df;
        }
        
        // Serial.printlnf("sent for message id: %03x", MSG_ID);
    }
}

void recvMessage() {
    CANMessage message;
    if (carloop.can().receive(message)) {
        if (message.id >= 0x700) {
            Serial.printf("time:%f,id:0x%03x,data:", millis() / 1000.0, message.id);
            for (int i = 0; i < message.len; i++) {
                if (i == 0) {
                    Serial.print("0x");
                }
                Serial.printf("%02x", message.data[i]);
            }
            Serial.println();
        } else if (message.id == 0x201) {
            rpm = (message.data[0] << 8) + message.data[1];
        } else if (message.id == 0x20f) {
            mph = ((((message.data[2] << 8) + message.data[3]) - 10000) / 100) * 0.621371f;
        }
        Serial.printlnf("RPM: %5d  MPH: %5.1f", rpm, mph);
        // delay(50);
    }
}

void setup() {
    carloop.begin();
}

void loop() {
    
    // Send every 1 second
    sendMessage();
    
    // Receive as fast as possible
    recvMessage();
}

I can read the RPM and MPH, but they update too slowly to be useful. I’m sure it’s possible to query them directly, but my crash course today hasn’t yielded the answer. My brain is telling me I can’t just send a message to CAN ID 0x201 to query the RPM, that would be like writing a new RPM value.

Resources

Wiki OBD-II PIDs
Ford Fiesta PIDs
Ford PIDs 2003-2004
ELM327 v2.2 Datasheet (This is a pretty informative datasheet that explains a lot about how CAN / OBD / PID works in cars)

Did some more reading today and learned about Mode 1 PID’s. My car supports RPM and SPEED mode 1 PID’s, so it’s pretty easy to make that request and send the info to a Gauge on the Blynk app.

Here’s the app I started with:

carloop/MODE_1_to_Blynk.ino at master · egarl004/carloop · GitHub

Reduce the number of PIDs to polls here to 2:

Comment out the PIDs in the array to just the RPM and SPEED:

Comment out everything except for RPM and SPEED:

I found that a decimal place on these values was annoying, so you can format that away like this:

String StringENGINE_RPM    = String::format("%.0f", ENGINE_RPM);
String StringVEHICLE_SPEED = String::format("%.0f", VEHICLE_SPEED);

Change the update rate to 100 ms per value, or in this case 200ms

I found it will update at 100 ms for just one value (which might be nice for a dedicated RPM display).

Other edits to make the app compile and function properly:

There were some other edits I made as well, like commenting out

And moving Blynk.begin() after Particle.connect() … necessary when using SEMI_AUTOMATIC mode, otherwise the system hangs in a disconnected from WiFi state trying to connect to Blynk.


Still looking to figure out how to get that boost gauge though …

So earlier I did cycle through CAN ID 0x7df to 0x7e7 and also 0x700 to 0x7e7, but was thinking maybe I should have tried one of them for a longer period of time.

After reading up more about how to talk to the ECU directly, I decided to change the code to just use 0x7e0 as the CAN ID for this boost request that uses the extended format.

I also made sure I’m receiving right after transmitting so as not to miss any response from the ECU.

I started getting a response right away from 0x7e8

id:0x7e8,data:0x037f223100000000

This is great! Now I need to know what this means because this doesn’t seem to be the Boost data since it never changes from this value. I’m guessing this is some kind of prompt or maybe a flow control message? Not sure why it would need to be though since boost is contained in two bytes.

So 0x03 bytes?, 0x7f, 0x22, 0x31. I’ll have to dig into this a bit later after a movie. If anyone is following along and can point me to the decoding reference for this, please feel free to drop in a link.

Updated Boost Code:

#include "Particle.h"
#include <carloop.h>

/*
PID: 221440
Long Name: Boost (that's what I use)
Short Name: MAP
Min Value: 0.0
Max Value: 30.0
Scale Factor: x1
Unit Type: psi
Equation: (((A*256)+B*0.03625)-BARO()
OBD Header: Auto
*/

// Getting response now!
// id:0x7e8,data:0x037f223100000000

// Don't block the main program while connecting to WiFi/cellular.
// This way the main program runs on the Carloop even outside of WiFi range.
SYSTEM_THREAD(ENABLED);

// Tell the program which revision of Carloop you are using.
Carloop<CarloopRevision2> carloop;

unsigned int MSG_ID = 0x7e0;
int rpm = 0;
float mph = 0.0f;

// Send a message at a regular time interval
void sendMessage() {
    static uint32_t lastTransmitTime = 0;
    uint32_t transmitInterval = 200; /* ms */
    uint32_t now = millis();
    if (now - lastTransmitTime > transmitInterval) {
        digitalWrite(D7, !digitalRead(D7));
        CANMessage message;

        // A CAN message has an ID that identifies the content inside
        message.id = MSG_ID;

        // It can have from 0 to 8 data bytes
        message.len = 8;

        // Pass the data to be transmitted in the data array
        message.data[0] = 0x03;
        message.data[1] = 0x22;
        message.data[2] = 0x14; //boost
        message.data[3] = 0x40;
        // message.data[2] = 0x04; // transmission oil temp
        // message.data[3] = 0x61;

        // Send the message on the bus!
        carloop.can().transmit(message);

        lastTransmitTime = now;
        
        // Serial.printlnf("sent for message id: %03x", MSG_ID);
    }
}

void recvMessage() {
    uint32_t startRecv = millis();
    const uint32_t RECV_TIMEOUT = 100UL;
    CANMessage message;
    while (millis() - startRecv <= RECV_TIMEOUT) {
        if (carloop.can().receive(message)) {
            if (message.id >= 0x700) {
                Serial.printf("time:%f,id:0x%03x,data:", millis() / 1000.0, message.id);
                for (int i = 0; i < message.len; i++) {
                    if (i == 0) {
                        Serial.print("0x");
                    }
                    Serial.printf("%02x", message.data[i]);
                }
                Serial.println();
            }
        }
    }
}

void setup() {
    pinMode(D7, OUTPUT);
    carloop.begin();
}

void loop() {
    
    // Send every 1 second
    sendMessage();
    
    // Receive as fast as possible
    recvMessage();
}

OBD-II PIDs - Wikipedia

7Fh this a general response usually indicating the module doesn’t recognize the request.

Hmm, I might be back at square one. I think I’ll try the other CAN ID’s 0x7e1 - 0x7e7 next.


EDIT: So it seems like maybe my car doesn’t support that extended Boost PID, because with 0x7e0 and a different PID I’m getting a valid response. Others are saying it works though, so I’m going to see if it works in the native Torque Pro app with a Vgate WiFi OBDII Elm327 dongle. I can sniff the traffic hopefully then with the Carloop on a Y-cable :slight_smile:

CAN ID: 0x7e0 PID: 220461
Response: id:0x7e8,data:0x0562046107aa0000

This is supposedly a Transmission Temperature, and when I run 0x07 and 0xAA through the equation and convert to Fahrenheit it comes out to 87.18F which seems pretty reasonable considering the outside temp was rising recently and at 92F. My car had been off for quite a while.

BTW just an update. It also doesn’t work with the dongle and Torque Pro, but Torque Pro does have a boost gauge that does seem to work. I don’t know how accurate it is though. I’ll have to reverse engineer how they do that. I’m almost thinking a mechanical boost gauge is going to be better though, and I’m now more interested in making a dedicated Neopixel RPM heads up display. I’ll update on that when I receive my pixel strips and start working on it.