Page 2 of 2

Re: Prius Gen2 A/C compressor inverter

Posted: Sun Jun 23, 2024 3:14 pm
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.

Re: Prius Gen2 A/C compressor inverter

Posted: Sun Aug 04, 2024 7:25 pm
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.

Re: Prius Gen2 A/C compressor inverter

Posted: Tue Aug 06, 2024 4:50 pm
by turnip73
Hmm, I didn't expect the CLK signal to come from the inverter.

Re: Prius Gen2 A/C compressor inverter

Posted: Tue Aug 13, 2024 2:31 am
by WOSS
CLK and ITE are from the inverter

Re: Prius Gen2 A/C compressor inverter

Posted: Fri Aug 23, 2024 3:30 am
by WOSS
Image

Re: Prius Gen2 A/C compressor inverter

Posted: Sat Aug 24, 2024 9:28 pm
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.

Re: Prius Gen2 A/C compressor inverter

Posted: Sun Sep 22, 2024 2:36 am
by WOSS
Have you figured out how to start the air conditioning compressor? What is the communication protocol of the air conditioning compressor?

Re: Prius Gen2 A/C compressor inverter

Posted: Sun Nov 03, 2024 9:21 pm
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

Re: Prius Gen2 A/C compressor inverter

Posted: Sat Feb 08, 2025 6:16 pm
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

Re: Prius Gen2 A/C compressor inverter

Posted: Sun Feb 09, 2025 1:06 am
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.

Re: Prius Gen2 A/C compressor inverter

Posted: Fri Feb 21, 2025 9:31 pm
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)

Re: Prius Gen2 A/C compressor inverter

Posted: Fri Feb 21, 2025 9:34 pm
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

Re: Prius Gen2 A/C compressor inverter

Posted: Fri Feb 21, 2025 11:21 pm
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?

Re: Prius Gen2 A/C compressor inverter

Posted: Sun Feb 23, 2025 12:26 pm
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

Re: Prius Gen2 A/C compressor inverter

Posted: Sun Feb 23, 2025 10:40 pm
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.

Re: Prius Gen2 A/C compressor inverter

Posted: Tue Feb 25, 2025 7:40 pm
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.

Re: Prius Gen2 A/C compressor inverter

Posted: Tue Mar 18, 2025 7:02 pm
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();
    }
}

Re: Prius Gen2 A/C compressor inverter

Posted: Tue Mar 18, 2025 7:31 pm
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?

Re: Prius Gen2 A/C compressor inverter

Posted: Sun Apr 06, 2025 10:38 am
by turnip73
johu wrote: Tue Mar 18, 2025 7:31 pm I didn't think of that aspect. Will you test this?
Yes