29
\$\begingroup\$

All my microcontroller devices that communicate with the PC via UART use ASCII strings for sending commands and receiving data (as implemented in Arduino). That's what I learned when I began digging into electronics and I always found sending bare strings to be enough. However I noticed that most devices I came across use sophisticated binary protocols that include function codes, addresses and CRC error checking.

When is basic ASCII communication acceptable and when should I consider something more advanced, like Modbus? Do commercial devices use such ASCII? Industrial?

\$\endgroup\$
3
  • 3
    \$\begingroup\$ Short answer: When your application needs it. Yes, commercial devices use ASCII. Take GPS NMEA as an example. (And again, I will refer my own question here) \$\endgroup\$
    – Eugene Sh.
    Commented Nov 18, 2015 at 15:34
  • 1
    \$\begingroup\$ Modbus has an ASCII mode. See Modicon Modbus Protocol Reference Guide \$\endgroup\$
    – Tut
    Commented Nov 18, 2015 at 16:09
  • \$\begingroup\$ @EugeneSh.: It's worth noting that NMEA has a checksum field, and dropping a single sample burst due to checksum failure (which happens more often than you might think) is generally not a critical failure. That very well may not be the case for other protocols... and there are plenty of binary GPS protocols in use (e.g. Garmin) for applications in which it may indeed be critical (or in which a higher-than-1Hz sample rate is required, for which NMEA is too verbose). Though this really only solidifies your point. \$\endgroup\$ Commented Nov 20, 2015 at 13:32

8 Answers 8

28
\$\begingroup\$
  1. ASCII and CRC are not mutually exclusive. ASCII is an encoding, and CRC is for error checking.

  2. ANYTHING can be sent as ASCII. Us oldsters certainly remember UUEncoding, which turns anything into an ASCII string.

  3. A) For me, it's usually a question of speed and efficiency. Sending a large 32-bit number by ASCII can take a long time, but it only takes 4 bytes to send it as binary via a serial protocol.

    B) Sending NUMBERS via ASCII means that you have to convert the number to ASCII, which is a clear extra step (this is part of what "printf" does).

  4. If you somehow lose your place, screw up, lose the format, get the wrong endian, etc., a binary communication protocol can certainly screw up. If you're sending ASCII, it can be easier to recover from screwups by just going in and LOOKING at the data stream.

\$\endgroup\$
8
  • 12
    \$\begingroup\$ +1 for "ASCII is an encoding". It's not a protocol; protocols can be built on top of ASCII. \$\endgroup\$ Commented Nov 18, 2015 at 16:06
  • 8
    \$\begingroup\$ Automatically recovering from a screwup isn't intrinsically any easier for a text-based protocol as a binary one, but inspecting and debugging it certainly acn be. \$\endgroup\$ Commented Nov 18, 2015 at 17:19
  • 1
    \$\begingroup\$ @NickJohnson -- absolutely. Once you're at the point of opening a file in a hex editor to see what you can recover, you're already at FUBAR so far as SOP goes \$\endgroup\$ Commented Nov 18, 2015 at 17:29
  • 1
    \$\begingroup\$ @nickjohnson that isn't really true. ASCII gives you lots of out of band framing / delimiter options to aid synchronization and recovery, which would require additional escaping, bit stuffing, time interval or other tricks if the channel is used for full width binary data. \$\endgroup\$ Commented Nov 18, 2015 at 19:48
  • 2
    \$\begingroup\$ I always favour ASCII when writing protocols for all the obvious benefits (readability, logability, etc). There are two cases when binary makes more sense : first, if speed is an issue and you need binary to cram as much data as possible into the stream, and second, marginally, if you're deliberately trying to obfuscate or even encrypt the data stream to hinder or prevent reverse-engineering. At that, I have reverse engineered binary protocols and it has largely just irritated me more than actually prevented the act. \$\endgroup\$
    – J...
    Commented Nov 19, 2015 at 19:11
10
\$\begingroup\$

Here are some thoughts about it:

  • ASCII is nice because you can use a serial monitor to have a manual look in what is send.
  • if your connection is not reliable,you must expect transmission-errors and should use a CRC to check the integrity of each received message. This can also be done on ASCII messages.
  • if your connection is too slow you can reduce the size of your messages by switching to a binary format
  • A specialized binary format may be easier to decode on the receiver side than ASCII
\$\endgroup\$
7
\$\begingroup\$

At the simplest level, you could say a simple communication protocol has three layers: physical, transport, and application. (There are models with more such as OSI with 7 or TCP/IP with 4. The number of layers isn't terribly important in the context of this question.)

The application layer is the layer you deal with directly in your code, and the focus of the question. As far as the transport layer is concerned, the byte you passed to it in send_data is just a binary pattern, but you may interpret it in your application code as the letter 'A'. The CRC or checksum calculation will be the same regardless of whether you consider the byte to be 'A,' 0x41, or 0b01000001.

The transport layer is the packet level, where you have your message headers, and error checking, whether it be CRC or a basic checksum. In the context of firmware, you may have a function such as send_data, where you pass it a byte to send. Inside that function it put into a packet that says, "Hey this is a normal message, requires an acknowledgement, and the checksum is 0x47, current time is X." This packet is sent out over the physical layer to the receiving node.

The physical layer is where the electronics and interface are defined: connectors, voltage levels, timing, etc. This layer could range from a couple of traces running TTL signals for a basic UART on a PCB, to a fully isolated differential pair as in some CAN implementations.

At the receiving node, the packet comes in on the physical layer, is unpacked at the transport layer, and then your binary pattern is available to the application layer. It's up to the receiving node application layer to know whether that pattern should be interpreted as 'A,' 0x41, or 0b01000001, and what to do with it.

In conclusion, it's pretty much always acceptable to send ASCII characters if that is what the application requires. The important thing is to understand your communication scheme, and include an error checking mechanism.

\$\endgroup\$
4
  • \$\begingroup\$ Ascii protocols can incorporate checksum as well. I've encountered Hex-as-ascii variations, using ascii representation of numbers. \$\endgroup\$
    – Eugene Sh.
    Commented Nov 18, 2015 at 15:38
  • \$\begingroup\$ @EugeneSh. Clarified that point \$\endgroup\$
    – Matt Young
    Commented Nov 18, 2015 at 15:44
  • \$\begingroup\$ Not to nitpick, but TCP isn't four layers; it is seen as fitting in layer four of the OSI model. Serial communications don't really fit the OSI model very well. \$\endgroup\$ Commented Nov 29, 2015 at 22:01
  • \$\begingroup\$ @batsplatsterson That is nitpicking, and pretty well irrelevant to the point I'm making. \$\endgroup\$
    – Matt Young
    Commented Nov 29, 2015 at 22:04
5
\$\begingroup\$

A point not yet mentioned is that whether one is using ASCII or a binary protocol, sending a rub-out character before each packet will ensure that even if line noise or framing errors appear before the start of a packet, all characters after the rub-out will be correctly framed in the absence of further noise. Otherwise, if one sends packets continuously and doesn't include any characters which are guaranteed to achieve resynchrononization, it's possible that one glitch may corrupt everything that follows until the next pause in transmission. The 0xFF character is nice because it guarantees that any recipient will be able to resynchronize on the following character.

(*) 0xFF--called rub-out because someone who types an erroneous character while typing data onto a paper tape can push the "step tape backward" button and hit rub-out to replace the erroneously-punched character with 0xFF, which will be ignored by most recipients).

\$\endgroup\$
2
\$\begingroup\$

One advantage of sending ASCII strings is that the control codes can then be used to signal start / end of message. e.g. STX (char 2) and ETX (char 3) can signal start transmission and end transmission. Alternatively you can add a simple line feed to mark the end of transmission.

When sending binary data this becomes more complicated as no particular bit pattern can be reserved for a control code (without some extra overhead or complexity) as a valid data byte may have the same pattern.

\$\endgroup\$
3
  • 3
    \$\begingroup\$ Many binary protocols DO reserve one or more bit patterns as control codes, but they also include an escape mechanism to handle those codes when they appear in the data. \$\endgroup\$
    – Dave Tweed
    Commented Nov 18, 2015 at 19:14
  • \$\begingroup\$ You can reserve any pattern to flag anything you want in binary. For example, I'm on a projects with a fast data stream and a slow data stream going out the same uart. I reserved the largest negative int32 as a flag for my slow data, and just saturate my negative data at the largest negative + 1. \$\endgroup\$ Commented Nov 18, 2015 at 22:25
  • \$\begingroup\$ Agreed. I clarified this in the edited answer, I hope. \$\endgroup\$
    – Transistor
    Commented Nov 18, 2015 at 23:21
2
\$\begingroup\$

ASCII is just fine, I use it in just about all projects. It makes debugging much easier for monitoring the port, and it would only become an issue if there was a lot of data to send.

Another bonus, I use serial radio devices to get messages between arduinos, and I can use a serial monitor connected to my laptop and inject messages to make certain things happen. Great for testing.

Also, sending things as binary is not impossible to debug and depending on your tools, you can have the binary extracted and converted to something human readable. Or if you know what you're looking for, you can visually inspect the datastream and recognise values where they should be and fault find that way, albeit, not so easily. ie, you'll recognise the patterns of bytes and recognise the expected values

\$\endgroup\$
2
\$\begingroup\$

Instead of Modbus consider HDLC. You get error detection (which is important on noisy serial lines). Synchronisation is robust, escaping is robust.

I have used HDLC in RS-485 networks with no problems and PPP uses it as well.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Would be nice if you pointed out why you suggest it over Modbus. \$\endgroup\$ Commented Nov 20, 2015 at 8:11
1
\$\begingroup\$

ASCII over the UART is the most popular partly because:

  • It's human readable when debugging (I have yet to see a logic analyzer that doesn't decode ASCII).

  • It's very easy to implement, you've got an ASCII table via quick google that is well standardized.

  • It has built in synchronization with the start/stop bits.

  • Pretty much the entire hobbyst world has set itself up with ASCII over serial, so any new methods will have to deal with that, and that's not easy by any means.

Then you get into a situation when you start to send specific encoding, such as sending the in memory representation of a float compared to converting a float to ASCII, send that over serial which can be way more than 4 bytes, and then convert that back to an in memory representation on the host. Instead, you just send the 4 byte representation every time. Sure, you can start handling the encoding yourself, but then you need to set up start/end tags, order, etc.

Instead, things like Protobuf can be used. This was actually used in a project I was working on and it was extremely beneficial, it does variable length messages, handles endian for you, and a few other cool features. It's also not that large in code size, and you can specify everything to be statically allocated upon startup. You would have to throw in checksum yourself though, if you need it.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.