I had already pulled data from a GS450h, it looks like this:
Code: Select all
Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6 Byte 7 Byte 8 Byte 9 Byte 10
Packet 1 0 0 0 0 0 0 26 103 0 125
Packet 2 0 0 0 4 0 0 154 108 0 245
Packet 3 0 0 0 4 0 0 29 108 0 113
Packet 4 0 0 0 4 0 0 29 109 0 113
Packet 5 0 0 1 4 0 0 154 110 0 242
Packet 6 0 0 1 4 0 0 30 110 0 110
Packet 7 0 0 1 4 0 0 154 110 0 242
Packet 8 0 0 1 4 0 0 30 108 0 112
Packet 9 0 0 1 4 0 0 154 109 0 243
Packet 10 0 0 1 4 0 0 30 110 0 110
Packet 11 0 0 1 4 0 0 154 108 0 244
Packet 12 0 0 1 4 0 0 30 109 0 111
Packet 13 0 0 1 4 0 0 154 110 0 242
Byte 9 is a checksum. It's the LSB of the sum of bytes 3 thru 7, subtracted from 254.
The data is sent as a clocked serial signal, looks very much like SPI, but is far too slow to function with SPI transceivers.
Each clock cycle is 3.2ms, each byte of data takes 26ms to receive, and each packet of 10 bytes is received over 315ms, with a 80ms gap in between. So less than 3 packets per second.
I wrote a basic program to bit-bang this out of an Arduino Nano. One interesting point is that the logic level is 12V, so I added some transistors and an optoisolator to communicate with this unit. This code produces exactly the same output as the HV ECU in the car.
Code: Select all
#define LEN 10 //message length
#define PACKET_GAP 76000 //79100 microseconds between packets
#define BYTE_GAP 6463 // 9650 microseconds between bytes
#define ETI_DROP 22500 //drop eti to 0v this long after packet transmission
#define CLK_HIGH_TIME 1573 //actually 1550 high clock pulse duration
#define CLK_LOW_TIME 3247 //actually 1700 low clock pulse duration (they are different for some reason)
byte response[10]={0,0,0,0,0,0,0,0,0,0};
byte data1[10]={0,0,0,0,0,0,26,151,0,125}; //packet 1
byte data2[10]={0,0,0,4,0,0,154,108,0,245}; //packet 2-4
byte data3[10]={0,0,1,4,0,0,154,110,0,242}; //packets after
byte data4[10]={0,1,2,3,4,5,6,7,8,9}; //testing
unsigned long lastpacket=0,lastbyte=0,clk_hightime=0,clk_lowtime=0;
bool bytecomplete=0;
#define ITE 12 //arduino pin. can be any pin, doesnt have to be SPI pins
#define ETI 11 //arduino pin. can be any pin, doesnt have to be SPI pins
#define CLK 13 //arduino pin. can be any pin, doesnt have to be SPI pins
void setup()
{
pinMode(ITE, INPUT);
pinMode(CLK, OUTPUT);
pinMode(ETI, OUTPUT);
Serial.begin(115200);
}
void transferbyte(byte packet_id,byte dataindex)
{
if(micros()-lastbyte>BYTE_GAP)
{
for(int i=0;i<8;i++)
{
clk_hightime=micros();
clk_lowtime=micros();
//send ETI primitive packet handling for proof of concept, easily improved in future
switch (packet_id)
{
case 1: digitalWrite(ETI, bitRead(data1[dataindex],i));break;
case 2: digitalWrite(ETI, bitRead(data3[dataindex],i));break;
case 3: digitalWrite(ETI, bitRead(data3[dataindex],i));break;
case 4: digitalWrite(ETI, bitRead(data4[dataindex],i));break;
default: digitalWrite(ETI, bitRead(data4[dataindex],i));
}
while(micros()-clk_hightime<CLK_HIGH_TIME)digitalWrite(CLK,0);
bitWrite(response[dataindex], i, digitalRead(ITE)); // capture ITE
while(micros()-clk_lowtime<CLK_LOW_TIME)digitalWrite(CLK,1);
}
//digitalWrite(ETI,0); //in case last bit of byte was 1 (inverted logic)
lastbyte=micros();
bytecomplete=1;
}
}
void transferpacket(byte packet_id)
{
if(micros()-lastpacket>PACKET_GAP)
{
for(int i=0; i<LEN; i++)
{
while(!bytecomplete)transferbyte(packet_id,i);
bytecomplete=0;
}
lastpacket=micros();
}
//HVECU in car holds ETI in last state for this long before sending 0
if(micros()-lastpacket>ETI_DROP)digitalWrite(ETI,1);
}
void serialdiag(byte packet_id)
{
Serial.print("Sent: ");
switch(packet_id)
{
case 1: {for(int i=0;i<LEN;i++){Serial.print(data1[i]);Serial.print(" ");};break;}
case 2: {for(int i=0;i<LEN;i++){Serial.print(data2[i]);Serial.print(" ");};break;}
case 3: {for(int i=0;i<LEN;i++){Serial.print(data3[i]);Serial.print(" ");};break;}
default: {for(int i=0;i<LEN;i++){Serial.print(data4[i]);Serial.print(" ");};}
}
Serial. print("\n");
Serial.print("Received: ");
for(int i=0;i<LEN;i++){Serial.print(response[i]);Serial.print(" ");}
Serial.print("\n");
Serial.print("\n");
}
void loop()
{
transferpacket(2);
serialdiag(2);
}
My plan for the 3 switch inputs is to just put an "if(all switches closed){run the compressor}" in the main loop. The serialdiag() functino will be replaced by a CAN transmit function, if the AC compressor is found to produce any useful data.
I've attached a schematic of the circuit I used. Note the pain numbers have changed - I've freed up the SPI pins so I can add a CAN controller in the future. I'll get this board fabricated when I next place an order.
I'm currently facing two issues:
1. The AC compressor pinout. I believe it is:
1 - CLK
2 - ETI (data from HV ECU to compressor)
3 - ITE (feedback from compressor to HV ECU)
4 - STBI
5 - GND
6 - 12V
However, the service manual contradicts itself over which pin is ITE and which is ETI. From metering the two, pin 2 has the same specs as CLK, while pin 3 is pulled up when pin 6 is connected to 12v via a 1M resistor. I am going with the assumption that my list above is correct.
To activate this unit, the STBI line needs to be connected to ground. Doing so causes the unit to pull 14mA from the 12v line. This isn't enough to power the unit, I believe that the unit is disabled until it sees voltage on the HV DC connector. I opened the unit, there is a sensing circuit connected to these lines. Likely the inverter is shutdown until it receives adequate HV voltage. I tried 12v. Nothing happened.
So, I'm about 10 hours in and have made plenty of progress already. I'll likely put this project on hold until I have access to a 288V DC source to try this again. I've posted this now in case anyone else wants to use my work so far to attempt to control a compressor of their own. These are widely available AC compressors, and if they can be controlled, it would provide a cheap source of AC for electric car conversions.