Skip navigation

You know your an Engineer when this combination of ICONS means something exciting

texas instruments.jpgArduino_logo_pantone.pngelement14logo_highres.jpeg

So a few of you may already have seen the video and post regarding using the DAC8734 from TI with the Raspberry PI so you will know what this is all about, for those that have not, you can see it here: Raspberry PI 2.0, Windows 10 and how to drive the DAC8734 from TI (AKA the DAC8734EVM Road test Board)

For those whome have not and or are interested in Interfacing the Arduino to this wonderful chip, lets begin


OK, so it’s been a busy week learning, researching and creating sample content for the Arduino, Launchpads and Raspberry PI, to drive the DAC8734 Quad 16bit DAc from Texas Instruments. you can find the others in seperate posts in this community


I have been learning about this chip for a few weeks and when I realized it was going to be part of a Road test I decided to clean up some of my test code and make a blog out of it to help others and in particular the road testers. this task was made more difficult because of the seeming lack of information outside of the actual datasheet for the chip which can be found here;


I also want to be clear The Road test is about the   I don’t have the  DAC8734EVMDAC8734EVM to hand but I do have a DAC8734 Chip and mounted one on a breakout board and created my circuit on a breadboard but there is nothing I am aware of that will prevent any of my software attached from working on the EVM without change

Here is my breadboard version and a view of some of the other end of the wires going to the Arduino UNO


This is the EVM module from the Road test, even though the chip is on an evaluation PCB with two VREFS and a bunch of switches, there should be no difference in operation of the software


if your wiring to this then you will need to connect the SPI lines and power to the J2A pins (Right hand side of the EVM)

If you jumper J2.2 to J2.8 and J2.6 to J2.12 then after the SPI and power is connected, the board will work with my software in the same way.


The DAC8734 stands out from many other DACs in that is can output upto +20V in unipolar mode and +10 to - 10 in Bipolar mode, this coupled with the 16Bit resolution and less than 1 bit error after calibration makes this DAC an Ideal candidate for my Power Supply project,

Here is a complete list of its features

DAC8734 Features.png


The dac consists of 4 analog outputs, a couple of GPIO pins, VREF inputs and the usual set of communications and power pins, here is a block diagram and a mode detailed drawing of one channel


One really cool thing to note here that is also fairly unique among DACs is the Zero and Gain registers, these allow the engineer to actually tune the zero offset of each individual DAC channel and also change its gain. this permits the engineer to not have to worry about software calibration that involves adjusting the actual Binary value being output to the DAC data registers, instead they can leverage the complete range of values (65535) for the best resolution. By making the jumper connections I listed above allows the engineer to also control the mode of operation for group A and B between Bipolar and Unipolar operation, rather then using the onboard switches or hard wiring it


To say there were not any issues that I still have not resolved would be wrong. This is not a critical issue but on the PI and according to the DAC8734 PDF, the chip is supposed to work in SPI Clocking MODE1 where the data is changed on the rising edge of the clock and then latched into the DAC on the falling edge and this works perfectly on the PI (Once I figured that out )


when I initially coded the cleaned up Arduino sketch, I set it to the same mode and it did not work. It was working before i tried to set any mode and simply accept the default, and I wrongly assumed that it was operating in Mode 1... wrong

Here is the timeing diagram from the PDF

DAC8734 Timing.png

the trace from the working RPI code

DAC1 AAAA mode 1.png

and this is what I had to use to get the Arduino working... just weird as the PDF has no mention of being able to reliably work this way... im still investigating


If anyone has any ideas, please let me know and I will try it out, for now be aware that you may also need to operate the DAC in this mode, even though it seems to be wrong (Yes I did adjust the Oscilloscope triggering correctly.. I thought of that )


For accuracy I was impressed but I have no means currently to measure the full accuracy of this DAC but I have my name in the hat too for the road test so we will see. For now I will simply say that i was able to tune it to within 2 digits on my 4.5Digit DMMs but the DAC is way better than that so I wont show measurements here as they will serve no point. I do have operational test measurements shown in the PI video and I will be creating a video for this DAC too, its just not ready yet


So lets look at the software. First let me say, I have tried to make it compatable with both the Arduino and the Launchpads and so far I have succeeded. there is an assumption that in all cases, where a board has a SS (Slave Select) defined, I have used it. this is all of the boards tested so far including the Arduino so here is the pinout I used for the Arduino

ArduinoUno_R3_Front.jpgbasically 10 is Chip select, 11 is MOSI (Master Out, Slave In), 12 is MISO (Master In, Slave Out) and 13 is the clock. this is all in hardware on the Arduino UNO with the exception of the SS pin that you still need to cotrol seperatly



OK, the code will be attached to this post as a complete sketch but below I will break it down as I go through its operation


The first section upto the area identified as // DAC8734 code starts here is standard stuff to do with reading the serial port, parsing the commands and a few buffers, all previously described in my earlier videos and posts


the part following is where we start into the DAC code



// DAC8734 code starts here

// PICK THE RIGHT chip select for the right board

#define DAC_8734_CS_PIN  SS  // All the boards tested so far has this pin defined and it is in a consistent place on the expansion headers

#define VREF 5.000
// Default values below assume 0-10V Unipolar or -5 to +5 bipolar output
#define DAC_VFSR VREF*2 // may be 0 - 10 or -5 to +5
#define DAC_8734_Command 0 // command register
#define DAC_8734_DataBase 0x04 // register offset to zero cal base register
#define DAC_8734_CalZeroBase 0x08 // register offset to zero cal base register
#define DAC_8734_CalGainBase 0x0C // register offset to gain cal base register
#define DACMAX 0xFFFF
#define DACMIN 0x0000

// default setup of DAC8734, these can be adjusted if needed, some functions also change them to change mode etc.
int DAC_Gain0 = 0; // 0 = *2, 1 = *4
int DAC_Gain1 = 0; // 0 = *2, 1 = *4
int DAC_Gain2 = 0; // 0 = *2, 1 = *4
int DAC_Gain3 = 0; // 0 = *2, 1 = *4
int DAC_GPIO0 = 1; // 1 = Group A in Unipolar 0=Bipolar (External connection to control pin)
int DAC_GPIO1 = 1; // 1 = Group B in Unipolar 0=Bipolar (External connection to control pin)
int DAC_PD_A = 0;  // 1 = group A power down
int DAC_PD_B = 0;  // 1 = group B power down
int DAC_DSDO = 0;  // 1 = Disable SDO bit.

// set up the speed, data order and data mode
// See for more details

#ifdef Arduino
// not available on energia yet
SPISettings settingsDAC8734(5000000, MSBFIRST, SPI_MODE0);

// Sine table, used to create a reasonable sinewave on a dac output, 256 16bit values, one complete cycle
byte val = 0;
unsigned int Sin_tab[256] = {  32768, 33572, 34376, 35178, 35980, 36779, 37576, 38370, 39161, 39947, 40730, 41507, 42280,
                               43046, 43807, 44561, 45307, 46047, 46778, 47500, 48214, 48919, 49614, 50298, 50972, 51636,
                               52287, 52927, 53555, 54171, 54773, 55362, 55938, 56499, 57047, 57579, 58097, 58600, 59087,
                               59558, 60013, 60451, 60873, 61278, 61666, 62036, 62389, 62724, 63041, 63339, 63620, 63881,
                               64124, 64348, 64553, 64739, 64905, 65053, 65180, 65289, 65377, 65446, 65496, 65525, 65535,
                               65525, 65496, 65446, 65377, 65289, 65180, 65053, 64905, 64739, 64553, 64348, 64124, 63881,
                               63620, 63339, 63041, 62724, 62389, 62036, 61666, 61278, 60873, 60451, 60013, 59558, 59087,
                               58600, 58097, 57579, 57047, 56499, 55938, 55362, 54773, 54171, 53555, 52927, 52287, 51636,
                               50972, 50298, 49614, 48919, 48214, 47500, 46778, 46047, 45307, 44561, 43807, 43046, 42280,
                               41507, 40730, 39947, 39161, 38370, 37576, 36779, 35980, 35178, 34376, 33572, 32768, 31964,
                               31160, 30358, 29556, 28757, 27960, 27166, 26375, 25589, 24806, 24029, 23256, 22490, 21729,
                               20975, 20229, 19489, 18758, 18036, 17322, 16617, 15922, 15238, 14564, 13900, 13249, 12609,
                               11981, 11365, 10763, 10174, 9598, 9037, 8489, 7957, 7439, 6936, 6449, 5978, 5523, 5085, 4663,
                               4258, 3870, 3500, 3147, 2812, 2495, 2197, 1916, 1655, 1412, 1188, 983, 797, 631, 483, 356, 247,
                               159, 90, 40, 11, 1, 11, 40, 90, 159, 247, 356, 483, 631, 797, 983, 1188, 1412, 1655, 1916, 2197,
                               2495, 2812, 3147, 3500, 3870, 4258, 4663, 5085, 5523, 5978, 6449, 6936, 7439, 7957, 8489, 9037,
                               9598, 10174, 10763, 11365, 11981, 12609, 13249, 13900, 14564, 15238, 15922, 16617, 17322,
                               18036, 18758, 19489, 20229, 20975, 21729, 22490, 23256, 24029, 24806, 25589, 26375, 27166,
                               27960, 28757, 29556, 30358, 31160, 31964

//Calibration table, you will need to adjust these values to suit your build
int DAC_CAL_tab[8] = { 0x08, 0xfF, 0x0F, 0x4F,   0x3f, 0x80, 0x80, 0x80}; // zero's then gain's

// The DAC8734 uses Mode 1 SPI, has an 8 bit address byte followed by 16bit data
// high byte first, high bit first
// This is the register descriptions
// Reg   description
// 0    Control Register
// 1    Monitor
// 2    Not Used
// 3    Not Used
// 4    DAC 0 Data Register
// 5    DAC 1 Data Register
// 6    DAC 2 Data Register
// 7    DAC 3 Data Register
// 8    DAC 0 Zero Cal Register
// 9    DAC 1 Zero Cal Register
// a    DAC 2 Zero Cal Register
// b    DAC 3 Zero Cal Register
// c    DAC 0 Zero Gain Register
// d    DAC 1 Zero Gain Register
// e    DAC 2 Zero Gain Register
// f    DAC 3 Zero Gain Register

This section defines loads of constants, register offsets and a SINE table for fun waveform generation, it is all configured in support of the DAC and I will walk through it in more detail in the pending video

All of the code has been heavily commented to make it easier to understand and extend. I have deliberatly NOT made a library yet as I want this to be as simple as possible for the reader to understand

now we start into real code for the DAC

At its core, WriteDACRegister function does everything or more specifically is the only unction that actually initiates communications with the SPI bus and the DAC chip, all the other functions end up here, so if you have issues, this is a good place to put debug statements

// the most basic function, write to register "reg", with a value "val"
void WriteDACRegister(byte reg, unsigned int val)
#ifdef Arduino 
  SPI.beginTransaction(settingsDAC8734); // ensures the SPI bus is in the right mode for this device irrespective of other devices used in the code
  digitalWrite(DAC_8734_CS_PIN, LOW); // Select the Chip
  SPI.transfer(reg); // Select the target register
  SPI.transfer(val >> 8); // Send the High Data Byte
  SPI.transfer(val & 0xFF); // Send the Low Data Byte
  digitalWrite(DAC_8734_CS_PIN, HIGH); // Release the Chip
#ifdef Arduino

it is very simple, it takes an 8bit register offset and a 16 bit value and sends it to the DAC via the SPI bus, register value first, followed by high Byte of value, then the low byte. A point to note in this function if the ifdef statements, these are compiler directives that are used if you set the Arduino Definition at the top of the code to active, the Arduino will work fine without it but this allows you to more easily change the speed of the bus and its mode, if for some reason you have more than one device on the bus with different operating modes, to read more, look here:


Now we have the DAC initialization, this simply takes all the "int" variables at the top of the DAC code, ors them all together shifting the bits as needed to create the command word to setup the DAC.The default I have currently is both Group A and B are powered up and running in Unipolar mode with a 2X gain

// Setup a default DAC using the variables set above
// note the variables can be chaned then recall InitDac to change mode
// of operation of the DAC
// Currently set to Unipolar, 2* Gain and Powered up
void InitDAC()
  // for now, gain of 2, gpio hiZ,
  int DAC_INIT = 0x0000 | DAC_PD_A << 12 | DAC_PD_B << 11 | DAC_GPIO1 << 9 | DAC_GPIO0 << 8 | DAC_DSDO << 7 | DAC_Gain3 << 5 | DAC_Gain2 << 4 | DAC_Gain1 << 3 | DAC_Gain0 << 2 ;
  Serial.print("Command Reg = "); Serial.print(DAC_INIT, HEX); Serial.print(" "); Serial.println(DAC_INIT, BIN);
  WriteDACRegister(DAC_8734_Command, DAC_INIT);


now we have Calibrate DAC, this function simply outputs an 8*16 bit table to the Zero and Gain registers of the DAC. when you try it, you will need to adjust the values of this table to suit your own DAC, have a read of the PDF here fo more details: page 19.

//Output the Calibration table to the DAC for all channels
void CalibrateDAC()
  for (int x = 0 ; x < 4 ; x++)
    WriteDACRegister(DAC_8734_CalZeroBase + x, DAC_CAL_tab[x]) ;// Zero cal
    WriteDACRegister(DAC_8734_CalGainBase + x, DAC_CAL_tab[x + 4]); // Gain cal


Another simple function that abstracts the DAC values from knowing the register offsets etc

// Helper Function to output to a specified DAC, a Desired value between 0 and 65535
// this could represent 0V - 10V or -5 to +5V depending on mode
void SetDAC( byte channel, unsigned int value)
  if (channel > 3)
    Serial.println("DAC  must be 0 - 3");
    //Serial.print("DAC "); Serial.print(channel); Serial.print(" Value "); Serial.println(value, HEX);
    WriteDACRegister(DAC_8734_DataBase + channel, value);

This simply takes a DAC value between 0 and 3 along with a 16bit value, performs a quick check on the DAC id and adds the DAC base register value to it and forwards it to the WriteDACRegister function


Now is a fun function, this itterates through the SineTable above and outputs each value in turn to the DAC, there are 256 values in the table, and the routine will send out each complete set upto the number provided as the value in the call, this can be from 0 to 65535 (A long time if you go there ). the first parameter simply selects the DAC channel you want to output the value to.

// Simple itteration through the sine table above to simulate a sinewave
// you can change the table contents above to send whatever wave shape you want
void sine( byte channel, unsigned int loopcount)
  if (loopcount > 65535) loopcount = 65535;
  if (channel > 3) channel = 0;
  Serial.print("Sine on DAC ");Serial.print(channel ); Serial.print(" Cycles= ");Serial.println(loopcount);
  for (int y = 0 ; y < loopcount ; y++)
    for (int x = 0 ; x < 256 ; x++)
      SetDAC(channel, Sin_tab[x]);


This one should be self explanatory

// Send to Serial port the Help Menu
void help()
  // Print some pretty instructions
  Serial.println("DAC8734 Test Program:");
  Serial.println("Peter Oakes, July 2015\n");
  Serial.println("help\" for this menu" );
  Serial.println("dacx  x is from 0 to 3, value 0 to 65535" );
  Serial.println("bpA or bpB mode  Bipolar");
  Serial.println("upA or upB mode Unipolar" );
  Serial.println("offA or offB Power Off group A or B" );
  Serial.println("onA or onB   Power On for Group A or B" );
  Serial.println("sinex cycles, x is DAC from 0 to 3, cycles 0 to 65535" );
  Serial.println("make sure \"NewLine\" is on in the console setup" );



The setup and loop functions are very basic and self explanatory too

// Standard Arduino / Energia setup() and loop() functions
// do not delete
void setup()
  // print the help menu for the DAC8734 tester
  // setup the chip select pin for the DAC
  pinMode(DAC_8734_CS_PIN, OUTPUT);
  // Initialisze the DAC8734
  // output the DAC_CAL_tab to the DAC
  // set the DAC outputs to Zero
  SetDAC(0, 0x0000);
  SetDAC(1, 0x0000);
  SetDAC(2, 0x0000);
  SetDAC(3, 0x0000);

void loop()
  // see if there is a command come in on the serial port and if so call the command processor
    if (CheckSerial()) DoCommand(inputBuffer);


Well OK, loop is a little strange for some of you. A long time ago i created several tutorials on how to read the serial port and parse the strings into usable commands with a single parameter. the statement in the loop simply chaecks the serial function to see if a command has arrived (Signified by he receipt of a newline character and if one is found, it sends it to the "DoCommand" function where all the magic happens. You can see the POST here if you want more information on how it works etc, Fast Track to Arduino Programming - Lesson 3 pt 1, Improved Parsing and memory usage (Fully working programs) and here Fast Track to Arduino Programming - Lesson 2, parsing the serial input into usable commands


here is the code anyway to complete the sketch

Checks the serial input for a string, returns true once a '\n' is seen
users can always look at the global variable "serialIndex" to see if characters have been received already
boolean CheckSerial()
  boolean lineFound = false;
  // if there's any serial available, read it:
  while (Serial.available() > 0) {
    //Read a character as it comes in:
    //currently this will throw away anything after the buffer is full or the \n is detected
    char charBuffer =;
    if (charBuffer == '\n') {
      inputBuffer[serialIndex] = 0; // terminate the string
      lineFound = (serialIndex > 0); // only good if we sent more than an empty line
      serialIndex = 0; // reset for next line of data
    else if (charBuffer == '\r') {
      // Just ignore the Carrage return, were only interested in new line
    else if (serialIndex < serialbufferSize && lineFound == false) {
      /*Place the character in the string buffer:*/
      inputBuffer[serialIndex++] = charBuffer; // auto increment index
  }// End of While
  return lineFound;
}// End of CheckSerial()

// Enhanced Command Processor using strtok to strip out command from multi parameter string
boolean DoCommand(char * commandBuffer)
  char* Command; // Command Parameter
  char* Parameter; // Additional Parameter
  unsigned int analogVal = 0; // additional parameter converted to analog if possible

  // Get the command from the input string
  Command = strtok(commandBuffer, commandDelimeters); // get the command
  Parameter = strtok(NULL, commandDelimeters); // get the parameter if any
  //if there are more than one parameter they will be ignored for now

  // Make sure we have an analog value if we are to allow PWM output
  unsigned int outparameter = isNumeric (Parameter);

  //if it is a number then convert it
  if (outparameter)
    analogVal = atoi(Parameter);
    // check the analog value is in the correct range
    if (analogVal < DACMIN || analogVal > DACMAX) outparameter = false;
  // Standard way to handle commands
  if (strcmp(Command, "help") == 0) { help(); }
  //DAC Outputs if we have a valid analog parameter
  else if (strcmp(Command, "dac0") == 0  && outparameter ) { SetDAC(0, analogVal);}   // Set the DAC 0 output 
  else if (strcmp(Command, "dac1") == 0  && outparameter ) { SetDAC(1, analogVal);}  // Set the DAC 1 output 
  else if (strcmp(Command, "dac2") == 0  && outparameter ) { SetDAC(2, analogVal);}   // Set the DAC 2 output 
  else if (strcmp(Command, "dac3") == 0  && outparameter ) { SetDAC(3, analogVal);}   // Set the DAC 3 output 
  //Sine Wave Outputs
  else if (strcmp(Command, "sine0") == 0  && outparameter ) { sine(0,analogVal);}    // Set the DAC 3 output 
  else if (strcmp(Command, "sine1") == 0  && outparameter ) { sine(1,analogVal);}   // Set the DAC 3 output 
  else if (strcmp(Command, "sine2") == 0  && outparameter ) { sine(2,analogVal);}   // Set the DAC 3 output 
  else if (strcmp(Command, "sine3") == 0  && outparameter ) { sine(3,analogVal);}   // Set the DAC 3 output 
  else if (strcmp(Command, "upA") == 0 ) { Serial.println("Setting UniPolar  Group A");  DAC_GPIO0 = 1; InitDAC(); }
  else if (strcmp(Command, "upB") == 0 ) { Serial.println("Setting UniPolar  Group B");  DAC_GPIO1 = 1; InitDAC(); }
  else if (strcmp(Command, "bpA") == 0 ) { Serial.println("Setting BiPolar  Group A");   DAC_GPIO0 = 0; InitDAC();  }
  else if (strcmp(Command, "bpB") == 0 ) { Serial.println("Setting BiPolar  Group B");   DAC_GPIO1 = 0; InitDAC();  }
  // Power Down
  else if (strcmp(Command, "offA") == 0 ) {Serial.println("GrpA Off "); DAC_PD_A = 1; InitDAC(); }
  else if (strcmp(Command, "offB") == 0 ) {Serial.println("GrpB Off "); DAC_PD_B = 1; InitDAC(); }
  // Power Up
  else if (strcmp(Command, "onA") == 0 ) { Serial.println("GrpA On"); DAC_PD_A = 0; InitDAC(); }
  else if (strcmp(Command, "onB") == 0 ) { Serial.println("GrpB On"); DAC_PD_B = 0; InitDAC(); }
  // Catch All
  else {  Serial.print("Error "); Serial.println(commandBuffer); }
  return true;
// Utility function to make sure the string is a numneric one
int isNumeric (const char * s)
  while (*s)
    if (!isdigit(*s)) return 0;
  return 1;


and basically thats it. If you thought it was going to be tough to use this DAC, I hope now you have other ideas. Feel free to use theis sketch as you please, provide feedback and any bugs you find and perhaps even feture requests ??, who knows what I may choose to do but one thing for sure, this is a nice little DAC with a lot of possibilities and I look forward to using it on future projects.


as soonas you load the sketch and attach the console it should print out the help screen telling you the commands available, I hope you find it useful, I will be creating a video to go with this and if I improve the code and add more features I will post that too.

Hellow guys, after watching tutorials and videos, about creating a new arduino coutdown with buttons, i found nothing so i can use, i am very new at this, all i want is a tutorial or something similar

i need something that has a button so players can hold for a amount of seconds so that they can wait to respawn.

At the same time i need it to have a screen with an amount of tickets left for the team, each player when respawn takes 1 ticket. 

Also need 2 buttons so we can increase/decrease  the amount of seconds to hold for player to respawn

and the numbers of tickets

Also need to have sound alarm ( pc speaker or same ) that sounds a alarm when tickets are down to 0



Button A - this is what players hold so that they can respawn ( hold for X amount of time ) Example : 5 secs hold - you can see a bar loading until you are alive again


Button 1 - increase time that players need to hold so that they can respawn / increase number of total tickets when u  are setting up  match


Button 2 - decrease time that players need to hold so that they can respawn / descrease number of total tickets when u are setting up match


Screen - when in menu setting the game  - select amount tickets  more/less  - select amount secconds so players have to hold so respawn / at the same time ticket needs to drop by 1 , each  respawn ( revive ) - select sound on/ off   - when tickets get to 0 , screen shows NO RESPAWN LEFT , GOOD LUCK  and reset to menu


Sound - Alarma sounds when number of tickets get to 0 for 5 seconds and off



Thank you very much hope this helps and someone that knows how to do or even point me out see this. THANK YOU AGAIN VERY MUCH

if you can point out to someone that hast these skills, i would like to chat with that person. Thank you







With the HMI Controller for Arduino you can create an interface between your Android device and Arduino, this app connection works with Bluetooth and Ethernet.


This app can be used in home automatization, you can control lights and devices or check the state of the doors or windows. With this app you can do alot of things.



[url=]HMI Controller for Arduino Lite Version[/url]


[url=]HMI Controller for Arduino FULL Version[/url]

Gesto is the World’s first open-source boards for wearable gesture control with a unique method that combines muscle signals with motion patterns.


There are available two kits:




Gesto Caelum is a ready to go solution. Caelum has all you need to add gesture control/recognition to your project, with a fast and easy setup, without the need to program it. So you can simply configure the gestures in your Android or iOS smartphone to get started right away.


Gesto Stella is a development kit. It is designed to interface to other boards such as Arduino or Raspberry Pi boards, and sends raw muscle data via its SPI interface.


Prosthetic arm controlled with Gesto


Gesto can handle three types of gestures: singular gestures, air drawing gestures, and directional gestures. The technology making sense of the muscle raw data is called “DualBurst” which combines muscle patterns and motion patterns for better accuracy.



Gesto also provide a modular 3D printed band to assemble all the Gesto electronics in your body (wrist, forearm, arm, torso, leg).


Check out the demo!

My last post was about the usage of a digital compass. The topic of that post was on the usage of the compass together with an Arduino to obtain the values of the components of the magnetic field vector measured by the sensor.


The values obtained with the technique illustrated there are, in fact, just a digitalisation of the "real" values. In other words, what you read with Arduino is just an 11-bits binary number whose value is proportional to the magnetic field sensed in each direction. The sensor used on the PmodCMPS digital compass can measure fields up to  ± 8 G (Gauss, a unit of measure for magnetic fields). According to the datasheet the value provided on each channel is a 16-bit number, whose 5 most significant bits are used as the sign: the reason for using 5 bits and not just 1 is that the ADC on the sensor has only 12-bits. With 11 bits we can get numbers up to 211=2048 (in fact from 0 to 2047). That means that a reading of +2048 corresponds to +8 G, at maximum gain, while reading of -2048 corresponds to -8 G. Playing with the sensor you can see that sometimes you also get +4096 or -4096. That is an artefact of the ADC that, if saturates, return zero and the reading appears as a 12-bit integer number.


We are then ready to make a map of the magnetic field provided by a small magnet. The setup can be made as follows: just use a piece of scotch to firmly attach the sensor on a piece of graph paper, as seen in the picture below.


fig1.pngWith the sensor in this position, the X-axis is perpendicular to the paper, the Y-axis  points toward left and the Z-axis points to the top of the figure.

The readings made on each channel must be converted to the proper units (G) before being used and to do that we need to know the calibration constants, i.e. the numbers C such that the value V of the field in a given direction can be obtained as C*R where R is the reading. Those constants depend on the gain of the device that can be adjusted using the sensor's configuration register. The default value of the gain is such that the maximum value of the reading (2048) is attained when the device senses a magnetic field of 1.3 G (see pag. 13 of the datasheet).

In order to get the value of the magnetic field in a given direction returning R as value, we then must multiply R by 1.3/2048.


Our purpose is to measure the magnetic field of the earth at the beginning of the run, keeping the magnets far from the sensor. Then, we move the magnet for which we want to map the field closer to the sensor and write down the values read for each position w.r.t. the sensor. From each value we subtract the corresponding value when the magnet is far from the device, to remove the contribution of the earth's magnetic field. Let's then look at the following sketch.

void loop() {  
  if (!(avgDone)) {
    for (int i = 0; i < 100; i++) {
      for (int j = 0; j < 3; j++) {
        b[j] += B(j);
    for (int j = 0; j < 3; j++) {
      b[j] /= 100.;
      Serial.print("] = ");
    avgDone = true;
  double Bx[3];
  for (int i = 0; i < 3; i++) {
    Bx[i] = B(i) - b[i];
  for (int j = 0; j < 3; j++) {
    Serial.print("] = ");


avgDone is a boolean variable set as false in the setup() method, such that, as soon as the system is powered on, it takes 100 measurements of the three components of the field by means of the B(i)method that returns the i-th component of the field. To take the average, we store the sum of the readings in a three components array b[]. At the end of the loop the components of such an array are divided by 100. Each of them, then, contains the reading corresponding to the average magnetic field of the earth.


The B(i)method is defined as


int B(int i) {
  int b[3];

  Wire.write(0x03); //select register 3, X MSB register
 //Read data from each axis, 2 registers per axis
  Wire.requestFrom(ADDRESS, 6);
  if(Wire.available()) {
    b[0] = * 256;
    b[0] +=; 
    b[2] = * 256;
    b[2] +=; 
    b[1] = * 256;
    b[1] +=; 
  return b[i];


In fact it can be made more efficient: this way it always reads both the three components, then returns one of them. Since performance is not an issue we can keep the code as it is: keep it simple is always a good rule.


The average values measured by us were (-284, -543, -17). In order to check that these values are correct values we compute their values in G using calibration constants. Multiplying each of them by 1.3/2048 we obtain (-0.180, -0.345, -0.011). The magnitude of such a vector is given by the Pythagoras' theorem as the square root of the sum of the squares of the components, i.e. the square root of 0.1802+0.3452+0.0112= 0.151546 (we can just ignore the signs since the squares are always positive), whose root is 0.39 G, not far from the expected value of about 0.5 G (in fact the earth's magnetic field ranges from about 0.3 to 0.6 G, depending on the location).


Once the average has been taken and shown on the Serial Monitor, we start taking measurements of the magnetic field every 5 s, so we have the time to move the magnet and write down the values.



The picture shows how we proceeded: the metal ring you can see close to the sensor is a small neodymium magnet. The position of the magnet w.r.t. sensor can be obtained from the graph paper. For each position we read the three values of the field as provided by the sensor and write them down on the paper in the position occupied by the magnet.


In this way we can obtain a complete map of the field, measuring the values at different positions.DSC_0580.JPG Remember that the values provided by our sketch are now the readings in excess w.r.t. the earth's magnetic field, so we can get the magnetic field of the magnet directly from them


The picture on the right shows such a map. We reported the values read for the earth's magnetic field and the reference frame (that in fact contains a mistake: the axis pointing up is in fact the z axis).


Taking the values on the top right (57, 1, -19) we can see that the magnetic field provided by that small magnet has almost no component along y, so it points below and toward the reader.


The real values of the magnetic field are (0.036, 0.000, -0.012) G (just multiply the readings by 1.3/2048). The magnitude of the vector is then 0.038 G.


Moving the magnet closer its magnetic field increases. For example, when the magnet is in the bottom left position the readings are (833, 703, -1254) corresponding to (0.529, 0.446, 0.796) G, and the strength of the field is 1.05 G.


This kind of magnets provides a field that decreases strongly with the distance, as you can see from the values measured by our sensor.


Using a similar technique you can measure the field generated by any magnet, either by moving the magnet or the sensor in space. You can then compute a complete map of the field or of the field strength.

Filter Blog

By date: By tag: