I’ve been playing with ways to detect the likelihood of a collision before trying to send data in order to try to minimize the chance of causing a collision. The most promising approach I’ve found is based on the idea that the RX (receive) pin on the ATmega328p can also be set-up to trigger what is called a “pin change interrupt”. The idea here is that the RX pin is normally high when no data is being sent across the bus by any of the devices. The moment data is sent, the first bit transmitted always pulls the bus low before transmitting the normal 8-bits for a byte and then the stop bit. (This first 0/low bit is referred to as a “start bit”.) There are actually several flavors of serial communication, but I use the standard 8N1, or 8 bits of data following the start bit followed by 1 stop bit. The start bit is always low and the stop bit is always high.
Trivia break: Thus the serial bus actually needs to send 10 bits for every byte of data. So when you calculate the theoretical maximum amount of data you can send down a serial bus like this one, you need to take the baud rate and divide by 10 to get the number of bytes per second. (Not 8!)
Since the RX pin can generate an interrupt whenever it changes, and it always changes from high to low at the very beginning of transmission by another device on the bus, this is a good way to detect if someone else has begun a transmission. Additionally, I already need to process serial data as it is received, so I treat the bytes as they come in to be a “bus continues to be busy” signal. To detect the end of transmission, I simply wait 1ms after the last byte is received. At 38400 baud (my current test case serial bus speed), this is the time to transmit about 4 bytes of data. (10 bits @38400 bits per second = 10/38400 ~= 1/4000 seconds or 0.25ms per byte of data.)
So using this approach to detect collisions, every microcontroller is always listening for the beginning of transmission of data from other devices. They mark that the bus is “busy” whenever this first start bit is sent. The bus then stays “busy” until a period of time passes where no data is sent. On then does the bus goes back to being considered “idle”, or free to transmit data on.
So the basic algorithm is:
- If you have data to send, first check if the bus is busy
- If it is not busy, go ahead and start sending. And hopefully any other devices trying to send will detect that you are sending before they try to send, and no collision occurs.
- If you detect that the bus is busy, then wait for the bus to become free again
- If you send data and want to be really sure it is received, the recipient should send a response immediately after receiving the packet. The response should be sent before the 1ms timeout occurs so that no other device starts sending another unrelated transmission tying up the bus and preventing the recipient from responding.
(Yes? Good!)
I’m sure I’m not the first person to do this, so I’m most likely reinventing the wheel here. But I didn’t have any good leads on simple approaches, so the above seemed to make sense. If I get stuck somewhere along the way implementing this, I’ll have to start digging around in the literature to understand why. But until then, this approach seems to make sense. Easy enough to implement, but likely to be pretty effective and efficient.