3

I'm sampling at high frequencies and need to transmit the 10-bit ADC value via UART out of my Arduino.

By default, it uses a byte per character. So if doing an analogRead would yield the value of "612", it would send via UART "6" as one byte, "1" as one byte, "2" as one byte, and the line terminator as the last byte.

Given that my sampling rate is truncated by this communication, it's important that it's as efficient and uniform as possible, so I'm trying to force it to use two bytes to transmit that data, doesn't matter what the data actual is (by default it would use three bytes to transmit "23", four bytes to transmit "883" and five bytes to transmit "1001").

Currently, I'm doing something like this, which is the best way I've found:

int a = 600; //Just an example
char high = (char)highByte(a);
char low = (char)lowByte(a);
Serial.print(high);
Serial.println(low);

Currently this uses three bytes (including \n) regardless of the value. Is there an even more efficient method?

Just printing it with something like

Serial.print(foo, BIN);

Doesn't work at all. It actually uses one byte per every single bit of the binary representation of foo, which is quite silly.

2
  • 1
    Not to send the newline at all? If you know that the data is always a pair of bytes, and don't worry about dropping a byte (you can have synchronization bytes spread in the stream more seldom), then there is no reason to use a newline. Besides, if you send the data as binary, what will happen if one of those bytes happen to be 10 (the same as ASCII newline)? Commented Oct 10, 2013 at 10:59
  • 1
    Serial.write() is the way to handle binary data. The Serial.print() functions are designed to convert the value into a human readable text. Commented Oct 10, 2013 at 14:11

2 Answers 2

11

I might be missing something, but why don't you use Serial.write(byte)?

You can use a methode like this one:

void writeIntAsBinary(int value){
    Serial.write(lowByte(value));
    Serial.write(highByte(value));
}

what are you planning to do with the data on your computer?

Sign up to request clarification or add additional context in comments.

Comments

8

If you're sending binary data over a serial line, you really shouldn't confuse everything by using a text-style linefeed separator.

On the other hand, it's kind of hard (for the other end) to know which byte is which, without some kind of synchronization help.

But, since you only have 10 bits of payload, but send 16 bits of data, you can "do a UTF-8" and use a free bit to signal "start of value". This will require using only 7 bits of each 8-bit byte for your payload, but that's fine since 7 + 7 = 14 which is way more than 10. We can let the 8th bit mean "this is the high byte of a new pair of bytes":

const int a = 600;
const unsigned char high = ((a >> 7) & 0x7f) | 0x80;
const unsigned char low  = (a & 0x7f);

Serial.print(high);
Serial.print(low);

In the above, the two bytes transmitted will be:

high == ((600 >> 7) & 0x7f) | 0x80 == 4 | 0x80 == 0x84
low  == (600 & 0x7f)               == 88       == 0x58

The receiver will have to do the above in reverse:

const int value = ((high & 0x7f) << 7) | low;

This should work, and uses the most-significant bit of the high byte, which is sent first, to signify that that is indeed the high byte. The low byte will never have the MSB set.

3 Comments

Except that this won't work if low byte also has MSB set. You can only transmit 7 bits on low byte.
I think it should be high = ((a >> 7) & 0xff) | 0x80; low = (a & 0x7f);
@user694733 Thanks! I didn't quite think this through enough, I edited.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.