[FIRST DRIVE] Toyota Prius gen2 plug and play

Tell us about the project you do with the open inverter
turnip73
Posts: 58
Joined: Wed Dec 26, 2018 1:38 pm
Location: Greystones Ireland
Has thanked: 22 times
Been thanked: 47 times

Re: [FIRST DRIVE] Toyota Prius gen2 plug and play

Post by turnip73 »

hmm...
So while testing my updated stm32-car code, I've realized that the Toyota hybrid control ecu is not sending the can bus information I'm expecting because the ecu/car is not in ready mode.
I knew I would have to deal with this eventually but seems that time is now...
That leads me to two options:-
A) Spoof the inputs to the Toyota hybrid control ecu
B) Implement the functionality from the Toyota hybrid control ecu needed in stm32-car.

Spoof the inputs to the Toyota hybrid control ecu
1) resolver/inverter
2) contactor/precharge charge voltages
3) Toyota BMS

Implement the functionality from the Toyota hybrid control ecu needed in stm32-car.
1) shift leaver
2) key authentication
3) A/C control
4) All the other stuff I don't know I need yet...

I'll start with spoofing resolver/inverter. I've had a go at this in the past, but not successful
see [viewtopic.php?p=44241#p44241]. To trick the Toyota hybrid control ecu to stay in ready mode and drive, it has to get feed back from current sensors and/or resolver.
I've seen in [ Latula EV test his SDU card using a signal generator

I think my options are
I) By any chance is the Toyota hybrid control ecu sending any information over CAN that Open Inverter ecu can use as resolver information to drive the motor
II) Can Open Inverter ecu hw/sw be modified to use the resolver information generated by the Toyota hybrid control ecu
III) Update Open Inverter ecu to create spoof resolver information for the Toyota hybrid control ecu

Attached Toyota resolver information
Attachments
resolver2.GIF
turnip73
Posts: 58
Joined: Wed Dec 26, 2018 1:38 pm
Location: Greystones Ireland
Has thanked: 22 times
Been thanked: 47 times

Re: [FIRST DRIVE] Toyota Prius gen2 plug and play

Post by turnip73 »

I) By any chance is the Toyota hybrid control ecu sending any information over CAN that Open Inverter ecu can use as resolver information to drive the motor
no

OK, so this is the plan...
First attempt:
Use external excitation from Toyota VH ecu in OI
IO excitation is 9v peek to peek and 4.4 kHz
IOResolverExciter3_01.png
Toyota excitation is 20v peek to peek 10 kHz.
ToyotaResoverExciter.png
Plan is:-
HW:
1) Get Toyota excitation and resolver output down to ~3v peek to peek.
2) Get Toyota excitation in to stm32 (can I use "START" PB6/TIM4_CH1 see [https://github.com/jsphuebner/inverter- ... 2-v1.0.pdf])

SW:
1) Set OI excitation to 10 kHz (can this be done?)
2) Get Toyota excitation edge
2) Compare Toyota and OI excitation edge and add/subtract to resolverSampleDelay [https://github.com/jsphuebner/stm32-sin ... ncoder.cpp]?

20231120 updates removed OI resolver pico scope output (this was old data not OI resolver), both resolver output are kHz.
20231121 got an OI resolver pico scope output confirmed 9v peek to peek
User avatar
Ev8
Posts: 818
Joined: Sat Jan 30, 2021 11:05 am
Has thanked: 43 times
Been thanked: 163 times

Re: [FIRST DRIVE] Toyota Prius gen2 plug and play

Post by Ev8 »

Wow I would imagine trying to keep the oem hv ecu happy while driving with the inverter controlled by OI would be a massive task possibly requiring generating appropriate current and motor position signals possibly proportional to the pwm of the phase driver outputs from the hv ecu, I suspect a fixed set of values would cause errors,

I’m starting to think that taking over contactor contol ect and cutting the hv ecu out of the equation is going to be an easier route,

On a separate note, I’d really appreciate a capture of the a/c comms,
turnip73
Posts: 58
Joined: Wed Dec 26, 2018 1:38 pm
Location: Greystones Ireland
Has thanked: 22 times
Been thanked: 47 times

Re: [FIRST DRIVE] Toyota Prius gen2 plug and play

Post by turnip73 »

Ev8 wrote: Sat Nov 18, 2023 8:24 am Wow I would imagine trying to keep the oem hv ecu happy while driving with the inverter controlled by OI would be a massive task possibly requiring generating appropriate current and motor position signals possibly proportional to the pwm of the phase driver outputs from the hv ecu, I suspect a fixed set of values would cause errors,

I’m starting to think that taking over contactor contol ect and cutting the hv ecu out of the equation is going to be an easier route,

On a separate note, I’d really appreciate a capture of the a/c comms,
See my options/resoing in post above. Car runs and drives with the hv ecu out of the equation but I ran into probelms to integrate OI with rest of car when not in ready mode e.g hv ecu happy. In the past I've been able to run OI sin software open loop with oem hv ecu connected.

Re a/c comms I hope to try johu's idea to use the a/c as changer unmodifed (maybe making sure the gates are permanently low achieves the same)
johu wrote: Tue Sep 05, 2023 7:30 am Yes much agreed. I modded the inverter and controller quite heavily to be able to charge reliably. Of course a controller can't take all of that away but it can be facilitated.

Mods for single phase boost charging:
- Swapped input and output of the buck/boost stage
- Removed 4 IGBTs of the A/C inverter and replaced them with a bridge rectifier (maybe making sure the gates are permanently low achieves the same)
- Fitted a current sensor to the cable going from said A/C inverter into the input of the boost converter to be able to measure charge current
- Disabling that current sensor with a relay while NOT charging
- Connected GSDN to the DC switch signal


It's the Hilux of inverters, virtually invincible unless I start mucking around with it ;)
turnip73
Posts: 58
Joined: Wed Dec 26, 2018 1:38 pm
Location: Greystones Ireland
Has thanked: 22 times
Been thanked: 47 times

Re: [FIRST DRIVE] Toyota Prius gen2 plug and play

Post by turnip73 »

turnip73 wrote: Fri Nov 17, 2023 9:10 pm 1) Set OI excitation to 10 kHz (can this be done?)
No luck so far changing the OI resolver excitation frequency.
If I comment out line:
https://github.com/jsphuebner/stm32-sin ... r.cpp#L471

Code: Select all

      gpio_set_mode(NORTH_EXC_PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, NORTH_EXC_PIN);
there is no resolver excitation.
My assumption was that the interrupt frequency that sets the resolver excitation frequency was set some where above in the InitResolverMode function but I haven't figure it out yet.
User avatar
johu
Site Admin
Posts: 6969
Joined: Thu Nov 08, 2018 10:52 pm
Location: Kassel/Germany
Has thanked: 455 times
Been thanked: 1771 times
Contact:

Re: [FIRST DRIVE] Toyota Prius gen2 plug and play

Post by johu »

Changing the excitation frequency is hard as it is indeed half the interrupt frequency of 8.8 kHz.
Not sure what you're trying to achieve on a higher level but if you want only excitation disabled you can switch to SinCos mode
Support R/D and forum on Patreon: https://patreon.com/openinverter - Subscribe on odysee: https://odysee.com/@openinverter:9
turnip73
Posts: 58
Joined: Wed Dec 26, 2018 1:38 pm
Location: Greystones Ireland
Has thanked: 22 times
Been thanked: 47 times

Re: [FIRST DRIVE] Toyota Prius gen2 plug and play

Post by turnip73 »

johu wrote: Sat Dec 09, 2023 10:26 pm Not sure what you're trying to achieve on a higher level but if you want only excitation disabled you can switch to SinCos mode
I want to use an external resolver exciter from the Toyota HV ECU so both Toyota HV ECU and OI can use the resolver.
turnip73
Posts: 58
Joined: Wed Dec 26, 2018 1:38 pm
Location: Greystones Ireland
Has thanked: 22 times
Been thanked: 47 times

Re: [FIRST DRIVE] Toyota Prius gen2 plug and play

Post by turnip73 »

Hmm, time for an update to this thread. After getting nowhere with trying to keep the HV ECU happy, I started on implementing/reverse engineering of functionality of the HV ECU. I picked the A/C control, as others on the forum had made some progress on the A/C Denso ES27C.
turnip73 wrote: Sun Nov 12, 2023 8:53 pm Implement the functionality from the Toyota hybrid control ecu needed in stm32-car.
1) shift leaver
2) key authentication
3) A/C control
4) All the other stuff I don't know I need yet...
Next I want to tackle is trying to charge using the A/C Inverter without modifications and the Buck/Boost converter.
However, the internet says I cannot use the IGBT K1517 4L2 50SE as a diode/rectifier in reverse because of how IGBT's are constructed.
My other options are:-
1) stack a diode on top of the IGBT
2) not use the A/C Inverter to rectify (use an external rectifier)
turnip73
Posts: 58
Joined: Wed Dec 26, 2018 1:38 pm
Location: Greystones Ireland
Has thanked: 22 times
Been thanked: 47 times

Re: [FIRST DRIVE] Toyota Prius gen2 plug and play

Post by turnip73 »

Working on setting up boost charging, using a Denso 13140-0050 current sensor. Converting the Denso 13140-0050 output 5V to 0V (0A at ~2.5V) to 0V to ~2.5 for the gen2 board using an esp32:

Code: Select all

// === Pin Definitions ===
const int adcPin = 34;   // ADC1_CH6 (GPIO34) — input: 0…2.5 V
const int dacPin = 25;   // DAC1 (GPIO25) — output: 0…2.5 V

// === Constants ===
const float VREF    = 3.3;    // ESP32 ADC reference voltage
const int   MAX_ADC = 4095;   // 12-bit ADC resolution
const float VIN_MAX = 2.7;    // maximum expected input (V)
const float VOUT_MAX = 2.7;   // desired maximum output (V)

void setup() {
  Serial.begin(115200);
}

void loop() {
  // 1) Read ADC
  int raw = analogRead(adcPin);
  float vin = (raw / float(MAX_ADC)) * VREF;  // scale to 0…3.3 V

  // 2) Invert & clamp: map vin from [0…VIN_MAX] → [VOUT_MAX…0]
  float vOut = (VIN_MAX - vin);
  if (vOut < 0)          vOut = 0;
  else if (vOut > VOUT_MAX) vOut = VOUT_MAX;

  // 3) Convert to DAC units (0…255→0…3.3 V), but we only drive up to VOUT_MAX
  int dacVal = int((vOut / VREF) * 255.0);
  dacWrite(dacPin, dacVal);

  // 4) Debug
  Serial.print("VIN = ");
  Serial.print(vin, 3);
  Serial.print(" V  →  VOUT = ");
  Serial.print(vOut, 3);
  Serial.println(" V");

  delay(100);
}
turnip73
Posts: 58
Joined: Wed Dec 26, 2018 1:38 pm
Location: Greystones Ireland
Has thanked: 22 times
Been thanked: 47 times

Re: [FIRST DRIVE] Toyota Prius gen2 plug and play

Post by turnip73 »

Had a busy couple of days working on the project. I replaced and balanced the cars original battery pack with some used ones I picked up. With the rest of the modules I started rebuilding a 2nd pack. I worked on the test setup for boost charging using the A/C inverter. I got as far as contactors closings and inverter and OI board in boost mode but ran into a issue. I was expecting the buck/booster converter to let power thought to the A/C inverter. I only have V1.1 cards and Johannes mentioned in a thread that the V1.3 will give you power to the A/C inverter and DCDC alt least in drive mode. I need at least 50V for the A/C inverter to start up or I fear it will just blow gates like it did for Johannes. Any ideas how I can get power to the A/C inverter to activate the gate? I also made a rudimentary web interface for the Battery ECU. The Current sensor setup also need some tinkering or maybe just use same setup as Johannes but that requires mods to the OI board.
Attachments
20250616_134731.jpg
20250616_135530.jpg
20250617_172332.jpg
turnip73
Posts: 58
Joined: Wed Dec 26, 2018 1:38 pm
Location: Greystones Ireland
Has thanked: 22 times
Been thanked: 47 times

Re: [FIRST DRIVE] Toyota Prius gen2 plug and play

Post by turnip73 »

Wrote a small Prius Battery ECU decoder using an ESP32 and a couple of 3.3V Can Boards before I get ahead of my self over sharing the Toyota hybrid batteries:

Code: Select all

// === ESP32 Prius CAN Parser ===
// Monitors Prius Gen2 Battery ECU on 2 CAN buses

#include <ACAN_ESP32.h>
#include <mcp_can.h>
#include <SPI.h>
#include <WiFi.h>
#include <ESPAsyncWebServer.h>

AsyncWebServer server(80);

// === Battery 1 (ESP32 CAN) ===
float packVoltage1 = 0.0;
float packCurrent1 = 0.0;
int soc1 = 0;
int ccl1 = 0, cdl1 = 0;
int temp1a = 0, temp1b = 0;
uint16_t faultCode1 = 0;
uint8_t deltaSOC1 = 0, flags1 = 0;
uint16_t calibX1 = 0, calibY1 = 0, calibZ1 = 0;
float blockVoltages1[14] = {0};
uint8_t isoTPBuf1[64];
size_t isoTPLen1 = 0;

// === Battery 2 (MCP2515 CAN) ===
float packVoltage2 = 0.0;
float packCurrent2 = 0.0;
int soc2 = 0;
int ccl2 = 0, cdl2 = 0;
int temp2a = 0, temp2b = 0;
uint16_t faultCode2 = 0;
uint8_t deltaSOC2 = 0, flags2 = 0;
uint16_t calibX2 = 0, calibY2 = 0, calibZ2 = 0;
float blockVoltages2[14] = {0};
uint8_t isoTPBuf2[64];
size_t isoTPLen2 = 0;

MCP_CAN can2(15);
unsigned long lastTesterPing = 0;
const unsigned long testerPingInterval = 3000; // 3 sec
int pid = 1;

void setup() {
  Serial.begin(115200);
  WiFi.softAP("PriusMonitor", "hybridpower");
  Serial.println(WiFi.softAPIP());

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    String html = "<html><head><meta http-equiv='refresh' content='2'></head><body><h1>Prius BMS Data</h1>";
    html += "<h2>Battery 1</h2>";
    html += "<p>Voltage: " + String(packVoltage1) + " V</p>";
    html += "<p>Current: " + String(packCurrent1) + " A</p>";
    html += "<p>SOC: " + String(soc1/2.0) + " %</p>";
    html += "<p>Delta SOC: " + String(deltaSOC1/2.0) + " %</p>";
    html += "<p>CCL/CDL: " + String(ccl1) + "/" + String(cdl1) + " A</p>";
    html += "<p>Temps: " + String(temp1a) + ", " + String(temp1b) + "</p>";
    html += "<p>Flags: 0x" + String(flags1, HEX) + "</p>";
    html += "<p>Blocks:</p><ul>";
    for (int i=0; i<14; i++) html += "<li>Block "+String(i+1)+": "+String(blockVoltages1[i])+" V</li>";
    html += "</ul>";

    html += "<h2>Battery 2</h2>";
    html += "<p>Voltage: " + String(packVoltage2) + " V</p>";
    html += "<p>Current: " + String(packCurrent2) + " A</p>";
    html += "<p>SOC: " + String(soc2/2.0) + " %</p>";
    html += "<p>Delta SOC: " + String(deltaSOC2/2.0) + " %</p>";
    html += "<p>CCL/CDL: " + String(ccl2) + "/" + String(cdl2) + " A</p>";
    html += "<p>Temps: " + String(temp2a) + ", " + String(temp2b) + "</p>";
    html += "<p>Flags: 0x" + String(flags2, HEX) + "</p>";
    html += "<p>Blocks:</p><ul>";
    for (int i=0; i<14; i++) html += "<li>Block "+String(i+1)+": "+String(blockVoltages2[i])+" V</li>";
    html += "</ul>";
    html += "</body></html>";
    request->send(200, "text/html", html);
  });
  server.begin();

  ACAN_ESP32_Settings settings1(500000);
  settings1.mRxPin = GPIO_NUM_16;
  settings1.mTxPin = GPIO_NUM_17;
  if (ACAN_ESP32::can.begin(settings1) == 0) {
    Serial.println("Internal CAN OK");
  } else {
    Serial.println("Internal CAN FAILED");
  }

  SPI.begin();
  while (CAN_OK != can2.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ)) {
    Serial.println("MCP2515 init failed. Retrying...");
    delay(1000);
  }
  can2.setMode(MCP_NORMAL);
  Serial.println("MCP2515 CAN OK");
}

void loop() {
  CANMessage frame;
  if (ACAN_ESP32::can.receive(frame)) parseCAN(frame, 1);
  long unsigned int rxId; uint8_t len = 0, buf[8];
  if (can2.readMsgBuf(&rxId, &len, buf) == CAN_OK) {
    CANMessage m; m.id = rxId; m.len = len; memcpy(m.data, buf, len);
    parseCAN(m, 2);
  }

  if (millis() - lastTesterPing >= testerPingInterval) {
    lastTesterPing = millis();
    uint8_t req1[] = {0x02, 0x21, 0xCE,0,0,0,0,0};
    uint8_t req2[] = {0x02, 0x21, 0xCE,0,0,0,0,0};
    uint8_t req3[] = {0x02, 0x21, 0xCE,0,0,0,0,0};
    uint8_t req4[] = {0x02, 0x21, 0xCE,0,0,0,0,0};
    CANMessage tx; tx.id = 0x7E3; tx.len = 8;

    if (pid==1) memcpy(tx.data, req1, 8);
    if (pid==2) memcpy(tx.data, req2, 8);
    if (pid==3) memcpy(tx.data, req3, 8);
    if (pid==4) memcpy(tx.data, req4, 8);

    ACAN_ESP32::can.tryToSend(tx);
    can2.sendMsgBuf(0x7E3, 0, 8, tx.data);
    pid++; if (pid>4) pid=1;
  }
}

void parsePIDMap(uint8_t* data, size_t len) {
  if (len < 4) return;
  uint8_t base = data[2];
  Serial.printf("[PIDMAP] Base 0x%02X\n", base);
  for (int i=0; i<4; i++) {
    uint8_t b = data[3+i];
    for (int bit=7; bit>=0; bit--) {
      if (b & (1<<bit)) {
        uint8_t pid = base + (i*8 + (7-bit)) + 1;
        Serial.printf("  -> PID: 0x%02X\n", pid);
      }
    }
  }
}

void decodeBlocks(uint8_t* data, size_t len, int bus) {
  float* blocks = (bus == 1) ? blockVoltages1 : blockVoltages2;

  // We expect at least 2 bytes of header + 14 * 2 = 30 bytes of data
  if (len < 2 + 14 * 2) {
    Serial.printf("[BUS%d] decodeBlocks() called with insufficient data!\n", bus);
    return;
  }

  Serial.printf("[BUS%d] data:", bus);
  for (size_t i = 0; i < len; i++) {
    Serial.printf(" %02X", data[i]);
  }
  Serial.println();

  for (int i = 0; i < 14; i++) {
    uint8_t D = data[2 + i * 2];
    uint8_t E = data[2 + i * 2 + 1];
    blocks[i] = (2.56f * D) + (0.01f * E) - 327.68f;
  }

  Serial.printf("[BUS%d] Blocks:", bus);
  float sum = 0.0;
  for (int i = 0; i < 14; i++) {
    Serial.printf(" %.2f", blocks[i]);
    sum += blocks[i];
  }
  Serial.printf("\n[BUS%d] Sum of blocks: %.2f V\n", bus, sum);
}

void parseCAN(const CANMessage &frame, int bus) {
  float &packVoltage = (bus==1) ? packVoltage1 : packVoltage2;
  float &packCurrent = (bus==1) ? packCurrent1 : packCurrent2;
  int &soc = (bus==1) ? soc1 : soc2;
  int &ccl = (bus==1) ? ccl1 : ccl2;
  int &cdl = (bus==1) ? cdl1 : cdl2;
  int &tempA = (bus==1) ? temp1a : temp2a;
  int &tempB = (bus==1) ? temp1b : temp2b;
  uint16_t &faultCode = (bus==1) ? faultCode1 : faultCode2;
  uint8_t &deltaSOC = (bus==1) ? deltaSOC1 : deltaSOC2;
  uint16_t &calibX = (bus==1) ? calibX1 : calibX2;
  uint16_t &calibY = (bus==1) ? calibY1 : calibY2;
  uint16_t &calibZ = (bus==1) ? calibZ1 : calibZ2;
  uint8_t &flags = (bus==1) ? flags1 : flags2;

  switch (frame.id) {
    case 0x03B: { int16_t raw = ((frame.data[0]&0x0F)<<8)|frame.data[1];
      if (raw & 0x800) raw -= 0x1000; packCurrent = raw*0.1;
      packVoltage = (frame.data[2]<<8)|frame.data[3]; break; }
    case 0x3CB: { cdl=frame.data[0]; ccl=frame.data[1]; deltaSOC=frame.data[2];
      soc=frame.data[3]; tempA=(int8_t)frame.data[4]; tempB=(int8_t)frame.data[5]; break; }
    case 0x3CD: { faultCode=(frame.data[0]<<8)|frame.data[1];
      packVoltage=(frame.data[2]<<8)|frame.data[3]; break; }
    case 0x3C9: { calibY=(frame.data[0]<<4)|(frame.data[1]>>4);
      calibZ=((frame.data[1]&0x0F)<<8)|frame.data[2];
      calibX=(frame.data[3]<<4)|(frame.data[4]>>4); break; }
    case 0x4D1: { flags = frame.data[7]; break; }
  }

  if (frame.id==0x7EB) {
    uint8_t* buf = (bus==1) ? isoTPBuf1 : isoTPBuf2;
    size_t &isoLen = (bus==1) ? isoTPLen1 : isoTPLen2;
    uint8_t pci = frame.data[0];
    if ((pci & 0xF0)==0x00) {
      isoLen = pci&0x0F; memcpy(buf, frame.data+1, isoLen);
      if (buf[1]==0x61 && (buf[2]&0xF0)==0x40) parsePIDMap(buf, isoLen);
      else decodeBlocks(buf+3, isoLen-3, bus);
    } else if ((pci&0xF0)==0x10) {
      isoLen=frame.data[1]; size_t cpy=frame.len-2;
      memcpy(buf, frame.data+2, cpy); isoLen=cpy;
      
      // Send Flow Control
      CANMessage fc;
      fc.id = 0x7E3;
      fc.len = 8;
      fc.data[0] = 0x30; // Flow Control
      fc.data[1] = 0x00; // No block size
      fc.data[2] = 0x05; // 5 ms
      for (int i = 3; i < 8; i++) fc.data[i] = 0;
      if (bus == 1)
        ACAN_ESP32::can.tryToSend(fc);
      else
        can2.sendMsgBuf(fc.id, 0, fc.len, fc.data);
    } else if ((pci&0xF0)==0x20) {
      size_t cpy=frame.len-1; memcpy(buf+isoLen, frame.data+1, cpy);
      isoLen+=cpy; if (isoLen>=14) decodeBlocks(buf+3, isoLen-3, bus);
    }
  }
}

r1ckyb0nd
Posts: 28
Joined: Sun Dec 22, 2024 1:34 pm
Has thanked: 3 times
Been thanked: 1 time

Re: [FIRST DRIVE] Toyota Prius gen2 plug and play

Post by r1ckyb0nd »

turnip73 wrote: Sun Aug 03, 2025 8:34 pm Wrote a small Prius Battery ECU decoder using an ESP32 and a couple of 3.3V Can Boards before I get ahead of my self over sharing the Toyota hybrid batteries:
Does this code allow you to use the prius battery pack natively?

I am looking to use a prius battery pack as my bench top testing battery and was hoping to not have to buy a different BMS to use as it will mot be my final battery configuration.
turnip73
Posts: 58
Joined: Wed Dec 26, 2018 1:38 pm
Location: Greystones Ireland
Has thanked: 22 times
Been thanked: 47 times

Re: [FIRST DRIVE] Toyota Prius gen2 plug and play

Post by turnip73 »

r1ckyb0nd wrote: Sat Aug 30, 2025 9:33 pm Does this code allow you to use the prius battery pack natively?
yes, visual for integration with OI see https://github.com/hakanrolsson/stm32-c ... prius-gen2
For info on the battery ecu's can messages, see http://www.eaa-phev.org/wiki/Prius_PHEV ... #CAN_Tools
Post Reply