Serial Communication Protocol

I'm working on an Arduino -> Scala/Java bridge and am having some issues with serial communication. Right now I have an extremely simple command protocol that will fail upon disordered data.

Any suggestions on what I can use just for the communication? Looking for something like a very primitive version of TCP that works over serial and protects data integrity.

And just for kicks here's how it works (this is all working now perfectly besides the data transfer issues) in Scala code. I'll probably put it out open source. This is a Scala class that listens to a number of input pins and sends out appropriate responses and issues commands directly on the computer it runs on.

val arduino = Arduino("/dev/ttyACM4", 115200, 1000)

// Create LED Pin Objects
val statusLed = arduino.out(3)
val waitingLed = arduino.out(4)
val errorLed = arduino.out(5)

waitingLed.on

// TODO: Initialize Music Player

waitingLed.off

// Create Music Control Pin Objects

val play = arduino.in(11)
play.listenOn {
  if (isDay()) {
    execute("amixer set \"Master\" toggle")
  }
}

val volume = arduino.in(12)
volume.attachInterrupt(InterruptType.Change)
volume.listenAnalogBelow(127) {
  // Unmute
  // Change Volume
  errorLed.off
}
volume.listenAnalogAbove(128) {
  errorLed.on
}

val mute = arduino.in(10)
mute.listenOn(execute("amixer set \"Master\" toggle"))

The way it works is that these "listeners" are sent to the arduino with identifiers, the arduino checks the appropriate pins in the loop. If and only if the input from that pin has changed from its previous value, a notification is sent back to the computer which triggers the listener function.

It works with interrupts as well, exactly the same except it uses Arduino's interrupt mechanism and does not check within the loop.

I read your whole post, twice, to make sure I didn't miss anything. I don't understand the question, I guess. Where does the "disordered data" originate? The Arduino or the PC?

What, exactly, does that code cause the Arduino object to send to the serial port? Where is the Arduino code that handles the data that the Arduino object sends?

What, exactly, are the "data transfer issues"?

tk26:
Any suggestions on what I can use just for the communication? Looking for something like a very primitive version of TCP that works over serial and protects data integrity.

Those are pretty sketchy requirements and nowhere near enough information to design a protocol, but it sounds to me as if your problem is essentially how to detect complete and valid message frames out of a byte stream. You could achieve that by defining start/end flags to denote your message boundary, define a message structure that incorporates a checksum, and if your start/end flags could legitimately appear within the message then also provide a mechanism to escape them.

PaulS:
I read your whole post, twice, to make sure I didn't miss anything. I don't understand the question, I guess. Where does the "disordered data" originate? The Arduino or the PC?

It could originate from either side, my point is I'm looking for a bit more reliability than just hoping that all of the data sent from PC -> Arduino or Arduino -> PC gets to the other side.

It probably wouldn't be disordered (sending is synchronized on the Scala side), but I could see a byte missing here and there.

PaulS:
What, exactly, does that code cause the Arduino object to send to the serial port? Where is the Arduino code that handles the data that the Arduino object sends?

There are about 9 commands, each with an integer identifier and a couple bytes of data along with them. The arduino gets them, looks at the first 2 bytes, and uses that to identify the command.

Commands are setMode, digitalWrite, digitalRead, etc. Basically the same as the Arduino's C++ functions.

PeterH:
Those are pretty sketchy requirements and nowhere near enough information to design a protocol, but it sounds to me as if your problem is essentially how to detect complete and valid message frames out of a byte stream. You could achieve that by defining start/end flags to denote your message boundary, define a message structure that incorporates a checksum, and if your start/end flags could legitimately appear within the message then also provide a mechanism to escape them.

I don't need anything fancy, just the most basic packet protocol.

I just went ahead and threw a very basic one together with a start/length/checksum.

I just went ahead and threw a very basic one together with a start/length/checksum.

Is the data in binary format? If so how do you detect the start of a frame?


Rob

Graynomad:

I just went ahead and threw a very basic one together with a start/length/checksum.

Is the data in binary format? If so how do you detect the start of a frame?


Rob

Just a 0 byte right now but I'm planning to change it to something less likely, a random number between 1 and 255.

start byte
length (2 byte int)
data (arbitrary)
checksum (2 byte int, fletcher-16)
end byte

Might put markers inbetween length/data/checksum for quicker identification of malformed data

I asked because if the data is binary (and can therefore be any value) there's no way I know of to reliably detect a start of frame by just looking at received bytes.

length (2 byte int)

Expecting a lot of data :slight_smile:


Rob

Graynomad:
I asked because if the data is binary (and can therefore be any value) there's no way I know of to reliably detect a start of frame by just looking at received bytes.

Oh yeah of course it's impossible to know 100%, but being a 1 in 255 chance plus the checksum and other marker bytes, it's pretty safe.

Graynomad:

length (2 byte int)

Expecting a lot of data :slight_smile:

Hey if they had made IP addresses more than 32 bits back in the day I wouldn't be paying 2 bucks a month for every site that uses SSL.

Extra byte, I figured why not (the alternative is 255 bytes). Even though the largest command I'll send here is 9 bytes.

Check this out:

arduino.out(4).blink(100).doFor(hours(1))

Blinks a pin every 100 milliseconds for an hour. And once you execute another statement, that blinking pauses until the next command is done.

arduino.out(4).analog(127).doFor(minutes(10))

After 10 minutes, the pin goes back to the original blink routine.

of course it's impossible to know 100%, but being a 1 in 255 chance plus the checksum and other marker bytes, it's pretty safe.

As long as you don't get out of sync with the incoming data in which case it's possible you will never recover without taking further measures.

Statistics aren't my strong point but I would say way it's less than 1 in 255, as any byte can == the start byte I think it's 1 in (255/frame length).

Is this data continuous or is there large gaps between the frames?


Rob

primitive version of TCP that works over serial and protects data integrity.

Primitive and protects data integrity don't really go together well.

At the very least, you'll need a start and end of packet marker that is unique, ie the rest of the data cannot duplicate these markers, which adds a layer of complication itself when dealing with binary data, and a checksum for the actual contents of the data packet.

You'll also have to decide how you want to handle the cases where the packet is compromised as well. You don't really have complete data integrity if you just ignore corrupted packets. The typical method of handling this is with some sort of Ack/Nak protocol. If data transmission isn't bandwidth constrained, a rather simple approach to implementing an Ack/Nak would be:
Sender transmits packet, and waits for an Ack or a Nak.
If no Ack/Nak is received within some timeout period, or a Nak is received, packet is retransmitted and Sender again waits for Ack/Nak.
If Ack is received, next packet is transmitted.

On the receiver side, if a valid packet is received, send an Ack, if a corrupted packet is detected, then send a Nak. Your corrupted packet detection will need to be fairly good for this to work well. For the most part, you just want to be able to detect missing start and end packet markers and verify checksum. Other potential data integrity checks would be a sequence number and/or packet length value added to the packet as well.