Serial Communication – CHR UM6-LT Inertial Sensor

A project I recently took part in required the use of an Inertial Measurement Unit (IMU), CH Robotics’ UM6-LT Inertial Sensor. There are two ways to acquire data from it: poll it for the desired data or simply collect data as the sensor broadcasts it. The IMU communicates via UART, with 3.3V TTL.

If you’re reading this then you probably already have the sensor, or are considering acquiring it. For those of you who don’t, this particular unit has three sensors on it:

  1. Magnetometer
  2. Rate gyroscope
  3. Accelerometer

The datasheet is very good and provides almost enough information to write up a program for it without a hitch, save for one thing: it doesn’t explain how to use the checksum to ensure that your data is good. The checksum (the last two bytes sent from a packet) is a sum of all the bytes sent from the IMU. To use it, you sum the bytes into an unsigned variable, then compare it to the checksum appended at the end of the packet you received. If they don’t match then either the data from the IMU is bad or there’s a flaw in your program.

I would like to leave off with the a version of the program written for the Arduino platform. I really have to take this time to acknowledge Caleb from CH Robotics for answering my pesky and persistent questions over email, regarding the functionality of the IMU. Those guys at CHR have quality customer service.

Before I show you the code, I would like to apologize for the fact that WordPress doesn’t allow for easy indentation. The result is a somewhat ugly coding style.


//AUTHOR: Jaz S
//Code for Arduino Uno to retrieve data from CH Robotics IMU
//Bytes sent from IMU is recieved in the following order (1st to last): B3, B2, B1, B0.

#include <SoftwareSerial.h>

#define Tx    3
#define Rx    2
#define BAUD  57600

#define REG_GYRO_PROC_XY   0x5C
#define REG_GYRO_PROC_Z    0x5D

#define GYRO_SCALE_FACTOR  0.0109863    // Convert register data to Degrees per second

/*PT byte we're sending out... */
#define PT_HAS_DATA        0x80  //10000000
#define PT_IS_BATCH        0x40  //01000000
#define PT_BATCH_LEN       0x08  //Batch length = 1

SoftwareSerial UM6Data(Rx, Tx); //pin2 is Rx; pin3 is Tx

struct IMU{
int in;
}phi, theta, psi; //Roll, pitch and yaw, respectively.

void setup(){
Serial.begin(BAUD);   //Sets baud rate for communication with PC.
UM6Data.begin(BAUD);
}

void loop(){

POLL_AGAIN:
poll_UM6_gyro();
read_UM6_gyro();  //Needs to be called before being used in the 'if' statement.

if (read_UM6_gyro() == true)  {
phi.in = phi.in*GYRO_SCALE_FACTOR;
theta.in = theta.in*GYRO_SCALE_FACTOR;
psi.in = psi.in*GYRO_SCALE_FACTOR;
}
else  {
goto POLL_AGAIN;
}

}//end VOID

void poll_UM6_gyro(){  //This void function makes a request for a packet from the IMU
Serial.print("\nPolling the UM6...\n");
byte chksum0 = 0, chksum1 = 0;
unsigned int chksum = 0;

chksum = 's' + 'n' + 'p' + (PT_IS_BATCH | PT_BATCH_LEN) + REG_GYRO_PROC_XY;
chksum1 = chksum >> 8;
chksum0 = chksum & 0xFF;

UM6Data.write('s');
UM6Data.write('n');
UM6Data.write('p');
UM6Data.write(PT_IS_BATCH | PT_BATCH_LEN);
UM6Data.write(REG_GYRO_PROC_XY);
UM6Data.write(chksum1);
UM6Data.write(chksum0);
}  //end poll_UM6_gyro

boolean read_UM6_gyro(){
Serial.print("Attempting to read IMU...\n");
phi.in = 0;
theta.in = 0;
psi.in = 0;

unsigned int c = 0;
unsigned int data[5] = {0};
unsigned long data_sum = 0;
byte blank = 0, temp = 0;
byte chksum1 = 0, chksum0 = 0;
unsigned int chksum = 0;

//If there's data in the serial temp register and we haven't finished retrieving data...
if ((UM6Data.available() > 0)){
c = UM6Data.read();
if ((c == 's')){
c = UM6Data.read();
if ((c == 'n')){
c = UM6Data.read();
if ((c == 'p')) {
c = UM6Data.read();
if (c == (PT_HAS_DATA | PT_IS_BATCH | PT_BATCH_LEN)){
c = UM6Data.read();
if (c == REG_GYRO_PROC_XY)  {
for (byte i = 0; i < 6; i++)  {
data[i] = UM6Data.read();
data_sum += data[i];
}
blank = UM6Data.read();
blank = UM6Data.read();
chksum1 = UM6Data.read();
chksum0 = UM6Data.read();
chksum = (chksum1 << 8) | chksum0;
if (chksum == ('s' + 'n' + 'p' + (PT_HAS_DATA | PT_IS_BATCH | PT_BATCH_LEN) + REG_GYRO_PROC_XY + phi.in + theta.in + psi.in)){
Serial.println("Checksum is good. Returning true!");
phi.in = (data[1] | (data[0] << 8));
theta.in = (data[3] | (data[2] << 8));
psi.in = (data[5] | (data[4] << 8));
return true;
}
else {
Serial.println("Flushing buffer...!");
FLUSH:
while ((UM6Data.available() > 0))  {
blank = UM6Data.read();
}
return false;
}
}
else  {
goto FLUSH;
}
}
else  {
goto FLUSH;
}
}
else  {
goto FLUSH;
}
}
else  {
goto FLUSH;
}
}
else {
goto FLUSH;
}
}//end if
else if (UM6Data.available() == 0)  {  //Requested data never arrived...
Serial.println("Data never arrived....");
return false;
}  //end else
else  {
Serial.println("Unexpected error.");
return false;
}
}  //end read_UM6_gyro

Posted in Uncategorized | 8 Comments

Serial Communication in C++ in the BeagleBone’s Linux environment, with an XBee example

EDIT: I’ve moved the files to a Git repository, and included the function that initializes all UARTs that should be available to the user. Also, this uses kernels BEFORE v3.8. That means the UARTs are initialized in the omap_mux directory, which has been subsequently removed in the newer kernel versions.

Programming a BeagleBone for serial communication is easy in C++, but may initially be a little confusing if you’re not familiar with the requirements. In Linux, serial communication is done through ports labeled with the prefix “TTY”, and in the BeagleBone it will be no different. There are six  ports: ttyO0 through ttyO5. Ports 1, 2, 4, and 5 can be programmed by the user. If you’re using C/C++, you will need several particular header files to do so. Please take note that I have not been able to configure the communication ports for bi-directional use, only for one direction.

Rather than go step-by-step on how to go about writing your own serial comm. program, I’ll just a mention couple things: the first is to define exactly which header files you’ll need and how they can help you; the second is to provide a list of resources that helped me get started. After I go over the steps necessary for successful serial communication, I’ll leave you with an example on sending/receiving data using an XBee transceiver. WIthout further ado…

1.  To write your own serial communication program, there are three header files that you will need: fcntl.h, termios.h, unistd.h. Each has their own set of functions that, when put together, allow for the programmer to develop a program for serial communication. These three header files are available in the arm-linux-gnueabi toolchain.

    • <unistd.h> – According to Wikibooks, this file allows the developer to access the functions that open, close, read from, and write to a particular port. They are, respectively, open(), close(), read(), write().
    • <fcntl.h> – Allows the user to perform a variety of operations on open files. I’m not too familiar with it, nor do I use any of its operations.
    • <termios.h> – This is used to configure various communication parameters on a file.

2.  Now the list:

  • Bit of a lengthy read, you don’t have to go through all of it: Serial HOWTO. Sections 3 and 4 provide a good review on communicating over UART and Linux serial ports.
  • Serial HOWTO’s examples. For simple serial communication, refer to Section 3.2 – Non-Canonical Input Processing.
  • Wikibooks’ examples. I would read this entire page, it’s very concise yet thorough. Again, for serial programming, look at the Non-Canonical example.

I’ll end this with some example files. This program I whipped-up enables one-way communication, sending data to an XBee transceiver through one UART, and receiving data through another. If anyone gets it going two ways, I’d sure like to know how. I don’t like wasting a whole port for just one direction. The files can be accessed in this repository: https://bitbucket.org/el_chino/beaglebone/src/a74f4b4500d5?at=master.

Posted in Uncategorized | 1 Comment

Beaglebone – Adding the missing UARTs

At the time of this writing, the BeagleBone is advertised to have 6 UARTs, 4 of which are supposed to be available to the prospective developer, but only two of which actually are (I am still quite a bit upset over this). In a project I recently worked on, I had to fix UARTs 4 & 5 so that I could use them. The information on doing this is scattered and sketchy. Since I’ve been able to successfully do it, I’ll fill you in on what is required. Please keep in mind that this is best done on a Linux OS (preferably Ubuntu), and that I’m assuming you have the gcc toolchain (arm-linux-gnueabi) installed, and that you possess a general understanding on how to program the BeagleBone. If not, please refer to Dr. Molloy’s videos, linked in the previous post.

To successfully add the missing UARTs (#4 and #5), you will need to…

  • learn how to use Git, and download some necessary files.
  • build a Linux kernel.
  • tweak a file to activate the missing UARTs.
  • rebuild the kernel to include your changes.
  • deploy the new kernel onto your BeagleBone.

Onward…

  1. You’re going to need to know how to use Git. According to Wikipedia,

    Git is a distributed revision control and source code management (SCM) system with an emphasis on speed.

    Simply put, it’s Dropbox for programmers, except you can use a number of different sites to store your source code. For example my friends and I use BitBucket, while others use GitHub. Instructions on installing and using Git can be found here: http://www.vogella.com/articles/Git/article.html. I highly suggest you take your time to go through it. You will need to set up and account, store user settings like your username and your password, and make a folder in which to store downloaded files.

  2. This post by Mickey F. explains, in simple terms, how to download the files from GitHub and build a new kernel. For a proper demonstration on building a kernel, watch this video by Dr. Molloy. He goes through great lengths to guide fellow users through the process. *Note: system.sh.sample undergoes periodic revisions. Here’s a small screen cap of mine:     system sh beforeWhere I have highlighted the area, uncomment the line by deleting the ‘#’, like this: system sh after
  3. After you’re done editing “system.sh.sample”, and have saved it as “system.sh”, type “./build_kernel.sh” in the terminal. *Note: keep in mind that when you try to build the kernel, you may see a message in you terminal window asking you to install some dependencies. Simply install ’em, just as Dr. Molloy does. You may do so by simply by typing “sudo apt-get install ” in Ubuntu, “opkg install” in Angstrom, or whatever your Linux distro requires. After typing “./build_kernel.sh”, find something else to do for a while; the process is going to take some time. Eventually, the Kernel Configuration Tool will start in the window, and you can change some of your desired features of your BeagleBone. For the time being, just exit out of it and continue the kernel building process. Once it finishes, you will find some new folders available in /linux-dev.
  4. In the new folder, /linux-dev, browse to and open the file /linux-dev/KERNEL/arch/arm/
    mach-omap2/mux33xx.c. Take a quick look at the BeagleBone System Reference Manual (SRM), at the P8 Mux Options and P9 Mux Options tables. They will show you under which file your UARTs are supposed to be located. Now look at mux33xx.c, specifically here: uart4 beforeand here: uart5 beforeI’ve highlighted where the UARTs are supposed to be. Notice that where the SRM shows “uart4_tx_mux[1]” and “uart4_rx_mux[1]”, for example, you see “NULL” in their place instead. Let’s change that to look like the following: uart4 afteruart5 afterSave your changes.
  5. Rebuild the kernel by typing “./tools/rebuild.sh”.
  6. You’ll see the Kernel Configuration tool again. Do what you want with it, then exit when you’re done.
  7. Deploy the kernel to the BeagleBone by typing “./install_image.sh”. Again, as noted by Dr. Molloy, there may be more dependencies. Just install them.

After this is all complete, you should find that UARTs 4 and 5 are in perfect working condition, and all it took was a simple change of text. I tested mine with both a GPS module and an XBee transceiver. It really makes me wonder why the BeagleBone devs couldn’t just do this themselves. In retrospect, the process of making the appropriate changes was surprisingly easy, but I’m still unsure of why all it takes is a simple replacement of NULL statements with some string text.

Posted in Uncategorized | 1 Comment