muCSense: Introduction and Example

In my last post I walked through the steps you need to take to establish a simple connection with the three sensors on SparkFun’s 9DoF Sensor Stick. If you took those code snippets, dumped them into one file, and ran it on your Arduino

you’d have a big mess.

It would work, but it would be awful to read, maintain, or upgrade.  And what if you wanted to put some real logic in it?  Maybe something that does more than dump raw readings to Serial?

And what happens when we get other sensors?  We will usually be doing the same things:

  1. Define device-specific constants, likely lifted from the datasheet.
  2. Initialize.
  3. Read data.
  4. Calibrate.
  5. Maybe tweak some device specific parameters, listen for interrupts, etc.

Everything but item 5 can be pretty well abstracted away, and we can encapsulate all of the device specific mess in classes that won’t clutter our code or namespaces.  Doing this will leave us with code that is more robust, easier to use correctly, and easier to maintain.

That’s why I am writing muCSense.

muCSense Overview

The purpose of muCSense is to provide a framework that abstracts basic operations of initializing, accessing, and calibrating sensors while allowing enough flexibility to allow clients full control over the specific features of each sensor.  Along with the framework, the library provides implementation classes for a variety of common sensors.

That’s the goal, anyway.  I’m still working on the calibration part of the library, and I will write about that — and about some of the cool algorithms behind it — once I get the code online.  As of today, I have checked in a basic sensor access framework along with implementation classes for all of the sensors on the 9DoF Sensor Stick.

Example Code

Let me give you a quick taste of how this library changes your code.  If you haven’t looked at the code I already posted for the 9DoF Sensor Stick, open a new tab and have a look now.

Now let’s look at a sketch that will produce the same output (modulo startup messages) using muCSense.  Start with a few needed #includes.

#include <wire.h>
#include <adxl345.h>
#include <hmc5843.h>
#include <ITG3200.h>

Next we’ll declare a few pointers for implemented sensors that we’ll construct on the heap. We’ll also keep an array of Sensor objects and an array of names that helps us print things later.

//Pointers to implemented sensors that we'll create on the heap.
ADXL345* pAccel;
HMC5843* pMag;
ITG3200* pGyro;
Sensor* sensors[3];
char* names[] = {"ACCEL: ", "MAG: ", "GYRO: "};

The setup routine is simple: we use factory methods to fetch the single instances of each of our sensors. It is possible that we have more than one of these sensors connected. In that case, you just need to pass the I2C bus address of the sensor you want as an argument to the ::instance() method.

Once they are created, we put the Sensor objects into an array and loop through to initialize them.

void setup() {
 Serial.begin(115200); //Default for BlueSMiRF
 Wire.begin();

 //Call factory methods to get single instances of each sensor.
 // To get a sensor at a specific I2C bus address, pass the 
 // address in to the factory method. 
 pAccel = ADXL345::instance();
 pMag = HMC5843::instance();
 pGyro = ITG3200::instance();

 //Put the sensors into the array. We could have
 //constructed them here from the start, but sometimes
 //it is nice to have access to the derived class pointers.
 sensors[0] = pAccel;
 sensors[1] = pMag;
 sensors[2] = pGyro;

 //All of our objects implement the Sensor interface,
 //so we can work with them uniformly. Here we initialize.
 Serial.println("Initializing sensors");
 for(size_t i=0; i < 3 ; ++i) {
     sensors[i]->init();
 }

 Serial.println("Sensors ready");

}

While running, we can loop through the sensors and tell each one to fetch a new reading, then loop through them again to see what the reading was. Why separate the steps like that? There are many times I want to have many clients listening to one sensor, and I want to them to all get the same data. In the calibration part of the library (not checked in yet!) I do this with an Observer pattern, where a DataCollector object tells the sensor to read(), then notifies all of the listeners to come get their data.

void loop() {

 //Now we read the sensors. This call actually triggers 
 // I2C communication (or whatever other communication is needed)
 // with the device and stores the readings internally.
 for(size_t i=0; i < 3 ; ++i) {
 sensors[i]->read();
 }

//Now that the sensors have been read, we can ask the sensors what
// the readings were. This DOES NOT trigger communication with the
// sensor and the rawReading method is const. This allows multiple
// clients to request the rawReading and they will all get the
// same result -- a feature that can be very important.
 const int16_t* buf = 0;

 for(size_t i=0; i < 3 ; ++i) {
     buf = sensors[i]->rawReading();
     Serial.print(names[i]);
     printInt16Array(buf,sensors[i]->dim());
 }

 Serial.println();

 delay(100);
}

Finally, here is a little helper function I used above (that really should be a template).

void printInt16Array(const int16_t* buf, size_t len) {
  size_t i;
  for(i=0;i

Yes, it is still a bit long but notice that most of it is whitespace and comments.  There are no device-specific #defines. You can create, initialize, and start reading data from a sensor in four lines of code.  If you have a bunch of sensors — like we do on the Sensor Stick — then you can put them in an array and loop through them using a standard interface.

Getting Started: Installing and Using muCSense

To make the sketch above work, you need to download and install muCSense.  To do that:

  1. Download and install Arduino 1.0.1.  I don’t know if it works on earlier versions, I’ll try to keep it working on later versions.
  2. In your Arduino directory, create a directory called “libraries” if it isn’t already there.
  3. Download the code as a zipfile from the GitHub repository and extract the files into the “libraries” directory you created in Step 2.  It will extract into a folder called something like “rolfeschmidt-muCSense-9b13d30” in your libraries directory.  Feel free to change the name.
  4. Now you can either put a sketch together from the snippets above, or grab one I already put together here.  You should be up and running in no time.  Enjoy!

Warning: This is Version 0.0

I’ve been using this for my own projects for a week or two, but it is far from complete and needs thorough testing.  So caveat downloador.

Some particular issues that currently need to be addressed:

  1. Need a unit testing framework and a set of tests.
  2. Could use an implementation class for ADXL335 (connects through analog pins).
  3. Could use implementation class for simple potentiometers — I particularly want to use flex sensors.
  4. A “keywords” file would be nice.
  5. There is no wiki or documentation.
  6. There is nothing in there for calibration.
  7. EEPROM storage doesn’t work.

Right now (August 2012), I’m working hard on item 6, making a calibration library, and hope to have something online within a week. If anyone wants to join in and help me get the other parts done, that would be great!

(UPDATE:  I originally had named this library “SensorLib” — very creative, I know! — and found there were already a few SensorLibs on the internet.  So I changed it to muCSense, for “micro-controller sensor”.  It will take me just a little while to purge everything of the name SensorLib, but I’m working on it.)

Advertisements
This entry was posted in Electronics, How To and tagged , , , , , , , . Bookmark the permalink.

5 Responses to muCSense: Introduction and Example

  1. Pingback: Connecting to Sparkfun’s 9DOF “Sensor Stick”: I2C access to ADXL345, ITG-3200, and HMC5843 | Chionotech

  2. Pingback: SensorLib: Using Calibration | Chionotech

  3. ItsLeeOwen says:

    I highly admire your work Rolfe! Google searches keep leading me to your posts, along with Aaron Berk’s ( http://mbed.org/users/aberk/ ). Both are absolutely fantastic resources.

  4. Rabindra Khadka says:

    It keeps printing
    0 0 0
    0 0 0
    0 0 0 .
    I didn’t use soldering. Would that be the issue? That is the only thing I am doing different than you. Any suggestions?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s