Arduino & AD7746

July 18, 2009

in Sensors, Tinkering

In my ongoing series about cool sensors Interactive Matter presents the AD7746 capacity sensor:

Arduino & AD7746

What is a capacity sensor good for? You can of course make a device to identify small capacitance (up to 4pF) but that’s boring. Much more interesting is how it reacts to touching.

Read on how to connect it to the Arduino and how to use it to sense touching

Fortunately there is a basic sketch how to use an AD7746 with Arduino. Unfortunately this sketch has one or two awkward structures and inconsistencies. Much more unfortunately it somehow works. So lets clean this thing a bit up.

I2C Basics

The I2C on Arduino uses analog pin 4 as SDA and analog pin 5 as SCL . With the Sparkfun AD7746 breakout board you need some pull up resistors for the I2C lines – So I added some 4.7k resistors to VCC (forgetting the pull up resistors is the most common source for an I2C connection not working – at least for me).

Connecting AD7746 to Arduino

The Sparkfun Ad7746 breakout board comes with two long lines to direct the sensing away from the logic and some ‘touch plate’. As you can see in the above photo you just connect the I2C pins, GND and VCC. Despite the marking ‘3.3V‘ on the breakout board the AD7746 accepts up to 5Volt as input voltage. So no special 3.3V voltage supply is needed.

The software part was a little more tricky. I used the AD7746 Arduino sketch I found and refactored it a bit. First of all I used my convenience extensions to read and write I2C registers conveniently (download the whole sketch – it is far too long to list here). Next we define our ADD7746 registers for convenience reasons:

#include <Wire.h>

//AD7746 definitions
#define I2C_ADDRESS  0x48 //0x90 shift one to the right

#define REGISTER_STATUS 0x00
#define REGISTER_CAP_DATA 0x01
#define REGISTER_VT_DATA 0x04
#define REGISTER_VT_SETUP 0x08


#define CAP_ZERO 0x800000L

Now we can write a little startup routine to initialize the AD7746. It performs a reset (you never know), enables uses channel 1 for measuring capacitance and tries to calibrate the AD7746:

void setup()

  Wire.beginTransmission(I2C_ADDRESS); // start i2c cycle
  Wire.send(RESET_ADDRESS); // reset the device
  Wire.endTransmission(); // ends i2c cycle

  //wait a tad for reboot

  writeRegister(REGISTER_EXC_SETUP, _BV(3) | _BV(1) | _BV(0)); // EXC source A

  writeRegister(REGISTER_CAP_SETUP,_BV(7)); // cap setup reg - cap enabled

  Serial.println("Getting offset");
  offset = ((unsigned long)readInteger(REGISTER_CAP_OFFSET)) << 8;
  Serial.print("Factory offset: ");

  writeRegister(0x0A, _BV(7) | _BV(6) | _BV(5) | _BV(4) | _BV(3) | _BV(2) | _BV(0));  // set configuration to calib. mode, slow sample

  //wait for calibration

  Serial.print("Calibrated offset: ");
  offset = ((unsigned long)readInteger(REGISTER_CAP_OFFSET)) << 8;


The calibration is a bit more tricky, later more on that. First we have to get an readout of the sensor:

long readValue() {
 long ret = 0;
 uint8_t data[3];

 char status = 0;
 //wait until a conversion is done
 while (!(status & (_BV(0) | _BV(2)))) {
 status= readRegister(REGISTER_STATUS);

 unsigned long value =  readLong(REGISTER_CAP_DATA);

 value >>=8;
 //we have read one byte to much, now we have to get rid of it
 ret =  value;

 return ret;

It waits until an measurement has finished an reads the data value from the data register. The read routine returns a 32 bit long value. We just need the first three bytes, so we shift it one byte to the left. The datasheet specifies 0x800000 as null point, so we should subtract this value from readout. This was omitted here, since the datasheet also say that the value in REGISTER_CAP_OFFSET contains the real zero point after an calibration. So we just give back the value of REGISTER_CAP_OFFSET as offset. The content of REGISTER_CAP_OFFSET can be used in the external program logic (later more on that).

The AD7746 is not very nice to read from. The datasheet says, that you can write an address and then read as many bytes from the chip as you like. But not if you sent a STOP after you have written the address. But this is exactly what the Wire library does. So the readout routine (e.g. for Long values) looks a bit nasty since the address pointer is set back to 0:

unsigned long readLong(unsigned char r) {
  union {
    char data[4];
    unsigned long value;

  byteMappedLong.value = 0L;

  Wire.beginTransmission(I2C_ADDRESS); // begin read cycle
  Wire.send(0); //pointer to first data register
  Wire.endTransmission(); // end cycle
  //the data pointer is reset anyway - so read from 0 on

  Wire.requestFrom(I2C_ADDRESS,r+4); // reads 2 bytes plus all bytes before the register

    while (!Wire.available()==r+4) {
      ; //wait
  for (int i=r+3; i>=0; i--) {
    uint8_t c = Wire.receive();
    if (i<4) {[i]= c;

  return byteMappedLong.value;


Now that we are getting some real values we can try to calibrate the chip. It comes factory calibrated, which is reset after each reset. First of all we try to perform an built in calibration:

void calibrate() {
  calibration = 0;

  Serial.println("Calibrating CapDAC A");

  long value = readValue();

  while (value>VALUE_UPPER_BOUND && calibration < 128) {
    writeRegister(REGISTER_CAP_DAC_A, _BV(7) | calibration);
    value = readValue();

After this i done we just switch over to continuous read mode, read out a value, print it with all calibration settings to the serial port:

  writeRegister(REGISTER_CAP_SETUP,_BV(7)); // cap setup reg - cap enabled

  writeRegister(REGISTER_EXC_SETUP, _BV(3)); // EXC source A

  writeRegister(REGISTER_CONFIGURATION, _BV(7) | _BV(6) | _BV(5) | _BV(4) | _BV(3) | _BV(0)); // continuous mode
void loop() // main program begins


  long value = readValue();

  if ((valueVALUE_UPPER_BOUND)) {
  if (outOfRangeCount>MAX_OUT_OF_RANGE_COUNT) {
    if (value < VALUE_LOWER_BOUND) {
    else {


The whole sketch contains a bit more code and can be much better understood if you got all the code – but that would be too much code for this post. So download the whole Arduino AD7746 Sketch and try it yourself.

Displaying the results

The results are displayed with an simple processing sketch, mimicking some kind of cheapo oscilloscope. It reads the values from the serial port and displays them. Anybody interested in the details of the sketch can download the whole AD7746 processing sketch.

The formula used to calculate the real capacitance is derrived by th datasheet:

capacitance = (value-calibration) * 2.44e-07-capdac*0.164;

I do not know if it completely perfect, but it gives some useful values, interestingly always smaller than 0 – despite the fact that the sensor should be calibrated to 0. For getting absolute capacitance readouts I suggest quadruple checking this to the datasheet and trying to optimize the adjustment of the sensor.

Leaving the sensor alone leads to some graphics like this:

AD7746 Screenshot 1

As you can see we there is some noise present, we could filter out, but for now we live with it. It is not very strong. Especially if you compare it with the next graph.

If you tap on the sensing wires it gets a very strong readout – just by touching the two fine lines on the board:

AD7746 Screenshot 4

This gives a much stronger readout (you can still see the noise – but that is very easy to distinct from the ‘signal’). This should be very easy to detect. If you look closer you will see that the readout does not really get back to the values just before touching the wires – It will go down slowly. So for detecting a signal it should be fairly easy to just compare the difference between two readouts and if it is bigger than some threshold it will interpreted as a ‘touch’.

If you use the touch pad, which comes with the AD7746 breakout board and touch the bare metal you get readout which is extremely strong – but that seems impractical because you would probably never you would never expose such big pads on your design, without some kind of coating.

AD7746 Screenshot 5

But if you lay a cover of paper over the sensor you get still an extremely sharp signal:

AD7746 Screenshot 6

The Bottom Line

In the end you see that the AD7746 is very interesting for creating some kind of hidden input elements. But it is some dificult part. The read routines are not really compatible with the wire library. Combing all the different possibilities to adjust the sensor and the complexity of calculationg the real value (the datasheet is a bit hard to understand on that topic) makes it very difficutl to tune the sensor optimally. But I hope that this material gives enough hints to use this sensor for a project. But nevertheless there are perhaps easier capacitive input sensors out there.

You can find the full source code over at github or directly download it from github.

{ 38 comments… read them below or add one }

Leave a Comment

{ 1 trackback }