P38 BECM communications

When making my p38 camper I needed to make a new wiring loom but still wanted to keep the look of the P38 internally as I feel it is a very tidy interior, I also wanted to remove the BECM and make a small neater one that did the bits I just needed.

The BECM talks to all the other systems (ABS,ENGINE,HVAC) via simple wires going hi and lo to indicate errors or status,

The outstations in the doors, display and the switch panel use a form of serial comms to talk to each other.

I only reverse-engineered the display but the comms system is the same on the others it will take someone else to sniff the codes using a simple cheap Arduino.

There is a working arduino sketch at the end of the post.

you just need three wires to the display, +12v and ground to make it work with any of the components., if you only want to listen then you can just sniff the data wire, to know which is sending the data is a little harder to do, because you cant detect which end pulled the line low.

The system runs on three wires,

  1. Clock wire
  2. Data Direction
  3. Data wire
  4. ground is always needed 🙂

The data wires to the display are easy to get to on the BECM they are on the plug just below the fusebox and can easy be identified because of the dual coloured wires. push a paperclip into the data wire and connect that to the a input pin on the arduino with a 1k resistor in series then connect a earth from the car to your arduino and then make a sketch to relay, use 4800 8N1 bps comms on the BECM wire to the serial monitor on the computer. then press things or make things happen on the car and see what gets sent to/from the BECM. you should see the mileage being sent from the display to the BECM it is easy to work it out.

Bytes : A6 13 83 73 45

A6 is the code to says its mileage then the mileage is in plain text here the mileage is 138373 (45 is the checksum)

Here is how the connections are wired into the switch panel for the windows

Range rover electric windows wiring diagram

three wires OG,LG and R (black = GND)

on the display the signals wires are doubled up for reliabilty .

Range rover wiring diagram, with dtata comms descrition and protocols

basically there is a red a green and a orange wire 2 & 12 ,3 & 13 , 5 & 15

orange is Data direction

red is clock signal

green is the actual data

the clock wire runs at 4.8 khz

the data dir wire is pulled low when sending data, you must wait 5ms before writing to the data line after pulling this low

and the data is 4800 8N1 just like any rs232

So what does this data ?

The data is in 10 byte blocks

which consist of two 5 byte sections
byte 0 : ID
byte 1 : data
byte 2 :data
byte 3 :data
byte 4 : Checksum byte : bytes 0,1,2,3 all XORed together

The display sleeps if it doesn’t get a valid data stream, so if you have power and nothing is on the screen then your data stream is wrong somehow.

to keep the screen awake when you dont need to send any data then you must send AA AA AA AA cs 00 which is the keep alive signal

Byte 1 is the control/action code

  • 0xA5 : is the lights on the display
  • 0x00 : are messageson the upper line of the LCD, I pressume these vary by Display ROM, you can set the language
  • 0x0F : message on the lower line of the LCD

0x

OK, if you now used my Arduino sketch and have the hazard lights on the display going on/off what else can you send to the display?

Here is what I have found that you can send the display, I didn’t decode the data coming from the display because I didn’t need it.

Byte #HexbitFunction
1A5 {lighting }
2 …. …0Left indicator
  …. ..1.Main beam indicator
  …. .2..Right indicator
  …. 3…Brake warning light
  …4 ….Low fuel light
  ..5. ….Over temp light
  .6.. ….Traction control light
  7… ….Seat belt light
3 …. …0BATT light
  …. ..1.Oil light
  …. .2..ABS light
  …. 3…Heater plugs light
  …4 ….Engine check light
  ..5. ….Nothing ???
  .6.. ….Transfer box warning
  7… ….Trailer light
4 …. …0 
  …. ..1.SRS light? Gauges ON must be set to make gauges work I.e. BYTE 4 must be at least 0x06 for back light to be on and the gauges working.
  …. .2..LCD back Light
  …. 3…Side light indicator
  …4 ….Air susp up light
  ..5. ….Nothing ??
  .6.. ….Nothing ??
  7… ….Nothing??
5  CS first 4 bytes XORed together
    
Byte #Hexbitfunction
100 {messages}
2 0x_0english
  0x_1french
  0x_2german
  0x_3 
  Upper nibble??
    
    
    
3 0x00Same as byte 4 but for lower LCD line
    
    
    
    
    
    
    
4 MessageOn upper line of lcd
  0x00 to 0x16“FUSE  xx  failed”
  17Right dip beam
  18Left dip beam
  19RH main beam
  1ALH main beam
  0x33“Check oil level”
  0x34“Engine oil overheat”
  0x35“gearbox overheat”
  0x36Transfer overheat
  0x48Ignition key in
  0x49Lights on
  0x4bOverspeed
  0x4eFuel guage fault
  0x4fTemp guage fault
  0xA1Diagnostic mode
  0xa3Start engine
  0xA6SLOW DOWN
  0xA8ICE alert
  0xffTest pattern
    
Byte #Hexbitfunction
10F 0x59 messes this up
2 Bit 0-3  0-7P N R D 2 1in lcd
    
  Bit 3LOW or L if PDRN is shown
  Upper nibble 
  1SPORT
  2high
    
    
3 …. …0 
  …. ..1. 
  …. .2.. 
  …. 3… 
  …4 …. 
  ..5. …. 
  .6.. …. 
  7… …. 
4 …. …0 
  …. ..1. 
  …. .2.. 
  …. 3… 
  …4 …. 
  ..5. …. 
  .6.. …. 
  7… …. 
5   
    

0x55 fuel and temp gauge

3

Warning lights come on automatic, but can be tested using  A5

0x49 temp warning light comes on

0x97 low fuel light comes on

NOTE

AA AA AA AA cs 00   = 0 keep alive

If not received by dash or another message then display goes into fail mode.

Warning about temp and fuel and both  gauges go to min fuel and max temp

erors and wierd stuff

If the DIR wire is disconnected the dash goes into sleep

0x59 does something wierd but never worked out what

0x15   turns stuff off?

A6,, message strategy?

{0x5a,0x9B,0xF5,0x00,0x3B,0xA5,0x00,0x00,0x04,0xA0};

This was Making the overall mileage run up


Arduino sketch for P38 BECM comms

Here is a working Arduino sketch , if you know what an arduino is then you knwo what to do,

I think i used pull up resistor to get 12v lines from the 5v , but i am not full sure of this so check, the signal will be inverted if I did

have fun and please send me updates.

#include <TimerOne.h>

/* comms with the P38 display
uses three lines + gnd
Dir  is data direction low = arduino sending , high = arduino recieving
the DIR pin runs at 21hz, dont know if its driven by the BeCm as a master
so display wait for hi to txr its 5 byte block

Clk  clock a 4.8 khz  only runs when txring should be driven by display when rcving
Data  data @4800 8n1

 arduino sends 10byte blocks 
 which are 2 x 5 bytes
  byte 0     ID
  byte 1     data
  byte 2     data
  byte 3     data
  byte 4     CS  byte 0,1,2,3 all XORed together
*/

//---------- define pins -----------------

#define TxrPin 2    //LG   yw
#define DirPin 3    //OG   gn
#define ClkPin 4    //RG   be

// the pin directions changed with Dir pin subr

/*
      interupt on timer for DIR pin which changes over the dir pin and send or recieve
      after 5ms of Dir_pin going low what ever is in the two txr buffers is transmitted
      by the Transmit Subr.
      
      the receive is ignored.
 */    
 
 // vars to set the data direction pin timer
 long DataDir_Period_txr = 27400;    //should be 20hz 31.8 ms
 long DataDir_Period_rxr = 15800;    // 15 ms
 long DataDir_Period_start_txr = 4400 ;  // 5 ms
 byte TransmitFlag ; 
 
 byte TxrBuffer[12];    // byte 5 and 10 are CS
 
 /*
 
    Chescksum in byte 5 and 10 is calced when the array is put in the
    txr buffer so ignore it here
    */
 
 
 byte TxrBufferDefault[10] = {0xAA,0xAA,0xAA,0xAA,0x00,0xAA,0xAA,0xAA,0xAA,0x00};  
 byte TxrBufferHazardOff[10] = {0xA5,0x00,0x00,0x06,0x00,0xAA,0xAA,0xAA,0xAA,0x00};
 byte TxrBufferHazardOn[10] = {0xA5,0x05,0x00,0x06,0xA0,0xAA,0xAA,0xAA,0xAA,0x00}; 
  byte TxrBufferHazard_1[10] = {0x00,0xA0,0xA1,0x00,0x00,0xAA,0xAA,0xAA,0xAA,0x00};
 byte TxrBufferHazard_2[10] = {0x55,0xae,0x4a,0x00,0x3B,0xAa,0xaa,0xaa,0xaa,0xA0};


// byte TxrBufferHazard_2[10] = {0xAA,0xAA,0xAA,0xAA,0x00,0xAA,0xAA,0xAA,0xAA,0x00};
 byte incomingByte;
  int i;
  int SeqCount =0;    //8 sets of def, then on, 8 defs, then off 
  
  
  //SHIFTOUT TIMING
  
  long SETUP_TIME =11;
  
  long CLOCK_HIGH = 96;
  long CLOCK_LOW = 96;

void setup() {

    // Initialize the digital pin as an output.
  // Pin 13 has an LED connected on most Arduino boards
  pinMode(13, OUTPUT); 
   TransmitFlag = 0;
  Serial.begin(9600);      // open the serial port at 9600 bps:  
  
  Timer1.initialize(100000); // set a timer of length 100000 microseconds (or 0.1 sec - or 10Hz => the led will blink 5 times, 5 cycles of on-and-off, per second)
  Timer1.attachInterrupt( timerIsr ); // attach the service routine here
  pinMode(DirPin,OUTPUT);
   Serial.print("Started");


}


void loop() {

          // send data only when you receive data:
         if (Serial.available() > 0) {
                 // read the incoming byte:
                 
                 TxrBuffer[0]= Serial.parseInt();
                 TxrBuffer[1]= Serial.parseInt();
                 
                 TxrBuffer[2]= Serial.parseInt();
                 
                 TxrBuffer[3]= Serial.parseInt();
                 TxrBuffer[4] = TxrBuffer[0] ^ TxrBuffer[1] ^ TxrBuffer[2] ^ TxrBuffer[3];
                 // say what you got:
                 Serial.print("I received: ");
                     for (i = 0; i < 9; i = i + 1) {
                          Serial.print(TxrBuffer[i],HEX);
                          Serial.print(" ");
                      }
                      Serial.println();
         }


}




/// --------------------------
/// Custom ISR Timer Routine
/// --------------------------
void timerIsr()
{
  uint8_t i;
   // Timer1.stop();				//stop the counter
    // Toggle LED
    digitalWrite( 13, digitalRead( 13 ) ^ 1 );
    if (TransmitFlag == 0 ){
     //set the pinmode to out put 
      TransmitFlag = 2;

      digitalWrite(DirPin,HIGH);
      digitalWrite(ClkPin,LOW);
      digitalWrite(TxrPin,LOW);
      pinMode(TxrPin,OUTPUT);
      pinMode(ClkPin,OUTPUT);
      Timer1.initialize(DataDir_Period_start_txr);
      
      
    } else if(TransmitFlag == 1){
      //set to rxr input
      TransmitFlag = 0;
      pinMode(TxrPin,INPUT);
      pinMode(ClkPin,INPUT);
      digitalWrite(DirPin,LOW);
          Timer1.initialize(DataDir_Period_rxr);
       delayMicroseconds(4900);
       for (i = 0; i < 51; i = i + 1) {
        digitalWrite(ClkPin, HIGH);
        delayMicroseconds(CLOCK_HIGH);
        digitalWrite(ClkPin, LOW);
        delayMicroseconds(CLOCK_LOW);
           
    
         }       
   

      
    } else {
      //set to 5ms delay to start transmit
      TransmitFlag = 1;
      Timer1.initialize(DataDir_Period_txr);
      // now tranmit the buffer bytes
   
      TransmitBuffer();
      }
      

  //Timer1.restart();    
  }

void TransmitBuffer(){


  // after transmit fill the buffer with the idle transmit 0xAA 0xAA 0xAA 0xAA 0x00
  // for both 5 byte lumps 
 SeqCount = SeqCount + 1;
 
 
 
 
 if (SeqCount == 19){

     for (i = 0; i < 10; i = i + 1) { 
          TxrBuffer[i] = TxrBufferHazardOn[i];
          CS_calc();
          SeqCount =0;
         }
     } else if ( SeqCount == 17){
/*TxrBufferHazard_1[1]++;
//TxrBufferHazard_1[2]++;
  //TxrBufferHazard_1[3]++;
   Serial.print(TxrBufferHazard_1[2]);
 Serial.print("\n");
 if (TxrBufferHazard_1[1] == 10){
   TxrBufferHazard_1[1]=0;
 }
 */
         for (i = 0; i < 10; i = i + 1) {
              TxrBuffer[i] = TxrBufferHazardOff[i];
              CS_calc();  
             }
     } else if ( SeqCount == 1 ){
         for (i = 0; i < 10; i = i + 1) {
              TxrBuffer[i] = TxrBufferHazard_1[i];
              CS_calc();
             }
     
     } else if ( SeqCount == 2){
         for (i = 0; i < 10; i = i + 1) {
              TxrBuffer[i] = TxrBufferHazard_2[i];
              CS_calc();

              }
     
     }     else {
         for (i = 0; i < 10; i = i + 1) {
              TxrBuffer[i] = TxrBufferDefault[i];
               CS_calc();

                 }
     }
     
    // transmits two 5 byte blocks together @4800 bps and controlls the clock
  // using shiftout
  
    for (i = 0; i < 10; i = i + 1) {
     // shiftOut(TxrPin,ClkPin,MSBFIRST,TxrBuffer[i]);
     putByteInvert(TxrBuffer[i]);
    }    
  
     
}

//------------------------------ CHECKSUM ------------------------------------------------
void CS_calc(){

TxrBuffer[4] = TxrBuffer[0] ^ TxrBuffer[1] ^ TxrBuffer[2] ^ TxrBuffer[3];  // xor first 4 to get CS
TxrBuffer[9] = TxrBuffer[5] ^ TxrBuffer[6] ^ TxrBuffer[7] ^ TxrBuffer[8];  // xor second 4 to get CS

  
  
  
}


void putByte(uint8_t value) {
  uint8_t mask = 0x01, i;
  // start bits
     
    digitalWrite(ClkPin, LOW);
    delayMicroseconds(SETUP_TIME);
    digitalWrite(TxrPin,LOW);
    delayMicroseconds(CLOCK_LOW-SETUP_TIME);

    digitalWrite(ClkPin, HIGH);
    delayMicroseconds(CLOCK_HIGH);
    
  
  
  
  for (i = 8; i > 0; i--) {
    // decide if delay is needed on gpoing low by 15.8ms
   //if ((value &amp; mask) != 0){
    digitalWrite(TxrPin, value &amp; mask);
    digitalWrite(ClkPin, LOW);
    delayMicroseconds(CLOCK_LOW);
  /* }else {
    digitalWrite(ClkPin, LOW);
    delayMicroseconds(SETUP_TIME);
    digitalWrite(TxrPin, value &amp; mask);
    delayMicroseconds(CLOCK_LOW-SETUP_TIME); 
   }
   */
    digitalWrite(ClkPin, HIGH);
    delayMicroseconds(CLOCK_HIGH);

    mask <<= 1;
  }
  //stop bits
    digitalWrite(TxrPin,HIGH);
    digitalWrite(ClkPin, LOW);
    delayMicroseconds(CLOCK_LOW);
    digitalWrite(ClkPin, HIGH);
    delayMicroseconds(CLOCK_HIGH);

  
  
  
}


void putByteInvert(uint8_t value) {
  uint8_t mask = 0x01, i;
//  Serial.write(value);
  
 // for use with bc547 drive tranny
  // start bits
     
    digitalWrite(ClkPin, HIGH);
    delayMicroseconds(SETUP_TIME);
    digitalWrite(TxrPin,HIGH);
    delayMicroseconds(CLOCK_HIGH-SETUP_TIME);

    digitalWrite(ClkPin, LOW);
    delayMicroseconds(CLOCK_LOW);
    
  
  
  
  for (i = 8; i > 0; i--) {
    // decide if delay is needed on gpoing low by 15.8ms

    digitalWrite(TxrPin, !(value &amp; mask));
    digitalWrite(ClkPin, HIGH);
    delayMicroseconds(CLOCK_HIGH);
 
    digitalWrite(ClkPin, LOW);
    delayMicroseconds(CLOCK_LOW);

    mask <<= 1;
  }
  //stop bits
    digitalWrite(TxrPin,LOW);
    digitalWrite(ClkPin, HIGH);
    delayMicroseconds(CLOCK_HIGH);
    digitalWrite(ClkPin, LOW);
    delayMicroseconds(CLOCK_LOW);

 
  
  
}


Please add/correct me on any info here so we can build a good knowledge of this subject

Share your joy of knowledge with the world..

One thought on “P38 BECM communications

  1. P38 Autobox comms with the M57 engine – BeaDy

    […] Simple hack for the P38 BECM serial communications […]

Leave a Reply

Your email address will not be published.