Author Archives: edmallon

Measuring EC ( electrical conductivity ) with Arduino

This post is a summary of my background research into electrical conductivity to serve as a backdrop for my own humble attempts at this interesting measurement challenge. I’m sure there are many other approaches that I’ve yet to discover, and if you know of one please leave a comment so that we can pass that knowledge on to others – Ed.

Obligatory blog-post monkey shot.

Pete & Trish doing profiles with a YSI EXO. As you might imagine, these puppies are pretty expensive. Now that we have A Flexible Arduino-Based Logging Platform to build on,  adding conductivity is our #1 priority. Creating a good drop-profile is an incredibly slow process because you have to wait for the probes to thermally equilibrate and you don’t want to disturb the delicate halo-clines as you pass through them. The last 35m profile I did with a Hydrolab took 2 hours to reach bottom.

The conduction of current through a water solution is primarily dependent on the concentration of dissolved ionic substances such as salt. Since most fresh water derives from relatively clean rainfall, variations in EC provide a way to track the chemical  and hydrological processes the water has been subjected to over time. High amounts of dissolved substances (usually referred to as salinity) can prevent the use of waters for irrigation and drinking, so conductivity ranks as one of the most important inorganic water quality parameters.

A huge number of resources are dedicated to measuring EC and rather than re-hashing all that material, I thought I’d start with links to a few good background reads:

Conductivity, Salinity & Total Dissolved Solids
-discusses the older TDS measurements in parts per million ( ppm ) which makes assumptions about the charge carriers that don’t reflect real world environments.  The conversion factor from EC (which is the thing you actually measure) to TDS changes for different dissolved solids, so instruments from different manufacturers often give you different TDS readings for the same solution, because the companies made different assumptions about what’s in your water.  Because of this confusion, straight EC measurements in siemens have been adopted as the standard by the international scientific community. One siemens is equal to the reciprocal of one ohm (S = 1/Ω)  and is also sometimes also referred to as the mho (℧) in older literature.

Conductivity Theory & Practice
-a white paper that covers basic probe designs, and mentions some non intuitive things like geometry/field effect errors.

Conductivity Sensing at
-many groups at have been working on different types of conductivity sensors and their overview page is another excellent introduction to DIY approaches. In fact it’s so good that I will be referring to several of those projects in this post.

Aqueous conductivity is commonly expressed in millisiemens/cm (mS/cm) and natural waters range from 0.05-1.5 mS/cm for freshwater lakes & streams up to about 55mS/cm for sea water. Water up to 3 mS/cm can be consumed, though most drinking/tap water is below 0.8 mS/cm.  Many of the Cave Pearl Project’s installations are in coastal areas where tidally driven haloclines require our instruments to cover that entire “natural waters” range.  Groundwater can vary even more, with measurements being complicated by organic acids and/or significant amounts of dissolved limestone.  Salt water is chemically aggressive and water hydrolyzes above 0.4v, so the probes for high-conductivity environments are usually made of resistant materials such as platinum, titanium, gold-plated nickel or graphite, making them somewhat expensive.

Ways to Measure Conductivity:

There are so many different approaches to measuring EC that it’s taken me a while to digest it all into some working categories.  I expect to build at least one prototype for each of these methods just to see if I can make it work.

Density Based Methods

Refractometers and density based hydrometers are used by aquarium hobbyists. Better quality acoustic doppler flow sensors can also calculate density based on the speed of sound through the water and infer salinity from that. Given then number of acoustic anemometer projects out there, I’m surprised someone has not already adapted the method for underwater applications, though this may be due to the timing limits of the affordable transducers.

Resistance Based methods:

a) Use submerged probes as part of a resistor divider / bridge :
This common approach measures the resistance between two probes using some type of voltage divider. Resistance =1/conductance, which allows you to derive conductivity with your cell K constant since conductivity=(conductance * length)/(area).  AC oscillators are tacked on to reduce electrode polarization, and this forces you to add even more electronics on the output side to convert the signal back to DC for reading. The resistance between the probes changes by several orders of magnitude in environmental waters so different probe surface areas & divider resistors are usually required to cover a significant conductivity range. Above 50% sea water, the resistance between the probes doesn’t change very much, so this method tends to get used more frequently for fresh water environments.

b) Change the pulse frequency of a 555 timer circuit:
You can use resistance between the electrodes as part of an RC relaxation oscillator and then measure the 555’s square wave output frequency to determine the resistance.

This circuit from Thomas Allen’s site provides galvanic isolation, uses AC measurement, and the output frequency varies from about 42 Hz with the probes in air to > 8000 Hz depending on conductivity. You can buy this circuit on pre-made a module for $24  from the EME site.

Circuits and instructions can also be found at at and there are many good tutorial videos describing 555 based EC sensors on YouTube.  At this point I’ve run into so many projects using this chip that I’d  be willing to bet every environmental sensor I’ve ever heard of could be cobbled together from a few op-amps and a low voltage 555 timer . There are several frequency counting libraries available to help you get started, and if you are ready to sink your teeth into some code, Nick Gammon has produced the some elegant solutions for pulse/frequency timing. Note there are some duty cycle issues  (also see: Schroeder Thesis)

c) Time the discharge of a capacitor through the solution:
Jim Conner’s describes this method in his youTube video at
EC Probes – How they work, and how to build one

A circuit like this might be easy to implement on an Arduino if you can put the internal 1.1v reference onto the comparator that’s also built-in to the 328.  Microcontrollers count time with far better resolution than you get from their ADC’s, but that doesn’t mean there aren’t other issues to deal with. Given that you can try this method with practically no extra circuitry, I will definitely be prototyping a few of these.  Like the 555 based circuits, it will be interesting to see if the method bumps into timing & interrupt handling limits (100 kHz?) when you use it with seawater.

Capacitance based approaches:

You often see capacitance used for liquid level sensors and soil moisture probes, and some of these could be adapted for EC.  To me, the raindrop detection pcb’s you see on eBay have always looked like prime candidates for re-purposing as capacitive sensors.

The circuit described for the Chirp Moisture Sensor uses a fixed resistor and a non-contact probe to create a low pass filter whose cutoff frequency changes with capacitance,  which is affected by the electrolytes in solution. This filters an 1-8MHz square wave and the output voltage is accumulated other side of simple diode peak detector circuit for reading. 

You can also vary 555 timer output frequency by changing the capacitor in the tank circuit, or create more complicated oscillator circuits. No matter which cap-based method you use, the supporting electronics have to be located near the sensor – because just about any length of wire will add enough stray capacitance to throw off your measurements. Another thing to keep in mind is that common ceramic capacitors have some of the worst thermal coefficients and aging effects imaginable. Plastic film capacitors using Polyphenylene Sulfide (PPS ±1.5%) or Polypropylene (CBB or PP ±2.5%) are much better for sensor circuits like this, and having a digital capacitance meter on hand is probably a good idea too.

DIY capacitor plate sensors are going to be small values (picofarads ?) and the resulting RC time constants make these methods more suitable for fresh waters, as this leverages the relatively large solution resistance to give you more accurate interval timing.

Potentiometric (4 electrode) Methods

Four-electrode cells uses two “driver” pins to place an electric field across two other “reading” pins that lie between them: 

This paper describes a DIY 4-probe sensor that was used for soil moisture sensing, and you will find quite a few articles using potentiometric methods over at IEEE and Sensors

Nokia/Apple audio jacks came to mind as soon as I saw this diagram, and they might be available with gold plating.  4-electrode methods often measure the voltage between the read pins, which is divided by the exciter pin current to determine the solution’s impedance = 1/conductance.  To obtain the conductivity, the conductance is multiplied by the cell constant of the inner poles. Tracking the pin current lets you compensate for fouling on the plates, and the method can cover a wide range of conductivity. Like inductive methods, this approach tends to work better as the concentration increases. 

Inductive Methods

The conductivity measurement is made by passing an AC current through a toroidal drive coil, which induces a current in the solution. This induced solution current, in turn, induces a current in a second coil, called the pick-up toroid. The amount of current induced in the pick-up toroid is proportional to the solution conductivity. You get industrial grade performance out of this non-contact method in many different types of solutions, but you also need industrial amounts of power the drive the sender coil so it’s hard to implement on the kind of power constraints you see on stand-alone data loggers.  Inductive sensors require a 3 inch radius from any other surface (bio-fouling?) and you see this pretty clearly in the ‘donut on a stick’ sensor heads. It occurs to me that you see very similar components in a wireless charging system, but there’s a lot of devils hiding in those details – like shielding, etc.  It might be possible to press one of the production line proximity sensor chips into service for a low power solution, or simply try measuring changes in inductance due to the presence of salt water.

Off-the-shelf Solutions for Arduino:   (using 2-Electrode Resistance Methods)

Atlas Scientific Conductivity Kit
A complete solution including calibration solutions, a range of probes and code libraries. All parts also sold separately: interface boards are ~$35 & EC probes come in around $120 each but they are durable enough for continuous long term submersion.  I2C data transfer is supported, so resolution is not limited to the Arduino’s ADC.  Whitebox labs Tentacle Shields ($35-$110) provide up to four galvanically isolated channels for full hydroponic rigs. Stand alone BNC carriers for $10. The notable Open CDT project makes use of these sensors.
CN0349 Conductivity Measurement System
The EVAL-CN0349-PMDZ has total error less than 1% FSR after calibration. The digital output is fully isolated eliminating ground loop interference. Even if you are using a different circuit at the sensor, its work looking at how they did that.  Thanks for the tip about this one from Joshua Girgis: “Its designed to work as a benchtop sensor but one can easily wire it up to the i2c lines on an Arduino. The code is a little cumbersome but I have it working for taking temperature and conductivity measurements for sea water.”  Update 201907 :  Joshua has released an Arduino library for this board on his GITHUB.
Gravity: EC Sensor Kit for Arduino (K=1)
Another complete K=1 kit solution, but the probes are not robust enough for long term submersion so several people replace the stock probe with the 208DH which is available on eBay for $35. Arduino ADC reads voltage.  The KnowFlow project uses the full set of DFrobot boards. DFR also has an inexpensive TDS kit, which cfastie has been testing over at
Vernier CON-BTA EC probe
This 5v K=1 probe covers 0-20,000 μS/cm  in the high range, and the analog voltage output is read by the Arduino ADC. You need an inexpensive adapter board for the BTC connector, and they provide a basic library. One key feature is built in hardware temperature compensation with a 10k thermistor in the probe head. My tests show this reduces the usual 2% / °C reading variation down to about 0.5%/° C, so you still need to do your own calibration to get higher accuracy. Like Atlas Scientific, Vernier has many other interesting sensors that are Arduino compatible. (Much cheaper on Ebay for older stock)
EC/pH Transmitters
This company offers a range of physically bulky turn-key solutions, with the $70 entry level unit claiming 0-5000 μS/cm (fresh waters) and continuous monitoring. Arduino ADC reads voltage. ~$200 units support PH with isolation.
Sparky’s widgets MiniEC
An indie who makes several other useful sensor breakout boards, including PH. You have to build or locate your own probes, though they use a standard BNC connector like most EC probes.  Arduino ADC reads voltage output. Works with many of the inexpensive probes you find on Alibaba – some of which look remarkably like the probes used by Atlas?
EC-Salinity Probe Interface by Ufire
Designed around an ATtiny configured as an I2C slave, probably using the cap-discharge method.
Hanna HI 73311 (K=1) Replacement probes
In the past we’ve used used these epoxy&graphite probes from Hanna DIST5 (HI 98311) and DIST6 (HI 98312) testers, which connect to a standard male audio jack.  You can also re-purpose one of the Vernier ABS/graphite probes if you get a used one cheap on eBay, and the Vernier probes have a 10k NTC thermistor built in, which you can read with a divider. The best completely DIY probes I’ve ever seen are the concentric electrodes built by Camilo Rada with epoxy & graphite rods.
Comercial Standard Solutions
For fieldwork, it’s often easier to transport the dry packets, and mix them on location.  Atlas sells calibration sets, but at the twice the cost of standards when you buy them in larger volumes.  You can find recipes for homemade calibration solutions at Reefnet Central and PublicLab. For a classroom situation, it’s much cheaper to mix secondary “lab standards” in larger quantities, and then test the resulting solutions with a commercial probe that’s been calibrated against commercial solutions. 5.566g of dry NaCl in 1 litre of distilled water will create an ~10,000 μS/cm solution, which you can dilute down for lower concentration standards.

This photo from Bitnitting gives you a sense of the physical space needed for the Atlas breakouts and a ‘mini’ form factor Arduino.

Hydroponics hobbyists have putting these kits to good use over the years with notable examples like the long running forum thread on Billies Hydroponic Controller, and the well documented adventures over at the Bitnitting Blog.  The people at OpenCTD and other academic projects have put the Atlas boards into real world deployments.

But to me these commercial solutions still leave you stuck with those expensive electrodes which sometimes cost more money than you would pay for a used 4-pole device. More annoying is the fact that those cell constants do not line up with my goal of measuring the entire “fresh” to “marine” range with one sensor, thought if I could extend it a bit the K=10 probe comes close.  This is illustrated by the following graph from Andy Connelly’s Blog which is worth digging through as he has posted lots of other interesting material on calibration, reproducibility, signal detection, etc. 


Of course the cell constant changes as your probes get older and dirtier, so you have to re-calibrate them with standard solutions just about every time you want to take a new reading. I’m pretty sure I will end up making my own probes, probably out of Nichrome 80 wire as the vaping fad has made it common on eBay. Some have had good EC results with gold plated PCB traces. Feedback on the forum suggests that Platinum-Rhodium Thermocouple Wire is another good option.  I’ve also been wondering about Ag/AgCl which is highly resistant to seawater and is commonly used for non-polarizing electrodes in medical/bio applications. (EKG electrodes?) It might also be a good idea to cobble together a DIY magnetic stirrer, based a PC fan and an old hard drive magnet

DIY 2-Probe EC Circuits

The easiest circuits to build yourself are the 555 timer oscillators, but there are plenty of quad-opamp solutions out there for people comfortable with a breadboard. The oldest example I’ve seen is this one by M. Ahmon from the Sept 1977 issue of Electronics magazine which uses the resistance of the solution to modify opamp output:

This circuit uses the first stage of the quad opamp in a Wien-bridge oscillator, reducing errors caused by electrolysis with a 1-kHz signal that gets attenuated by the solution’s resistance before it reaches the driving amplifier A2.  Pot P1 controls oscillator amplitude, and P2 adjusts gain of A2.  A3-A4 form a precision rectifier giving output voltage equal to absolute value of input voltage. This one chip solution seems to have been the basis for many of the current EC projects on the web, including these two exceptionally well documented examples:

Octavia’s EC/TDS/PPM Meter On Limited Budget

Daniel Kramnik’s Digital Salinometer Project

Similar circuits can be found on the breakout modules from Sparky’s Widgets and DFrobot . Using the solution’s resistance in the feedback divider controlling an op-amp is a neat idea, but having only one opamp there imposes hard limits the range you can measure with a given K value probe. There is a more advanced multi-opamp approach over at that can step over several decades.

On more recent EC projects I’m seeing single supply RRIO opamps for the oscillator & gain stages, which are easier to integrate with battery operated Arduino’s. (though any dual supply opamp can be used as a single supply in a pinch; since voltage is relative the opamp doesn’t know whether V- is a negative voltage or ground) To keep using an AC signal, this requires a virtual GND at 1/2 VCC, but the integration also gives you the option of getting rid of the oscillator entirely, since you can use PWM output as your source.

This is beautifully illustrated by the circuit from bhickman’s Conductivity & Temperature Meter over at PublicLab:

Ranging is accomplished with the (red) bank of R1 resistors, and (yellow)R2’s 5/6 can be substituted in for the probe (R8) with those known resistances can be used to track drift. The AC–DC converter stage is built with precision peak detectors. I think this is the best voltage divider approach I’ve seen to date.  To simplify things a bit, you might replace that output stage with an RMS-DC converter; though I’ve not seen any breakouts for those, and I hate working with raw SMD parts.

Sources of Error: 

Even with a clever circuit like the one above you still need to address things like temperature compensation before you get an accurate, repeatable, and stable device. Electrical conductivity measurements are typically referenced to 25 °C using standard temperature compensation factors (α). The conductivity of natural waters exhibit strongly nonlinear temperature behavior, though in practice linear correction factors are most frequently used.  NaCl-based solutions typically have a temperature coefficient (α) of 0.02-0.0214 (~ 2% change/degree C). So to convert your “ambient” conductivity measurement into 25°C “specific” conductivity, the simple linear conversion is:

EC25=ECambient /[ 1 + α (tambient – 25) ], α= 0.02

Field effect errors are significant, causing read errors if bare 2-pole electrode get within 2-5 cm of the solution container: which will completely mess up your calibration and cell constant determination. This is one reason that virtually every EC probe is encased inside a plastic shroud of some sort. That causes field effect errors too, but at least its the same error every the time, rather than one that varies depending on how far you are from the edge of the beaker. Four probe methods also require a fixed volume of solution between the driver electrodes, so the shroud provides that.

Grazing through the hydroponics forums shows plenty of people struggling with cross-sensor interference.  Most notably when conductivity probes affects the accuracy of a PH probe in the same tank.  Any time two devices are immersed in the same environment differences between them can generate ground loop voltages and induce currents which degrade the readings and exacerbate corrosion.  Sometimes you can address these issues with optical or I2C isolators. One helpful contributor at suggests:

pH electrodes are very high impedance devices and the cabling and connectors are all important – even flexing a decent cable will distort the readings…. Ground loops are the enemy of pH and any other specific ion electrode. I used them a lot in difficult situations and the most trouble-free solution is always to put a buffer op amp (FET type) as close to the electrode as possible – some commercial electrodes come already equipped. Find a decent op amp like the old MAX406, high impedance techniques like PTFE insulators or simply keep the input pin off the board. Modern FET’s take single-sided supplies and run at better than 2-microamps – a 3.6-V lithium cell will give you in excess of 5-year’s trouble and ground loop -free operation. Once you have buffered the signal, you can use any cable you like. As a bonus, you can convert a pH electrode into an ammonia electrode by separating the water from the electrode with PTFE tape as used by plumbers.”

Well, I think that covers most of the stuff I had in my notes, and hopefully gathering it all here saves someone else from burning away that time. I have been experimenting with conductivity quite a bit lately, and I think I might have  come up with an analog approach that will allow people to play with conductivity on shoestring budgets. I just have a little more calibration testing to do before I let that one out of the bag  🙂

Addendum 2018-12-06

Folks working on EC might want to check out our tutorial video showing how to build underwater connectors  ( part of the 2017 logger build series )   Near the end of that video we mount an Atlas EC probe on a long cable for a student project. 

Arduino Pro Mini Data Logger : Add Screw-Terminal Board (2017 Update)

This is a build variation of the basic 3-Module logger described in the Cave Pearl Data Logger paper at Sensors.  This configuration sleeps at 0.20 mA or less if you use an Arduino with an MCP1700 series regulator, so a 4xAA battery pack like the one shown should be good for more than a year. If the stacking shown in this tutorial is a bit too much for you, take a look at the EDU version from 2019 where all the parts are flattened out into a pre-made box. It’s a bit easier to build.

If you need a logger with a rugged waterproof housing, it’s still hard to beat the crimped-jumpers build released in 2016. However sometimes I just want a quick bare-bones unit for bookshelf test runs while I shake down a new sensor. I can whip up a breadboard combo in about twenty minutes, but they stop working if I bump one of the wires loose. I’ve lost SD cards from this half way through a long term test, and I’ve also run into issues with noise & resistance from those tiny breadboard contacts.

To address this I’ve come up with a new configuration that uses a screw-terminal expansion shield originally intended for the Nano.  This requires a reasonable amount of soldering, and after some practice, between 2-3 hours to finish depending on how many “extras” you embed into the basic three component core. In return for that time you get all the pins broken out, making this approach almost as flexible as a breadboard, and much more physically robust. Pop them into some pre-made boxes and these little guys qualify as deploy-able for relatively stable environments (though I wouldn’t use those pre-made boxes outside unless I could pot everything inside that housing with paraffin wax)

Connection Diagram:



Bill of Materials: $8.55
Pro Mini Style clone 3.3v 8mHz
I always get the ones with A6 & A7 broken out at the back edge of the board.
Nano V1.O Screw Terminal Expansion Board
Note: To save time, you can spend an extra $1 for pre-assembled boards by Deek Robot, Keyes, & Gravitech.  Note that bad vendors show photos of the pre-assembled boards in their listing, but then ship you the no-name assemble-it-yourself part kit. That kind of bait-n-switch tactic is very common with dodgy eBay suppliers. Or if you have a board with unusual dimensions, you could fabricate a custom screw terminal shield from scratch.
DS3231 IIC RTC with 4K AT24C32 EEprom (zs-042)
Some ship with CR2032 batteries which will pop if you don’t disable the charging circuit!  Also the DS3231N or DS3231SN chips are much better with ± 2-3ppm error, The DS3231M is a very different chip with ± 5ppm, so buy a small number from each vendor until you find out which chips they put on the module.
SPI Mini SD card Module for Arduino AVR
Be sure to buy the ones with four ‘separate’ pull-up resistors! The second resistor from the bottom of these boards needs to be removed because it is pulling up the SCK pin, which with our SD driver needs to rest low because the SD card libs uses mode 0.  I found the best results by removing the bottom 3 resistors, and then using the Arduino’s internal pull-ups on mosi & miso.
4xAA 6V Switched Battery Holder
The logger works with battery packs holding 3 to 8 AA batteries (with the default MIC5205 regulator). When using non-switched battery holders, I add Amass XT30U Bullet Connectors between the holder and the logger.
CR2032 lithium battery  $0.40
Sandisk or Nokia Micro SD card 256mb-512mb 
Older Sandisk & Nokia cards have lower sleep & write currents. They also are better suited to access via the SPI interface, while larger newer cards are not. Test used cards well  before putting them in service.
Common Cathode Bright RGB LED 5mm 
( & 30kΩ limit resistor)  A brighter bulb lets you use a larger limit resistor for the same light output.
Double Sided Tape,  2x 10MΩ resistors, 28awg silicone wireheader pins, etc… $0.50
Donation to
If you don’t use a ‘real’ Promini from Sparkfun to build your logger, you should at least consider sending a buck or two back to the mothership to keep the open source hardware movement going…so more cool stuff like this can happen!
Comment:   You might need one of these to get started:                            (not included in the total above)
3.3V 5V FT232 Module
  ***Be sure to set the UART module jumpers to 3.3v before using it!*** and you will need a USB 2.0 A Male to Mini B cable.
Micro SD TF Flash Memory Card Reader
Get several, as these things get lost easily. My preferred at the moment is the SanDisk MobileMate SD+ SDDR-103 which can usually be found on the ‘bay for ~$5.


In 2018, a visiting colleague inquired about the steps involved in building a Cave Pearl logger, and I figured the best way to convey that was to simply built one while they recorded the process. This took about three hours, and those videos are now available on YouTube. The order of operations in the videos is slightly different from the written instructions below, but the changes are relatively minor.  


Clean absolutely everything. I go over every surface of every module with 90% isopropyl alcohol and cotton swabs until those boards are squeaky clean. Then all the surfaces that I’m not soldering get a layer of conformal coating. (except the SD card spring contacts…)  The pads I do solder get cleaned afterward to make sure there’s not one speck of circuit wrecking flux left, and then I coat those joins too.  I usually get several years of continuous operation of these loggers, and I’m convinced it’s because I clean the parts thoroughly during the build process.  Just as a reference, I recently took a look at the the ADXL345 module (pictured at right) inside my first drip sensor prototype which I built in 2014.  Since it was just a bench prototype, the module did not go through my usual cleaning procedure, and it was never deployed.  So this is what happens when uncleaned boards are simply left sitting on a bookshelf inside the house – just imagine what could happen to the parts you actually deploy in the field…

Bread-board your logger before soldering!

With the quality variation you typically see in these cheap parts, I make a pre-soldering breadboard version of each unique combination to confirm that the components in my build aren’t drawing excessive current – especially the SD cards!  I bin between 10-20% of the low-end sensor modules I get from eBay, simply because they draw excessive power for no obvious reason, as compared to the others from the same batch…
(note: in the photo to the right D2 is hard wired via a jumper to the rtc SQW line – leaving an unused breadboard row which I’ve re-purposed to bridge vcc to the power rail on the opposite side of the board. And in this example, D4 provides GND for the indicator LED)

The Main Board:

In this build the six serial UART I/O pins must have 90 degree angled headers to make more room for the RTC board which will sit on top of the main board later.  Solder those header pins onto your Arduino board, and test it with your UART adapter. Generally speaking, about 10% of the cheap modules I buy from eBay are flakey in some way, and it’s quite annoying to discover that after you’ve assembled a logger. Once you know the board is working, remove the power and pin13 LED resistors.  These limit resistors tend to move around from one manufacturer to the next, so you might have to hunting for them on your particular board.  You also need to remove the RESET switch from the board, or that button will be compressed when you put the SD card adapter into place:

{Click any images to see larger versions.}


Solder the side rows of straight header pins so that they project from the bottom of the board.  I usually skip the two reset pins, so that I can re-purpose those screw terminals later as GND and Vcc (photo 3 below) but if your application needs reset functionality then  solder those headers as normal.  Add wires to the top of the board for the A4 (SDA white) & A5 (SCL yellow) lines of the I2C interface.  Add wires to the A6 & A7 vias so that they project from the bottom of the board.

Once all the pins are in place clean any flux residue from the board with 90% isopropyl alcohol and a cotton swab. The final step for the main board preparation is to trim the pin header solder points on the TOP of the board flush with the surface: D4-D9, D10-D13, and A0-A1.  Then affix some double sided tape  in place over those trimmed pins, which will mate with the bottom surface of the SD adapter.

The RTC Module:

The simplest modification to these DS3231 RTC boards is to remove the charging circuit resistor and power LED limit resistor from the circuit board (indicated with the red squares in the first picture).  LIR2032 rechargeable batteries are nominally 3.6v, and will not charge with this module connected to a 3.3v Arduino. Replacing that with a CR2032 will backup the RTC for many years of operation 


Add two layers of double sided foam tape, so that the thickness matches the top surface of the DS3231, and the inside edge aligns with that side of the chip. These two surfaces will mate with tape on the SD adapter board.

Since the RTC board already has 4.7k pullups on the SDA (data) and SCL (clock) lines, you will not need to add them to your I2C sensors.  This board also has a 4.7k pullup on the SQW alarm line.  We will be connecting SDA, SCL, GND and VCC wires to the small cascade port on the module.

The SD Card Adapter:

This SD card adapter comes with small surface mount pullup resistors on the MOSI, MISO & SCK (clock) lines (removed from the dashed red line area photo 2 below).  The Arduino SDfat library uses SPI mode 0 communication, which sets the SCK line low when the logger is sleeping. This would cause a constant drain (~0.33mA) through the 10K SCK pullup on the module if we did not remove it.  I prefer to pull MOSI & MISO high using the internal pullups on the 328P processor, so those physical resistors on the breakout board can also be removed. But be careful to leave the top-most resistor of the four in place to pull up the DAT1 & DAT2 lines.  This keeps those unused pins on the μSD card from floating when the cards are accessed in SPI mode.

sd1 Only remove the bottom three pullup resistors. keep the top one

Add jumper wires to each of the headers pins on the bottom of the SD adapter and trim those solder joints till they have a relatively low profile . Then cut away the vertical header pins from the top of the board. Place a strip of double sided tape on the bottom of the SD card module opposite the soldered wires. This strip acts as a spacer to level the SD board when it is placed in contact with on the double sided tape on the mini style Arduino board.

The SPI connections:
RED:           3.3v regulated
Grey:          Cable select (to D10)
Orange:     MOSI   (to D11)
Brown:      SClocK (to D13)
Purple:      MISO   (to D12)
BLACK:     Ground

The Screw Terminal board:

These screw terminal boards are designed for use with Arduino Nano boards, but if you orient the two correctly when you connect them, labels on one side of the shield will be in alignment with promini pins:


Drilling a pass-through hole lets you bring the jumpers down to those unused pins, and to make other connections to solder points on the underside of the shield without blocking the M3 mounting holes. It is also possible to fabricate your own terminal board.


Attach SD adapter to the Pro mini:

The first step is to attach the SD adapter to the board, but this must be done with a slight overhang, so that at least the red Vcc wire on the SD adapter extends beyond the top surface of the pro-mini board.  It’s OK to leave more overhang than I’ve shown here, but if you leave less, the wires on the RTC cascade port might interfere with access to the serial I/O pins.

Place a strip of double sided tape across the SD adapter board as shown, taking care not to cover the hole showing the card lock spring.  When that tape is in place, bring the ground and Vcc lines from the SD board forward and make a gap in wire insulation so you can splice-solder them to the GND & Vcc pins on top of the pro mini board. This procedure simultaneously connects the rails to the SD adapter, and brings power to the I2C cascade port on the RTC module.

Connect the RTC board:

I recommend that you take a bit of time holding the RTC board in place over the SD & mini combination while the protective covering is still on the tape, so that you get a feel for the alignment before you actually try to stick these parts together. With the cascade port oriented towards the Arduino’s serial I/O pins, the topography of the SD adapter fits snugly into place against the DS3231 chip on the RTC module.

After you stick the pieces together, trim and solder the I2C bus wires to the RTC’s cascade port. Note that it is possible to unstick the parts afterwards by gently levering them apart with a screw driver, but be careful you don’t rip the metal shield off of the SD card adapter in the process.

Attach everything to the Screw Terminal Shield:

If you’ve gotten this far, then you can now relax, because all the tricky stuff is done.  Trim and tin the four SD lines and bring the down to the D10-13 SPI screw terminals just below. Note that D12(MISO)  & D13(SCLK) lines must crossover.  Bend the pins on the RTC board downward and solder jumpers onto all but the 32K output line.

Pass the SQW alarm line (in blue) through the hole and solder it to the D2 pin projecting from the underside of the terminal adapter board.  If you left out the reset pins when initially soldering the headers, bridge those unconnected terminal screws to the adjacent Vcc & GND lines.  Then patch A6/A7, and the four I2C lines from the RTC board to the unused pins at the end of the screw-terminal shield.  I generally run these loggers on 3xAA battery packs with a 2x10M ohm voltage divider providing 1/2 of that battery voltage to A0.  So the last step is to add that voltage divider, along with some extra tape to serve as foot pads.

The battery voltage calculation for a divider with equal value resistors is:   float batteryVoltage = float((analogRead(A0)/ 511.5)*3.3);  But the MIC5205 regulator found on most promini style boards will accept anything between 3.4 to 12v input, so you will need size your resistors to convert the peak battery pack voltage into something below the 3.3v aref limit. To cover that whole range, you’d need a pair that puts 1/4 of the battery voltage on A0, and a R1(high side) = 3*R2(low side) combination would do that, changing the 511.5 constant in the equation above to 255.75    With 5205’s dropout potentially rising to 300mV during 200mA SD writes, I usually shut down the loggers when the main battery falls below 3.75 volts. With Meg-ohm size resistors, I leave that divider connected all the time, but there is a wonderful self-disconnecting voltage divider idea over at JeeLabs for those who want to use smaller resistance dividers. Also keep in mind that resistors you get off of eBay are usually +- 5% (no matter what the vendor claims) which can really throw off your battery readings: ALWAYS measure high value resistors with a good quality DVM before making a divider with them. And with resistances in the megohms environmental contamination such as skin oils, soldering flux residue, etc. can easily reduce the effective resistance in time-varying ways.

As we removed the pin13 LED back at the start,  solder a limit resistor onto the ground of a common cathode RGB and connect that to one of the ground connections, with the other legs going to D4R-D5G-D6B.  I usually add a few labels to keep track of the extra terminal connections, and any re-allocated any pins for a specific build. Unfortunately black sharpie marker doesn’t stick to those green terminal shrouds very well. 

In this example I’ve re-allocated the screw terminals that would normally have been connected to the two reset pins, but you could use under-board wires to re-assign any of the terminals in a similar fashion. For example, if your application will not be using the RX/TX pair, those could be turned into extra Ground or Vcc points. I’ve never understood why the pro-mini design breaks out reset twice but leaves the Aref pin hidden, so adding a wire to the little aref stabilizing cap would let you fix that issue.

Your Logger is ready to go!

As this is simply a different physical arrangement of the same core components, you can follow the logger testing procedures described at the end of  the 2016 Dupont Jumper Build , which also provides links to a basic data logger script to help you get started on your project.  For the build described above, the pro-mini’s MIC5205 regulator delivers sleep currents less than 0.25mA (Promini~0.05mA,  sleeping SDcard~0.05-0.09mA & RTC~0.09mA)
That should should reach a year of operation on 4xAA’s. 

While it took me a day to get the first one of these sorted, the second one took less than three hours, and the third took less than 2 hours. I lost count after that, and now these things seem to be multiplying like tribbles.   If you need unobstructed access to the SPI bus, you can move the SD lines to under-side solder connections as we did for the I2C bus.  This also makes the logger a little prettier, but since I’m usually making these in a hurry, I often leave those wires on the surface.

The photos in this series were made with Adafruits 26AWG silicone wire, but if you are adding more bottom-side connections, switch to smaller diameter 28AWG wire, or make the pass-through hole a bit larger to accommodate the extra lines. Switching the  90° I/O header pins to the bottom of the promini board gives you more room for the RTC wiring.

You can make the component stack more rigid by adding a few strategically placed beads of epoxy putty.  In fact you could hold the whole thing together that way, so long as you take care not to bridge any contacts – especially where the DS3231 header pins are near the metal top of the SD adapter. Also keep in mind that the putty sets rock hard in about five minutes, so if you make a mistake with that assembly method then you’ve bricked the unit – so test all your connections before this last step!

If you are careful about placement of the batteries, you can fit this new screw-terminal design onto the abs knockout plugs that I’ve been using as mounting platforms. This means that you can still fit the logger into the inexpensive 4″ housings
that I outlined for earlier builds with room under the platform for a second battery bank if needed.  Given how often makers need to put a shell around their projects, I’m surprised that no one has taken the old B-Squares idea into three dimensions to create a re-configurable snap-together housing system. These videos from the tutorial set walk you through the method we developed in 2016 to build housings from PVC plumbing parts that are robust enough for real world deployments:

Battery Platform & Sensor Connections

In addition to the I2C bus, I’ve started breaking out A0-A3, rather than digital lines, since those A ports can do double duty as either analog or digital I/O with some code-side settings. With screw-terminals on all lines anyway, I only break those out to connectors for quick sensor swaps in the field.

Potting Sensors & Waterproof Housing Pass-Through Connections

One thing to keep in mind for any project built from eBay parts is that most of those boards use cheap Y5V capacitors; which have terrible temp-coefficients compared to X7R/NPO’s.  So you need to test your project extensively if you want it to operate over a wide temperature range. My home freezer tests to date have been running ok, but I’m not relying on the Pro Mini’s oscillator/clock for anything that is timing-critical.  I do expect to see bus timing drift out of spec at the low temperatures.  For loggers built with I2C sensors, stick with 100kHz for your first few builds, then things “just work every time”…75% of the time. The 1.1v internal band-gap also changes significantly with temperature, so if you use it as Aref, expect to see the readings go up, as the temperature goes down.

Addendum 2017-06-21:

I’ve been on a steep learning curve since the beginning of this project, and you don’t have to dig very far to find stuff on this blog that seemed like a good idea at the time, but later turned out to be completely wrong.  I should write some sort of disclaimer,  but instead I’ll pass along a recent forum comment that summarizes the kind of criticism we’ve been getting lately:

“In the old days, an embedded enthusiast would have designed the thing (and think AVR) from the outset to meet objectives / specs, not struggle with integrating the various modules and meeting very-so-so sleep currents (while thinking Arduino). Surely, this is a textbook example of how not to do embedded engineering if you are doing it for a salary.”

It’s good to have someone rattle your cage once and a while, and I’ll admit they have a valid point( In addition to the fact that I’m not an engineer, and I don’t get paid…)  People complain like that about the pitfalls of using modules & libraries all the time, but the thing I like about the Arduino platform is that you don’t have to know everything before you can do anything. I’m just figuring it out as I go along.

Still, an affront like that demands some kind of response.  So to defend the honor of my fellow Arduino Kool-Aid drinkers, let’s look at how you might tweak those modules to improve this loggers sleep current performance:

1) Pin Power the RTC:

These DS3231 boards don’t get a lot of love because they have about the worst battery charging circuit ever devised, and an equally useless LED power indicator. But for less than a buck delivered to my door, these boards are considerably cheaper than the raw components they carry: so yeah, I’m going to look under that rock and see what I find.  That charger can be disabled with a simple flick of the soldering iron, and at this point we have years of successful run time using a non rechargeable CR2032.

More interesting is the fact that on a 3.3v system, you can leave the charger in place, cut the Vbat line at the battery terminal, and patch in a 1N4148 diode to match the one already on the module.  After the CR2032 burns down a bit the two circuits balance out, and the main battery then takes over supplying the 3µA timekeeping current: 

This mod cuts your loggers sleep current by about 0.09 mA.  Keep in mind that this only works because the RTC was designed for a controlled switch over to the backup power circuit.  In general, de-powering I2C devices is not a good idea because the pullup resistors keep the SDA and SCL lines high. When a regular I2C connected chip has no power, you could leak current via SDA and SCL through the I2C device to GND. 

2) Buffer your data before saving:

Those DS3231 RTC modules also have a 4K EEprom on the board, and that lets me save data in 32byte page-writes with reasonably simple code. While the I2C bus is dead dog slow by embedded system standards, you can hang oodles of things off those wires without worrying about cable select lines, or some gummy protocol weirdness.  For an extra buck, you can add 32K more memory without any significant changes to your Arduino script. That usually buffers about a week’s worth of data before I need to save to the SD, even though I’m still making the unforgivable programming sin of storing everything in ASCII string variables

Small red-board versions of the AT24C256 tuck nicely into the 12mm gap between the headers, but you could just as easily put an I2C sensor into that space. If you get boards with the address pins broken out (the one above doesn’t), you can connect up to four of these eeproms to the same logger. A side benefit is that the 32K eeproms are rated to 400kHz, while the 4k’s are only 100kHz, so the upgrade also lets you accelerate the I2C bus clock, since the DS3231 is also rated for 400kHz. 

This carrier module lets you configure up to eight I2C addresses with DIP-8 chips, or you can roll your own.

Even larger eeproms are available in the code compatible AT series, but I’m not sure if the wire library supports the 64byte page writes they typically use. If I had the chops, the path to an IC-only logger is obvious. Paul Stoffregen’s SerialFlash library is another interesting option, as it allows one to write data to an SPI Flash memory chip with a filesystem-like interface similar that on an SD card. It’s also worth mentioning that the 64k AT24C512 is pin-compatible with the SOIC family that includes the 32k AT24C256 and the AT24C32.  So you could also just replace the 4k eeprom that comes with the RTC module with a 64k chip or take it all the way up to the 128k AT24C1024. One thing to watch out for with some eeproms like the 24LC512: “When doing a write of less than 128 bytes, the data in the rest of the page is refreshed along with the data bytes being written. This will force the entire page to endure a write cycle, for this reason endurance is specified per page.” So it’s worth the time to buffer your data until you can do a page write with some eeproms. AT24C512’s have the same 128-byte Page size with 16-bit addressing , but partial page writes are allowed.

I’d love to switch over to superfast Fram chips, but Adafruits module lists a very high standby current of 27uA, while most eeproms sleep between readings at about 1uA. In a data logging application that kind of power use between cycles removes the benefit of the faster data-saving. However if I was working with really fast data inputs, I’d consider Fram for something like a ramdisk.

3) Cut power to the SD card:

The Promini clones I’m using have a tap at the back that is conveniently located for ground side switching of the SD cards. This lets me tuck a 2N2222A under the board with that extra eeprom.  Cards hit the regulator pretty hard when they initialize, causing significant voltage drops, and if you find that your unit is not saving properly with this technique it’s probably because those transient lows are causing restarts.  I usually add caps to provide and extra 30μF on the rails to help handle those spikes, and I may bump that even higher for cold climate deployments since cheap ceramic caps have terrible temperature constants. Older Nokia 256 & 512 Mb SD cards have significantly lower writing currents than modern SD cards – often holding between the 50-75 mA range during init.

Code and information about this technique are described in some detail on the
SD power post This is a relatively high risk strategy, but it can cut your sleep current by another 0.1mA.   (Note that while the BJT shown above works fine, on more recent builds I’ve switched to the Supertex TN0702 mosfet for ground side switching with 3.3v logic)

4) Replace the voltage regulator:

The MIC5205 on those pro-mini clones is not very efficient at low power (~10-20%), so replacing that with an MCP1702-3302E/TO can cut your remaining sleep current by more than 50%.  The 10uF caps from the original reg. are still in place on the board, so this upgrade has been working fine with the 1700 just hanging off one side. Also keep an eye on the dropout voltage, which on the MCP1700 series can rise as high as 600mv if you push them to their 250mA maximum – many SD cards will pull up to 200mA for brief periods. This requires a fairly high input cutoff around 4v.  

If soldering in close quarters like this gives you the heebie jeebies, you can dead-bug the reg & voltage divider onto the battery connector like I did for the 2016 builds.  Alternatively you can simply build your logger around a small form-factor boards that already have an MCP1700 series regulator, such as the Rocket Scream Mini Ultra  or the Moteino. The HT7333-A is a less expensive TO-92 LDO regulator with similar specs.

AN1025 FIG 2:  Efficiency %  ~=  VOUT  /  VIN

All linear regulators (including LDO’s) are less efficient as the raw input voltage increases. (So a 4xAA 6v battery supplying a 3.3v rail system will be about 55% efficient under load)  For higher drain processors sometimes people use buck converters (typically 90% or better efficiency) to step down higher input voltages, and then feed that ripply output into an LDO to smooth it out rail for delicate components.  But switching regulators often fall down to 20% efficiency or less at the low micro-amp sleep currents you see with dataloggers, while the MCP1700 keeps chugging along at around 50% until your load approaches its 1.6μA quiescent current.

(Note:  that the pin maps are different for each board, so you might need to adapt the wiring connections shown in the information above.  Also keep an eye out for fake LDO regulators with high dropout voltages. If you’re not sure what you’ve received, bump the input voltage cut-off to at least 1v. The Promini’s 10uf tantalum caps should be enough to damp oscillations, but it probably wouldn’t hurt to add another on the output for extra stability)

And the result?

This optimised logger is drawing less than 0.02mA sleep current with a MS5803 pressure sensor in tow. That’s 5x more than you’d see from a raw 328p, but not bad considering that we built a fully functional data logger out of 99¢ eBay modules. (Note: with the default MIC5205 reg. in place, the same logger would draw ~0.055mA)

These modifications to the basic build plan probably violate some important electrical engineering rules, and I can almost guarantee that nothing will work properly the first time you try it.  But don’t let the fact that you might destroy a few cheap components along the way prevent you from just going for it.  Although it might be best if you don’t show your project to any engineer friends at the beginning… unless they’re working on a new textbook 🙂

Addendum 2017-12-22:  Low sleep current the easy way with a TPL5110

Lady Ada demonstrates their new board HERE. and cfastie has been wiring up his own breakout boards at

It can take a bit of Kung-Fu to implement the power optimization methods described this post, and if you haven’t quite reached that level the Adafruit TPL5110 Low Power Timer can bring any 3.3v logger down to ~0.02mA for a fiver.  Cfastie has is putting this board through it’s paces over at It’s too bad the TPL5110 timer IC can’t tolerate more than 5.5 volts, because it would be a god-send for all those UNO based loggers out there, but their minimum input is ~6v.

The only problem I can see with the TPL5110 is that you don’t have precise control over the sleep interval [Timer accuracy 1% (typ)], and temporal inconsistency becomes important for many kinds of analysis on time series data over long periods.  Since the sleep currents I’m seen now are already in the 20uA range, I probably will not bother with full power-down methods till I start needing to run on tiny batteries.  But when I do get there I will probably use the DS3231 RTC alarm (which outputs low) to control  a P-channel Mosfet on the high side of the main battery supply. The trick is finding a mosfet with a low enough gate voltage, so that it will be fully turned off till the RTC alarm fires ( AO3401? ).  When the SQW alarm goes low, it turns the p-mosfet on and powers everything including the mcu board which would then get to work taking samples and storing data. The final step after everything is done would be to re-program the next RTC alarm, and then write zeros to the alarm flag registers (A1F and/or A2F) which would release the SQW line on the gate of the mosfet. (+ you would need a pullup resistor on the gate to make sure the pFet turned off properly – but that’s just a pullup and could be quite large- in the Meg ohm range). The RTC itself would run off a coin cell, and DS3231 RTC will generate such alarms when running on backup power.  Kevin Darrah talks about a slightly more advanced latching kill switch approach that could also be adapted.

My biggest concern about kill switches is that the SD card can be corrupted if power is immediately removed during numerous conditions, so you need to make sure you can safely shut down after buffering all your persistent variables in the eeprom.  Most Arduinos will boot in less than a second, but capturing rapidly occurring events like the sensor interrupts we use for our cave drip counters would be nearly impossible.

Addendum 2018-02-13: 

Brian Davis (another builder who tortures his loggers in caves) passed on a neat idea for using Pro Minis with Nano screw-terminal shields. Simply add male pin headers beyond the edge of the board, and then solder the A4/A5 and A6/A7 connections to the top of those pins. Then pull Vcc from the UART connector. If you run the SQW alarm wire over the top to D2,  you no longer need to drill a hole in the shield to bring wires down to the bottom of the board like I did, and you can still pull the Pro Mini out of the adapter board later if you have to. 

An alternative hack that may be useful to people wanting to break out those pesky offset A4 & A5 pins would be to jump them over to re-purposed A2&3 pins, which you make available with a couple of trace cuts. Of course this reduces the number analog input lines on your logger, so it’s not quite as functional as Brian’s extended headers.

Addendum 2018-05-23:

Well Brian’s been at it again, coming up with more alternative build ideas:

Another benefit of the screw-terminal board is that you can connect the assembly to a 2″ test cap by simply looping a zip tie through one of the mounting holes.

Both of us are working with the limitations of a 2″ housing tube, and using the battery holder as a mounting bracket is a great solution. I also like the addition of the screw terminals on the RTC’s cascade port (Sparkfuns Quic adapter board is also a pin-matched option there if you are using their sensors ) For the Pearls, we wire the DS3231 in the opposite direction, and we have some high drain multi-sensor builds that require ganged battery packs to go the distance.  So for us it makes sense to stick with the ‘stacked sandwich’ arrangement, with the batteries in completely separate containers. With high temperature variations, and long duration deployments, we also see a fair number of battery leaks.  This ruins the terminals forcing us to replace the packs more often than we’d like.

Even so, I’m utterly thrilled to see these creative ideas coming out of the logger building community, as that will ultimately give the open science hardware movement some real legs. And while we are on the topic of build variations, it’s worth noting that there are a host of Pro-Mini style board variations out there that may help with these part re-arrangements:

A4 to A7 on back row Analog & SPI on the back row(s)
Julian Illet uses these in his
ammeter build.
328PB =  two hardware SPI serial ports though I’ve had success with software SPI on the analog lines to drive displays.

I’ve been using the more common variant with GND-A6-A7 on the back row as I bring the I2C lines forward to connect the RTC, but I can see how having those offset pins at the back row could make soldering easier for some builds.

Cutting Power to Secure Digital Media cards for Low Current Data Logging

This composite graph compares logger current during identical sd.begin(), File Open & File Close sequences with two different Sandisk brand SD cards on the same datalogger. The 256MB card used less than 3mAs, while the 2GB card burned more than twice as much power during this initialization. Older low cost/size cards often perform better in SPI mode, which is simply an after-thought for high end cards, because it’s required by the SD spec. Other file open/close artifacts can occur. Write methods are also a factor.

The tweaks I discuss in the  power optimization post bring sleep current on a typical build into the 0.1-0.12mA range; leaving the sleeping μSD cards  as the largest remaining power consumer on the Cave Pearl loggers.   And those cards have been a burr under my saddle for quite some time now, as they are probably responsible for most (if not all..) of the power consumption irregularities that were showing up in some of the battery voltage logs.

I already knew the various brands of secure digital media (SD) cards could have dramatically different sleep current, but a comment in the forum by William Greiman (the author of the SdFat library for Arduino) made me look a little deeper at what they were doing the rest of the time:

“Performance of microSD cards is not easy to predict on Arduino.  Arduino uses SPI to access the cards and the SD library has a single 512 byte cache. Modern SD cards are designed to be used on the high speed 4-bit SDIO bus with very large (16 KB or larger) multi-block writes and reads so they must emulate the single block access that Arduino libraries use.  This can mean much internal data movement, erasing of flash, and rewriting of data.”

This shows up clearly during data save events:

These screen captures of IDE serial potter output show current drawn during a data writing event with 256mB (left) and 2GB (right) Sandisk SD cards.  4kB of CSV format ASCII data was saved, and the gaps between the writing spikes were caused by i2c coms while SDfat’s cache was filled with data retrieved from the Eeprom on the RTC module.   (Note: see our article in Sensors for more details about the the code behind this logging event)

After seeing that I tested a variety of different SD cards, finding that save event power use increased by more than 5x over the range from old 64 & 128mb Nokias to newer 2 & 4Gb Sandisk cards. There is no guarantee that any given brand’s controller will handle SPI coms gracefully, and newer cards from top manufactures often have bad SPI performance since they are are not expecting anyone to use high capacity cards with bus.

It took me a good while to realize that I had fallen into yet another forest-for-the-trees situation, because even the worst offenders were only using ~30 mAs per save event, but all the cards were delivering similar sleep currents.  A day has 86,400 seconds in it, so the best sleepers, coming in around 70μA, were still burning six thousand milliamp seconds per day overall…

That brought me back to the question of de-powering those SD cards. I had been discouraged from trying this early in the project by some of Grieman’s other forum remarks where he suggested that there was nothing in the default SD library to support multiple shut downs & restarts safely.  But over time I found that Nick Gammon, and several others had card power control working with SdFat, and seeing the folks at OSBSS claim they had power cycled SD cards more than a hundred thousand times, was really the final straw.

As I had no logic level P-channel fets lying around I went with a garden variety 2n2222 BJT, configured as a ground side switch with a 30k pulldown. Driving it to saturation using a 330Ω base resistor (assuming Hfe = 30) should give me enough wiggle room to handle 150mA spikes, though it will burn 10mA to keep the BJT on for full second I needed to wait before pulling the plug.  Write latencies for SD cards can be quite large, and some cards have more than one stage of sleep, drawing around 1.0 ma for maybe a second before entering deep sleep.  But with 6000 mAs/day on the other side of the scale, I could afford the extravagance.

The cross leakage stuff I’d seen on EEVblog convinced me that I needed to actively pull up all of the SPI pins after the ground was disconnected. I cobbled together a set of  ON/OFF functions with pinmode commands, but it did not work reliably until I switched over to port manipulation (like they did at OSBSS), so the lines were all pulled simultaneously.  I was already disabling peripherals like the ADC with the PRR register, but that was just to save a little runtime power. Now it was required because when SPI is active, it controls MISO,MOSI & SCLK.  So you must shutdown the SPI interface before you can set those pins directly.

#include <LowPower.h>
#include <avr/power.h>
#include <SPI.h>
#include <SdFat.h>
SdFat sd;                                   // Create the objects to talk to the SD card
SdFile file;
const byte slaveSelect = 10;   // sd card slave select on pin D10
#define SDpowerPin 9            // pin controlling the BJT on the ground line for the SD card
boolean SDcardOn = true;     // flag for error routines
byte  keep_SPCR;
// spacer comment for blog layout
void setup () {
keep_SPCR=SPCR;                  // save the default SPCR register contents
. . . }

void turnOnSDcard() 

pinMode(SDpowerPin, OUTPUT); digitalWrite(SDpowerPin, HIGH); //turn on the BJT on SD ground line
delay(6);                                            // let the card settle
// some cards will fail on power-up unless SS is pulled up  ( &  D0/MISO as well? )
DDRB = DDRB | (1<<DDB5) | (1<<DDB3) | (1<<DDB2); // set SCLK(D13), MOSI(D11) & SS(D10) as OUTPUT
// Note: | is an OR operation so  the other pins stay as they were.                (MISO stays as INPUT) 
PORTB = PORTB & ~(1<<DDB5);  // disable pin 13 SCLK pull-up – leave pull-up in place on the other 3 lines
power_spi_enable();                      // enable the SPI clock 
SPCR=keep_SPCR;                          // enable SPI peripheral
delay(10);  SDcardOn = true;       // just a flag

void turnOffSDcard() 

SPCR = 0;                                         // disable SPI
power_spi_disable();                     // disable SPI clock
DDRB &= ~((1<<DDB5) | (1<<DDB4) | (1<<DDB3) | (1<<DDB2));   // set All SPI pins to INPUT
PORTB |= ((1<<DDB5) | (1<<DDB4) | (1<<DDB3) | (1<<DDB2));     // set ALL SPI pins HIGH (~30k pullup)
// Note: you must disconnect the LED on pin 13 or you’ll bleed current through the limit resistor
LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_OFF); // wait 1 sec for internal SDcard housekeeping 
pinMode(SDpowerPin, OUTPUT); digitalWrite(SDpowerPin, LOW);  //turn off BJT controlling the ground line
delay(6); SDcardOn = false;

These two functions book-end any code that needs to write data to the SD cards:

turnOnSDcard();    flushEEpromBuffer();    turnOffSDcard();

That SD card data saving function starts by checking the main battery to make sure there is enough power to save the data without a brown-out, and then re-initializes the card with sd.begin before opening any files:

vBat = readBattery();   // This function shuts down the logger if the main battery is below 3.65V
if (!sd.begin(chipSelect, SPI_FULL_SPEED)) {
Serial.println(F(“Could NOT initialize SD Card”));Serial.flush();
error();  // note: the error event includes:  if (SDcardOn) {turnOffSDcard();}    inside the function
delay(10);, O_WRITE | O_APPEND);  //see this post by Grieman
//…save your stuff…

Looking at the datasheets for the Mic5205, or the Mcp1700,  you see the regulator dropouts can reach 300mV at 100mA+ currents you could see during SD card initialization, so your input cutoff for a 3.3V system needs to be above 3.65V to handle the load.   After the data is saved it is critical that all open files are closed properly before the turnOffSDcard function gets called, otherwise your data will be lost. The graphs tell me that a full one second delay before powering down the card is probably longer than it needs to be,  but in data logger applications it’s pays to err on the side of caution. According to Greiman:

“The standard says reliably removing power is not supported in SPI mode. It does suggest that you can remove power one second after the card goes not busy but does not guarantee this will work. You can’t depend on isBusy() to power down a card. It only means the card can accept a command. It may still be programming flash or moving data for wear-leveling. You really need the one second delay after not busy.”

Lately I’ve been using these 60¢ SD adapters, and removing the bottom three 10k smds that these boards have on the SCLK, MOSI & MISO lines. (the other resistor keeps the ‘RSV’ pins from floating) Having a pullup on the clock line wasted power during mode o sleep as the clock idles low, but now that I’m cutting power rather than just sleeping the SD cards, I could leave those resistors in place…then I wouldn’t need to pull up those lines in the code, (though I’d still have to pull SS…) Thing is, Grieman says SPI should not have pull-ups or pull-downs on MISO, MOSI or SCK. So I’ll stick with the 328’s internal 25k pulls for now, because doing it in code is reversible. But that does leave some pins floating during the boot process.

Of course, it was pretty flakey the first few times I tried it. Half of the loggers worked, but the other half were restarting every time there was a data save event (killing off SD cards in the process…) This problem affected every logger built around the Rocket Scream Ultra, which has been one of my favorite small form factor boards.  Closer examination of the two-penny clones that were working ok revealed that they had 10μF tantalum capacitors beside the voltage regulator rather than the little 1μFs beside the Ultra’s MCP1700.  So those cards were hitting the rails pretty hard when the SD ground line was re-connected, and this caused brief transients that were low enough to restart the processor on half of my units. Some add a small (33Ω) resistor in series to limit these inrush currents, but I found that adding 2-3 10μF (106) ceramics to buffer that spike got them all working ok, and for field deployment units I’ll probably add more.

I set a several units running on the bookshelf, with a rapid six second sampling interval. A couple of weeks later they were all still going, with some of them seeing more than 30,000 SD card power cycles without error. Given that the loggers normally see less than one save event per day, I’m calling that a successful test. If you run into issues, the first thing to try is extending that delay after sd.begin() and adding a few more delays throughout your functions. If you look at the spec you find that SD cards are allowed to take huge amounts of time for everything from initialization, to file open/close. While I did not see that in cards I used for my tests, these latencies are ‘officially’ allowed to stretch well beyond 100ms.

With both pin-powering on the RTC, and ground line switching on the SD card, the loggers get down to between 0.03-4mA between samples, which should push my operating lifespan into multi-year territory. Or, if I’m really lucky, they’ll make it through one winter-time deployment in Canada 🙂

I was also pleased to discover that the On/Off code seems to work on loggers that do not have the ground side switch installed provided I do not try to re-initialize the cards with sd.begin.  SPI shutdown & line pullup seems to cause the SD cards to enter sleep mode more quickly than they did before, and I have not seen any current leakage. So hopefully I won’t have to maintain vastly different code versions for older non-switched loggers. (Update 2017-06-12: Further tests of SPI shutdown, without the BJT to disconnect power from the SD card have not been reliable. Some worked, some didn’t. When I figure out why that is I will post an update)

Addendum 2017-06-06

A commenter over at Dangerous Prototypes made a point about my use of the 2n2222 which is important enough that I should pass it on:

“I’m surprised he didn’t check the 2N2222. Look at its data sheet, the V(CE) performance is not great. Take 0.3V at 100mA, then the SD card would have been actually running at 3.0V, right at the -10% VCC rating edge. I’m surprised the problems are not worse. Of course it would be extremely sensitive to VCC sag…”

The drop across the collector-emitter was something I had simply missed, and I still struggle to read those datasheet graphs properly.  And I was so used to seeing card operating voltage specified between 2.7-3.6v, that I also missed the fact that in SPI mode, only 3.3v is officially supported. The net result is that I’m probably sailing closer to the wind here than I realized, and I’m going to call this technique “experimental” until I see real-world deployments saving more than a year of data safely. And if I stay with ground-side switching in future, I will start looking for a good logic level N-channel  MOSFET, with low on resistance, to replace that BJT. The Supertex TN0702 looks like a good option with the promini’s with 3.3v logic.

Addendum 2017-06-06

This card gets thrown straight into the garbage.

Just thought I should post a reminder to test your SD cards thoroughly before embarking on SD power shut down experiments. I use SD formatter v4.0 & H2testw.  An occasional check with H2testw after deployment is also a good way to make sure that you are not damaging your cards over time…




Addendum 2018-03-22

I’ve been doing more experimenting with de-powering the SD cards during sleep.  It works great most of the time, but I was still seeing a few frustrating re-boots on some loggers when the SD power was restored. To get to the bottom of this I set up a unit repeating a standard SD data-save cycle and looked at the current through a shunt resistor with my Arduino DAQ.  The results made it pretty clear what was happening:   (click image for larger versions)

Kingmax 128mb  (40mA peaks,  25mA sustained ~60ms,  best performance of all cards tested)

Nokia 256mb  (50mA peaks, 35mA sustained ~40ms)

Nokia 128mb  (65mA peak,  35mA sustained ~200ms and 55 mA sustained 2x 100ms)
[these artifacts occurred during every single save, and on the other Nokia 128mb cards tested]

Sandisk 512mb  (75mA peaks, 50mA sustained ~60ms)

Sandisk TRANSflash 128mb  (150mA peaks, 90mA sustained ~80ms)

SanDisk 2Gb  (90mA peak, 55mA sustained ~350ms)

NONAME 1Gb   (110mA peaks,  90mA sustained ~45ms)

SanDisk 32Gb Ultra  (110mA peaks, >110mA sustained ~200ms)

MUVEmusic 1Gb(+3Gb)  (>110mA peaks, >110mA sustained ~400 ms!!! )

All these tests were done with the same code, on the same logger – only the cards were changed.  All of the cards shown above were successfully saving the data to the CSV file. I was using a fairly large 10 ohm shunt, with the internal 1.1v as Aref so those clipping plateaus at ~110 mA were a limit of my method. The SD spec says cards can actually draw up to 200mA during initialization events, and I suspect those last two get up to at least 150mA.

Given the huge difference between the peak currents, and the size of the sustained power loads, it’s not surprising that some of the loggers were suffering from brown-out restarts. A few caps could buffer the short spikes, but those larger sustained loads were too much for the MCP1700’s I’m using.   Another thing that’s important to note here is that (with the exception of the 32Gb) these cards were selected from a batch that I had already tested for low sleep currents.  So it looks like I’ll have to retest all cards that are destined for the low-power logger deployments that de-power the SD cards. Generally speaking it’s still better to stick to the older 256mb cards, though some of those have strange housekeeping events at every data save. It’s all just a reminder that SD memory is actually more complex than the Arduino since the card itself may contain a 32 bit arm core.

Addendum 2018-10-03

I’ve a new crop of identical drip loggers running through pre-deployment testing with the same code.  Since these units were de-powering the SD cards between saves, I didn’t worry too much about which cards I put in the units figuring that a few intermittent power loads would not affect the operating lifespan very much. 

In the following records I read the battery voltage every 15 minutes (the blue line) and also track the “lowest” battery reading during logger events like SDcard saves (the orange lines)  with small daily saves, and a big data transfer event about every 5 days.  The 15 minute record is quite variable as the AA alkaline battery voltage responds to the ambient temperature in the room.

This first graph is from a logger with a Nokia 256mb SD card, and the lowest main battery readings hold within 50mv of the typical readings:

and this record is from a logger with the generic 1GB sd card shown in the earlier graphs:

The width of those drops is an artifact from the fact that “lowest reading per day” variable only gets reset at midnight. Since there’s a 5-10ms cap stabilizing delay in my ADC reading function, and the SD code is blocking during the high-drain card initializations, it’s likely that the maximum voltage drop on those cells was  larger than the 250mv that showed up in this record. Any main battery reading that approaches the main regulators minimum input voltage sends the loggers into a controlled shutdown, so those dips could result in a significant amount of missed run-time by triggering the shutdown too early. If you can’t get your hands on those old Nokia cards via eBay, I’d suggest you use lithium batteries to avoid this voltage droop issue when using larger, newer, micro SD cards. The tests I did with lithium cells showed virtually no SD writing drops no matter which card was in the unit, and this effect will no doubt be amplified when deployment temperatures approach zero degrees Celsius.

Tutorial: Calibrating Oversampled Thermistors on an Arduino Pro Mini

Selecting a thermistor (& series resistor) value

Most of the material you find on thermistors makes the assumption that you are trying to maximize sensitivity and interchangeability. But oversampling gives you access to enough resolution that sensitivity is less critical, and interchangeability only makes sense if you are putting them in a product with good voltage regulation. In that case, precision thermistors like the ones from US sensor are a good option, but according to Campbell Scientific, that choice has other knock-on implications:

“The resistors must be either bought or selected to 0.02% tolerance and must also have a low temperature coefficient, i.e. 10 ppm or preferably 5 ppm/°C.”

Like many better quality components, these resistors are often only available in SMD format, with minimum order quantities in the thousands. If you use a typical 1% resistor with a T.C. of 50 ppm or more, you could introduce errors of ±0.1°C over a 50°C range, which defeats the point of buying good thermistors in the first place.

Still, if I was only building a few sensors, I’d spring for the good ones. But now that I have oversampling working on the Arduino, I’d like to add a thermistor to every logger in the field, and the mix of different boards already in service means I’ll have to calibrate each sensor/board combination. That time investment is the same whether I choose a 10¢ thermistor or $10 one.

Power consumption is also important, making 100kΩ sensors attractive although I couldn’t even find a vendor selling interchangeable thermistors above 50k.  A low temperature limit of 0°C (the units are underwater…) and putting 1.1v on aref to boost sensitivity,  requires a 688k series resistor, which is far from the 1-3x nominal usually recommended:

Here I’ve overlaid an image from Jason Sachs excellent thermistor article at Embedded Related, which shows I will only see about ⅓ of the sensitivity I would get if I was using a 100k series resistor. I highly recommend reading Jason’s post, despite the fact that I’m ignoring almost all of his good advice here…  🙂

Using the internal band-gap voltage as aref improves the ADC’s hardware resolution from 3.22mV/bit to 1.07mV/bit.  This trick gives you a extra bit of precision when you use it at the default 10bit resolution, and I figured I could do it again to compensate for the lost sensitivity due to that big series resistor.

In return, I get a combined resistance of at least 700k, which pulls only 4.7μA on a 3.3v system.  Such low current means I could ignore voltage drops inside the processor and power the divider with one of Arduino’s digital pins.  In practical terms, burning less than a milliamp-second per day means adding a thermistor won’t hurt the power budget if I leave it connected to the rails all the time; which you can only do when self-heating isn’t a factor.  This is quite handy for the bunch of old loggers already in service out there, that I want to retrofit with decent temperature sensors. 

Even 100 ohms of internal chip resistance would produce only 0.5mV drop,  so depending on your accuracy spec,  you could use 16-channel muxes to read up to 48 thermistors without worrying about cable length.  There aren’t many of us trying to connect that many temperature sensors to one Arduino, but using a 100k  thermistor also makes me wonder if you could mux a bank of different series resistor values, pegging the divider output at it’s maximum sensitivity over a very large temperature range.

What is a reasonable accuracy target?

Combining 5¢ thermistors & 1¢ metfilms, means my pre-calibration accuracy will be worse than ±1°C.  Cheap thermistor vendors only provide nominal & βeta numbers, instead of resistance tables, or a proper set of Steinhart-Hart coefficients. So I might be limited to ±0.4°C based on that factor alone.  And it took me a while to discover this, but βeta values are only valid for a specific temperature range, which most vendors don’t bother to provide either.  Even with quality thermistors, testing over a different temperature range would give you different βeta values.

In that context, I’d be happy to approach ±0.1°C without using an expensive reference thermometer.  Unfortunately, temperature sensors in the hobby market rarely make it to ±0.25°C.  One notable exception is the Silicon Labs Si7051, which delivers 14-bit resolution of 0.01°C at ±0.1°C.   So I bought five, put them through a series of tests,  and was pleasantly surprised to see the group hold within ±0.05°C of each other: 

Temps in °CCompared to what I usually see when I batch test temperature sensors, this is pretty impressive for an I2C chip that only cost $9 on Tindie.

Ideally you want your reference to be an order of magnitude better than your calibration target, but given the other issues baked into my parts, that’d be bringing a gun to a knife-fight. 

So my calculations, with oversampling, and the internal 1.1v as aref become:

1) MaxADCReading                  (w scaling factor to compensate for the two voltages)

= ( [2^(OverSampledADCbitDepth)] * (rail voltage/internal aref) ) -1

2) Thermistor Resistance        (w series resistor on high side & thermistor to GND)

= Series Resistor Value / [(MaxADCReading / OverSampledADCreading)-1]

3) Temp(°C)                                  (ie: the βeta equation laid out in Excel)

=1/([ln(ThermResistance/Tnominal R)/βeta]+ [1.0 / (NomTemp + 273.15)]) -273.15

Seeing the error in my ways

I knew that the dithering noise would have some effect on the readings, and all the other source ADC of error still apply.  Switching to 1.1v reduces the absolute size of most ADC errors, since they are proportional to the full scale voltage. But the internal reference is spec’d at ±0.1v; changing the initial (rail voltage/aref voltage) scale factor by almost 10%.  Since all I needed was the ratio, rather than the actual voltages, I thought I could address this chip-to-chip variability with the code from Retrolefty & Coding Badly at the forum.  This lets Arduinos read the internal reference voltage using the rail voltage as aref.

I started testing units in the refrigerator to provide a decent range for the calibration:

Si7051 in blue vs 100K thermistor in red. The sensors were held in physical contact. ADC was read with 1024 oversamples providing approximately 15bit resolution. Temps in °C.

and strange artifacts started appearing in the log.  The voltage readings from both the main battery and the RTC backup battery were rising when the units went into the refrigerator, and this didn’t seem to make sense given the effect of temperature on battery chemistry:

Si7051 temp. in °C on the left, with the RTC backup battery (V) in green on the right axis. The CR2023 is monitored through a 2x10MΩ divider, using the 3.3v rail as aref. The large number of ADC readings needed for oversampling has the side benefit that it lets you read very high impedance dividers, but by the time you reach 10Meg ohms, you pick up 5-10 points of noise in the readings. Which is why that coincell voltage line is so thick.

I think what was actually happening was that the output from the regulator on the main board, which provided the  ADC’s reference voltage for the battery readings, was falling  with the temperature.

When I dug into what caused that problem, I discovered that temperature affects bandgap voltages in the opposite direction by as much as 2 mV/°C.  So heating from 0°C to 40°C (and some loggers will see more than that…) reduces the 328P’s internal reference voltage by as much as a tenth of a volt. In fact, bandgap changes like this can be used to measure temperature without other hardware.  This leaves me with a problem so fundamental that even if I calculate S&H constants from a properly constructed resistance table, I’d still be left with substantial accuracy errors over my expected range.  Argh!

Becoming Well Adjusted:  (Beta ain’t better…)

These wandering voltages meant I was going to have to use the internal voltmeter trick every time I wanted to read the thermistor.  It was mildly annoying to think about the extra power that would burn, and majorly annoying to realize that I’d be putting ugly 10bit stair-steps all over my nice smooth 15bit data. This made me look at that final temperature calculation again:

Temp(°C) =
1/([ln(ThermResistance/Tnominal R)/βeta]+ [1.0 / (NomTemp + 273.15)]) -273.15

which I interpret as:

 =fixed math(  [(ADC outputs / Therm. nominialR ) / Therm. βeta]  + (a #) ) – (a #)

Perhaps tweaking the thermistor’s nominal value (which I only know to ±5% anyway) and changing the (fictional) βeta values would compensate for a multitude of sins; including those voltage reference errors?  Then I could just pretend that (rail/aref) scaling factor had a fixed value, and be done with it:         (click image to expand)

So in my early tests, all I had to do was adjust those two constants until the thermistor readings fell right on top of the reference line.  Easy-peasy!

Well …almost. Repeat runs at 15bit (1024 samples) and 14bit (256 samples) didn’t quite yield the same numbers.  Applying the best fit Nominal and βeta values obtained from a 15bit run to 14bit data moved the thermistor line down by 0.05°C across the entire range (and vice versa). So the pin toggling method I used to generate the dither noise introduces a consistent offset in the raw ADC readings.  While that doesn’t completely knock me out of my target accuracy, I should generate new calibration for each oversampled bit depth I intend to use. It’s still good to know that the dithering offset error is consistent.

Throwing a Big Hairy Fit

I was pleased with myself for the simplicity of the Nominal/βeta approach for about two days; then I pushed the calibration range over 40° with a hot water bath:

Blue=Si7051 , Orange = 100k NTC thermistor.  1024 oversamples = ~15bit. Temps in °C.

This gave me targets at around 40, 20 and 5°C.  But no combination of Nominal & βeta would bring all three into my accuracy range at the same time.  Fitting to the 20 & 40 degree data pushed the error at 5°C beyond 0.2° :             (click image to enlarge)

…and fitting to 20 & 5, pushed the 40C readings out of whack.  After more tests I concluded that tweaking βeta equation factors won’t get you much more than 20° of tightly calibrated range. 

My beautiful plan was going pear-shaped, and as I started grasping for straws I remembered a comment at the end of that Embedded Related article

“… in most cases the relationship between voltage divider ratio and temperature is not that nonlinear. Depending on the temperature range you care about, you may be able to get away with a 3rd-order polynomial or even a quadratic..”

Perhaps it was time to throw βeta under the bus, and just black-box the whole system?   

To find out, I needed to prune away the negative temperature regions where the voltage divider had flat-lined, and remove the rapid transitions since the thermistor responds to changes more quickly than the si7051:                 (click image to inflate)

Then it was time for the dreaded Excel trend line:

Ok, ok. I can hear people inhaling through their teeth from here. But with 15 sigfigs, Excel seems like the height of luxury compared to the constraints in μC land.  I wonder what an advanced modeler like Eureqa would have produced with that dataset? 

The trick for getting workable constants is to right-click the default equation that Excel gives you, re-format it to display scientific notation, and then increase the number of displayed digits to at least six.  

Some people use the LINEST function to derive these polynomial constants but I’d advise against it because seeing the raw plot gives you a chance to spot problems before you fit the curve. When I generated the first Temp vs ADC graph, the horizontal spread of the data points showed me where the thermistor and the reference thermometer were out of sync, so I removed that data.  If I had generated the constants with =LINEST(Known Y values, X values^{1,2,3,4})  I could have missed that important step.

For the following graphs, I adjusted the trend line to display to nine insignificant digits:     

Blue =Si7051 reference, Orange is that 20&40 best fit from tweaking Nominal & Beta values, and the yellow line is the 4th order polynomial from Excel.   Temps in °C. (Click to embiggen)

It took a 4th order polynomial to bring the whole set within ±0.1° of the reference line and 5th order did not improve that by much.  Now I really have no idea where the bodies are buried!  And unlike the βeta equation, which just squeaks in under the calculation limits of an Arduino, it’s beyond my programming ability to implement these poly calcs on a 328 with high bit depth numbers. I certainly won’t be writing those lunkers on the bottom of each logger with a sharpie, like I could with a pair of nominal/βeta constants.

This empirical fit approach would to work for any type of sensor I read with ADC oversampling, and it’s so easy to do that I’ll use it as a fall back method whenever I’m calibrating new prototypes. In this case though, a little voice in my head keeps warning me that wrapping polynomial duct tape around my problems, instead of simply using the rail voltage for both aref & the divider, crosses some kind of line in the sand. Tipping points can only be predicted when your math is based on fundamental principles, and black-boxes like this tend to fail dramatically when they hit one.  But darn it, I wanted those extra 1.1v aref bits! Perhaps for something as simple as a thermistor, I’ll be able to convince the scientist in the family to look the other way.

Making the Steinheart-Heart equation work

Seeing that trend-line produce such a good fit to the temperature data, made me think some more about how I was trying to stuff those system-side errors into the βeta equation, which doesn’t have enough terms to cope.  By comparison, the Steinheart-Heart equation is a polonomial already, so perhaps if I could derive some synthetic S&H constants (since my cheap thermistors didn’t come with any…), it would peg that ADC output to the reference line just as well as Excel did?

I rolled the voltage offsets into the thermistor resistance calculation by setting the (rail voltage/internal aref) scale factor to a fixed value of 3, when in reality it varies from slightly below to slightly above that depending on the board I’m using:

1) MaxADCReading                  (w scaling factor to compensate for the two voltages)

=(2^(OverSampledADCbitDepth) * (3)) –1

2) Thermistor Resistance        (w series resistor on high side & thermistor to GND)

= Series Resistor Value / ((MaxADCReading / OverSampledADCreading)-1)

and I went back to that trimmed 40-20-5 calibration data to re-calculate the resistance values. Then to derive the constants, I put three Si7051 temp. & thermistor resistance pairs into the online calculator at SRS:

(Note: There are premade spreadsheets that you can download which will generate S&H constants, or you can build your own in Excel. There’s also coefficient calculators out there in C, Java, etc. if that’s your thing.)

With those Steinhart-Hart model coefficients in hand, the final calculation becomes:

3) Temp °C =1/( A + (B * LN(ThermR)) + (C * (LN(ThermR))^3)) – 273.15

and when I graphed the S&H (in purple) output against the si7051 (blue) and the 4th order poly (yellow), I was looking at these beauties:

and that fits better than the generic poly;  nearly falling within the noise on those reference readings. With the constants being created from so little data, it’s worth trying a few temp/resistance combinations for the best fit. And this calibration is only valid for that one specific board/sensor/oversampling combination;  but since I’ll be soldering the thermistors permanently into place, that’s ok.  I’m sure if I hunt around, I’ll find a code example that manages to do the S&H calculations safely with long integers on the Arduino. 

So even with cheap parts, oversampling offsets & bandgap reference silliness, I still made it below ±0.2°C over the anticipated temperature range.  Now, where did I put that marker…

Addendum 2017-04-27

Just a quick note to mention that you need to tape the thermistor to the si7051 sensor so they are held in physical contact with one another. The thermistors are tiny & react to temperature changes much faster than the si7051’s which have a much larger thermal mass because of the breakout board they are mounted on. So the temp/resistance pairs don’t match up as well as they could if the sensors are in physical contact with one another.

Addendum 2017-06-05

With 1.1v aref in the mix,  my 15bit oversampled resolution on those 100k thermistors varies between 0.002 and 0.004°  from 20-40°C. But I was throwing the bandgap aref in just to see if I could still make it work. From a calibration point of view, it’s better to better to use the rail voltage on aref, and remove that 3x ratio from the MaxADCReading calculation.  This will lower the resolution to somewhere between 0.006 to 0.012C with a 688k series resistor unless you bump up the oversampling to compensate. In addition to tripling my noise/toggle-pin current, how much extra power do I have to pay to get that resolution back if I’m using the 3.3v rail as aref?

In my oversampling experiments, I found that the Arduino ADC works well at 250 kHz, delivering just under 19230 ADC readings /second. For the purpose of estimation, assume the Pro-mini style boards I’m using draw about 5mA during the sampling time, and I take a reading every 15 minutes (= 96 readings per day) :

15bit= 1024 reads/19230 r/sec =0.053s*5mA =0.26 mAs*96/day=~ 25 mAs/day
16bit= 4096 reads/19230 r/sec = 0.213s*5mA =1.00 mAs*96/day= ~102 mAs/day
17bit= 16384 reads/19230 r/sec = 0.852s*5mA =4.26 mAs*96/day= ~408 mAs/day

so it would cost me another 385 mAs/day to reach a resolution slightly better than I was achieving with the 1.1v bandgap on aref. Given that a typical AA battery runs about 2000 mAh = 2000 mAh*3600 sec/hour =~7,000,000 mAs, it would be quite a while before that breaks the power budget.  Removing the ratio dependency also means that your S&H constants are for the resistor/thermistor pair only, making that calibration independent of what system you connect them to.

Using an Rnominial=100k series resistor would give about the same effective resolution boost as going to 17 bit, but that option costs you more power if you are leaving the thermistor powered all the time:

3.3v / 780k combined resistance  = 4.23μA x 86400 sec/day  = 366 mAs/day
3.3v / 200k combined resistance  = 16.5μA x 86400 sec/day  =  1425 mAs/day

You can power the thermistor from a digital pin, but since I’m already using digital-pin toggling to generate noise for the oversampling, I still need to test if I can combine pin power for the sensor with my oversampling technique. It’s possible that the thermistor bridge needs to be powered by the more stable rails, while I’m shaking aref inside the processor, because if the voltage on the divider started moving in sync with the ADC noise, the dithering noise will effectively disappear, and my oversampling would stop working.

Even before doing this test, I have a sneaking suspicion that 100k series vs. oversampling vs. other techniques  will end up converging on the same effective resolution in the end. And I’ll even hazard a guess that the point of diminishing returns is somewhere around 0.001°C, since that’s what you see offered by quite a few high-end temperature loggers.

Addendum 2017-09-24

Just posting an update about pin-powering the thermsitor dividers while using the 3.3v rail as aref: everything works, but as I suspected you need to stabilize the thermistor with a small 0.1uF capacitor or the dither noise vanishes.  This also requires you to take the RC time constant into account, waiting at least 5x T  for that parallel cap to charge before you start reading the divider. You can sleep the processor during this time, since I/O pin states are preserved.

Degree Celsius vs. Time with lines offset for easier visual comparison:  The blue line is over-sampled output from a pro-mini clone reading a 100k Thermistor /100k series voltage divider. Aref was set to the 3.3v rail, with a 100nF capacitor in parallel with the thermistor on the low side.  This RC combination has a time constant of ~10 milliseconds.  A 0.12 mA pin-current provided sufficient noise for dithering 1024 readings: to deliver an effective resolution of ~0.0028° at 24°C.  For comparison, the red line is the output from an I2C si7051 sensor on the same logger, with a resolution of 0.01°C.

So using a 100k series resistor with 3.3v aref really does deliver the same effective resolution as the 680k series/1.1v aref combination, and it does not suffer the problem of bumping into the aref voltage at a certain temp.  I’m using 100k termistors so the pin resistance (~40 ohms) will introduce less than 0.05% error over the range; though this pin-drop error would be higher for therms with lower Rnominal values.

Since I’m using cheap eBay 100k’s and a host of other no-name components, I have to calibrate each logger/thermistor/O.S. bit-depth combination.  This isn’t much of a burden for the overall workflow, since I always give new loggers a shake-down run, in fact, I usually do a fast sampling burn for at least a week before considering a unit ready for deployment:

That Degree vs Time image above was an excerpt from a calibration run like this. I’ve found that Freezer (morning)->Fridge (afternoon)->Room (overnight) is easier to manage than the reverse order, and gives enough time at each temperature to deal with thermal lag differences between the thermistors and the reference sensors.

As before, when I do the thermistor resistance calculation I make the assumption that everything in the system is behaving perfectly (which is obviously not true). So errors from things like pin drops, temp. coefficients, ADC gain, etc., are getting rolled into the S&H constants.  Essentially, I’m eliminating a host of different corrections in exchange for the interchangeability between sensors that I might have if I took all those factors into account individually. This makes it easier to standardize the code , and is a reasonable trade-off for loggers that I won’t be seeing again for several years, but if I have to swap some components at that time, I’ll need to do  another calibration.

The other factor is that every time you introduce one of the many possible corrections, you necessarily limit your final output to the stability, resolution, or number of significant digits in that correction.  In one case the limits of my rail voltage reading method produced random spikes in the record whenever that factor in the calculations had a brief toggle:

Note: spike errors are also diagnostic of calculation errors due to over-running your variables. The difference is that variable overflow problems are not random like the one shown above. They repeat regularly whenever the data passes some threshold in the calculation.


In more extreme cases this noise shows up as a overall thickening of the output from correction factors that toggle their relatively low-rez bits more frequently.  As an example I did some runs where I took a Vcc reading with the internal bandgap trick, and rolled that into the thermistor calculation to improve the accuracy. the net result was that the 4-digit Vcc reading placed a limit on the final output so that there was no “effective difference” in the thermal resolution between oversampling at 15bit & 16 bit because that VCC correction had been included.  (Note: You’ll run into this problem more often if you change aref voltages and forget leaving enough time for the aref capacitor to stabilize…)

The Arduino’s reference (and ADC) do not have a zero tempco.  However, if you make the “perfect” regulator/band-gap/ADC assumption the only limits placed on your resolution are the significant figures in your S & H constants.  Even so, there are so many other factors at play here, that I suspect that you can’t use my pin-toggle oversampling technique to push the Arduino’s ADC much past 16 “effective” bits before some other limitation occurs. Then there’s the issue of long term drift of the various components and the fact that it takes over 200ms each 16-bit reading; adding about 20 seconds of CPU run time to my logger’s daily duty cycle.  Remember that my goal here was a dirt cheap temp sensor that I could add to every logger with a modest accuracy in the 0.1-0.2C range.  If you need both resolution and accuracy, then you should switch to ratiometric measurements, with an instrumentation amp like the INA826, and a 24bit ADC.

Addendum 2017-11-05

Looks like Sensirion’s new STS35 has ± 0.1°C accuracy like the si7051 I’m currently using as a calibration reference. Since the Steinhart-Hart” equation has a built-in error of ~0.1°C and the si7051 ref is ~0.1°C, that might get me into the ball park of ±0.25°C accuracy.  Hopefully that shows up on Tindie soon.  Of course, it’s important to remember that we’re miles away from a real ITS-90 level calibration with a triple point cell.

Addendum 2018-03-14

I recently found out about a method using temperature-sensitive liquid crystals as thermal calibration references at 55, 75, and 90 deg°C. These were custom-made by Hallcrest UK ( and apparently the transitions were sharp enough to resolve 10 mK..?  That’s still a bit rich for my blood, but I also thinking about experimenting with virgin coconut oils (on amazon) which melt at ~24 °C  – the actual value is imprecise, but hopefully will remain constant for a given batch of oil.  So could provide a nice melting point plateau…we will have to see…

Addendum 2018-06-10

Still hunting for a good method to provide nice thermal plateaus for the calibration runs covering >30°C of range. The refrigerator gives a nice 5°C point, and of course room temp is easy, but getting that third calibration point up at ~35°C is a bit trickier because I want that peak to be long and slow.  In the winter that’s available on the house radiators, but during the summer I don’t have a ‘slow’ dry heat source in the right range.  I’ve been following some threads suggesting that you can convert a regular water bath into a “dry-bath” with copper coated BB shot, or aluminum pellets. Both would be a heck of a lot cheaper than lab grade dry bath beads, though for an application where i am simply looking for a slow temperature ramp (so hot & cold spots don’t matter) sand or rice might suffice to provide the thermal mass I need. And I could use an old bath from eBay for the job – these sometimes sell for as little as $25 if they have surface rust on them.  Or perhaps I could hack the temp sensor on a charity shop crock-pot to keep the temp really low….

Addendum 2019-03-25:

I’ve been developing a new method for reading thermistors with the Input Capture Unit on pin D8. Micro-controllers count time much more precisely than ADC’s measure voltage, so this new approach delivers more resolution than 16-bit oversampling in about 1/10th the time & power.

Give your Arduino a high resolution ADC by Oversampling with noise (from a toggled pin)

Thermistors are really twitchy, so you need to put them inside a big lump of thermal inertia before you start.

The slightest breeze makes glass bead thermistors jitterbug like crazy, so put them inside something with a decent amount of thermal inertia before you do any oversampling experiments. Otherwise thermal noise could make it look like your dithering is sufficient for oversampling, when it’s not.

While I was figuring out how to read thermistors with our Arduino based data loggers, I came across claims that you can improve the resolution of any Analog-to-Digital converter with a technique called oversampling & decimation. I had already doubled the number of ADC bits covering my target temperature range by powering a thermistor divider from the rails and using the internal 1.1v as the analog reference.  And my gut feeling was that aref-based ADC bits were somehow better than any I could synthesize, but I was still curious to see if I could add over-sampled bits to the ones obtained with the bandgap trick.

At first bounce, the method appeared to be incredibly simple, to get n extra bits of resolution, you need to read the ADC four to the power of n times.  Generally you have to add three extra bits (43= 128 samples) to see approximately an order of magnitude improvement in your real world resolution. With thermistor dividers, you typically get about 0.1°C from the default ADC readings, and 128 samples bumps that to 0.012°C.  Taking (46= 4096) samples would bump that up to ~0.0015°C which, as the saying goes, is good enough for government work… 

I usually over-sample one power more than needed for my target resolution, so I’d use for four extra bits to be sure of that order of magnitude improvement, which requires the sum of 44= 256 readings:

uint32_t extraBits=0;    // use an unsigned integer or the bit shifting goes wonky
for (int k = 0; k< 256; k++) {
extraBits = extraBits +analogRead(AnalogInputPin);

which is then decimated by bit shifting right by n positions:

Oversampled ADC reading = (extraBits >> 4);

This combination lets you infer the sub-LSB information provided there is enough random noise in the signal for the lowest ADC bits to toggle up and down while you gather those readings. But without the noise, all of the original ADC readings are the same, and the oversampling technique does not work.  To show you what that kind of failure looks like, here is oversampling & decimation being done over 4096 readings with no noise or dither signal applied to a 10k NTC thermistor divider read with 1.1v aref:

This is an example of oversampling with no dither signal being applied. So this is the nul result

These are readings from a 10k NTC thermistor divider, and I’ve offset these records from each other by 0.1° for easier comparison. The one-shot ADC readings of the thermistor bridge in purple are converted to °C, as are 4096 sample readings at the default 125kHz(ps64) in grey,  250kHz(ps32) in orange and 500kHz (ps16) in green. With such a large number of samples, the averaging produces some smoothing whenever the raw ADC readings near a transition point, but if you see “rounded stair steps” like this then oversampling is not working properly  the curves shown above are all FAILURES.

Some microprocessors have enough jitter in their readings to use oversampling technique with the natural toggling of the least significant bit.  A few brave souls have even tried to improve the AVR’s crude internal temperature sensor with the technique.  But most of the time, there is not enough naturally occurring noise, and you need to add a synthetic dithering signal to force those LSB’s to toggle.  This is mentioned from time to time in the forums, with a number of references to AVR121: Enhancing ADC resolution by oversampling, but I found frustratingly few implementations using an Arduino that were described in enough detail for me to replicate them.  Most of the technical docs were focused on audio applications, and I was quickly buried under thick mathematical treatments warning me not to interpret the Effective Number of Bits (ENOB) as Effective Resolution (what?), and describing a host of other quid pro quos like signal synchrony.

This is qwerty's original dither circuit from the freetronics forum post at:

This is Qwerty’s original dither circuit from the freetronics forum. If you are using an UNO, this works well. Of course the ratio between the 5v rails, and the internal bandgap reference,  means you also have extra ADC resolution available without oversampling if you use the 1.1v aref trick, but oversampling gives you more bits for your effort.

About the only useful thing I got out of most of those refs was the apparent consensus that any synthetic dithering signal needs to be at least 2x the voltage per bit on your ADC (although you can use a larger dither signal without causing problems) and triangular dither signals work better than natural noise.  But few of those references said anything about extending ADC resolution, as they were primarily focused on improving the ADC’s signal to noise ratio.

And then there was the fact that several of the older hands seem to dismiss the whole idea as not worth the bother because you had to add so much additional circuitry that using an external ADC was a simpler, cheaper approach.  In fact the subject triggered the closest thing to a flame war I’ve ever seen at the usually staid Arduino playground.  So I was about ready to give up on the idea when I came across a post by user QWERTY at the Freetronics forum explaining how he used a simple RC filter to turn an Arduino’s 480 Hz PWM output into a 9mv p-p triangular dither, which he patched directly into the center of a thermistor bridge.

Yes it is possible to add a jumper on the Aref line of a pro mini.

You can patch into the aref line on a Promini by soldering a jumper to the end of the little stabilizing capacitor.

Holy cow! A solution that only needed a few cheap parts and couple of pins. What the heck were those other guys gassing on about?   My first thought was to try to take the output from Qwerty’s RC filter, and put it onto the Aref as they did in AVR 121.  A compelling idea since putting the dither directly on aref means you don’t have to interfere with the sensor(s), and the same dither circuit would work for all of the analog inputs.  In addition, I was using large resistance voltage dividers to monitor Vbat without wasting power and the high impedance forced me to add a capacitor to feed the ADC’s sample and hold input.  I knew that low esr cap would kill any dither signal that was applied directly to the main battery divider.


This L-P filter from AVR121 ap-note that everyone mentions works great, but modifying the circuit to give you other aref base voltages is a bit of a pain.

I tried many different combinations, but I never saw the voltage on aref that I expected.  It took ages to discover that ~32k of internal resistance gets connected when you place an external voltage on the aref line, and that forms a ‘hidden’ voltage divider with your circuit. Grrr…

I did eventually get a few of those circuits working, but that internal resistor  seemed  to be slightly different on each board I tried, and I didn’t know if it was going to be stable with temperature, time, etc.  Another important issue was that I was switching from the internal 1.1v aref to read the thermistor, back to using the default 3.3v for other readings during the logger operation. So to put the dither directly into aref meant I would also need some way to modify the baseline aref voltage on the fly.  

Tune the resistor ratio, and roll PWM2 duty cycle and I'm pretty sure this circuit form Open Labs would give you variable Aref voltages.

Tweak the resistors & this circuit could give you variable arefs AND dithering.

I suppose that a truly elegant solution would do that with a PWM/RC filter circuit generating a variable DC voltage, and using a second PWM input to add the much smaller dither signal.  You could tune the dithers pk-pk amplitude to match the adjusted LSB, by the way you varied PWM2’s duty cycle (or by using the tone function)  during the readings.  But working that out would probably give me a host of other problems to resolve (esp. with timing) and I was after a simple solution, with the smallest number of parts.  So I eventually abandoned the “dither on aref” approach.

This brought me back to Qwerty’s method of putting the triangular dither signal on the center of the thermistor bridge. My first task was to change that RC filter: lowering the 9mv swing on his 5v circuit to match the much lower 1.1mv/LSB you get when using the internal bandgap as aref.

The power supply ripple calculator at OKAWA Electric was a perfect tool for this job:


3.6mV was just an arbitrary 'close enough' point for me to start at as I had those components in the parts bit already.  But if you see random flat spots in your oversampled readings at the default ADC speed, then try increasing the ΔV pk-pk of your dither signal a little bit.

3.6mV was just an arbitrary ‘close enough’ point for me to start at as I had those components in the parts bin already.  But if you see random flat spots in your oversampled readings at the default ADC speed, then try increasing the ΔV pk-pk of your dither signal a little bit.

…which revealed that a 4.7MΩ/0.1uF RC combination would take the 3.3v 480Hz PWM on D6 and bring it down to  ~3.6mv peak to peak.  I immediately  hopped over to the Falstad circuit simulator to see the see how this worked.  To simulate an Arduino’s positive PWM, I used a 3.3v square wave source with an offset of 3.3v.  The little 10nf coupling cap prevents the pins DC voltage from affecting the thermistor reading, and the 2k2 bridge resistor prevents the dither signal from being grounded out when the 10K NTC thermistor resistance gets very low.  One of the coolest features of this simulator is that if you build a circuit with it, you can export a web link (like the ones above) that rebuilds the circuit instantly, so you can compare different versions simply by keeping those links in your log.


The RC settling time is shown on the Okawa calculator’s step response graph, or you can watch the voltage rise on the scopes in Falstad by restarting the simulation with the buttons on upper right.

I love using Falstad for “What happens if I do this?” experiments. Of course these usually fail, but in doing so they show me that there are things about a circuit that I still don’t understand.  One thing that gave me a lot of grief when I started working with these  dithering circuits was that I did not appreciate how much time they need to stabilize.  This gets worse if you start disconnecting the thermistor  divider to save power between readings.  

So although I was getting smoother curves, and resolution that looked about 10x better than my raw ADC readings:

excerpt from 1024 oversampled temp record on Arduino ADC with triangular dither , 100kthermistor

Here I’ve converted these 1024 sample curves to °C , and artificially offset each curve by 0.05° from the next to it for easier visual comparison. The one-shot 10bit ADC reading at the default 125kHz (ps 64) is in purple, with other ADC speeds:  250 kHz (ps32) in orange,   500 kHz (ps16) in green, and 1 MHz (ps8) in blue.

At the height of my coupling capacitor lunacy I produced this beast, thinking I could simultaneously read a reference bridge, and correct away any offsets.

At the height of my coupling capacitor infatuation I produced this beast, thinking that if I could simultaneously add dither to a reference bridge I would be able to correct away ADC offset & gain errors, along with the offset caused by the dither signal, at the oversampled bit depth. But all those capacitors added artifacts to the readings when I reconnected GND through that mosfet, producing weird spikes in the data if I took readings less than two minutes apart (?)

…in any set of successive readings, the offset between the oversampled readings and the one shot ADC reading was changing depending on how long the PWM had been running.  No problem I thought, I’ll just throw in another coupling cap to block that slowly rising DC voltage, and connect the ADC input on the thermistor side. Unfortunately replacing the 2k2 bridging resistor with a coupling capacitor forms a high pass filter with the thermistor itself, forcing you to increase the size of the cap to raise the filters cutoff frequency above your 480Hz PWM. But that increases your RC time constant so then the filter starts to act like a differentiator: distorting your nice triangular dither signal (see pg12 of this pdf), and in some cases even reverting it back to the original square wave you started with… Argh!

So the result of all that trial & error is the basic PWM->triangular dither method works well, but you have to wait for the RC filter’s output to stabilize or it messes with your accuracy. And you still end up with a small offset in the ADC readings of 1/2 your dither signals peak to peak, because the original PWM square wave can only be positive.

Crank it up

But no one wants to see a data logger burning away precious milliamp-seconds just twiddling its PWMs!  With guidance from Nick Gammon’s fantastic ADC page, I had already been messing around with pre-scalars to increase the temporal resolution of my UNO DAQ.  I was further encouraged by this line from AVR120    “For optimum performance, the ADC clock should not exceed 200 kHz. However, frequencies up to 1 MHz do not reduce the ADC resolution significantly.  …and there were some tantalizing hints that cranking up the speed might also increase the internal noise enough to make oversampling work better. 

To figure out how fast your ADC is running:

System clock / prescalar = ADC clock,  ADC clock /13 = # of ADC reads/second

The core clock speed on 3.3v promini style boards is 8 MHz, providing:

8 MHz / 64 = 125 kHz /13 ticks    = 9600 /sec      (256 reads =27.6ms, 1024 =106ms, 4096 =426ms)  (default) 
8 MHz / 32 = 250 kHz /13             = 19230 /sec     (256 reads = 13ms,  1024=53ms, 4096=200ms)
8 MHz / 16 = 500 kHz /13             = 38000 /sec     (256 reads = 6.7ms, 1024=27ms, 4096=108ms)
8 MHz /   8 = 1 MHz /13                 = 76900 /sec     (256 reads = 3.3ms, 1024=13ms, 4096=53ms)

Your sensors output must be stable while you gather these samples and this limits what kind of phenomenon you can measure. At the default ADC clock speed, trying to add six extra bits of resolution (46 = 4096 readings) means you can only capture about 2 samples per second. That’s pretty darned slow for data acquisition! In fact, it’s so pokey that some people implement ring-buffer schemes to provide access to an oversampled reading at any time, without having to grab a whole new set of samples. A neat trick if you are continuously monitoring a sensor that changes slowly, and you have enough memory to play with.  Given the powers of 4 relationship between the different bit depths, it’s easy to see how you might hop-scotch through shorter 64 sample readings, and then combine those into a sort of rolling average version of a 256 sample reading if you don’t have quite enough ram for the full ring buffer approach.


My tests agree with the results posted at Open Labs. You can only push the ADC clock so far before you lose hardware bits, and this defeats the resolution gained from oversampling by making your accuracy worse. You can see this effect in the 1MHz line in the previous 1024 sample graph. Most AVR’s are lucky to get 9 ENOB’s at their default settings.

200 kHz is the ‘official’ ADC speed limit for 10 bit accuracy, but I didn’t see any  significant difference between oversampled readings taken at the default 125kHz clock (ps 64), and those taken at 250kHz (ps 32).  At 500kHz (ps 16) the readings were good most of the time, but during rapid temperature transitions the readings started to ‘wiggle’ as though the dither signal was occasionally dropping out.   At 1MHz (ps 8) the curves wander around quite a bit, and I was seeing errors of ±0.05°C or more with some prolonged flat spots starting to appear. What’s interesting about this is that the triangular dither RC filter puts a capacitor across the thermistor, which should reduce the input impedance seen by the ADC and allow for faster readings.  But this did not reduce the 500kHz wiggle / 1MHz wandering in any of my test runs.  The ATmega328P datasheet quotes 2 LSB’s (typical) of absolute accuracy with an ADC clock at 200 kHz, but 4.5 LSB’s (typical) at an ADC clock of 1 MHz. There is no point in pushing clock speeds if the accuracy gets worse by that much in the process.

So you can always double the ADC clock speed for oversampling, but going up to 500kHz depends on whether you can live with the accuracy errors that prescalar creates.  Those 500kHz wiggles become less evident as you progress from 256, to 1024, to 4096 readings, but that’s probably just an artifact of the smoothing.  The other thing to keep in mind is that one full cycle of the 480Hz PWM takes  ~2 milliseconds, but 256 readings at a 500kHz ADC clock takes only 6.73 milliseconds – so there is a high probability that dither signal synchrony issues creep in at the higher ADC speeds to produce offsets that affect the entire curve. Ideally you’d want the time you spend gathering the over-samples to be an exact multiple of the dither cycle time…

Let’s make some noise!

Hotter prescalars cut the oversampling time down dramatically, but I could not see how to avoid that RC settling time, which seemed to require about 50-60ms of PWM operation before the offsets became tolerable.  So I went back to the proverbial drawing board and asked myself, what if forget about the triangle dither signal, and try oversampling with some sort of random noise?

The first hurdle there was:  How was I going to generate this noise if the processor was already busy taking ADC readings?  The beauty of PWM based dither is that it just chugs away in the background, leaving the processor free.  As usual, Nick Gammon provided an elegant solution to this problem with code on his page about interrupts which showed how to read the ADC asynchronously:  

// Note: Before calling this function, I change to the internal 1.1v aref and set the ADC prescalars
// but you can leave them at the defaults: see: for more details
volatile int adcReading;
volatile boolean adcDone;
boolean adcStarted;
unsigned int  adc_read;

unsigned long asyncOversample(int readPin, int extraBits)

int i=0;int j=0;
int var=256;                                  //default is 4bits worth of oversampling
if(extraBits == 5){var=1024;}
if(extraBits == 6){var=4096;} //I’ve only included three options here, but hopefully you see the pattern
unsigned long accumulatedReading = 0;
adc_read=analogRead(readPin);   // a throw away reading to connect the ADC channel
//delete me:  simply as spacer
pinMode(5, OUTPUT); digitalWrite(5, LOW);  // set the pin you are toggling to OUTPUT!
//delete me:  simply a spacer a spacer comment for blog layout
while(i < var){    // asynchronous ADC read from
  if (adcDone)
  {adcStarted = false; accumulatedReading += adcReading; adcDone = false;i++;}
  if (!adcStarted)
  {adcStarted = true; ADCSRA |= bit (ADSC) | bit (ADIE);}

  PORTD ^= B00100000;  // XOR toggle D5 w green LED & 30k limit resistor (see  below for details)
}   // end of while (i < var)

pinMode(5, INPUT);digitalWrite(5, LOW);  //turn off the toggle pin
if(extraBits == 4){accumulatedReading=(accumulatedReading >> 4);}  // Decimation step for 4 extra bits
if(extraBits == 5){accumulatedReading=(accumulatedReading >> 5);}  // 5 bits
if(extraBits == 6){accumulatedReading=(accumulatedReading >> 6);}  // 6 bits
return accumulatedReading;
}   //end of asyncOversample function

ISR (ADC_vect)     // ADC complete ISR needed for asyncOversample function  
  {  adcReading = ADCL | (ADCH << 8);adcDone = true; }

(NOTE: copy/pasting code from WordPress blogs is almost guaranteed to give you stray/302 errors because of hidden shift-space characters that the layout editor inserts. If that happens to you, look at the line your compiler identifies, delete all the spaces and/or retype it slowly using only ASCII characters.)

Next I had to generate the noise itself. People use Zenner diode breakdown to produce random numbers, and connecting an analogue input to the collector of a run-of-the-mill transistor, with the emitter grounded and base open also creates noisier randomSeed(); input. But thought I would see if I could generate noise inside the processor, since there seemed to be no end of people complaining about the Arduino’s ADC in the forums. However when I actually tried to do this by connecting pull-ups,  changing I/O settings, an every other kind of processor toggle I could think of, I got nothing.  That ADC was solid as a rock until I started flipping the pins connected to the external indicator LED.   Even then, the early results were wildly inconsistent, with the same code producing good oversampling on one unit, but not another.

Like the hidden resistor problem, it took me a while to notice that the random bunch of LEDs on my breadboard test units had significantly different forward voltage drops from one LED the next, and from one RGB color channel to the next.  Once I realized how much that was affecting the results,  it didn’t take long to determine that that the noise generating sweet spot (with 1.1v aref…) was somewhere around 0.04mA of pin current:

An example of oversampling with pulsed pin current of 0.038mA to generate ground line noise.

One-shot ADC reading shown in purple, with oversampled readings taken at 125kHz (ps64 default)  in grey, 250kHz (ps32) in orange, 500kHz (ps16) in green. All readings are converted to °C, and I’ve offset these curves for clarity, as they would otherwise be on top of one another. You can clearly see the PS16 wiggle as the temperature falls, and the sharp eyed will notice there are still offsets between the different runs which were all taken in quick succession. These seem to be more apparent in the longer slower oversampling runs than they are in the the shorter faster ones… darn it…

Unlike triangular dither techniques, which will tolerate a fairly large ΔV, this noise based method stopped working (ie: flat spots started appearing) when the toggled pin current went below 0.02mA, and the curves became pretty scratchy above 0.06mA  indicating there was too much noise.  That’s a fairly tight range, and it was sheer luck that the 30k limit resistor I was using on my indicator LED’s brought me close enough to spot the effect.  So my current target is ~0.04mA of pin current for 1.1v dithering. And there was nothing special about the LED being there either, as tests using a simple 82.5KΩ resistor from the  PORTy ^= _BV( PDx/PBx );    toggled pin to ground produced good results.  This is pure conjecture on my part, but if you assume the mosfets on the I/O pins have about 40Ω of internal resistance with 3.3v control, then 0.04mA pin current would produce a voltage drop of ~1.6 mv – which is suspiciously close to the 1.1mv/LSB resolution of the ADC with the internal bandgap set as aref.  That puts this dither noise right in the 1-2x volts/bit recommendation from the literature.


Here I’m oversampling with 1024 readings from a 2x10MΩ divider which cuts the voltage of the RTC’s backup coin cell in half. 250kHz (psS32) in orange, and 125kHz(ps64) in grey. These are the raw readings with aref set to the default 3.3v and there is no capacitor on the divider. This is far beyond the 10k input impedance the ADC was designed for, but I think the many repeated readings you do with oversampling helps the 14pF sample&hold caps do their job. At this resolution, the CR2032 seems to be acting like another temperature sensor …(?)      UPDATE: So this actually was the battery responding to temperature rather than the dithering method, which does not work with the rail voltage on aref unless you add a cap to the voltage divider.

This pin-toggling noise technique is not exactly a one size fits all solution, and the exact current required to induce ADC bit toggling will vary depending on which board you are using, and especially on which capacitors are being used smooth the output from the voltage regulator.  So you will have to noodle around a bit to find the correct resistor value to use for your particular Arduino.

I’d start with a resistor value that draws enough current to give you a voltage drop on the digital pins mosfet that is close to 2x your ADC’s mV/LSB resolution. With 3.3v as aref (so 3.22mV/bit), I would use a pin resistor of  about 27.5k for a pin current of 0.12mA which should cause a pin vdrop of ~4.8mV.  Given that limit resistor for the pin13 LED is usually around 1K, you might be able to toggle that on-board LED to generate this dithering noise without adding any extra components.

With 5v control logic, the mosfets controlling the digital pins are more fully turned, so the pin resistance is somewhat lower; around 25-30 ohms. With 5v on aref your resolution is about 4.88mv/bit, and the dither resistor would have to pull around 0.39 mA to shake the rail with a vdrop twice that mv/bit, so the dithering resistor would need to be somewhere around 12.8 kΩ.  

On new builds I will measure the forward voltage drop of the indicator LEDs and change the limit resistor to give me the current through those I need to generate dither noise. That way I don’t need to any new digital lines for the oversampling process, though this will entail checking every LED, as there is significant vf variation between batches.  The blue channel on the RGB’s I have lying around have a vf of ~2.473v, so 0.827v will be left for the resistor to cover with a 3.3v rail.  To achieve a target pin current of 0.12mA the limit resistor (for that blue LED) would have to be 0.827v/0.00012mA = 6.89kΩ.

This method is also critically dependent on the tiny capacitor stabilizing the aref voltage. When I tried it on the units I had left over from the ‘dither on aref’ experiments, the pin toggling method did not work if the aref stabilizing capacitor had been removed.  I also suspect that the voltage on the capacitor ‘adjusts’ to the noise pulses over time, which might be causing the 0.02C difference between the 256 & 1024 readings shown above. So there could be another settling time issue if you take a large number of over-sampled readings in rapid succession. Larger caps stabilizing the rail voltage on breakout boards may also affect the method.

This technique will work with any resistive sensor being read with a simple voltage divider, provided there are no capacitors nearby to smooth out the noise which is vital for oversampling.  I’m not going to pretend to understand all the math behind it,  but it’s probably safe to say you can add somewhere between 2-5 extra bits of resolution to your ADC before the technique suffers from limiting problems somewhere else.  Although the 256 sample curves are a bit gritty, you can make that many samples with the ADC clock at  250kHz in ~13milliseconds, which doesn’t impact the power budget much. If something interesting starts happening with your sensor, you can enable another bit or two of resolution on the fly to zero in on the phenomenon.

Overall, the results from oversampling with toggled-pin noise are not as smooth as the curves you get with a well tuned triangular dither, but I’m happy to trade that last bit of synthetic resolution for a method that’s instantly available on all of the ADC inputs.  The icing on the cake is that I won’t have to add any extra circuitry to use oversampling on the fleet of loggers already on deployment, because all I have to do is toggle the indicator LEDs they already have on board, since their limit resistors were already in the current range I need…YES!

Addendum 2017-04-26:

I’ve moved on to calibration, and in the process I learned that regulator & bandgap voltages change a fair bit with temperature. So it’s probably not a good idea to use the internal bandgap on aref with this oversampling method if you want thermistors calibrated over a wide temperature range. But I did it anyway.

In those tests I used a 688k series resistor with a 100k thermistor, so I was far from divider’s optimum of Rseries=RTnominal. I was taking 1024 oversamples, adding five oversampled bits to ADC, and I was using the internal bandgap voltage on aref, which added another bit.  Since I was on the tail end of the divider sensitivity curve, the effective resolution changed quite a bit over the range: the output shifted from ~0.0018°C/bit at 20°C, to about 0.0038 °C/bit up at 40°C. This is better resolution than some people achieve reading thermistor bridges with the 16bit ADS1115, though gathering all those readings means I can only capture 18 samples per second – even with the ADC clock at 250kHz.

I have a long way to go before I reach the accuracy levels you see at the geotechnical high end, but I think that’s still good for readings with a humble Arduino ADC!

Addendum 2017-09-24:

Several people have contacted me about their attempts to get this ‘pin-toggling noise’ method working with different Arduinos at higher voltages.  If I had to summarize the kernel of understanding that was missed in the unsuccessful cases it is this:

If you jiggle one part of the system with noise – stabilize the other part.

It does not matter if the noise shows up on aref, or on the sensors output, so long as it is not present in the same form on both.  With the bandgap 1.1v as aref, you can rely on that to be the stable side, so you want the voltage divider with your sensor not to have a capacitor on it, since the sensor side needs to shake by ±2 LSB volts when the pin is toggling. The internal reference is slightly different on each individual chip (from 1V to 1.2V), so you’ll also need to “calibrate” if you go this route. Don’t forget to throw away the first reading after changing the analog channel, and if you have a high resistance voltage divider, add a one ms delay after that first analog read.

If you use the rail voltage as aref (the default) with an un-stabilized voltage divider then your pin toggling current shakes the aref ground in perfect synchrony with the ground line on your sensor, and no matter how many samples you read & decimate you will never get beyond the 10 bit resolution of the ADC. So to use the rail as aref when oversampling you need a small (around 0.1uF) capacitor across the lower half of your thermistor divider so the sensors input to the ADC becomes the stable side. It’s also a good idea to remove the little 0.1uF stabilizing capacitor that’s normally present on the aref line, since it’s whole purpose is to prevent aref from jittering. 

Degree Celsius vs. Time with lines offset from each other for easier visual comparison:  The blue line is over-sampled output from a pro-mini clone reading a 100KΩ  NTC Therm/100KΩ series voltage divider. Aref was set to the 3.3v rail, with a 100nF capacitor parallel to the thermistor on the low side.    A 0.12 mA pin-current provided sufficient noise for dithering 1024 readings, delivering an effective resolution of ~0.0028° at 24C. For comparison, the red line is the output from an I2C si7051 sensor on the same logger, with a resolution of 0.01C.

The question of which side should be treated as stable comes into play when you want to over-sample analog output from more complex sensor circuits. If the circuits on a sensors supporting breakout board are already doing a good job of stabilizing output, say with feedback, caps and some sort of buffer at the end of an amplification cascade, then you have no choice but to set aref to the rail voltage and shake that. I’ve had success with this approach and a complex sensor circuit on a 5v Nano, by pulsing a pin connected to ground through a 12KΩ resistor (~ 0.4 mA of pin current).

No matter which side you shake, everything else in your system is feeling this noise to some extent, and this may cause issues with sensitive sensor IC’s, or with micro-controllers other than the 328p.  Of course, the higher the aref you use, the more of a voltage swing you need to introduce for sufficient dither. The effect of the pin current is also being limited by capacitance distributed throughout the system, which varies from board to board, so this is definitely a “try it an see” method: when it works it really works, producing smooth curves with no hint of the underlying 10-bit ADC peaking through(Most of the time I get acceptable oversampling results toggling the green channel of a three color RGB indicator LED with ~24k limit resistor but that is somewhat dependent on the LED’s forward voltage. When in doubt, use a smaller limit resistor to increase the pin current – and check the actual value with a DMM)

If you see any flat spots or rounded stair steps in your temp. data, especially in areas where the changes are occurring slowly over time, then you know the dithering is not working:  

This is an example of the natural noise problem: oversampled (blue line) thermistor readings achieved high bit depths the refrigerator (left), but developed flat spots in the room (right) where the changes were happening more slowly. This was a test run with the noise circuit disconnected,which I followed with run using the same code +noise applied so I could compare the two. Doing two runs (with & without dithering) is good general approach to use when testing a circuit that uses oversampling.

Any natural signal variation over your sampling interval will make it look like your generated dithering noise is sufficient for oversampling, when it is not.  The photo above shows how that this test is almost impossible to do in the refrigerator, because the natural on/off cycle of the compressor generates enough change/time to make oversampling work without dithering. 

With stabilizing capacitors on the voltage divider you also have the trickier problem of spotting the influence of the RC time constant when you only power the voltage divider during readings.  Oversampling before the cap is fully charged will provide more than enough change in the readings to hide inadequate dithering.  In fact, if you scale the capacitor/series resistor combination, and sample over the 3T-5T interval after applying power, you get reasonably good oversampling results with no other noise in the system.  In some ways, using RC rise time is better than pin toggling when you are using the rail as aref, since it does not have to fight against the other capacitance distributed around the system to produce a delta on the ADC readings.  I’d use this rather than pin toggling with aref=rail  if it weren’t for the fact that capacitors can have the worst variation coefficients of any electronic component you are ever likely to run into.

Garden variety Y5V ceramics vary by up to 82% over their rated temperature range, and even the X7R’s that most engineers use vary by +/-15%. I might be able to calibrate that thermal variation away, but for environmental monitoring the drift over time is a much bigger problemwith caps commonly loosing 10-15% of their rating over the first year (~8900 hours) of operation. There are stable NPO rated ceramic caps out there, but they are only available in relatively small pF sizes, and a good 0.1uF NPO cap will set you back about $7 each even if you buy them in quantity, so that part alone costs more than a decent IC based temperature sensor.

Plastic film capacitors have much better thermal coefficients: Polyphenylene sulfide (PPS ±1.5%) or Polypropylene (CBB or PP ±2.5%)A quick browse around the Bay shows those are often available for less than $1 each, and the aging rate (% change/decade hour) for both of those dielectrics is listed as negligible. The trade off is that they are huge in comparison to ceramics, so you are not going to just sneak one in between the pins on your pro-mini. 

For most rail-as-aref situations, Qwerty’s PWM based dither method (mentioned at the beginning of this post) is a more robust way to dither with cheap ceramic caps, since it can tolerate significant variation in a way that does not affect your accuracy that much – but you still have to keep an eye on the circuit settling time. 

Addendum 2017-10-15:

Just came across AN2668 from STMicroelectronics which sums the input signal and triangular dither signal through an op-amp before sending it to the ADC:

Still seems like a lot of work to me, although that ap-note does have me wondering if the pin-toggle dither noise is actually Gaussian…?

Addendum 2019-03-25:

Pin Toggled Oversampling has been delivering solid results for more than a year now in the field, but I’ve recently been developed a new method for reading thermistors with the Input Capture Unit on pin D8. Micro-controllers count time much more precisely than ADC’s measure voltage, so this timer based approach delivers more resolution than 16-bit oversampling in about 1/10th the time & power.  That doesn’t meant that we’ll stop using oversampling – just that there’s another technique for high resolution sensor readings with an Arduino.

The 2016 Cave Pearl Project ‘Year in Review’

That's a chain with 24xDS18b20 sensors pulling only 0.15 mA sleep current. Woot!

That’s a chain with 24 DS18b20 sensors pulling only 0.15 mA sleep current. Woot!

We made great strides in 2016 with development of new calibration procedures and continued refinement of the housings & connectors for multi-sensor builds. At this point I’ve cobbled together more than 130 loggers for the Cave Pearl Project. While I still have a way to go before I reach Gladwell’s 10,000 hours, we haven’t spent $71 million, and I think I’m starting to get the hang of it. Of course, I felt that way last year too, so perhaps that’s just as good as it gets when you are figuring things out as you go along. It’s not unlike that moment when your boss calls you in for the annual performance review, and then starts the meeting by asking you to rewrite your job description, again, because less than half of the work you actually did last year was in the previous one.

Despite all that building, the number of loggers out on deployment leveled off around sixty five as newer units were often used to retire earlier generations out of the fleet. It’s worth noting that those old dogs are all still running, but field logistics have reached the point where we can’t expand the project unless everything on active duty has more than a year of operating life.  Fortunately the latest Pearls are consistently delivering sleep currents around 0.1mA , so builds with more than three AA cells are probably going to run for two years or more. 

No one ever seems to bother our equipment installations...I wonder why that is?

No one ever seems to bother our equipment installations.  I wonder why that is?

The temp. string loggers are currently the most complicated builds, but they have been delivering some very impressive data sets. Despite their flagship status, most are deployed in sites with a nasty mix of salt water and hydrogen sulfide that’s been destroying the marine grade stainless steel anchor weights. As they take a while to assemble, on-site service and redeployment will be the norm for  those logger installations until we get more of them into circulation. Rapid turn-arounds like that were one of the original goals of the project, but that’s only possible in practice because none of our current sensors are affected the bio-fouling that accumulates over a deployment.

There are plenty of new sensor combinations on the todo list as we round out the hydrology tool kit with more cheap & cheerful ‘tattle-tale’ builds. The recent discovery that you can stretch the I2C bus out to 10’s of meters has opened up many new sensor possibilities.  Long-cable MS5803 pressure sensors are now being put into service for borehole logging and if I can reduce the thermal equilibration time, that sensor also shows promise as part of a drop profiler.  From very the beginning, we’ve had our sights set on a cheap reliable CTD with extended logging capability.

Our oldest monitoring stations are reaching the three year mark, which means we are starting to see repeating annual cycles in the data. As an example, here is the temperature and tilt record from one of the reef stations in Akumal Bay:


The occasional hurricane means there are some lovely ( …to a hydrologist  🙂 ) event records peppered throughout, but the real prize is the baseline data that you only get from multi-year deployments. From the builders perspective, those long records also give me a sense of the inter-unit consistency, which is looking  good despite substantial  changes to the loggers over time. As the project matures, more of my time is being dedicated to sensor calibration and normalization.

Though we rarely have enough visibility to capture it with our little point&shoot camera, the underwater procedures are getting smoother too:

Cave Pearl data loggers

At  ~6000 unique IP’s a month, traffic continues to grow, though I will have to manage more than one post per month if the project is ever going to be a real contender for science geek fame.  The UNO logger has now passed the RTC post in the daily rankings, which shows that the number of interested beginners is also increasing.  The US dominates the overall traffic at (with about 50% of the total), and Germany leads the European traffic, often coming in second only to the US.  If other DIY sites are seeing a similar trend, then I think it’s pretty obvious why Germany continues to be the economic powerhouse of Europe

Many years ago I did a short stint as a high school science teacher and some of my good friends are still in the trenches.  Conversations with them often highlight two things:  How their job now depends on standardized test results (so they don’t have much room to change the curriculum), and that resource budgets are shrinking to zero. These are dedicated people who are more than willing to go the extra mile if they can get their hands on the right material.  So I’ve been working to expand the online tutorials into a set that would help a teacher add Arduino based lessons to their curriculum, even if they have do it out of their own pocket:

2016 was another great run of Trish's Instrumentation & field methods course. It's impressive with how quickly the students pick up the Arduino, and everyone got their final projects running though most of them had never held a soldering iron before.

2016 saw another great run of Dr. Beddow’s Instrumentation & Field Methods course. It’s impressive how quickly the students pick up the Arduino system, and everyone got their final projects running though most of them had never used a soldering iron before.

  1. Build your own Arduino classroom
  2. Arduino UNO Logger for Beginners
  3. Using the UNO for Data Acquisition
  4. ProMini based Data Logger (update)

I’m sure that most people missed the significance of the DAQ post when it came out, but in terms of teaching it’s probably the most important one in the set. The IDE’s new plotting function makes it easy to do live demonstrations in front of a classroom simply by adding one print statement to the code:  nothing I’ve tried before even comes close to this level of simplicity.

I use my  ‘UNO-scope’ all the time now because I can do a quick screen capture  or I can send a series of runs to a serial text monitor. In both cases, calculations can be done on the Arduino before the output is sent, converting the raw ADC readings into something more meaningful like current or power.  Then I can go hunting for unexpected events by graphing it up in Excel.  This approach has let me track down things like SDcard weirdness that are very hard to capture during normal logger operation because they only happen when you pass some threshold inside the flash controller:

Cave Pearl data loggers

I’ve overlaid the bold numbers, but that’s a screen capture of the IDE output. Modern scopes can do this kind of thing too, but you’ll be hard pressed to find one for the price of an Arduino clone on eBay. With a low value shunt resistor, you can push the ADC clock pre-scalars into the 20-40kHz range, which is more than adequate for the kind of diagnostic work I’m doing, and most stuff you’d be trying to demonstrate in a high school physics class. 

DIY Cave Pearl data loggers based on Arduino Microcontrollers

My PVC housings sport smaller separate wells for each sensor as I’m trying to reduce the surface area exposed to pressure & salt water. So far we have only deployed them to ~30m, but deeper sites are on the calendar in 2017.

3D printers are finally reaching a price point where people can afford one to help them pursue other more interesting hobbies, and as key patents continue to expire,  new/old high-end tools are entering the consumer market: Forget 3D-Printed Knick-Knacks.  But it’s worth noting I haven’t used any of these tools on the project because I can still build more durable housings out of plumbing fittings from the local hardware store. Total part cost on those is ~$15, and I probably spend less time making them than I would repairing holes or fixing mesh errors on a constantly evolving 3D printed version. But I’m sure the strength and print quality tipping point will occur eventually… probably when DLP’s with stronger resins reach the kind of price point we are currently seeing for extrusion printers.

I will add more tutorials to that set over time, and hopefully we’ll manage to publish a paper or two this year.  The plan is to put them in open source journals so everyone in the world has access, and if we spin up all the collaborative projects we’ve been planning with other researchers, 2017 promises to be a very busy year.

Addendum 2017-01-10

2016 was also a banner year for the Maker movement in terms of media coverage. So I thought I would add selection of those articles here:

Why the Maker Movement Matters: Part 1, the Tools Revolution

Why the Maker Movement Matters: Part 2, Agility


Sometimes those geeky editorials make me laugh, but even then they are still thought provoking.  It’s also good to see more thoughtful criticism and self reflection going on as the movement matures it’s way through the Hype Cycle (beautifully illustrated by this 100+ year old debate about the Wright brother’s)

Making It

Makerspace: Towards a New Civic Infrastructure

Why I Am Not a Maker

Unfortunately that also means the market has grown to the point that the big boys want a piece of the action. While this probably won’t be another  “Embrace, Extend & Extinguish” situation, commercial players inevitably increase the pressure to commoditize the product into easier to use (& thus more sellable) packages. I can see good arguments to support this but I have some concern about developments (like the ESLOV) which eliminate the user’s exposure to actual code: turning great problem-based learning exercises into plug & play activities.  Unless you let students see ‘under the hood’, they’ll walk away thinking technology is about connecting little black boxes for participation marks.  By now it’s clear that we are heading toward a IoT powered world of self driving carsBaxter bots, and staffless stores.  So I can’t help thinking that unless our young people can handle Arduino level programming tasks, they won’t qualify for a job making toast.

Addendum 2017-04-30

On the topic of media: looks like the one-word journals are finally starting to notice the open source hardware movement. Better late than never, though I suppose with all the mergers going on, the whole makers movement isn’t much more than a rounding error on corporate scale balance sheets. 

<— Click here to continue reading the story—>

Arduino Pro Mini Data Logger : with Dupont Jumpers (2016 Update)

Addendum 2019-02-21: 

In 2019, a teacher friend asked us for a build with minimal soldering because they didn’t have time in their course, or the budget for soldering stations. So we re-designed the 2016 ‘EDU build’ , adding a screw-terminal shield and a pre-made housing to reduce assembly time.  We also added support to the updated code base for using the indicator LED as a light sensor:

The schematic diagram is essentially the same, but the 2019 version is probably a better starting point for people building their first Arduino-based logger than this older model from 2016. For more background information see: A Flexible Arduino-Based Logging Platform for Long-Term Monitoring in Harsh Environments and the PDF is free to download from the open access journal Sensors.

Original post from 2016: Its been almost a year since the last stand-alone logger tutorial, and I continue to receive questions from people adopting the platform in education settings.  That feedback makes it pretty clear that the lack of soldering skills is a significant stumbling block for beginners, so I have come up with a build that uses pre-made DuPont style jumper cables wherever possible.

The core connections for this 3-Module logger are the same as those shown in our recent paper:  A Flexible Arduino-Based Logging Platform for Long-Term Monitoring in Harsh Environments, but here I’ve added colors to match the wires shown in this Pro-Mini based tutorial. In this diagram the battery monitoring divider is shown on A3, but that’s simply to make the diagram more readable. You can connect the battery dividers output to any analog pin. I’ve also added an RGB common cathode indicator LED, but a single color LED could be used.

This build uses a different SD card adapter than previous builds and I’ve changed the resistor location to make the overall assembly easier for beginners.This comes at the expense of having more wires to deal with on the limited real-estate of the knock-out cap platform, and re-positioning the modules to make room for the overhang of the DuPont connector housings. The overall result is a little uglier, and not as robust to knocking about as a unit where every connection is soldered in place, but the platform takes about a third less time to build (~2.5h) with a part cost under ten bucks before you add sensors. (NOTE: in 2019 we redesigned this classroom logger, with the new build taking only 1 hour to assemble.)


Parts Total $8.25
Pro Mini Style clone 3.3v 8mHz $1.80
DS3231 IIC RTC with 4K AT24C32 EEprom (zs-042)
Some ship with CR2032 batteries which will pop if you don’t disable the charging circuit!
SPI Mini SD card Module for Arduino AVR
Be sure to buy the ones with four ‘separate’ pull-up resistors.
20cm Dupont Ribbon Cable 40pin F-F 2.54mm
You use ~1/2 of a 20cm cable per logger. Get the ones without the shrouds to save time.
2xAA Battery Holder & 1xAA Battery holder $0.60/both
CR2032 lithium battery  $0.40
4″ Knock-Out Test Cap $0.40
Deans Ultra-T Style Battery Connector Plug $0.30
2x Nylon M2 12mm standoff, Nut & Screw M2 5mm $0.40
Micro SD card 64mb-1gb 
Older Sandisk  cards have lower sleep currents. Test used cards well  before putting them in service.
Dupont Crimp PinsHousings
Most parts  cost about a 1-2 cents each, after you buy the crimping tool.
Common Cathode Bright RGB LED 5mm 
A brighter bulb lets you use a larger limit resistor for the same light output.
Double Sided Tape,  2x 4.7MΩ resistors, 26awg silicone wire, (104) 0.1uF caps, 4″ cable ties, heat shrink tubingheader pins, etc…etc… $0.50
Donation to
It is now possible to build a decent data logger for less than $9 because of the hard work of many people around the world. If you don’t use a ‘real’ Promini from Sparkfun to build your logger, you should at least consider sending a buck or two back to the mothership to keep the open source hardware movement going…so more cool stuff like this can happen!
Comment:  And a few tools you might need to get started:                     (not included in the total above)
CP2102 USB-UART Bridge module
This 6-pin connector module was specifically designed for compatibility with the Pro Mini. (note that most 2102 boards are not because they only have 5 pins) and these boards work with Mac & Windows after you install the drivers  Or for a safer option you could use the FTDI chip version.
***Be sure to confirm any UART board is set to 3.3v before using it!***
Yihua 936b soldering station 110v
These have enough power to solder the large battery connectors.  Get extra handles & tips.
DT-830D Digital Multimeter $3.00
Heaterizer XL-3000 Heat Gun $14.00
SN-01BM Crimping tool $22.00


CLEANING:  Boards from reputable vendors like Adafruit / Sparkfun are usually clean, but cheap eBay modules are often covered with leftover flux from the manufacturing process and dirty contacts are guaranteed to corrode.  Most of us use 90% isopropyl alcohol to clean this residue, but some prefer Polyclens brush cleaner. A couple of five minute sessions in an ultrasonic bath with Iso90 usually works great, but you can also do it by hand with a cotton swab, etc.   (Never put vibration sensitive components like accelerometers into an ultrasonic bath)

Preparing the Main Board:

Begin by soldering header pins into place on the 3.3v Pro Mini style board.  This is much easier to do if you use a small breadboard to hold the pins in place, however you should not use that breadboard for anything else afterward because the transferred heat will ruin the contacts. I have a few of these old “for soldering only” breadboards lying around.  I prefer to use clone boards with A4-A7 broken out to the edges of the board to make the soldering the pin headers a bit easier, but those are not always available.      {Click on any images to see larger versions.}

Lay out the required header pins as in the images below. Arrange the pins for the sides of the Pro Mini board on a breadboard so that the two 3x rows extend sideways from the main board at the VCC line and the GND line on the analog side of the Arduino. This is a little tricky because you are working from the bottom of the board, not the top, so there is a left right side transformation. Also note that you can add more sidecar headers than I’ve shown here if your logger will need more connections to the power rails to supply sensors.

 {Click on any of the images below to see larger versions.}

Solder the straight riser pins into place along each edge. Then add a tiny amount of flux to the extra header pins and coat the ends of the pins with solder. Tin the wire on one end of a resistor and solder it to the GND header pin on the Pro Mini.

Then weave the resistor leg through the sidecar pins and use that wire to bridge the solder connection to all of the sidecar pins beside the ground pin on the Pro Mini. Repeat the procedure again for the VCC sidecar headers. Use a good amount of solder here to bridge the pins.  The sidecar pins are a bit fragile since they are not attached to the module, and could be accidentally broken when attaching Dupont connectors later on unless you provide some extra mechanical support with solder.

Now that the risers along the sides of the Pro Mini board are in place, we tackle the other riser pins on the board. These are a bit trickier because the A4 & A5 riser pins (needed for the I2C interface) are not aligned with standard breadboard pin spacing. Place the remaining pins into the board face up, then carefully turn the board over onto a flat surface making sure the loose pins stay in place.

It is good to have the six serial UART I/O pins bent slightly away from the other risers to make more room for connecting the UART board. Once the pins are in place clean the flux residue on the bottom of the board with some 90% isopropyl alcohol and a cotton swab.

On the adapter in this picture, the USB to TTL adapter pins are in the reverse order of the Pro Mini style board. This is a fairly common issue with clone boards and simply requires that you flip the adapter. I have connected 3.3v boards the wrong way round many, many times, and not one of them has been harmed by the temporary polarity reversal.

Before proceeding further with your logger build, take a few moments and connect the board to your computer via the UART adapter to test it out by uploading the Blink sketch. You need to install drivers to get the serial communications adapter working with your operating system, and this part of the process can be challenging for people who have never done that before. There are well written guides at Sparkfun & Adafruit if you are using boards with the FT232RL chip from FTDI. If you are using this CP2102 from Silicon Labs then you need to sort out driver installation on your own, but a quick google search should bring up lots of guides posted around the web. Once you have the drivers installed, the Arduino IDE should display a new com port when the adapter is plugged in to the usb port of your computer. At that point you can upload sketches to your Arduino through the serial adapter after selecting Board: Arduino Pro or Pro Mini   and  ATmega328(3.3v, 8mhz)   in addition to the new com port provided by the drivers. If you are ordering parts from eBay, then buy at least three of everything – it’s not uncommon to see a 10-15% failure rate on these budget parts…that’s why they are so cheap. I also order those backup parts from different vendors, since some will take months to ship, while others get the modules to you in a week.

Once you have confirmed that the board is working,  remove the power LED limit resistor and the resistor for the LED on pin 13. Simply hold the tip of the iron on one side of the small resistor and apply a gentle sideways pressure. When enough heat has conducted through the resistor, the solder will melt and you should be able to simply ‘flick’ the resistor off the board. Disabling these LEDs conserves battery power. (Note: these limit resistors are in different locations from one Arduino compatible board to the next, so you might have to hunt around to find them.)

Power LED limit Resistor

Pin 13 limit resistor

Trim the A4 & A5 pins flush to the board with a pair of side snips. Add two layers of double sided tape to the underside of the Arduino board so that it can be affixed to the platform. Due to the riser pins extending along the side of the pro-mini, two layers are needed: The first layer should fit “between the pins” and the second layer should extend past the pins to the outer edges of the board.

The RTC Module:

I don’t recommend using LIR2032 rechargeable batteries with this module as the charging circuit wastes a great deal of power, and a CR2032 will backup the RTC for many years.  Remove the charging circuit resistor and power LED limit resistor from the circuit board indicated with the red squares in the first picture below.  Next add four 90-degree header pins to the I2C cascade port on the RTC board. These four pins will become the connection point for any I2C sensors you add to the data logger:

rtc1 Cave Pearl data loggers rtc3
Cave Pearl data loggers Note the battery connector leg on the top surface is trimmed a bit Cave Pearl data loggers

Since the RTC board already has 4.7k pullups on the SDA (data) and SCL (clock) lines, you will not need to add them to your I2C sensors. This board also has a 4.7k pullup on the SQW alarm line.  The GND and VCC pins (3.3v) on that cascade port can also be tapped to power other sensors you connect to the data logger.

The SD Card Adapter:

This SD card module comes with small surface mount pullup resistors on the MOSI, MISO & SCK (clock) lines (which have already been removed from the dashed red line area in the diagram).  The Arduino SDfat library uses SPI mode 0 communication, which sets the SCK line low when the logger is sleeping. This would cause a constant drain (~0.33mA) through the 10K SCK pullup on the module (second smd from the bottom) if we did not remove it.  I prefer to pull MOSI & MISO high using the internal pullups on the ProMini’s 328P processor, so those physical resistors on the breakout board can also be removed. But be careful to leave the top-most resistor of the four in place to pull up the DAT1 & DAT2 lines. This keeps those unused pins on the μSD card from floating when the cards are accessed in SPI mode.

sd1 Only remove the bottom three pullup resistors. keep the top one sd3

Then add a layer of double sided tape to the bottom of the SD card module and trim it to size.

Dupont Connectors:

trimdupontcableThe RTC & SD card modules will be connected to the Arduino by custom jumpers that you assemble from a 20cm, 40pin F-F Dupont style ribbon cable and 0.1″ (2.54mm) crimp connector housings. Test your cable for end-to-end continuity, then cut the cable into three sections.  Then peel off individual 10cm wires and insert them into the black crimp connector ends to create the cables shown. The longer 20cm wires can be used later when we assemble the LED indicator cable, and for other sensor attachments.

For the I2C connections:
Blue:          SQW alarm signal.
Yellow:     SCL (clock) (to A4)
White:       SDA (data) (to A5)
RED:          3.3v regulated
BLACK:    Ground
For the SPI cables:
RED:           3.3v regulated
Grey:          Cable select (to D10)
Orange:     MOSI   (to D11)
Brown:      SClocK (to D13)
Purple:      MISO   (to D12)
BLACK:     Ground

I’ve left one of the six RTC connector shroud slots empty for future use with the 32K clock signal but you can change that to a 5-pin housing if you wish.  Single 0.1″ (2.54mm) crimp connector housings are shown on the ends of the red, black and blue wires ,but I find that putting heat shrink over single-wire DuPonts help them hold onto the pins better.


Attach Battery Holders & solder in series

On a 4” PVC knockout cap, arrange your battery holders as close to the edge as possible to maximize room for other parts on the platform, without letting the holders hang off the edge. Mount the battery holders in place with double sided tape.

Join the red wire from the bottom of the single battery holder to the black wire from the double battery holder, so that the three AA batteries are connected in series. Fold the soldered joint and the long RED wire into the gap between the battery holders. Use some heat-shrink to bind them so they stay together inside the gap. Mark and drill 1/8” pair of holes so that you can tie down the wires from the battery pack neatly. Secure the wires to the cap with a zip tie through the holes.

Cave Pearl data loggers Cave Pearl data loggers Cave Pearl data loggers

Risers & Tie Downs

Place the RTC board alongside the batteries with one corner near, but not extending over, the edge of the cap. You want the back edge (with the four-wire cascade port) as close as possible to the single battery holder and the outer edge of the platform. The larger 6-pin connector side of the RTC module will be at a slight angle  to make room for the long connector housing that will be placed over those pins. Mark two places for the nylon standoffs and drill 2mm holes through the knockout cap. Thread the nylon standoffs through the holes and attach them to the platform with the plastic nut on the underside of the cap.

Then add two pairs of ¼ inch holes in the platform to be used later to tie-down the connector cables:

Cave Pearl data loggers Cave Pearl data loggers Cave Pearl data loggers

Battery Pack Connector

Join the two pieces of the Deans battery connector, and add solder to the end tabs of the female side of the square battery connector. For big connectors like this I turn the iron up to ~700°F (370°C). Be sure to heat the part long enough for the solder to “flow” like a liquid, and for the flux to burn off, but not so long that the nylon starts melting. Then trim the battery wires so that they extend to a mid-point over the battery holders on the platform. Strip about 5mm of insulation from the wires and tin the ends. Build up some extra insulation thickness to protect the wires from bending stress by adding two or more layers of heat shrink to the wires just before the tinned ends. Thread two larger pieces of heat shrink over the wires and then solder the wires to the connector and seal the heat shrink over the exposed metal:

Cave Pearl data loggers Cave Pearl data loggers batterycon3

Note: the top of the ‘T’ shape on this connector is the positive (red wire) side. Set your iron back down to ~600°F(315°C) after you get the solder on the big connector tabs. As a general rule: the smaller the component – the less time it can withstand heat. It’s not unusual to see maximum tolerances of 5 sec @ 288C for tiny SMD’s.

Attach modules to the platform

Layout of main components on the 4 inch knock-out cap.

Remove the tape backing from the pro-mini and the SD card adapter and affix them to the platform. As you can see the space is pretty tight. The SD card adapter needs to be close to the battery holder, because the SDcard will extend another 5mm from the holder itself, and this must not extend past the edge of the disk.

Then attach the RTC board to the nylon risers. Check to make sure there is good clearance between the RTC pins and the Arduino pins.

Connect the RTC

After the modules are placed on the board begin the interconnections by affixing the I2C cables to the RTC.  Then add the white & yellow two wire connector to risers on A4 (white) and A5 (yellow) of the promini board. Gather the white wires and pull them into the gap between the RTC module and the battery holders. Trim the wires as a group, so that their length just reaches the single battery holder. Then strip, twist & solder the ends of the wires together, and cover the join with heat-shrink.

Repeat the process with the yellow wires, and thread the joined wire sets through a cable tie that is routed from the bottom of the platform. Do not tighten the cable tie loop too much, as the black and red power lines will go through that later. To finish the RTC connection, plug a single blue wire to pin D2 on the Arduino [int(0)],  and connect it to the blue RTC alarm line as you did for the other lines. Wrap the soldered join with heat shrink and thread it under the cable tie with the I2C bus wires.

Connect the SD card

Now we will join the SPI lines which connect the SD card adapter to four pins on the Arduino.   Connect the 4-wire SPI cable to pins D10-D13 of the Arduino so that the grey wire is connected to D10 and the brown wire is on pin D13.  Attach the 6-wire SPI cable to the SD card adapter so that the black ground wire is closest to the pro-mini board.

Take each matching color pair of wires and fold them over beside the SD adapter board. Strip, twist & solder the ends together and add heat shrink to the join. Then bind them to the platform with a cable tie.

Join the Power Rails

The final step for the platform interconnecting cable is to join the “power rails”. Connect a single red jumper to the VCC pin on the Arduino compatible board, and add a single black jumper wire to the GND pin (behind the yellow wire on A5). Then gather ALL the black wires together and trim them to length along the same channel used for the three RTC wires.  Strip, twist & solder the black wires together.

Repeat the procedure for the red (+3.3v) Vcc lines. After soldering the ends of these bundles together, cinch them out of the way with the same cable tie that you left loosely holding the RTC connection wires earlier:

Battery Connection with Voltage Divider

Note:  The battery connection wires see a lot of handling compared to the other connections on the logger platform.  Since I have crimping pliers to add my own DuPont ends, this is one place on the build where I prefer to use silicone wire that is more flexible than the PVC insulated wire. 

With the main interconnect wires completed the last part of the logger platform build is the Arduino side power connector. Put the male battery connector in a vise with a spare female side connected to prevent the nylon from sagging due to heat, then add solder to the terminal flaps of the male battery connector. You might need to turn your iron up to 700°F to tin the connections the large connector, but turn it back down to 600°F (315°C) before attaching the voltage divider resistors, or you will cook those smaller components.

Build a voltage divider from two 4.7MΩ resistors, bridging across the low side resistor with a 0.1µF (104) ceramic capacitor. Add a black wire to the capacitor side of the divider, a pink wire to the center, and a red wire to the opposite side. Then solder this divider across the pins on the male battery connector, being sure to connect the black wire side to the ground pin, and the red wire side to the (+) pin.  Blow on the solder to cool it down as soon as the join to the connector pins is completethere is more than enough residual heat stored in the battery connector plates to cook the little capacitor, which then usually becomes a short.

divider3 divider2 divider4

This voltage divider will cut the battery voltage in half, allowing us to read the status of the 3xAA battery pack on any analog input pin (via the pink wire in the photos above) even though the main battery voltage is above the Arduino’s 3.3v Aref.  The 4.7Meg resistors are large enough to limit losses on the bridge (to about 0.6uA), and because they exceed the input impedance limit of the ADC, the capacitor is needed to provide electrons to fill the ADC’s sample and hold capacitor when a reading is needed. The battery voltage calculation for this combination is:   float batteryVoltage = float((analogRead(A0)/ 511.5)*3.3);  The default MIC5205 regulator found on most promini style boards has ~200-300 mv dropout during SD card writes, so when your AA battery pack gets down 3.65 volts, it’s time to shut down the logger.

Add heat shrink to protect the connections, and then plug the connector into it’s mate on the logger platform. Trim the black wire to length for the ground pin beside D2 (with the blue RTC alarm wire) , and then add a female crimp connector to that wire. Do the same for the pink voltage divider wire which gets attached to your choice of analog inputs from A0-A3, and the red battery wire which gets attached to the RAW input pin near the serial/UART terminal.

Cave Pearl data loggers divider6

Indicator LED

Since we removed the pin 13 LED from the pro-mini, we will now construct an external indicator. This is another place where soft silicone wires are preferred, but if you do not have them, peel away 20cm Red, Green, Blue and Black ribbon cable wires and solder them into place on a common cathode RGB LED, matching the wire colors to the correct pin. Then add stiff heat shrink around the connection points to protect the wire from flexing. Place the ends of the Red, Green and Blue wires into a 3-connector housing.  The R-G-B connector attaches to digital pins 4-5-6 on the Arduino.

Cut the black wire near its mid-point, and insert a 20K resistor to limit the current through the LED. I often replace the bottom half of the wire with a softer flexible length of wire with a MALE crimp connector at its end. The male ground pin connects to the ground pin beside the D2 connector.



Typical pro-mini loggers built with this design sleep at 0.25mA, before extra sensors are added. At that current draw, the logger should deliver at least eight months of operation on three brand new AA batteries with a 15min duty cycle; depending on sensor load.

Now you can run testing utilities to verify that everything is working. But before you jump into that it’s worth keeping in mind that the entire construction can easily be disassembled for debugging if you suspect a bad connection somewhere, and it is even possible to pop the modules off with a small screwdriver and replace them:

Test continuity on suspect wires with a DVM.  If you carefully lift the little tabs holding the crimps inside the black housings, individual wires can be extracted from the connectors and replaced.  If your crimps don’t feel like they are securely in place when they slide over the riser pins – then you should replace that connector.


platform partsYou can build a robust, inexpensive waterproof housing for your logger from a couple of 4″ plumbing end-caps. I use Loctite E30-CL to pot sensors on the exterior of the housing and you can see a complete walk through of the process in the  Sensors & Housing post from last year. Note that I did not use the 4-pin Deans connectors you see there on this newer build because they are fairly expensive, and a few people have been having a hard time getting their hands on them. If you shell out for crimping pliers, you can make as many custom connectors as you need for pennies each.  Dupont’s are not as robust, but I’ve had loggers running for years with them, so they are not a bad place to start. If the male pins don’t mate tightly, put a thin layer of solder on them to make them a bit thicker. Quality varies quite a bit, and Pololu sells good crimp pins.


1. Test the LED
Connect your indicator LED, to pins 4(R) – 5(B) – 6(G), and the ground line to one of your spare GND connections. Open Blink with the LED connected to the Arduino. Change the LED to Pin 4. Verify and upload. The red LED should start flashing. Repeat with pins 5 and 6 to test green & blue respectively. Note that with the common ground connection, you can not light up two colors of the LED at the same time.

Make polarized (non reversable) connectors by mixing Male & Female connectors

You can make non reversible sensor connectors by mixing male & female pins in the same housing. But never use male pins on wires that supply power to a sensor circuit, or the connector could short out if those pins come into contact with a conductive surface.

2. Test the I2C bus
Use an I2C scanner  to determine if your devices are responding on the I2C bus. With the scanner running, specify P to return to output only the addresses that have devices, and specify S to run a Single Scan. The 4k AT24C32 eeprom on the RTC board is at address 0x57 and the DS3231 will show up at address 0x68. The 32K eeprom should show up at 0x50 and other sensors will show up at different addresses. Note that the 4K AT24C32 is only rated for 100khz operation, but the DS3231 is rated for 400khz bus speeds.

3. Test the RTC
Use the settime & gettime scripts to set and check the time on the RTC. Generally, I set my windows computer to UTC before uploading setTime so that the logger is operating on UTC. This is not possible on a Macintosh, so you will have to check if London time is currently in sync with GMT/UTC, or find some other city that is. Alternatively you could avoid the problem by using the new serial setting script from Mr Alvin’s gitHub.

4. Test μSD Card communication
Insert the microSD in the adapter and test it with the CARDinfo utility at the Arduino  playground.  Remember to change the CSelect variable to 10 to get the CARDino to operate properly, as that is the pin we connected to the μSD card Cable Select line.


Loggers with the default pro-mini’s MIC5205 regulator usually delivers sleep current in the 0.21-0.26mA range depending on the SD card (Promini~0.07mA, SDcard: ~0.05-0.1mA, RTC~0.09mA) That should give you at least eight months of operation on 3xAA’s, and with minor modifications you can pull that down into the 0.15mA range to run almost two years.  (Or you could pass the one year mark by powering the logger with 4xAA cells in series. If you do this you will have to change the resistor ratio on the main battery voltage divider to keep it’s output below the 3.3v aref voltage on the analog input pins. For higher input voltages I  use a 3.3/10 Meg Ohm combination) Write the build date & your initial sleep current on the bottom of the logger platform – it’s a handy piece of diagnostic information to have later.

5. Test the EEprom(s)
A utility to test I2C eeproms was posted by bHogan at the Arduino playground forum, but that version is now quite old and does not compile in the current IDE without editing. I’ve posted an updated version on gitHub. Setting #define EEPROM_ADDR 0x57 will test the 4k eeprom on the RTC board, and if you install the extra 32k eeprom that shows up on the bus at 0x50. The serial window will return  A through T & 33 through 48 if the eeprom being tested is working OK.

6. Check Sleep Current
#include the LowPower.h library from RocketScream at the beginning of a blank sketch and the put the following line at the beginning of the main loop:



Then enable some internal pull-up resistors in to the setup section of your code:
(be sure to do this before you call sd.begin)

// Always pullup CS:
pinMode(chipSelect, OUTPUT); digitalWrite(chipSelect, HIGH); //pullup the CS pin to tell the SD card go to sleep
//and you may need to pullup MOSI/MISO lines:
//pinMode(MOSIpin, OUTPUT); digitalWrite(MOSIpin, HIGH); 
//pinMode(MISOpin, INPUT); digitalWrite(MISOpin, HIGH);   

Upload that sketch and your logger will be put to sleep as soon as the program runs.  Note: the CS line should have a pullup when using the standard SD libraries, and the MOSI/MISO lines may also need to be enabled to lower the sleep current with some SD cards. But SPI is a complex protocol, and you might have to turn off these pull-ups if you have other SPI sensors attached.

7. Build your datalogger code
A good place to start would be Tom Igoe’s analog pin reading example at  ( be sure  const int chipSelect = 10; for the build described in this tutorial) and there is a nifty little function  for automatically creating log file names over on Adafruit’s site.  For something a little more advanced, I have prepared a basic data logger script that puts the data logger to sleep and wakes it up again based on timed alarms from the real time clock. These are all just starting points for you to add to as you learn more about programming an Arduino.

The first major thing to tackle to achieve a decent operating lifespan is buffering your sensor data to the 100 kHz 4k EEprom on the RTC board. Examples of the code I use for this eeprom buffering is embedded the I2C eeprom tester example I’ve placed on gitHub. You don’t want to wake up the card more than once per day, since that operation draws up to 100mA, for 200-400ms, which is more than any other run-time power use on the logger.  Older Sandisk cards (<1Gb) tend to have lower sleep currents around 70µA, but it’s not unusual to see newer large capacity cards drawing ~200µA or more during sleep.

You can extend that power saving strategy by adding a larger 32K AT24C256 EEprom to the logger platform. Because all I2C devices use the same bus wires, simply add the four EEprom connections to the bundles as you make them:

In addition to saving power by reducing the number of SD write events, the larger EEprom also extends the operating lifespan by allowing you to safely accelerate the I2C bus to 400khz, and this can reduce run-time duty cycle length significantly.   Because all I2C bus devices are connected in parallel, simply add the four EEprom jumpers to the appropriate wire bundles as you make them.  Alternatively, you could add a high resolution temperature sensor like the TMP102, or the MCP9808 to the logger in exactly the same way.

To fully optimize your code it is helpful monitor current during “logging events”, but this usually requires an oscilloscope.  In this post, I have outlined a method to use an UNO with the serial monitor built into the IDE to capture and display these brief events.

Addendum 2017-06-22

I wouldn’t bother for just one or two builds, but Dangerous prototypes just introduced a Dirty Cables service to order custom cables. Be interesting if I could build something like this logger interconnect wire with their tool. Might be a good way to throw a few kits up on Tindie…