Prius Gen2 A/C compressor inverter

Topics concerning the Toyota and Lexus inverter drop in boards
turnip73
Posts: 53
Joined: Wed Dec 26, 2018 1:38 pm
Location: Greystones Ireland
Has thanked: 15 times
Been thanked: 38 times

Re: Prius Gen2 A/C compressor inverter

Post by turnip73 »

Observations:
1) I'd say my A/C pressure switch is gone when I bypass it the A/C start
2) If your hybrid battery is bad the car will not run A/C https://www.mavyn.com/blog/decoding-p0a ... xes-causes
3) I was using an SFH618A Optocoupler to connect the 12V signal to my 5V logical analyser to capture logs. This stops the A/C running.
turnip73
Posts: 53
Joined: Wed Dec 26, 2018 1:38 pm
Location: Greystones Ireland
Has thanked: 15 times
Been thanked: 38 times

Re: Prius Gen2 A/C compressor inverter

Post by turnip73 »

turnip73 wrote: Fri Dec 29, 2023 4:39 pm From the Denzo ES27C thread seems I now need to connect high voltage to get the inverter to wake up.
High voltage is ~40V.
turnip73
Posts: 53
Joined: Wed Dec 26, 2018 1:38 pm
Location: Greystones Ireland
Has thanked: 15 times
Been thanked: 38 times

Re: Prius Gen2 A/C compressor inverter

Post by turnip73 »

Hmm, I didn't expect the CLK signal to come from the inverter.
Attachments
20240806_174303.jpg
20240806_174401.jpg
WOSS
Posts: 4
Joined: Tue Aug 13, 2024 12:52 am
Been thanked: 4 times

Re: Prius Gen2 A/C compressor inverter

Post by WOSS »

CLK and ITE are from the inverter
WOSS
Posts: 4
Joined: Tue Aug 13, 2024 12:52 am
Been thanked: 4 times

Re: Prius Gen2 A/C compressor inverter

Post by WOSS »

Image
Attachments
1724383780147(1).png
turnip73
Posts: 53
Joined: Wed Dec 26, 2018 1:38 pm
Location: Greystones Ireland
Has thanked: 15 times
Been thanked: 38 times

Re: Prius Gen2 A/C compressor inverter

Post by turnip73 »

Thank you WOSS for confirming. I'm now trying to figure out how to update the Arduino software from the ES27C thread to use an incoming clk instead of sending the clk.
WOSS
Posts: 4
Joined: Tue Aug 13, 2024 12:52 am
Been thanked: 4 times

Re: Prius Gen2 A/C compressor inverter

Post by WOSS »

Have you figured out how to start the air conditioning compressor? What is the communication protocol of the air conditioning compressor?
turnip73
Posts: 53
Joined: Wed Dec 26, 2018 1:38 pm
Location: Greystones Ireland
Has thanked: 15 times
Been thanked: 38 times

Re: Prius Gen2 A/C compressor inverter

Post by turnip73 »

WOSS wrote: Sun Sep 22, 2024 2:36 am Have you figured out how to start the air conditioning compressor? What is the communication protocol of the air conditioning compressor?
Making some small progress as mentioned previous in this thread it's some kind of SPI interface.
See script below for Ardunio Naon that can decode and reply to inverter:

Code: Select all

// Pin Definitions
const int clockPin = 13;     // SPI Clock Pin (active low)
const int dataInPin = 11;    // Data Pin for receiving data
const int dataOutPin = 12;   // Data Pin for sending response
const int packetSize = 10;  // Number of bytes in a packet

// Timing Definitions
const unsigned long clockCycleTime = 3200;      // Clock cycle time in microseconds (3.2ms)
const unsigned long byteReceiveTime = 26000;    // Time to receive one byte (26ms)
const unsigned long packetGapTime = 80000;      // Time between packets (80ms)

// Fixed Response Data
byte responseData[packetSize] = {0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF};
//byte responseData[packetSize] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A};

// Variables
byte receivedData[packetSize];  // Buffer to store received data
unsigned long lastPacketTime = 0; // Last time a packet was received
bool receivingPacket = false;     // Are we currently receiving a packet?

void setup() {
  pinMode(clockPin, INPUT);       // Set the clock pin as input
  pinMode(dataInPin, INPUT);      // Set the data input pin as input
  pinMode(dataOutPin, OUTPUT);    // Set the data output pin as output
  Serial.begin(9600);             // Start serial communication for debugging
  digitalWrite(dataOutPin, LOW);  // Initialize data output to low
}

void loop() {
  unsigned long currentTime = micros();

  // Check for the 80ms gap and the first clock pulse (active low)
  if (!receivingPacket && (currentTime - lastPacketTime >= packetGapTime) && digitalRead(clockPin) == LOW) {
    // Ready to receive a new packet
    receivingPacket = true;
    lastPacketTime = currentTime; // Reset the last packet time
    receiveAndRespondPacket();
  }
}

void receiveAndRespondPacket() {
  unsigned long startTime = micros();

  for (int byteIndex = 0; byteIndex < packetSize; byteIndex++) {
    byte receivedByte = 0;

    // Read 8 bits (1 byte) from the data line, synchronized with clock
    for (int bitIndex = 0; bitIndex < 8; bitIndex++) {
      //waitForClockPulse();       // Wait for the clock to go low (active low)
        // Wait for the clock to go low (active low)
  while (digitalRead(clockPin) == HIGH) {
    // Wait for clock to go low
  }
        // Send corresponding bit from the response byte
      bool responseBit = (responseData[byteIndex] >> (7 - bitIndex)) & 1;
      digitalWrite(dataOutPin, responseBit ? HIGH : LOW);
  // Wait for half the clock cycle to synchronize bit read
  delayMicroseconds(clockCycleTime / 2);
      receivedByte <<= 1;        // Shift left to make space for the new bit
      if (digitalRead(dataInPin) == HIGH) {
        receivedByte |= 1;       // Set the bit if the data input pin is high
      }



      // Wait for the clock to complete its cycle
      waitForClockReturn();
    }

    // Store the received byte in the buffer
    receivedData[byteIndex] = receivedByte;

    // Wait for the remainder of the 26ms per byte, if needed
    while (micros() - startTime < byteReceiveTime * (byteIndex + 1)) {
      // Waiting until time for the next byte
    }
    if (byteIndex < (packetSize -1))
    {
    digitalWrite(dataOutPin, LOW);
    }
  }

  receivingPacket = false;
  lastPacketTime = micros();  // Update the last packet time

  // Debugging: Print the received packet
  Serial.print("Received Packet: ");
  for (int i = 0; i < packetSize; i++) {
    Serial.print(receivedData[i], HEX);
    Serial.print(" ");
  }
  Serial.println();
}

void waitForClockPulse() {
  // Wait for the clock to go low (active low)
  while (digitalRead(clockPin) == HIGH) {
    // Wait for clock to go low
  }
  // Wait for half the clock cycle to synchronize bit read
  delayMicroseconds(clockCycleTime / 2);
}

void waitForClockReturn() {
  // Wait for clock to go high to complete the cycle
  while (digitalRead(clockPin) == LOW) {
    // Wait for clock to go high
  }
}


Output:
20:33:54.002 -> Received Packet: 0 0 0 0 0 0 C 0 0 73
20:33:54.399 -> Received Packet: 0 0 2 0 0 0 18 0 0 E5
20:33:54.798 -> Received Packet: 0 0 2 0 0 0 18 0 0 E5
20:33:55.196 -> Received Packet: 0 0 2 0 0 0 18 0 0 E5
20:33:55.594 -> Received Packet: 0 0 2 0 0 0 18 0 0 E5
20:33:55.992 -> Received Packet: 0 0 2 0 0 0 18 0 0 E5
20:33:56.390 -> Received Packet: 0 0 82 0 0 0 18 0 0 65
20:33:56.788 -> Received Packet: 0 0 82 0 0 0 18 0 0 65
20:33:57.186 -> Received Packet: 0 0 82 0 0 0 18 0 0 65
20:33:57.584 -> Received Packet: 0 0 82 0 0 0 18 0 0 65
20:33:57.982 -> Received Packet: 0 0 82 0 0 0 18 0 0 65
20:33:58.380 -> Received Packet: 0 0 82 0 0 0 18 0 0 65
20:33:58.778 -> Received Packet: 0 0 82 0 0 0 18 0 0 65
20:33:59.176 -> Received Packet: 0 0 82 0 0 0 18 0 0 65
20:33:59.574 -> Received Packet: 0 0 82 0 0 0 18 0 0 65
20:33:59.972 -> Received Packet: 0 0 82 0 0 0 18 0 0 65
20:34:00.371 -> Received Packet: 0 0 82 0 0 0 18 0 0 65
20:34:00.769 -> Received Packet: 0 0 82 0 0 0 18 0 0 65
20:34:01.167 -> Received Packet: 0 0 82 0 0 0 18 0 0 65
20:34:01.531 -> Received Packet: 0 0 82 0 0 0 18 0 0 65
20:34:01.930 -> Received Packet: 0 0 82 0 0 0 18 0 0 65
20:34:02.328 -> Received Packet: 0 0 82 0 0 0 18 0 0 65
20:34:02.726 -> Received Packet: 0 0 82 0 0 0 18 0 0 65
20:34:03.124 -> Received Packet: 0 0 82 0 0 0 18 0 0 65
20:34:03.522 -> Received Packet: 0 0 82 0 0 0 18 0 0 65
20:34:03.920 -> Received Packet: 0 0 82 0 0 0 18 0 0 65
20:34:04.318 -> Received Packet: 0 0 82 0 0 0 18 0 0 65

PulsView file attached
Attachments
chatgtpv25.sr.gz
(2.33 KiB) Downloaded 348 times
chatgtpv25.pvs.gz
(685 Bytes) Downloaded 337 times
turnip73
Posts: 53
Joined: Wed Dec 26, 2018 1:38 pm
Location: Greystones Ireland
Has thanked: 15 times
Been thanked: 38 times

Re: Prius Gen2 A/C compressor inverter

Post by turnip73 »

Finally managed to capture logs of the A/C turning on for real.
I did this using my 5V logical analyzer with a CD4504B voltage level shifter.
There was some noise, which I filtered out.
Now, I'm trying to figure out the CRC and send back ETI messages using an Arduino Nano.

Sigrok files attached.

Videos how I captured




CD4504B pin connections
1 VCC (12V)
2 AOUT (CLK to 5V logical analyzer)
3 AIN (CLK)
4 BOUT (ETI to 5V logical analyzer)
5 BIN (ETI)
6 COUT (STB to 5V logical analyzer)
7 CIN (STB)
8 VSS GND
9 DOUT (ITE to 5V logical analyzer)
10 DIN (ITE)
11 EOUT
12 EIN
13 SELECT (GND)
14 FIN
15 FOUT
16 VDD (5V)

Decoder settings
Attachments
ac on for real video2 20250201 edited MISO ETI.txt
(3.32 KiB) Downloaded 321 times
SPI 20250201.gif
ac on for real with tis video 20250201 edited.zip
(13.44 KiB) Downloaded 328 times
ac on for real video2 20250201 edited.zip
(11.92 KiB) Downloaded 311 times
MattsAwesomeStuff
Posts: 1013
Joined: Fri Apr 26, 2019 5:40 pm
Has thanked: 388 times
Been thanked: 258 times

Re: Prius Gen2 A/C compressor inverter

Post by MattsAwesomeStuff »

turnip73 wrote: Sat Feb 08, 2025 6:16 pmVideos how I captured
Image

What in the unholy Cthulhu hell of Mom's Spaghetti...

I noped right out of trying to understand it, and skipped right forward to appreciating whatever it is that was done.
turnip73
Posts: 53
Joined: Wed Dec 26, 2018 1:38 pm
Location: Greystones Ireland
Has thanked: 15 times
Been thanked: 38 times

Re: Prius Gen2 A/C compressor inverter

Post by turnip73 »

Success! It Works! 🎉

We can now control the Gen2 A/C compressor inverter! 🚗💨

🔗 Arduino code & logs: GitHub Repository

Also, I figured out the CRC codes using ChatGPT:
Byte 5 = (B1 ≪ 1) ⊕ B2 ⊕ B3 ⊕ B4

📌 Next Steps
1️⃣ Create a wiring diagram in KiCad.
2️⃣ Decode the log: AC on for real - video
- Update the Arduino code to send continuous messages instead of replaying recorded log messages.
- This will allow full control of the inverter.
3️⃣ Update the Wiki with the latest findings.
4️⃣ Start testing with different inverter versions & car models to ensure compatibility.

🔧 Potential Uses for the A/C Inverter
✅ Run A/C on Gen2 conversions.
✅ Charging applications: Discussion.
✅ Power three-phase accessories.

📺 Demo Video: YouTube Shorts

Big thank you to xp677 for the work on Denso ES27C AC Compressor control (Toyota/Lexus)
turnip73
Posts: 53
Joined: Wed Dec 26, 2018 1:38 pm
Location: Greystones Ireland
Has thanked: 15 times
Been thanked: 38 times

Re: Prius Gen2 A/C compressor inverter

Post by turnip73 »

MattsAwesomeStuff wrote: Sun Feb 09, 2025 1:06 am Image

What in the unholy Cthulhu hell of Mom's Spaghetti...

I noped right out of trying to understand it, and skipped right forward to appreciating whatever it is that was done.
This is a man in the middle board for toyota prius gen2 HV ECU see https://github.com/hakanrolsson/PriusHVConnector
MattsAwesomeStuff
Posts: 1013
Joined: Fri Apr 26, 2019 5:40 pm
Has thanked: 388 times
Been thanked: 258 times

Re: Prius Gen2 A/C compressor inverter

Post by MattsAwesomeStuff »

turnip73 wrote: Fri Feb 21, 2025 9:31 pmSuccess! It Works! 🎉
We can now control the Gen2 A/C compressor inverter! 🚗💨
Image

I have a prius gen 2 air conditioner system, I could presumably hook it up and use it for its intended purpose now. Or, repurpose it as a charger perhaps (though, I do want A/C, but I also have a Gen 3 air con system which has its inverter built into the compressor and just needs PWM input I think).
johu wrote:


Can this be integrated into the existing hardware/software for the Prius Gen 2 board, or is something standalone needed?
turnip73
Posts: 53
Joined: Wed Dec 26, 2018 1:38 pm
Location: Greystones Ireland
Has thanked: 15 times
Been thanked: 38 times

Re: Prius Gen2 A/C compressor inverter

Post by turnip73 »

turnip73 wrote: Fri Feb 21, 2025 9:31 pm 1️⃣ Create a wiring diagram in KiCad.
https://github.com/hakanrolsson/Prius-G ... verter.pdf
MattsAwesomeStuff wrote: Fri Feb 21, 2025 11:21 pm Can this be integrated into the existing hardware/software for the Prius Gen 2 board, or is something standalone needed?
In my opinion, more suited for stm32-car or ZombieVerter.
turnip73 wrote: Fri Feb 21, 2025 9:31 pm 3️⃣ Update the Wiki with the latest findings.
Done
User avatar
johu
Site Admin
Posts: 6618
Joined: Thu Nov 08, 2018 10:52 pm
Location: Kassel/Germany
Has thanked: 342 times
Been thanked: 1484 times
Contact:

Re: Prius Gen2 A/C compressor inverter

Post by johu »

Thanks very much for your work!
MattsAwesomeStuff wrote: Fri Feb 21, 2025 11:21 pm Can this be integrated into the existing hardware/software for the Prius Gen 2 board, or is something standalone needed
To be honest after initial excitement the Gen2 controller became a bit of a shelve warmer so I'm not currently planning a new revision. I agree this would fit better into one of the VCU projects.
Support R/D and forum on Patreon: https://patreon.com/openinverter - Subscribe on odysee: https://odysee.com/@openinverter:9
MattsAwesomeStuff
Posts: 1013
Joined: Fri Apr 26, 2019 5:40 pm
Has thanked: 388 times
Been thanked: 258 times

Re: Prius Gen2 A/C compressor inverter

Post by MattsAwesomeStuff »

johu wrote: Sun Feb 23, 2025 10:40 pmTo be honest after initial excitement the Gen2 controller became a bit of a shelve warmer
Longer discussion for another day, but, partial solutions with moderate documentation never become a viable choice for an amateur.

People don't need 10 choices, they would be happy with 1 choice that is the easiest to use. Or rather, people will choose the one solution that is most likely to solve their problem for them.

As it turns out, the Gen2 transaxle was kind of anemic without dual motor control, and maybe speed limited too. Without fully using all of the things on board in the inverter as a system, or the One Stop Junkyard trip to get it all at once, I think the lackluster transaxle performance poisoned the cheapness and utility of the rest of the Gen2 stuff, especially the inverter. If you're going to use a Leaf motor, you're going to grab the Leaf stack. If a dual-motor version had been the default, then the transaxle doesn't look so wimpy anymore, and it becomes a more viable option again.

The "judge how much more development to put into it, based on sales of the half-completed development version" isn't that useful of a metric, especially when it's low-end and the major advantage is how well bundled of a solution it could potentially be. Though from a developer standpoint, "Keep putting effort into something not many people have shown enthusiasm for" isn't attractive either. It's entirely possible you dump double the effort into it, and get no additional results. In the end, a crappy metric might be better than no metric, and, there isn't really any way of predicting this except to imagine what people might want if it was done.

I empathize.
turnip73
Posts: 53
Joined: Wed Dec 26, 2018 1:38 pm
Location: Greystones Ireland
Has thanked: 15 times
Been thanked: 38 times

Re: Prius Gen2 A/C compressor inverter

Post by turnip73 »

OK, so I have a solution that kind of works to control the A/C compressor inverter. I took the messages from the TIS run and the Arduino program and converted it to esp32 that have a Wi-Fi module and made an interface similar to TIS. Roughly mapped messages with RPM (still haven't fully decoded the content in the messages). I will park this development for now. If someone want to pick up the thread, I'll be more than happy to help with development boards etc. The most important objective for me has been achieved that, that the inverter is active and can be turned off, thus hopefully making sure the gates are permanently low to be able to use the inverter for charging without modifications.

Code: Select all

#include <WiFi.h>
#include <ESPAsyncWebServer.h>

// Pin Definitions
const int clockPin = 18;     // SPI Clock Pin (active low)
const int dataInPin = 19;    // Data Pin for receiving data
const int dataOutPin = 23;   // Data Pin for sending response
const int chipSelectPin = 5; // Chip Select Pin
const int packetSize = 10;   // Number of bytes in a packet

// Timing Definitions
const unsigned long clockCycleTime = 3200;      // Clock cycle time in microseconds (3.2ms)
const unsigned long byteReceiveTime = 26000;    // Time to receive one byte (26ms)
const unsigned long packetGapTime = 80000;      // Time between packets (80ms)

// Fixed Response Data
byte responseData[packetSize] = {0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
int compressorTargetSpeed = 0;  // Default target speed

// Wi-Fi Access Point Credentials
const char* ssid = "CompressorControl";  
const char* password = "12345678";       

// Web Server
AsyncWebServer server(80);

// Variables
byte receivedData[packetSize];  
unsigned long lastPacketTime = 0; 
bool receivingPacket = false;    

void processReceivedData() {
    // Modify response based on compressor target speed
    if (compressorTargetSpeed == 100) {
        byte defaultResponse[packetSize] = {0xFB, 0x3F, 0xFE, 0xFF, 0xC5, 0xFB, 0x3F, 0xFE, 0xFF, 0xC5};
     memcpy(responseData, defaultResponse, packetSize);
    }
    else if (compressorTargetSpeed == 200) {
        byte defaultResponse[packetSize] = {0xDE, 0x3F, 0xFE, 0xFF, 0xE0, 0xDE, 0x3F, 0xFE, 0xFF, 0xE0};
       memcpy(responseData, defaultResponse, packetSize);
    }
    else if (compressorTargetSpeed == 300) {
        byte defaultResponse[packetSize] = {0xF2, 0xDF, 0xFE, 0xFF, 0x2C, 0xF2, 0xDF, 0xFE, 0xFF, 0x2C};
     memcpy(responseData, defaultResponse, packetSize);
    }
    else if (compressorTargetSpeed == 400) {
        byte defaultResponse[packetSize] = {0xE1, 0x5F, 0xFE, 0xFF, 0xBF, 0xE1, 0x5F, 0xFE, 0xFF, 0xBF};
     memcpy(responseData, defaultResponse, packetSize);
         }  
     else if (compressorTargetSpeed == 500) {
        byte defaultResponse[packetSize] = {0xD7, 0x5F, 0xFE, 0xFF, 0x99, 0xD7, 0x5F, 0xFE, 0xFF, 0x99};
       memcpy(responseData, defaultResponse, packetSize);
    }  
     else if (compressorTargetSpeed == 600) {
        byte defaultResponse[packetSize] = {0xE1, 0x5F, 0xFE, 0xFF, 0xBF, 0xE1, 0x5F, 0xFE, 0xFF, 0xBF};
      memcpy(responseData, defaultResponse, packetSize);
    }
     else if (compressorTargetSpeed == 700) {
        byte defaultResponse[packetSize] = {0xC4, 0x5F, 0xFE, 0xFF, 0x86, 0xC4, 0x5F, 0xFE, 0xFF, 0x86};
    memcpy(responseData, defaultResponse, packetSize);
    }   
     else if (compressorTargetSpeed == 800) {
        byte defaultResponse[packetSize] = {0xFD, 0x9F, 0xFE, 0xFF, 0x63, 0xFD, 0x9F, 0xFE, 0xFF, 0x63};
    memcpy(responseData, defaultResponse, packetSize);
    }  
       else if (compressorTargetSpeed == 900) {
        byte defaultResponse[packetSize] = {0xEF, 0x1F, 0xFE, 0xFF, 0xF1, 0xEF, 0x1F, 0xFE, 0xFF, 0xF1};
    memcpy(responseData, defaultResponse, packetSize);
    }  
       else if (compressorTargetSpeed == 1000) {
        byte defaultResponse[packetSize] = {0xC9, 0x1F, 0xFE, 0xFF, 0xCF, 0xC9, 0x1F, 0xFE, 0xFF, 0xCF};
    memcpy(responseData, defaultResponse, packetSize);
    } 
       else if (compressorTargetSpeed == 1100) {
        byte defaultResponse[packetSize] = {0xD3, 0xEF, 0xFE, 0xFF, 0x3D, 0xD3, 0xEF, 0xFE, 0xFF, 0x3D};
    memcpy(responseData, defaultResponse, packetSize);
    } 
       else if (compressorTargetSpeed == 1200) {
        byte defaultResponse[packetSize] = {0xE6, 0xEF, 0xFE, 0xFF, 0x04, 0xE6, 0xEF, 0xFE, 0xFF, 0x04};
    memcpy(responseData, defaultResponse, packetSize);
    }        
       else if (compressorTargetSpeed == 1300) {
        byte defaultResponse[packetSize] = {0xF9, 0x6F, 0xFE, 0xFF, 0x97, 0xF9, 0x6F, 0xFE, 0xFF, 0x97};
    memcpy(responseData, defaultResponse, packetSize);
    }   
       else if (compressorTargetSpeed == 1400) {
        byte defaultResponse[packetSize] = {0xDC, 0x6F, 0xFE, 0xFF, 0xB2, 0xDC, 0x6F, 0xFE, 0xFF, 0xB2};
    memcpy(responseData, defaultResponse, packetSize);
    }      
       else if (compressorTargetSpeed == 1500) {
        byte defaultResponse[packetSize] = {0xF0, 0xAF, 0xFE, 0xFF, 0x5E, 0xF0, 0xAF, 0xFE, 0xFF, 0x5E};
    memcpy(responseData, defaultResponse, packetSize);
    }
       else if (compressorTargetSpeed == 1600) {
        byte defaultResponse[packetSize] = {0xD5, 0x2F, 0xFE, 0xFF, 0xFB, 0xD5, 0x2F, 0xFE, 0xFF, 0xFB};
    memcpy(responseData, defaultResponse, packetSize);
    }  
       else if (compressorTargetSpeed == 1700) {
        byte defaultResponse[packetSize] = {0xC7, 0xCF, 0xFE, 0xFF, 0x15, 0xC7, 0xCF, 0xFE, 0xFF, 0x15};
    memcpy(responseData, defaultResponse, packetSize);
    } 
       else if (compressorTargetSpeed == 1800) {
        byte defaultResponse[packetSize] = {0xFE, 0xCF, 0xFE, 0xFF, 0x30, 0xFE, 0xCF, 0xFE, 0xFF, 0x30};
    memcpy(responseData, defaultResponse, packetSize);
    }
       else if (compressorTargetSpeed == 1900) {
        byte defaultResponse[packetSize] = {0xCA, 0x4F, 0xFE, 0xFF, 0x9C, 0xCA, 0x4F, 0xFE, 0xFF, 0x9C};
    memcpy(responseData, defaultResponse, packetSize);
    }   
       else if (compressorTargetSpeed == 2000) {
        byte defaultResponse[packetSize] = {0xF7, 0x8F, 0xFE, 0xFF, 0x79, 0xF7, 0x8F, 0xFE, 0xFF, 0x79};
    memcpy(responseData, defaultResponse, packetSize);
    } 
       else if (compressorTargetSpeed == 2100) {
        byte defaultResponse[packetSize] = {0xE4, 0x8F, 0xFE, 0xFF, 0x66, 0xE4, 0x8F, 0xFE, 0xFF, 0x66};
    memcpy(responseData, defaultResponse, packetSize);
    }   
       else if (compressorTargetSpeed == 2200) {
        byte defaultResponse[packetSize] = {0xC3, 0x0F, 0xFE, 0xFF, 0xD3, 0xC3, 0x0F, 0xFE, 0xFF, 0xD3};
    memcpy(responseData, defaultResponse, packetSize);
    }
       else if (compressorTargetSpeed == 2300) {
        byte defaultResponse[packetSize] = {0xFA, 0x0F, 0xFE, 0xFF, 0xF4, 0xFA, 0x0F, 0xFE, 0xFF, 0xF4};
    memcpy(responseData, defaultResponse, packetSize);
    } 
       else if (compressorTargetSpeed == 2400) {
        byte defaultResponse[packetSize] = {0xE4, 0x8F, 0xFE, 0xFF, 0x66, 0xE4, 0x8F, 0xFE, 0xFF, 0x66};
    memcpy(responseData, defaultResponse, packetSize);
    }
       else if (compressorTargetSpeed == 2500) {
        byte defaultResponse[packetSize] = {0xF7, 0x8F, 0xFE, 0xFF, 0x79, 0xF7, 0x8F, 0xFE, 0xFF, 0x79};
    memcpy(responseData, defaultResponse, packetSize);
    }
       else if (compressorTargetSpeed == 2600) {
        byte defaultResponse[packetSize] = {0xED, 0x4F, 0xFE, 0xFF, 0xAB, 0xED, 0x4F, 0xFE, 0xFF, 0xAB};
    memcpy(responseData, defaultResponse, packetSize);
    } 
       else if (compressorTargetSpeed == 2700) {
        byte defaultResponse[packetSize] = {0xD8, 0xCF, 0xFE, 0xFF, 0x0E, 0xD8, 0xCF, 0xFE, 0xFF, 0x0E};
    memcpy(responseData, defaultResponse, packetSize);
    }
       else if (compressorTargetSpeed == 2800) {
        byte defaultResponse[packetSize] = {0xFE, 0xCF, 0xFE, 0xFF, 0x30, 0xFE, 0xCF, 0xFE, 0xFF, 0x30};
    memcpy(responseData, defaultResponse, packetSize);
    }
       else if (compressorTargetSpeed == 2900) {
        byte defaultResponse[packetSize] = {0xE2, 0x2F, 0xFE, 0xFF, 0xC2, 0xE2, 0x2F, 0xFE, 0xFF, 0xC2};
    memcpy(responseData, defaultResponse, packetSize);
    }
       else if (compressorTargetSpeed == 3000) {
        byte defaultResponse[packetSize] = {0xCE, 0xAF, 0xFE, 0xFF, 0x68, 0xCE, 0xAF, 0xFE, 0xFF, 0x68};
    memcpy(responseData, defaultResponse, packetSize);
    }    
       else if (compressorTargetSpeed == 3100) {
        byte defaultResponse[packetSize] = {0xDC, 0x6F, 0xFE, 0xFF, 0xB2, 0xDC, 0x6F, 0xFE, 0xFF, 0xB2};
    memcpy(responseData, defaultResponse, packetSize);
    }
       else if (compressorTargetSpeed == 3200) {
        byte defaultResponse[packetSize] = {0xF9, 0x6F, 0xFE, 0xFF, 0x97, 0xF9, 0x6F, 0xFE, 0xFF, 0x97};
    memcpy(responseData, defaultResponse, packetSize);
    }     
       else if (compressorTargetSpeed == 3300) {
        byte defaultResponse[packetSize] = {0xE6, 0xEF, 0xFE, 0xFF, 0x04, 0xE6, 0xEF, 0xFE, 0xFF, 0x04};
    memcpy(responseData, defaultResponse, packetSize);
    }                                                                                                        
    else {
        byte defaultResponse[packetSize] = {0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
    memcpy(responseData, defaultResponse, packetSize);
    }
}

void receiveAndRespondPacket() {
    unsigned long startTime = micros();
    for (int byteIndex = 0; byteIndex < packetSize; byteIndex++) {
        byte receivedByte = 0;

        for (int bitIndex = 0; bitIndex < 8; bitIndex++) {
            while (digitalRead(clockPin) == LOW) { delayMicroseconds(1); }
            
            bool responseBit = (responseData[byteIndex] >> (7 - bitIndex)) & 1;
            digitalWrite(dataOutPin, responseBit ? LOW : HIGH);
            
            delayMicroseconds(clockCycleTime / 2);
            receivedByte <<= 1;
            if (digitalRead(dataInPin) == HIGH) receivedByte |= 1;

            while (digitalRead(clockPin) == HIGH) { delayMicroseconds(1); }
        }

        receivedData[byteIndex] = receivedByte;

        while (micros() - startTime < byteReceiveTime * (byteIndex + 1)) { delayMicroseconds(1); }

        if (byteIndex < (packetSize - 1)) digitalWrite(dataOutPin, LOW);
    }

    receivingPacket = false;
    lastPacketTime = micros();

    Serial.print("Received: ");
    for (int i = 0; i < packetSize; i++) {
        Serial.printf("%02X ", receivedData[i]);
    }
    Serial.println();
}

void updateSpeed(int change) {
    compressorTargetSpeed += change;
    if (compressorTargetSpeed < 0) compressorTargetSpeed = 0;
    if (compressorTargetSpeed > 7500) compressorTargetSpeed = 7500;
    processReceivedData();
}

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

    pinMode(clockPin, INPUT);
    pinMode(dataInPin, INPUT);
    pinMode(dataOutPin, OUTPUT);
    pinMode(chipSelectPin, OUTPUT);
    digitalWrite(dataOutPin, LOW);
    digitalWrite(chipSelectPin, HIGH);

    WiFi.softAP(ssid, password);
    Serial.printf("Wi-Fi started. Connect to: %s\n", WiFi.softAPIP().toString().c_str());

    server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
        String html = "<html><head><title>Compressor Control</title>";
        html += "<meta name='viewport' content='width=device-width, initial-scale=1'>";
        html += "<style>body { font-family: Arial; text-align: center; } ";
        html += "button { font-size: 20px; padding: 10px 20px; margin: 10px; } ";
        html += "</style></head><body>";
        html += "<h2>Compressor Target Speed</h2>";
        html += "<h1 id='speed'>" + String(compressorTargetSpeed) + " RPM</h1>";
        html += "<a href='/decrease'><button>&lt; Decrease</button></a>";
        html += "<a href='/increase'><button>Increase &gt;</button></a>";
        html += "<script> setInterval(() => { fetch('/speed').then(res => res.text()).then(data => document.getElementById('speed').innerText = data + ' RPM'); }, 1000); </script>";
        html += "</body></html>";
        request->send(200, "text/html", html);
    });

    server.on("/increase", HTTP_GET, [](AsyncWebServerRequest *request) {
        updateSpeed(100);
        request->redirect("/");
    });

    server.on("/decrease", HTTP_GET, [](AsyncWebServerRequest *request) {
        updateSpeed(-100);
        request->redirect("/");
    });

    server.on("/speed", HTTP_GET, [](AsyncWebServerRequest *request) {
        request->send(200, "text/plain", String(compressorTargetSpeed));
    });

    server.begin();
}

void loop() {
    unsigned long currentTime = micros();
    if (!receivingPacket && (currentTime - lastPacketTime >= packetGapTime) && digitalRead(clockPin) == HIGH) {
        receivingPacket = true;
        lastPacketTime = currentTime;
        receiveAndRespondPacket();
    }
}
User avatar
johu
Site Admin
Posts: 6618
Joined: Thu Nov 08, 2018 10:52 pm
Location: Kassel/Germany
Has thanked: 342 times
Been thanked: 1484 times
Contact:

Re: Prius Gen2 A/C compressor inverter

Post by johu »

Great effort, thanks a lot
turnip73 wrote: Tue Mar 18, 2025 7:02 pm that the inverter is active and can be turned off, thus hopefully making sure the gates are permanently low to be able to use the inverter for charging without modifications.
I didn't think of that aspect. Will you test this?
Support R/D and forum on Patreon: https://patreon.com/openinverter - Subscribe on odysee: https://odysee.com/@openinverter:9
turnip73
Posts: 53
Joined: Wed Dec 26, 2018 1:38 pm
Location: Greystones Ireland
Has thanked: 15 times
Been thanked: 38 times

Re: Prius Gen2 A/C compressor inverter

Post by turnip73 »

johu wrote: Tue Mar 18, 2025 7:31 pm I didn't think of that aspect. Will you test this?
Yes
Post Reply