LIN Bus network with a MCP2003 - Wiring of code problem?

Hi guys,

I’m working on a LIN bus network and I am using the MCP2003 LIN bus chip form Microchip.
I have read a couple of forums and then started working on the prototypes using a breadbord. I have tried a lot of things but can’t get any communication.

I’m using the MCP2003 chip but have learned that you can write to it using the Software Serial communication (http://forum.arduino.cc/index.php?topic=279272.0).
But the SoftwareSerial does not use some kind of protocol so after some searching i found the RS485 library from Nick Gamon (Gammon Forum : Electronics : Microprocessors : RS485 communications).

I tried to use this library to make the MCP2003 send data over the LIN bus. Every time i run the sketch i get the “Error!” and the LED on the master is lit.

I’m not sure what the problem is because i’m kind of a newbie to this. Help is much appreciated!

The schematics are attached.
Code for the Master:

#include "RS485_protocol.h"
#include <SoftwareSerial.h>

const byte ENABLE_PIN = 4;
const byte LED_PIN = 13;
int myAdr = 0x00;  // Adres of this slave (0x00 = master)
byte old_level = 0;
char serString;

SoftwareSerial LIN (2, 3);  // receive pin, transmit pin

// callback routines
void fWrite (const byte what)
{
  LIN.write(what);  
}

int fAvailable ()
{
  return LIN.available ();  
}

int fRead ()
{
  return LIN.read ();  
}


void setup()
{
  Serial.begin(9600);
  LIN.begin (19200);
  pinMode (ENABLE_PIN, OUTPUT);  // driver output enable
  pinMode (LED_PIN, OUTPUT);  // built-in LED
  Serial.println("LIN Master test");
}  // end of setup


void loop()
{
  int i = 0;
  // read potentiometer
  byte level = analogRead (0) / 4;

  // no change? forget it
  if (level == old_level)
    return;

  // assemble message
  byte msg [] = { 
    myAdr,  // My adres
    1,      // Adres receiver
    2,      // turn light on
    level   // to what level
  };
  Serial.print("Sending: ");
  for (i==0 ; i< sizeof(msg) ; i++){
    Serial.print(msg[i]);
    Serial.print(", ");
  }
  Serial.print(".");

  // send to slave  
  digitalWrite (ENABLE_PIN, HIGH);  // enable sending
  sendMsg (fWrite, msg, sizeof msg);
  Serial.println("Sent");
  digitalWrite (ENABLE_PIN, LOW);  // disable sending

  // receive response  
  byte buf [10];
  byte received = recvMsg (fAvailable, fRead, buf, sizeof buf);

  if (received == 0){
    digitalWrite (LED_PIN, HIGH);  // turn on LED if error    
    Serial.println("Error!");
  }

  // only send once per successful change
  if (received){
    digitalWrite(LED_PIN, LOW);
    Serial.print("Received: ");
    for (i==0; i<sizeof(buf); i++){
      Serial.print(buf[i]);
      Serial.print(", ");
    }
    old_level = level;
  }
  else {
    Serial.println("Receive failed");
  }
}  // end of loop

Code for the slave:

#include <SoftwareSerial.h>
#include "RS485_protocol.h"

const byte ENABLE_PIN = 4;
const int myAdr = 0x01; // Adres of this slave (master = 0x00)
SoftwareSerial LIN (2, 3);  // receive pin, transmit pin

void fWrite (const byte what)
{
  LIN.write (what);  
}

int fAvailable ()
{
  return LIN.available ();  
}

int fRead ()
{
  return LIN.read ();  
}

void setup()
{
  Serial.begin(9600);
  LIN.begin (19200);
  pinMode (ENABLE_PIN, OUTPUT);  // driver output enable
  Serial.println("LIN Slave test");
}

void loop()
{
  byte buf [10];
  int i = 0;
  byte received = recvMsg (fAvailable, fRead, buf, sizeof (buf));

  if (received){
    Serial.print("Received: ");
    for (i==0; i<sizeof(buf); i++){
      Serial.print(buf[i]);
      Serial.print(", ");
    }
    Serial.println(".");
    if (buf [0] != 0)
      return;  // not sent from master
    if (buf [1] != 1)
      return;  // not my device

    if (buf [2] != 2)
      return;  // unknown command

    byte msg [] = {
      myAdr,    // my
      buf[0],  // adres sender (master)
      0xFF,  // Message received correctly (ACK)
    };

    Serial.print("Sending: ");
    for (i==0 ; i< sizeof(msg) ; i++){
      Serial.print(msg[i]);
      Serial.print(", ");
    }
    Serial.println(".");

    delay (1);  // give the master a moment to prepare to receive
    digitalWrite (ENABLE_PIN, HIGH);  // enable sending
    sendMsg (fWrite, msg, sizeof msg);
    digitalWrite (ENABLE_PIN, LOW);  // disable sending

    analogWrite (11, buf [2]);  // set light level
  }  // end if something received
}  // end of loop

I’ve tested the protocol on it’s own with a direct connection between the 2 arduino’s (so no LIN components in between) and it works just fine. Therefore the problem is with te LIN components.

After reading the datasheet once more i’ve found a diagram with de states of the chips. This diagram is attached with the name “Operational Modes State Diagram - MCP2003”. The diagram shows the chip only goes into ready mode when the battery voltage goes above 5.5V. Since I powered the chips with 5V from the Arduino, that’s fault nr. 1 right?
What I don’t understand about this diagram is which state the chip needs to be in when I want the chip to be able to transmit and receive the messages from the LIN bus and the Arduino and which pins i need to pull HIGH or LOW.

The other attached file represent the schematics i followed for the first schematics. My questions about these schematics:

  • The diode for the master node. Do I need it when the power is from a DC power source?
  • The Vren of the chip is an output but i don’t know any voltage regulators that have 4 pins?
  • What do I do with the WAKE pin? Make it an I/O on the Arduino? I don’t want the chip to power down in this network but what are the possibilities?
  • Do I need the ESD protection diode, optional resistor and transient suppressor for testing? I want to add these to the PCB later but they make the breadbord a bit crowded…

Hi Stef,

I'm not sure why you went with RS485 library. You can use the regular software serial just fine with the MCP2003. It was only when using the BMW IBUS protocol that required 8E1 (Even parity) that causes problems. If you stick with regular 8N1 (No parity), you will be fine, and avoid any extra complication.

Your first problem is as you suggested. You really need 12V (6v to 27v is the spec, but 12v is ideal) for the MCP2003/LIN bus. I would also get rid of the 4.7K resistors you have between VBB and Rx. If you had been using 12v, you would have been pulling the Arduino Rx line up to 12v, which is not good. If you look at the MCP2003 datasheet, it shows the 4.7K resistor between the output of the voltage regulator (5v) and Rx, but I've never found it necessary to have a resistor there.

If you order any more components in the future, you may want to get the MCP2025. It's basically the same device, but has a 5v voltage regulator built in. All you need to do, is power the MCP2025 with 12v, and it will generate 5v to power the Arduino.

All you need to do to the MCP2003/2004/2025 chips to get them running is provide 12v and ground, and then drive the CS pin high to enable the LIN transmitter. This is best done by driving an Arduino digital output pin high. You can ignore the Vren and Wake pins for now. It should work without them.

If you do want a voltage regulator with an enable pin, look at the ST LF50 (in PPAK package), or the Micrel MIC29150/29300/29500/29750 range. I'm sure there's others, but those are ones I'm familiar with.

For testing, you don't really need any of the ESD or Transient suppression components. Unless you have polarised connectors, you may want to use a diode to protect against reverse polarity though. These chips are designed for industrial and automotive use, so are pretty robust.

Cheers,

Ian.

Hi Ian,
Thanks for the response!

ian332isport:
I’m not sure why you went with RS485 library.

I use the RS485 protocol for the CRC check. With this the slaves don’t proces faulty messages.
I’ve chanced the schematics (new attachment) and tried once more to send messages. Still no luck. The code i used for the master:

#include "RS485_protocol.h"
#include <SoftwareSerial.h>

const byte CS_PIN = 4;
const byte LED_PIN = 13;
SoftwareSerial LIN (2, 3);  // receive pin, transmit pin

// callback routines
void fWrite (const byte what)
{
  LIN.write(what);  
}
int fAvailable ()
{
  return LIN.available ();  
}
int fRead ()
{
  return LIN.read ();  
}


void setup()
{
  Serial.begin(9600);
  LIN.begin (19600);
  pinMode (CS_PIN, OUTPUT);  // driver output enable
  pinMode (LED_PIN, OUTPUT);  // built-in LED
  digitalWrite (CS_PIN, HIGH);  // enable sending CS can't be LOW!
  Serial.println("RS485 Master");
}  // end of setup

byte old_level = 0;

void loop(){
  // read potentiometer
  byte level = analogRead (0) / 4;

  // assemble message
  byte msg [] = { 
    0x01,  // device 1
    2,     // turn light on
    level, // to what level
    0x05   // random number
  };

  if (Serial.read() != -1){

    for (int i=0 ; i< sizeof msg; i++){
      Serial.print(msg[i]);
      Serial.print(",");
    }
    Serial.println(".");

    // send to slave
    digitalWrite (CS_PIN, HIGH);  // just to make sure it's high
    sendMsg (fWrite, msg, sizeof msg);
    Serial.println("Sending");

    // receive response  
    byte buf [10];
    byte received = recvMsg (fAvailable, fRead, buf, sizeof buf);
    Serial.print("Receiving: ");

    if (received != 0){
      Serial.println("OK");
    }

    if (received == 0){
      digitalWrite (LED_PIN, received == 0);  // turn on LED if error
      Serial.println("FAIL");
    }
  }
  digitalWrite(LED_PIN, LOW);
}  // end of loop

and the slave:

#include <SoftwareSerial.h>
#include "RS485_protocol.h"

SoftwareSerial LIN (2, 3);  // receive pin, transmit pin
const byte ENABLE_PIN = 4;

void fWrite (const byte what)
{
  LIN.write (what);  
}

int fAvailable ()
{
  return LIN.available ();  
}

int fRead ()
{
  return LIN.read ();  
}

void setup()
{
  Serial.begin(9600);
  LIN.begin (19600);
  pinMode (ENABLE_PIN, OUTPUT);  // driver output enable
  digitalWrite (ENABLE_PIN, HIGH);  // enable sending
  Serial.println("RS485 Slave");
}

void loop()
{
  byte buf [10];

  byte received = recvMsg (fAvailable, fRead, buf, sizeof (buf));
  for (int i=0; i< sizeof buf; i++){
      Serial.print(buf[i]);
      Serial.print(",");
  }
  Serial.println(".");

  if (received){
    Serial.println("Received something!"); 
    for (int i=0; i< sizeof buf; i++){
      Serial.print(buf[i]);
      Serial.print(",");
    }
    Serial.println(".");
    if (buf [0] != 1){
      Serial.println("Not my device");
      return;  // not my device
    }

    if (buf [1] != 2){
      Serial.println("Unknown command");
      return;  // unknown command
    }

    byte msg [] = {
      0x00,  // device 0 (master)
      3,  // turn light on command received
      HIGH   // random number
    };

    delay (1);  // give the master a moment to prepare to receive
    sendMsg (fWrite, msg, sizeof msg);

    analogWrite (11, buf [2]);  // set light level
    Serial.print("level = ");
    Serial.println(buf[2]);
  }  // end if something received

}  // end of loop

The slave gives me constant message “95,1,94,10,170,0,11,2,95,11,.”. I have no idea what it means but i’m certanly not sending it because the message does not change when i do send something. Both the LED’s on the breadbord are lit indicating the CS pin is HIGH. I really don’t know what i’m doing wrong here…

You can use the regular software serial just fine with the MCP2003.

Because the RS485 method does not work I tried only the SoftwareSerial.
Code for master:

#include <SoftwareSerial.h>
const byte ENABLE_PIN = 4;

SoftwareSerial LINbus(2, 3); // RX, TX
int i = 0;

void setup()  
{
  pinMode (ENABLE_PIN, OUTPUT);
  digitalWrite (ENABLE_PIN, HIGH);  // enable sending
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  Serial.println("Goodnight moon!");
  // set the data rate for the SoftwareSerial port
  LINbus.begin(2400);
}

void loop() // run over and over
{
  if (Serial.read() != -1){
byte msg [] = { 
      0xA1,  // My adres
      1,      // Adres receiver
      2,      // turn light on
    };
    for (i=0 ; i< sizeof(msg) ; i++){
    LINbus.write(msg[i]);
    }
    Serial.println("Sent");
  }
}

and the slave:

#include <SoftwareSerial.h>
const byte ENABLE_PIN = 4;

SoftwareSerial LINbus(2, 3); // RX, TX

void setup()  
{
  pinMode (ENABLE_PIN, OUTPUT);
  digitalWrite (ENABLE_PIN, HIGH);  // enable sending
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  Serial.println("Goodnight moon!");
  
  // set the data rate for the SoftwareSerial port
  LINbus.begin(2400);
}

void loop() // run over and over
{
  if (LINbus.available())
    Serial.println(LINbus.read(), HEX);   
}

These sketches work if i do not include the LIN components. But when I insert the LIN components there are no messages between the 2 arduino’s.

If you order any more components in the future, you may want to get the MCP2025. It’s basically the same device, but has a 5v voltage regulator built in. All you need to do, is power the MCP2025 with 12v, and it will generate 5v to power the Arduino.

That is a good idea! This wil give the arduino a constant power supply of 5V when the slaves will have to work without a USB connection.

I still reckon your problem is the RS485. I don't think the LIN standard supports RS485 at the hardware level. Try going back to regular serial 8E1 and generate your own CRC.

You just need to calculate the CRC at one end (and send it with the message) and use the same algorithm at the other to confirm it matches. If it doesn't, you have an incomplete or corrupt message.

I'm not familiar with RS485, but it looks to be a two wire balanced signal. You can't send this down a single wire LIN bus.

Ian.

ian332isport: I still reckon your problem is the RS485. I don't think the LIN standard supports RS485 at the hardware level.

I tested all 4 sketches posted above with a direct connection between the 2 arduino's and they work perfectly.

Try going back to regular serial 8E1

Don't the sketches posted above with the SoftwareSerial do this? Not the 8E1 probably?

and generate your own CRC.

I've been seaching for a CRC check and then found the RS485 library. I will search the forum for this.

I'm not familiar with RS485, but it looks to be a two wire balanced signal. You can't send this down a single wire LIN bus.

RS485 is a Hardware protocol and an example of a chip is the MAX485. Both this one as the LIN chips have Tx and Rx pins so i thought this would not be a problem?

Da_Beast: I tested all 4 sketches posted above with a direct connection between the 2 arduino's and they work perfectly.

That's because you can use Tx & Rx at the same time. You can't do that with the LIN bus.

Don't the sketches posted above with the SoftwareSerial do this? Not the 8E1 probably?

Not sure what's going to happen when the RS485 library is being used.

RS485 is a Hardware protocol and an example of a chip is the MAX485. Both this one as the LIN chips have Tx and Rx pins so i thought this would not be a problem?

LIN chips have both Tx and Rx, but you can only drive one at a time. You can send from one device to another, but while this is happening, no other devices can use the bus. They have to wait for the bus to go idle before taking the line. RS485 drives both lines at the same time (with inverted signals) which simply won't work with the LIN bus. You can use LIN or RS485, but you cannot combine the two.

Maybe using RS485 on its own would be a better solution for your project.

Ian.

Hi Ian,

Thanks again for all the help so far!

ian332isport: LIN chips have both Tx and Rx, but you can only drive one at a time. You can send from one device to another, but while this is happening, no other devices can use the bus. They have to wait for the bus to go idle before taking the line. RS485 drives both lines at the same time (with inverted signals) which simply won't work with the LIN bus. You can use LIN or RS485, but you cannot combine the two.

That's why i wanted to introduce a token. From what I understand out of the documentation about the LIN protocol is that the master initiates all communication. So the master sends data to a specific slave requesting data or to change data. The slave then replies. That's what I was trying to do with the RS485 protocol but obviously failed.

Should I use the Software serial to do something like this (in words, code will be a lot harder):

1. Enable the CS pin
2. Write a message on the LIN bus (LIN.write?)
3. Wait until the slave has processed the message and replies
4. Read the response (LIN.read?)
5. Proces the response
6. start again at 1 (or 2?)

Maybe using RS485 on its own would be a better solution for your project.

Perhaps, there is not much difference in price between the components but on a large scale (like 1000 slaves in total) that will be a huge difference. Therefore my dad is pushing for the LIN components.

Your pseudo code is pretty much what you need to aim for. You just need to come up with a message structure that will be sent on the bus.

Most of my knowledge is based on the BMW IBUS which is based on the LIN bus. Although the instrument cluster is the master, each slave can put data on the bus without first being asked for it. Things like the CD changer can transmit track names to the radio display without being asked to do so by the master. Each slave can talk to any other slave as well, so you do have to manage data collisions.

It sounds like your system where the master initiates all communication will be a lot easier to implement. You won't have to worry about collisions etc.

Your message structure would ideally contain the address of the slave, length of message (assuming it's not a fixed length), whatever commands or data you wish to send, and finally the checksum. On the IBUS, the checksum is calculated by doing an Xor on all the bytes in the message (except the checksum itself). Once you have built the message, you calculate the checksum and add it to the end of the message. Your slave will do the same calculation on the message it receives and compares the calculated checksum with the one transmitted with the message. If they match, it's all good. If they don't, then you should discard the message as it's corrupt.

I'm not sure how many slaves you can have on the LIN bus, but 1000 sounds quite a lot. Most of the literature I've seen talks about 16 slaves per master, but I'm not sure what the practical maximum would be. RS485 seems to have a limit of 32. That said, the BMW IBUS has more than 16 slaves, but probably less than 30.

Ian.

ian332isport: Your pseudo code is pretty much what you need to aim for. You just need to come up with a message structure that will be sent on the bus.

Most of my knowledge is based on the BMW IBUS which is based on the LIN bus. Although the instrument cluster is the master, each slave can put data on the bus without first being asked for it. Things like the CD changer can transmit track names to the radio display without being asked to do so by the master. Each slave can talk to any other slave as well, so you do have to manage data collisions.

This would be ideal! The slaves collect data that almost instantly needs to be send to the master. If there is no collision management there will be a lot of faulty messages and the bus would become clogged. What collision management technique does the IBUS use?

Your message structure would ideally contain the address of the slave, length of message (assuming it's not a fixed length), whatever commands or data you wish to send, and finally the checksum.

I'm thinking of using a fixed message length. Can I use an array like in the previous sketches? The message would be something like: {sender ID, receiver ID, type of message, data, checksum}

On the IBUS, the checksum is calculated by doing an Xor on all the bytes in the message (except the checksum itself). Once you have built the message, you calculate the checksum and add it to the end of the message. Your slave will do the same calculation on the message it receives and compares the calculated checksum with the one transmitted with the message. If they match, it's all good. If they don't, then you should discard the message as it's corrupt.

The arduino references (Arduino Refecence BitwiseAnd )gives this example code for the XOR:

// demo for Exclusive OR
void setup(){
DDRD = DDRD | B00100000; // set digital pin five as OUTPUT 
Serial.begin(9600);
}

void loop(){
PORTD = PORTD ^ B00100000;  // invert bit 5 (digital pin 5), leave others untouched
delay(100);
}

I'm I correct to say that the "B00100000" in the example loop needs to be changed to "B11111111" to do an Xor on all the bits?

I'm not sure how many slaves you can have on the LIN bus, but 1000 sounds quite a lot. Most of the literature I've seen talks about 16 slaves per master, but I'm not sure what the practical maximum would be. RS485 seems to have a limit of 32. That said, the BMW IBUS has more than 16 slaves, but probably less than 30.

The 1000 slaves is in total. The LIN bus would have a max of 16 (or a couple higher) slaves. Then a CAN bus network is used on top to get the messages to a arduino that is connected to a PC for displaying the position of the train.

Da_Beast:
What collision management technique does the IBUS use?

It uses a technique where each slave monitors the bus line. As soon as the line goes LOW (it idles HIGH), it knows the bus is busy, so knows not to send anything. After the line goes HIGH and stays high for more than 10ms, it knows the line is clear and can safely send.

I’m thinking of using a fixed message length. Can I use an array like in the previous sketches? The message would be something like: {sender ID, receiver ID, type of message, data, checksum}

That should be fine, but make life easy and stick to HEX for each byte. Your example appears to have a mix of HEX and Decimal.

iBus example:

When key is inserted into the ignition lock this messages is sent.

44 05 BF 74 04 00 8E // detected key 0

Structure of an iBus packet (Ref message above):

1st byte is the Source ID - 0x44
2nd byte is length of the packet excluding the first two bytes (source & length) - 0x05
3rd byte is the destination ID - 0xBF
4th byte onwards is the actual data - 0x74, 0x00, 0xFF
Last byte is the checksum - 0x8E

Checksum is generated by XOR of the entire packet excluding the checksum itself.

HEX - Binary
44 = 01000100
05 = 00000101
BF = 10111111
74 = 01110100
04 = 00000100
01 = 00000001
XOR = 10001110 = 8E

This is the code I use to calculate the checksum:

byte checksumByte = 0;  
      for (int i = 0; i <= length; i++){
        checksumByte ^= inByte[i];
      }

In the example above, length is 0x05 and inByte contains 0x44, 0x05, 0xBF, 0x74, 0x04, 0x01, 0x8E

Ian.

ian332isport:
It uses a technique where each slave monitors the bus line. As soon as the line goes LOW (it idles HIGH), it knows the bus is busy, so knows not to send anything. After the line goes HIGH and stays high for more than 10ms, it knows the line is clear and can safely send.

I think this is called Collision Avoidance? This is not possible with the MCP2003/2025 because there is no such pin on the chip? You gave an alternative for these chips (Melexis TH3122) but can’t find a store that sels this chip? The TH3122 has a pin that give the state of the bus?

Checksum is generated by XOR of the entire packet excluding the checksum itself.
HEX - Binary
44 = 01000100
05 = 00000101
BF = 10111111
74 = 01110100
04 = 00000100
01 = 00000001
XOR = 10001110 = 8E

This is the code I use to calculate the checksum:

byte checksumByte = 0;  

for (int i = 0; i <= length; i++){
        checksumByte ^= inByte[i];
      }




In the example above, length is 0x05 and inByte contains 0x44, 0x05, 0xBF, 0x74, 0x04, 0x01, 0x8E

Is the inByte the actual message? Does this piece of code ad the 0x8E at the end of inByte as the checksum or does the checksumByte need to be added after it has been calculated?

Da_Beast: I think this is called Collision Avoidance? This is not possible with the MCP2003/2025 because there is no such pin on the chip? You gave an alternative for these chips (Melexis TH3122) but can't find a store that sels this chip? The TH3122 has a pin that give the state of the bus?

Although you don't have a pin on the MCP2003/2025, you can simulate this signal with the Arduino. If you loop the Rx pin into one of the interrupt pins (2 & 3 on an Uno), you can simulate this pin. Every time the Rx pin (and therefore the interrupt pin) goes low, you set a flag that signals that the line is busy. This signal will stay high until it sees the Rx pin stay high for 10us or more. This is a logic analyser plot that shows this signal. The top trace is an IBUS message being received on the Rx pin, and the bottom trace is the signal that indicates the bus is busy. You can only send when this signal is low.

Although the Melexis TH3122 is obsolete, you can still get them from here. Not cheap though.

http://www.ac-services.eu/shop/product_info.php?language=en&info=p151_melexis-th3122-chip.html&

Is the inByte the actual message? Does this piece of code ad the 0x8E at the end of inByte as the checksum or does the checksumByte need to be added after it has been calculated?

In that example, inByte is the message that has just been received, and that code snippet is looking at this message and calculating what the checksum should be. You would then compare the calculated checksum with the last byte of the message (in this case 0x8E). If they match, it's a good message. If you were generating a message to send, you would use the same code to calculate the checksum for this message and then add it to the end of the message before you send it. The calculation method is the same for send and receive, it's what you do with the result that differs.

Ian.

ian332isport:
Although you don’t have a pin on the MCP2003/2025, you can simulate this signal with the Arduino. If you loop the Rx pin into one of the interrupt pins (2 & 3 on an Uno), you can simulate this pin. Every time the Rx pin (and therefore the interrupt pin) goes low, you set a flag that signals that the line is busy. This signal will stay high until it sees the Rx pin stay high for 10us or more. This is a logic analyser plot that shows this signal. The top trace is an IBUS message being received on the Rx pin, and the bottom trace is the signal that indicates the bus is busy. You can only send when this signal is low.

I have no experience with the interrupt pins and therefore no idea how to use them? In what i understand is that I need to see if the interrupt pin (Rx) is low for 10µs and only then send the message? How can I do this? I’m thinking the arduino needs to watch the interrupt pin for a falling edge during 10µs and if there is no falling edge the message can be transmitted. Now how i translate this into code? I have NO idea and need to do some research.

Although the Melexis TH3122 is obsolete, you can still get them from here. Not cheap though.

It sure is pricey! For that price i can buy 4 MCP2025 chips! The MCP chips also have no need for extra components to do the same task. But maybe the arduino needs to work harder for the MCP chips than the TH chip?

In that example, inByte is the message that has just been received, and that code snippet is looking at this message and calculating what the checksum should be. You would then compare the calculated checksum with the last byte of the message (in this case 0x8E). If they match, it’s a good message.
If you were generating a message to send, you would use the same code to calculate the checksum for this message and then add it to the end of the message before you send it. The calculation method is the same for send and receive, it’s what you do with the result that differs.

The question I have about the code is that it just calculates the checksum and you need to ad the calculated sum to the message? Like so:

byte checksumByte = 0;  
      for (int i = 0; i <= length; i++){
        checksumByte ^= inByte[i];
      }
inByte[5] = checksumByte;

or is does the code ad the checksum instantly?

Da_Beast:
I have no experience with the interrupt pins and therefore no idea how to use them? In what i understand is that I need to see if the interrupt pin (Rx) is low for 10µs and only then send the message? How can I do this? I’m thinking the arduino needs to watch the interrupt pin for a falling edge during 10µs and if there is no falling edge the message can be transmitted. Now how i translate this into code? I have NO idea and need to do some research.

I can’t say it’s the best or only way to do it (it’s probably not), but I used the falling edge on Rx to flag that it’s ‘not clear to send’ and to reset a timer. Then on a rising edge on Rx, I started a timer that fires another interrupt after 10ms to flag ‘clear to send’. If the Rx signal falls before the 10ms timer has finished, the timer is reset again. You only get ‘clear to send’ if Rx stays high for longer than 10ms.

The MCP chips also have no need for extra components to do the same task. But maybe the arduino needs to work harder for the MCP chips than the TH chip?

I think the component count is much the same between the MCP2025 and TH3122, as both have the voltage regulator that requires input/output capacitors. It’s only the MCP2003/2004 that require fewer components.

The question I have about the code is that it just calculates the checksum and you need to ad the calculated sum to the message? Like so:

byte checksumByte = 0;  

for (int i = 0; i <= length; i++){
       checksumByte ^= inByte[i];
     }
inByte[5] = checksumByte;

Correct. Once the checksum is calculated, YOU need to add the checksum to the correct place in the message you want to send. For arguments sake, you would probably call it outByte, or sendByte or something suitably descriptive. inByte is better for the receive side of things, but you can use what you like really.

You would do it the other way around on the receive end:

byte checksumByte = 0;  
      for (int i = 0; i <= length; i++){
        checksumByte ^= inByte[i];
      } 

      if (inByte[length + 1] == checksumByte){

       // do stuff
      }

If you use a fixed length message, you won’t need to use inByte[length + 1], you would just use inByte[5] or wherever the checksum is located.

Ian.

The schematics are attached.

FYI,
Fritzings are not schematics. A schematic is a drawing with schematic symbols and labels for all the pins and voltages. A Fritzing has none of that. It is just a graphic of chips with lines connected. There are no pin labels or numbers. You have to trace the lines to see where it goes. A schematic can simply be a box with
lines attached with pin numbers , each having a label and something like “To U2-pin-3” etc. (or you can draw the lines. Also, with a schematic, you can simply use power pin and ground symbols eliminating those lines. It is much easier to work with. Fritzings are used by people who don’t know how to draw schematics.
If that is the case , then so be it, but it would be to your advantage to learn.

for future posts, please draw a real schematic and post a photo of it. The Fritzing just results in more work because we have to trace the lines and look up the pin numbers . A schematic has pin numbers and
pin Labels which is more useful. It’s too late for this post but I thought you should know that Fritzings are
not very popular here . One thing you should do anyway is take a closeup photo of the actual circuit from
directly above about 12 " away and post that.

The discussion about the interrupts can be found here: Timing with an Interrupt
I’ve been trying to send a message from the master to a slave arduino with the code from that topic but once again failed.

I’ve attached a Fritzing diagram to show you the connections i made. I know the Fritzing is not popular here but this way faults from the conversion of a schematic to fysical wiring become clear. I’ve also attached both sketches (master & slave).

MCP2003_collision_avoidance_MASTER.ino (2.16 KB)

MCP2003_collision_avoidance_SLAVE.ino (2.11 KB)

I've yet to try hooking anything up, but a couple of observations.

  1. You are not implementing the collision detection at all. You need something like this:
if (buttonState == HIGH && clearToSend == true){
        byte msg[] = {
          0x01 };
  1. In your slave sketch, I think you're turning led2Pin on and then off so fast you probably won't see it. When you receive a byte, you read it straight away, so serialAvailable() becomes false, and your else statement turns the led off.

  2. Just for reliability, you probably want to use the internal pullup resistors on your buttonPin.

pinMode(buttonPin, INPUT_PULLUP);

It just stops the input floating and giving false button presses.

Ian.

ian332isport:

  1. You are not implementing the collision detection at all. You need something like this:
if (buttonState == HIGH && clearToSend == true){

byte msg = {
         0x01 };

I’ve changed this in the master sketch but there is no transmission (indicated by the Tx led not flickering). The led on pin 13 also does not change its state. Therefore I think the clearToSend must be true? If I change the state of the led during setup maybe it will help indicate this better?

  1. In your slave sketch, I think you’re turning led2Pin on and then off so fast you probably won’t see it. When you receive a byte, you read it straight away, so serialAvailable() becomes false, and your else statement turns the led off.

If I add a delay in the if statement it will be lit longer, but most forum members are not fans of delays?

  1. Just for reliability, you probably want to use the internal pullup resistors on your buttonPin.
pinMode(buttonPin, INPUT_PULLUP);

It just stops the input floating and giving false button presses.

Does this eliminate the need for the debounce timings or just an extra precaution?

Da_Beast: I've changed this in the master sketch but there is no transmission (indicated by the Tx led not flickering). The led on pin 13 also does not change its state. Therefore I think the clearToSend must be true? If I change the state of the led during setup maybe it will help indicate this better?

I've just been thinking about this some more, and the initial state of clearToSend is false. In my application, I [u]always[/u] receive something before I have to send somethng. It's the act of receiving the first byte that gets the ball rolling and sets clearTosend to true. In your test sketch, you're trying to send before you've received anything, hence clearToSend is always false and you can't send.

Try changing the initial state of clearToSend to true.

If I add a delay in the if statement it will be lit longer, but most forum members are not fans of delays?

It's fine to use delay for testing purposes to prove the code works. It's certainly good practice to avoid delays in the final code though.

Does this eliminate the need for the debounce timings or just an extra precaution?

You still need to debounce. If you don't pull the input up, it can float around and be influenced by stray voltages. I've had situations where static from my fingers can change the state of a floating input just by getting my finger close to the pin.

Ian.