For this I sniffed around the CAN bus of the OBD port and listend to the traffic send there (no request, just listening). I found a few interesting things (battery voltage, SOC, speed...)
As hardware i took one of the dev-samples of the ESP32 OBD logger, wich is a ESP32 with MCP2517 CAN controller, super low IQ power supply, microSD card, GPS and an IMU + barometer.
I only use the CAN and IMU for wakeup. If the Smart is woken up, the CAN traffic starts and wakes the esp32. It then tries to connect to some WiFi APs set in WiFiMulti and published the CAN data received to MQTT.
With this I am able to stop the wallbox charger when a certain SOC level is reported
In winter it is nice to defrost the windscreen and preheat the car + battery using grids supply instead of draining the small battery even before starting to drive. For this I start charging (turn on the wallbox in Homeassistant) wait for the esp32 to become available in MQTT and tell it to send the preheat command. I don't know who fount this out, but I found it here: https://github.com/MyLab-odyssey/ED4scan/issues/6 this will turn on the heater and blower while charging the battery (and hopefully already preheating the battery with the 500W 12v heater it has)
For me the preheating only last for a few minutes, so if it is super cold you need to resend the command. The car only wake up if it starts charging, so if it is already charged to 100%, the wallbox will not wake it up.
Sorry for not sharing code, but as the hardware is unique and the mqtt setup very individual, I just share the DBC file and some code snippets
#define MSB 0
#define LSB 1
typedef struct {
uint32_t can_id;
String name;
int16_t startbit;
uint8_t length;
bool sign;
bool isfloat;
float add;
float mult;
uint32_t div;
int byteorder;
float value;
uint32_t last_pub;
} CAN_Signal;
const int num_signals = 6;
CAN_Signal can_signals[num_signals] = {
{0x000, "null", 0,0,0,0,0,0,0,MSB,0,0},
{0x42E, "hv_volt", 24,15,0,0,0,1,64,MSB,0,0},
{0x42E, "range", 0,15,0,0,0,1,270,MSB,0,0},
{0x654, "soc", 24,8,0,0,0,1,1,MSB,0,0},
{0x5D7, "odo", 16,32,0,0,0,0.00062137119,1,MSB,0,0},
{0x62D, "chp", 8,16,0,0,0,1,1,MSB,0,0},
};
#define PUBLISH_INTERVAL 10000
float bit_to_float(uint8_t* data, CAN_Signal sig ){
uint8_t bytes = (sig.length + (sig.startbit%8))/8 + 1*((((sig.length + (sig.startbit%8))%8)>0));
uint8_t startbyte = sig.startbit/8;
uint64_t len_mask = 0xFFFFFFFFFFFFFFFF >> (64-sig.length);
uint64_t pre =0;
if(sig.byteorder == LSB){
for (int i = 0 ; i < bytes; i++){
//Serial.print("LSB ");Serial.print(i+startbyte);Serial.print(":");Serial.print(data[i+startbyte],HEX); Serial.print("<<");Serial.print(i*8); Serial.print(" ");
pre += (uint64_t)(data[i+startbyte]) << (i*8) ;
}
//Serial.println();
} else { // MSB
for (int i = bytes-1 ; i > -1; i--){
//Serial.print("MSB ");Serial.print(i+startbyte);Serial.print(":"); Serial.print(data[i+startbyte],HEX); Serial.print("<<");Serial.print(((bytes-1)-i)*8); Serial.print(" ");
pre += (uint64_t)(data[i+startbyte]) << ( ((bytes-1)-i)*8) ;
}
//Serial.println();
}
pre = (pre >> (sig.startbit%8)) & len_mask;
//Serial.println(pre,HEX);
if(!sig.isfloat){
if(sig.sign) {
if(pre & (1 << (sig.length-1))){ // negative
//Serial.print("val: ");Serial.println(((float)(((double)pre - (double)(len_mask))*mult)/div)+add);
return ((float)(((double)pre - (double)(len_mask))*sig.mult)/sig.div)+sig.add;
} else {
//Serial.print("val: "); Serial.println(((float)((double)(pre)*mult)/div)+add);
return ((float)((double)(pre)*sig.mult)/sig.div)+sig.add;
}
}
if(!sig.sign) {return (float)((double)( ((double)(pre)) *sig.mult)/sig.div)+sig.add;}
} else { // float
float out = *((float *)&pre);
return (float)((out * sig.mult)/sig.div)+sig.add;
}
return 0; // error
}
bool is_neg(int in){
if(in < 0) {return true;}
return false;
}
int parse_canmsg(CANMessage* inmsg){
for (int i = 0; i< num_signals; i++){
if(can_signals[i].can_id == inmsg->id){
...
Read more »
Flo