LIN-BUS, MCP2004, Serial write does not work

Hi,

I'm trying to send messages on a LIN-BUS in my car. Receiving works fine but for some reason I can't send a message. I'm using the MCP2004 and I've checked that the voltage on CS-pin and FAULT-pin is high(5V). I'm using the SoftwareSerial and have modified it to use 8E1.

Have anyone had the same problem?

Best regards, Hannes

Are you sure softwareSerial is sending 8E1 correctly ?

I've used the MCP2025 in the past and only managed to get 8E1 working on hardwareSerial.

Ian.

Hi,

No, I'm not sure of that. However, I have now switched to the hardware serial but still no luck.

I have not put the 220 pF capacitor on the LBUS, could that be the problem? I will buy one today and try it out.

I'm also not sure what to do with the fault pin. The manual says that it's a bidirectional pin. I have just set it to high.

Best regards, Hannes

I'm only familiar with the BMW iBus, but I've never needed a capacitor on the bus line. You may want to try a pull up resistor to pull the bus up to 12v though. The datasheet suggests 1K.

I shouldn't worry about the Fault/Txe pin for now. As long as you drive it up to 5v you should be fine.

Can you post your code and a circuit diagram ?

Ian.

I've attached my circuit.

This is a Ranger rover from the time BMW owned it so this is the iBus also!

I have two problems.

  1. I can't send messages
  2. When I start the radio, the arduino just gets corrupts messages and after a while the complete iBus goes down. If I reboot the arduino the iBus start to work again.

Your help is very much appreciated

Best regards, Hannes

mcp.jpg

1 Like

Hi Hannes,

I actually had some MCP2004 chips in my box of bits. I've hooked it up as per your diagram, but without the 4.7K resistor between Rx and 5V, and I can send and receive on the iBus. Try removing that resistor and see what you get.

I'm also not driving the FAULT/TXE pin high, but I don't see that driving it high would cause a problem.

Sending on the iBus is quite tricky, as you need to avoid collisions/bus contention.

Can you post your Arduino code please ?

Ian.

Wow, thats great to know. I have tried and removed the resistor but still no luck...

This is the code. As you see I don't call the handleMessage right now but when I do, it doesnt work

#include <SoftwareSerial.h>

//SoftwareSerial IBusSerial(10, 11); // RX, TX for IBus

// Pins we use for MCP2004
const int faultPin = 6;
const int cspin = 7;
const int ledpin = 13;
const int spin1 = 2;
const int spin2 = 3;
const int spin3 = 4;
const int spin4 = 5;


boolean read_byte = false;
boolean led = false;
boolean s1 = false;
boolean s2 = false;
boolean s3 = false;
boolean s4 = false;

byte readbuffer[64];
int i;
int buffer_index = 0;
int buffer_max = 64;
int cksum;
long lastrx;
long lasttx;

void setup() {
  // initialize buffer array to 0's
  memset(readbuffer, 0, sizeof(readbuffer));

  // Open serial communications to host (PC) and wait for port to open:
  Serial.begin(9600, SERIAL_8E1);
  //  Serial.println("IBus Debugging begins");

  // set the data rate for the SoftwareSerial port
  //IBusSerial.begin(9600);

  // Set high to enable TX on MCP2004
  pinMode(cspin, OUTPUT);
  digitalWrite(cspin, HIGH);

  // Set high to enable something ont he MCP2004
  pinMode(faultPin, OUTPUT);
  digitalWrite(faultPin, HIGH);

  lastrx = millis();
  lasttx = millis();
}

void loop() {
  delayMicroseconds(1000);

  lasttx = millis();


  if ((millis() - lastrx) > 15) {
    memset(readbuffer, 0, sizeof(readbuffer));
    buffer_index = 0;
    read_byte = false;
    buffer_max = 64;
    lastrx = millis();
    return;
  }


  if (Serial.available()) {


    readbuffer[buffer_index] = Serial.read();
    read_byte = true;
  }

  if (read_byte) {
    if (buffer_index == 1) {
      buffer_max = readbuffer[buffer_index] + 2;
      cksum = readbuffer[0] ^ readbuffer[1];
    } else if ((buffer_index > 1 ) && (buffer_index < buffer_max)) {
      cksum = cksum ^ readbuffer[buffer_index];
    }
  }
  //0x50 <-- Multi function wheel
  //0x46 <-- Center display
  //0xC0 <-- Multi info display
  //0xE7 <-- Front display <-- 20:59
  //0x3B <-- Navigation display, visas i navi, radiokanal namn
  // Reset buffer_index when it is buffer_max - 1.
  if (buffer_index == (buffer_max - 1)) {
    if (cksum == 0) {
      if (s2) {
        digitalWrite(spin2, HIGH);
      }
      else {
        digitalWrite(spin2, LOW);
      }
      s2 = !s2;
      Serial.print("Good message: ");
      //Serial.print(millis());
      Serial.print(" S:");
      Serial.print(readbuffer[0], HEX);
      Serial.print( " L:");
      Serial.print(readbuffer[1], DEC);
      Serial.print( " ");
      for (i = 2; i < buffer_max; i++) {
        Serial.print(readbuffer[i], HEX);
        Serial.print(" ");
      }


    } else {
      Serial.print("Invalid message. cksum: ");
      Serial.println(cksum, HEX);
      for (i = 0; i < buffer_max; i++) {
        Serial.print(readbuffer[i], HEX);
        Serial.print(" ");
      }
    }
    
    //    handleMessage(0x2D);
    Serial.println();
    // Empty the buffer
    memset(readbuffer, 0, sizeof(readbuffer));
    buffer_index = 0;
    read_byte = false;
    lastrx = millis();
  }

  // Increment index if we put something into the buffer
  if (read_byte == true) {
    read_byte = false;
    buffer_index++;
    lastrx = millis();
  }

}


void handleMessage(byte messageType) {
  byte volup[6] = { 0x50, 0x04, 0x68, 0x32, 0x11, 0x1F};
  byte voldown2[6] = { 0x50, 0x04, 0x68, 0x32, 0x10, 0x1E};
  byte voldown[6] = { 0xF0, 0x04, 0x68, 0x32, 0x10, 0xBE };
  byte pressmode[6] = { 0xF0, 0x4, 0x68, 0x48, 0x23, 0xF7 };
  byte releasemode[6] = { 0xF0, 0x4, 0x68, 0x48, 0xA3, 0x77 };
  byte ibussucks[22] = {
    0x68, 0x12, 0x3B, 0x23, 0x62, 0x10, 0x49, 0x42, 0x55,
    0x53, 0x53, 0x55, 0x43, 0x4b, 0x53, 0x20, 0x20, 0x20,
    0x20, 0x20, 0x20, 0x40
  };
  byte infoMessage[27] = {0x30, 0x19, 0x80, 0x1A, 0x35 , 0x0 , 0x20 , 0x20 , 0x43 , 0x48 , 0x45 , 0x43 , 0x4B , 0x20 , 0x43 , 0x4F , 0x4E , 0x54 , 0x52 , 0x4F , 0x4C , 0x20 , 0x4F , 0x4B , 0x20 , 0x20 , 0x83};
  // OP Was setting faultPin HIGH to write?
  // digitalWrite(faultPin, HIGH);
  switch (messageType) {
    case 0x2B: // + for vol up
      Serial.println("Sending vol up.");
      sendMessage(volup, 6);
      break;
    case 0x2D: // - for vol down
      Serial.println("Sending vol down.");
      sendMessage(voldown2, 6);
      break;
    case 0x42: // B for IBus Sucks
      Serial.println("Sending Info message.");
      sendMessage(infoMessage, 27);
      break;
    case 0x6D: // m for mode
      Serial.println("Sending Mode.");
      sendMessage(pressmode, 6);
      delay(150);
      sendMessage(releasemode, 6);
      break;
  }
 }

void sendMessage(byte message_data[], int length) {
  //byte message_cksum = gen_cksum(message_data);
  for (int k = 0; k < length ; k++) {
    Serial.write(message_data[k]);
  }
 
}
//Not used
byte gen_cksum(const byte message[]) {
  byte cksum = 0x00;
  for (int i = 1; i <= message[0]; i++) {
    cksum = cksum ^ message[i];
  }
  return cksum;
}

Hi Hannes,

My first observation is that you are reading the iBus with hardware serial, but also using hardware serial to print debug messages. Every time you use Serial.print, you are effectively trying to write that message on the iBus which is causing bus contention/collisions.

You need to read the iBus with hardware serial, and use software serial to write debug messages out to a terminal window. I use PuTTY, but any similar program will work. You can't use the Arduino Serial Monitor window, as that's looking at the hardware serial port.

Ian.

I just tested your code on my breadboard test rig, and with debug on Software Serial, it does read the iBus.

Ian.

Haha, well thats embarrassing. I forgot to remove all Serial.print after I went from SoftSerial till Hardware serial. I removed many of them as you can see in the code but forgot the most imporant ones.

I was just down in the garage and I can send messages now! :slight_smile:

Thank you very much Ian. Now I can continue with my project!

1 Like

That's excellent news 8)

Yes indeed.

My knowledge regarding the components is very low. I've read other threads that says that ju MUST have the resistor between rx and +5V. Obviously that was not correct

Hi Guys!

I'm thinking about a LIN bus network with the MCP2003. I understand the code above, but do not get where the LIN bus protocol is implemented? Is the protocol embedded in the chip?

The LIN bus protocol is more a description of the hardware side of things. How you format/structure the data is pretty much up to you unless you're interfacing to an existing system like the BMW iBus.

In general, the data format is standard 8 bit, No parity & 1 stop bit (8N1), but there's nothing to stop you using something different. BMW use 8E1 (even parity) in the iBus.

Chips like the MCP2003/2004/2025 are just interface chips that allow you to interface the single wire 12V LIN bus to 5V Tx/Rx pins on a microcontroller or other processor. They don't care what format the data stream is in.

Ian.

1 Like

ian332isport:
The LIN bus protocol is more a description of the hardware side of things. How you format/structure the data is pretty much up to you unless you're interfacing to an existing system like the BMW iBus.

In general, the data format is standard 8 bit, No parity & 1 stop bit (8N1), but there's nothing to stop you using something different. BMW use 8E1 (even parity) in the iBus.

Chips like the MCP2003/2004/2025 are just interface chips that allow you to interface the single wire 12V LIN bus to 5V Tx/Rx pins on a microcontroller or other processor. They don't care what format the data stream is in.

Ian.

Does this mean i can use de MCP2003 with a sketch designed with the Serial library? The Serial library uses the Rx & Tx lines. This library doesn't use the exact LIN protocol, but the MCP2003 will transform the code to a useable signal?

Da_Beast:
Does this mean i can use de MCP2003 with a sketch designed with the Serial library? The Serial library uses the Rx & Tx lines. This library doesn't use the exact LIN protocol, but the MCP2003 will transform the code to a useable signal?

The MCP2003 will work with Arduino serial Rx & Tx as long as you're using a recognised serial protocol (8N1, 8E1 etc) that the Arduino recognises. As long as you use a serial protocol the Arduino is compatible with, then you will be fine.

The MCP2003 will transmit anything you send to the Arduino Tx pin, and it will pass anything it receives to the Arduino Rx pin. The MCP2003 doesn't know or care what protocol you use. It's the Arduino that needs to recognise the protocol.

You just need to make sure you don't send anything on the Arduino Tx line at the same time as it's receiving something on the Rx line. This will cause bus contention/data collision and is generally a bad thing. This is quite hard to do on the MCP2003, and why most people use the Melexis TH3122 as it has built in hardware to detect bus contention.

Ian.

ian332isport:
The MCP2003 will work with Arduino serial Rx & Tx as long as you're using a recognised serial protocol (8N1, 8E1 etc) that the Arduino recognises. As long as you use a serial protocol the Arduino is compatible with, then you will be fine.

I've been reading some info on the 8N1 with the arduino and out of this i think the arduino itself uses the 8N1 protocol with the Serial function. Is this the right conclusion or am i wrong?

The MCP2003 will transmit anything you send to the Arduino Tx pin, and it will pass anything it receives to the Arduino Rx pin. The MCP2003 doesn't know or care what protocol you use. It's the Arduino that needs to recognise the protocol.

You just need to make sure you don't send anything on the Arduino Tx line at the same time as it's receiving something on the Rx line. This will cause bus contention/data collision and is generally a bad thing. This is quite hard to do on the MCP2003, and why most people use the Melexis TH3122 as it has built in hardware to detect bus contention.

So if i create a data array that is something like this:
transmitData[] = (source address, destination address, data, token)
then send it with Serial.write(). The other devices will receive the array. If i filter the messages on destination adress, the data that's not for the device will be tossed and the data that's for the device will be processed futher.

I learned a protocol like this in school but don't know if the arduino can transmit the array in one piece?
I'm sorry if i ask dumb questions but i'm just a newbie!

Da_Beast:
I've been reading some info on the 8N1 with the arduino and out of this i think the arduino itself uses the 8N1 protocol with the Serial function. Is this the right conclusion or am i wrong?

You are correct.

So if i create a data array that is something like this:
transmitData[] = (source address, destination address, data, token)
then send it with Serial.write(). The other devices will receive the array. If i filter the messages on destination adress, the data that's not for the device will be tossed and the data that's for the device will be processed futher.

That should work fine. I send messages in this format:

byte DATA [6] PROGMEM = { 0xC8 , 0x04 , 0xE7 , 0x2C , 0x33 , 0x34 };

This code should send the message:

Serial.write(DATA, sizeof (DATA));

Ian.

ian332isport:
That should work fine. I send messages in this format:
byte DATA [6] PROGMEM = { 0xC8 , 0x04 , 0xE7 , 0x2C , 0x33 , 0x34 };
This code should send the message:
Serial.write(DATA, sizeof (DATA));

Ian.

Is the size of the message limited in size of format?? For example the data i want to send is 0x00F00352 that stands for Train 352.
The message could be this: DATA[4] = {0x02, 0x00, 0x00F00352, TRUE}
Translation: Source = node 2; Destination = node 0 (master); trainnumber = 352; Token = true (i can use the bus).

byte DATA [6] PROGMEM = { 0xC8 , 0x04 , 0xE7 , 0x2C , 0x33 , 0x34 };

What is the meaning of the PROGMEM after data[]?

Da_Beast:
Is the size of the message limited in size of format?? For example the data i want to send is 0x00F00352 that stands for Train 352.
The message could be this: DATA[4] = {0x02, 0x00, 0x00F00352, TRUE}
Translation: Source = node 2; Destination = node 0 (master); trainnumber = 352; Token = true (i can use the bus).

You are not so much limited by the number of bytes you send, but you are limited to 8 bits of data per byte (8N1). This is a maximum of 0xFF in HEX, so you can't send 0x00F00352. You also can't send the word True. You can send the ASCii code for each letter though.

What is the meaning of the PROGMEM after data[]?

That just means that the byte array is stored in Flash memory, instead of RAM.

Ian.