Question

Problem:

I have an 6 DoF accelerometer that I can access through SDA/SCL via an ArduinoUNO. I can read inputs through the commands like:

LreadAccX()

There are 2 registers for the sensor, a LOW and HIGH value. The above would read the lower register's X acceleration. These are 8 bit numbers. For example, the above might return:

LreadAccX()
>>>> 01000010

41 in decimal, by the way. I need to get these out as fast as possible, we'd like 1kHz to 400Hz if possible. That means just spitting out binary data and then post processing it.

Example:

0000 0001  1100 1001

This may be a value for the X acceleration. In decimal it says: 457. That's 3 different ASCII chars that I have to log, not 2 as in binary.

1111 0001  1100 1001

This is 61897, so 5 ASCII chars, vs. just the 2 binary ones. Obviously, I want to use binary to optimize for speed.

My Solution

void loop() {
  /* 
  print data to processing. Data broken up into 2 parts, one for the high and low
  registers in the sensor.
  */
  print_bytes(HreadAccX(),LreadAccX(),HreadAccY(),LreadAccY(),HreadAccZ(),LreadAccZ());
              print_bytes(HreadGyroX(),LreadGyroX(),HreadGyroY(),LreadGyroY(),HreadGyroZ(),LreadGyroZ());
  Serial.print("A");
}

inline void print_bytes (int HaX, int LaX, int HaY, int LaY, int HaZ, int LaZ)
{
char l = (LaZ >> 48) & 0xff ;
char k = (LaZ >> 44) & 0xff ;
char j = (HaZ >> 40) & 0xff ;
char i = (HaZ >> 36) & 0xff ;
char h = (LaY >> 32) & 0xff ;
char g = (LaY >> 28) & 0xff ;
char f = (HaY >> 24) & 0xff ;
char e = (HaY >> 20) & 0xff ;
char d = (LaX >> 16) & 0xff ;
char c = (LaX >> 12) & 0xff ;
char b = (HaX >> 8) & 0xff ;
char a = HaX & 0xff ;
putchar (l) ;
putchar (k) ;
putchar (j) ;
putchar (i) ;
putchar (h) ;
putchar (g) ;
putchar (f) ;
putchar (e) ;
putchar (d) ;
putchar (c) ;
putchar (b) ;
putchar (a) ;
Serial.print(l);
Serial.print(k);
Serial.print(j);
Serial.print(i);
Serial.print(h);
Serial.print(g);
Serial.print(f);
Serial.print(e);
Serial.print(d);
Serial.print(c);
Serial.print(b);
Serial.print(a);
}

The output is something like:

>>>> asFkDi?g-g^&A
as Fk Di ?g -g ^& A (for clarity, the diff 16 bits have spaces, and the 'A' is
                     to show a start/stop bit)

It's just garbage in ASCII, but it translates to meaningful 16bit numbers

However, this Chewbacca as far as elegance goes. It also runs at about 200Hz, waaay too slow. I feel that it's the

Serial.print();

functions that are slowing it down. However, if I try:

Serial.print(a+b+c+d+e+f+g+h+i+j+k+l);

all I get is the addition of the numbers.

My Question

How do I get the Seria.print() to output just a string of binary numbers from a set of arguments?

Was it helpful?

Solution

You may want Serial.write. Assuming HaX etc are each one byte, you could use something like:

char data_bytes[] =  {HaX, LaX, HaY, LaY, HaZ, LaZ};
Serial.write(data_bytes, sizeof(data_bytes));

To send all those 8-bit values.

Some issues though:

  • With raw binary on serial, it leaves no character codes for indicating the start and end of a frame, so you may have problems with synchronisation. Normally binary serial protocols have a special start of frame character, and special escape character. Whenever one of these appears in the data stream, you need to "escape" it (send a modified 2-byte sequence in place of the original "special" byte) so as not to confuse the receiver.

  • When you have a function call that gives separate high and low values and then combines them, you need to make sure that they are a matching pair. If there is a change in value between the calls, you can get errors.

  • You are more likely limited by the baud rate of the serial connection than in the time executing the serial printing functions. If you are sending a 12 byte sequence at best you will only get 160Hz at 19.2kbps, or 960Hz at 115.2kbps

OTHER TIPS

firt of all Serial.print() convert to char, use Serial.write() to write binary data, as sayd by sj0h.

this

Serial.print(a+b+c+d+e+f+g+h+i+j+k+l);

is just summing ll valiue in one char and discarding the overflow value. also because of sum, aven if no overflow occurred, you can't get your value back.

Another trick is to buffer the data so you will use all bit; for example, normally IMU use 10bit sensor, so you can buffer 4 axes reading, and put them in just 5 byte.

Finally the real trick is to use higest as possible non-standard baudrate, like 460.800, i've tested arduino at around 1.000.000 but tecnically should go as fast as 2.000.000 (check for real value) with a 16MHz clock

Most likely you're running into the serial link speed limit rather than anything to do with your sensor or your code or your printing strategy. Typical serial link speed is 9600 baud (bits per second). In "round numbers" that's only roughly 1000 cps (characters per second). ["Round number" means convenient for back-of-the-envelope calculations in your head, the right order of magnitude, and only one (or maybe two) significant digits.] If each message were for example 5 characters, you could get a maximum of 200 messages per second ...which is very similar to the 200Hz you mention. Also what counts in serial communication is the number of characters that have to go over the serial link to be displayed: a binary 41 will come out as "101000" which counts as six characters, not one. So the rough rule of thumb is to not do binary.

Most likely the baud rate limit will never let you get even anywhere close to the sensor speed limit ...and if somehow you do succeed in getting all the readings over the serial link, they would flip by so fast you couldn't read them anyway. Turning the baud rate way up will only sorta solve your problem (if it works at all- serial communications at hyper speeds is quite sensitive to distance and interference such as from fluorescent lights or parallel extension cords, and may not be sufficiently reliable for you); it still won't get real close to sensor speed, and it will be unreadable.

I suggest you instead dump only every Nth sensor reading rather than all of them. (If you're really concerned about the values of the other readings, you could send an "average" of N readings rather than every single one. If you're really concerned that a sensor reading happened -without caring so much about its exact value- you could use just one bit to indicate "it happened": so for example F would mean 4 readings happened, FF would mean eight readings, 7 would mean 3 readings, 3F would mean 6 readings, and so on.)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top