// = ALDL_160_Baud_Interface =========================================================================================== // // This sketch reads the ALDL (Assembly Line Diagnostic Link) data from a 1988 Chevrolet Corvette ECU (Engine Control // Unit). This particular ECU requires a 10k ohm resistor between ALDL connector pins A and B to cause it to start // streaming diagnostic data. The diagnostic data appears on pin E of the ALDL connector. // // The 160 bps (bits per second) ALDL data stream is received as a continuous stream of data bits. Each data bit is // made up of a high to low, and a low to high voltage transition within a time of 6.25 mSec (milliseconds) or // 6,250 uSec (microseconds). // // Each data bit starts as a falling edge of the ALDL data stream. If the bit being sent is a zero (0) the data line // will return to a high level after a small delay of ~300 uSec. If the bit being sent is a one (1) the data line will // return to a high level after a larger delay of ~4,500 uSec. // // |----- 6,250 Microseconds ---->|----- 6,250 Microseconds ---->|----- 6,250 Microseconds ---->|--- repeat --> // 5v --+ +---------------------------+ +---------------------------+ +---------+ +---------- // | | | | | | | | // | | | | | | | | // 0v +--+ +--+ +--------------------+ +--+ // ^__^ = 0 ^__^ = 0 ^____________________^ = 1 ^__^ = 0 // // Both 0 and 1 bits, take exactly the same time ~6,250 uSec. There are therefore 1,000,000/6,250 or 160 bits // transmitted per second. // // The ALDL message consists of multiple 9 bit data units. Each data unit begins with a start bit '0' followed // by eight data bits zero (0) or one (1). // // The first data unit is always nine zero (0) bits. Followed by the actual data units. The message ends with a // quiet period of approximately 50,000 uSec. // // ALDL Data Stream Map: // // 1 MODE WORD 2 // 0 OVERDRIVE ON 1 = ON, 0 = OFF // 1 MALF 14 OR 15 THIS STARTUP // 2 REFERENCE PULSE OCCURED // 3 1 = IN ALDL MODE, 8192 LOCKED IN, AND MODE 4 // 4 DIAGNOSTIC SWITCH IN DIAGNOSTIC POSITION // 5 DIAGNOSTIC SWITCH IN ALDL POSITION // 6 HIGH BATTERY VOLTAGE // 7 SHIFT LIGHT 1 = ON, 0 = OFF // 2 FIRST PROMID WORD PROM ID A MSB (My 1988=00011110b 1Eh 30d) // 3 SECOND PROMID WORD PROM ID B LSB (My 1988=10101011b ABh 171d) // 4 IAC PRESENT MOTOR POSITION STEPS = N // 5 COOLANT TEMPERATURE DEG C = N*192/256 - 40 // 6 MILES PER HOUR N = MPH // 7 EGR DUTY CYCLE % DUTY CYCLE = N/2.56 // 8 ENGINE SPEED (RPM) RPM = N * 25 // 9 THROTTLE POSITION VOLTS = N * .0196 // 10 BASE PULSE CLOSED LOOP CORRECTION N = COUNTS // 11 OXYGEN SENSOR MILLIVOLTS = N*4.44 // 12 MALFUNCTION FLAG WORD 1 // 0 C23 MAT SENSOR LOW // 1 C22 THROTTLE POSITION SENSOR LOW // 2 C21 THROTTLE POSITION SENSOR HIGH // 3 C16 NOT USED // 4 C15 COOLANT SENSOR LOW TEMPERATURE // 5 C14 COOLANT SENSOR HIGH TEMPERATURE // 6 C13 OXYGEN SENSOR OPEN // 7 C12 NO REFERENCE PULSES (ENG. NOT RUNNING) // 13 MALFUNCTION FLAG WORD 2 // 0 C35 NOT USED // 1 C34 MAF SENSOR LOW // 2 C33 MAF SENSOR HIGH // 3 C32 EGR DIAGNOSTIC // 4 C31 NOT USED // 5 C26 NOT USED // 6 C25 MAT SENSOR HIGH // 7 C24 VEHICLE SPEED SENSOR // 14 MALFUNCTION FLAG WORD 3 // 0 C51 PROM ERROR // 1 C46 VATS FAILED // 2 C45 OXYGEN SENSOR RICH // 3 C44 OXYGEN SENSOR LEAN // 4 C43 ESC FAILURE // 5 C42 EST MONITOR ERROR // 6 C41 CYLINDER SELECT ERROR // 7 C36 BURNOFF DIAGNOSTIC // 15 MALF FLAG WORD 4 // 0 C63 NOT USED // 1 C62 NOT USED // 2 C61 NOT USED // 3 C56 NOT USED // 4 C54 ADU ERROR // 5 C53 FUEL PUMP VOLTAGE // 6 C52 OVER VOLTAGE // 7 C51 CAL PACK MISSING // 16 AIR/FUEL MODE WORD // 0 NOT USED // 1 LEARN CONTROL ENABLE FLAG 1 = ENABLE STORED // 2 NOT USED // 3 NOT USED // 4 VEHICLE SPEED SENSOR FAILURE // 5 EECC SLOW 02 RICH/LEAN FLAG // 6 RICH - LEAN FLAG 1 = RICH, 0 = LEAN // 7 CLOSED LOOP FLAG 1 = CLOSED LOOP // 17 MANIFOLD AIR TEMPERATURE SEE TABLE 1 // 18 MCU INPUT STATUS WORD // 0 1 = IN PARK/NEUTRAL // 1 1 = NOT IN THIRD GEAR // 2 1 = OVERDRIVE REQUEST // 3 NOT USED(POWER STEERING ON = 1) // 4 1 = EGR DIAGNOSTIC SWITCH CLOSED // 5 1 = TCC LOCKED // 6 1 = FAN REQUEST BIT // 7 0 = A/C REQUEST // 19 OLDPA3 - ESC COUNTER INPUT N = COUNTS // 20 BLM N = COUNTS // 21 ALDL RICH LEAN CHANGE COUNTER / TOTAL CROSSOVER COUNTS ** // 22 AIR FLOW RATE (MSB) // 23 AIR FLOW RATE (LSB) G/S = (MSB) * 256 + (LSB) // 24 INJECTOR BASE PULSE WIDTH (MSB) // 25 INJECTOR BASE PULSE WIDTH (LSB) WIDTH=(MSB) * 256 + (LSB) /* = Constants ====================================================================================================== */ // ALDL data line is connected to this pin. // const int ALDL_PIN = 2; // ALDL Message length: 32 * (StartBit + 8 DataBits) = 288 // const int ALDL_BIT_BUFFER_SIZE = 288; // ALDL inter-message quiet time in microseconds. // const long ALDL_QUIET_TIME = 50000; // ALDL time span needed to frame a single bit. // const int ALDL_BIT_TIME_SPAN = 6250; // ALDL time span of a '0' bit in microseconds. // const int ALDL_0_BIT_TIME_SPAN = 250; // ALDL time span of a '1' bit in microseconds. // const int ALDL_1_BIT_TIME_SPAN = 2500; /* = Globals ======================================================================================================== */ // Character array to store 1's and 0's // char gALDL_Bit_Buffer[ALDL_BIT_BUFFER_SIZE]; /* = Helper Functions =============================================================================================== */ void WaitForLineStateChange(unsigned int &oCurrentLineState, unsigned long &oStateChangeTime) { // This routine waits for an ALDL line state change. It tests any state change to see // if it is a valid state change vs. a state change caused by a noise spike. Most // electrical interference (ignition, alternator etc.) can be filtered out by ignoring // state changes with a duration of less than 100 uSec. unsigned int LineStateAtEntry; unsigned long TimeAtEntry; unsigned int CurrentLineState; unsigned long CurrentTime; unsigned long StateChangeTime; unsigned int TimeOut; // Read the ALDL line state. // LineStateAtEntry = digitalRead(ALDL_PIN); // Read the current time. // TimeAtEntry = micros(); // Initialize time out flag. // TimeOut = false; // Start a forever loop. We will remain inside this loop until a state change // can be validated. // for(;;) { // Start another forever loop. We will remain inside this loop until a state // change is detected. If no state change is detected within 6,250 uSec we // fake a state change. // for(;;) { // Read the ALDL line state. // CurrentLineState = digitalRead(ALDL_PIN); // Has line state changed? // if(CurrentLineState != LineStateAtEntry) { StateChangeTime = micros(); break; } // Read the current time. // CurrentTime = micros(); // Check the current time. // if ((CurrentTime - TimeAtEntry) > ALDL_BIT_TIME_SPAN) { TimeOut = true; break; } } // Did we time out? // if (TimeOut == true) { // Invert the current line state. // oCurrentLineState = (CurrentLineState == HIGH) ? LOW : HIGH; // Set the state change time to now. // oStateChangeTime = micros(); break; } // Wait 100uS for line state to settle. // delayMicroseconds(100); // Has line state remained unchanged? // if(digitalRead(ALDL_PIN) == CurrentLineState) { // A valid (not noise induced) line state change has occured, so // return the current line state and state change time to caller. // oCurrentLineState = CurrentLineState; oStateChangeTime = StateChangeTime; break; } } } /* = Program Setup ================================================================================================== */ void setup() { // Initialize built-in LED. // pinMode(LED_BUILTIN, OUTPUT); // Initialize serial comms. // Serial.begin(115200, SERIAL_8N1); // Initialize ALDL message buffer. // for(int i = 0; i < ALDL_BIT_BUFFER_SIZE; i++) { gALDL_Bit_Buffer[i] = '-'; } } /* = Program Loop =================================================================================================== */ void loop() { unsigned int PreviousLineState; unsigned long PreviousStateChangeTime; unsigned int CurrentLineState; unsigned long CurrentStateChangeTime; unsigned int Interval; unsigned int ALDL_Bit_Buffer_Index = 0; // When we arrive here we need to start collecting ALDL bit // data. So wait for an ALDL state change. // WaitForLineStateChange(CurrentLineState, CurrentStateChangeTime); // Remember the line state and state change time. // PreviousLineState = CurrentLineState; PreviousStateChangeTime = CurrentStateChangeTime; // Start collecting ALDL data bits. // for(;;) { // Wait for an ALDL state change. // WaitForLineStateChange(CurrentLineState, CurrentStateChangeTime); // Calculate the interval in milliseconds between this // state change and the last state change. // Interval = CurrentStateChangeTime - PreviousStateChangeTime; // Remember the line state and state change time. // PreviousLineState = CurrentLineState; PreviousStateChangeTime = CurrentStateChangeTime; // What is the current line state? // switch(CurrentLineState) { case LOW: // If we captured an empty bit, then we're into the quiet time. So, we // can take this opportunity to transmit the bits we've gathered. // if(Interval > ALDL_BIT_TIME_SPAN) { // Is there data in the ALDL bit array? // if (gALDL_Bit_Buffer[0] == '-') { break; } // Walk the ALDL bit array and emit each '1' or '0' character. // for(int i = 0; i < ALDL_BIT_BUFFER_SIZE; i++) { // Print a space before each data unit (except the first). // if((i % 9 == 0) && (i != 0)) { Serial.print(' '); } // Print array element. // Serial.print(gALDL_Bit_Buffer[i]); // Reset the array element to a '-' after printing it. // gALDL_Bit_Buffer[i] = '-'; } // Add a carriage return and line feed to stream. // Serial.write(0x0D); Serial.write(0x0A); ALDL_Bit_Buffer_Index = 0; } break; case HIGH: // What kind of bit is this? // if(Interval >= ALDL_1_BIT_TIME_SPAN) { digitalWrite(LED_BUILTIN, HIGH); gALDL_Bit_Buffer[ALDL_Bit_Buffer_Index] = '1'; ALDL_Bit_Buffer_Index++; } else if(Interval >= ALDL_0_BIT_TIME_SPAN) { digitalWrite(LED_BUILTIN, LOW); gALDL_Bit_Buffer[ALDL_Bit_Buffer_Index] = '0'; ALDL_Bit_Buffer_Index++; } break; } } }