Author Archives: edmallon

How to make ‘High-Resolution’ Sensor Readings with DIGITAL I/O pins

AN685 figure 8: The RC rise-time response of the circuit allows microcontroller timers to be used to determine the relative resistance of the NTC element.

For more than a year we’ve been oversampling the Arduino’s humble ADC to get >16bit ambient temperature readings from a 20¢ thermistor. That pin-toggling method is simple and delivers solid results, but it requires the main CPU to stay awake long enough to capture multiple readings for the decimation step. (~200 miliseconds @ 250 kHz ADC clock) While that only burns 100 mAs per day with a typical 15 minute sample interval, a read through Thermistors in Single Supply Temperature Sensing Circuits hinted that I could ditch the ADC and read those sensors with a pin-interrupt method that would let me sleep the cpu during the process. An additional benefit is that the sensors would draw no power unless they were actively being read.

Given how easy it is drop a trend-line on your data, I’ve never understood why engineers dislike non-linear sensors so much. If you leave out the linearizing resistor you end up with this simple relationship.

The resolution of time based methods depends on the speed of the clock, and Timer1 can be set with a prescalar = 1; ticking in step with the 8 mHz oscillator on our Pro Mini based loggers. The input capture unit can save Timer1’s counter value as soon as the voltage on pin D8 passes a high/low threshold. This under appreciated feature of the 328p is faster than typical interrupt handling, and people often use it to measure the time between rising edges of two inputs to determine the pulse width/frequency of incoming signals.

You are not limited to one sensor here – you can line them up like ducks on as many driver pins as you have available.  As long as they share that common connection you just read each one sequentially with the other pins in input mode. Since they are all being compared to the same reference, you’ll see better cohort consistency than you would by using multiple series resistors.

Using 328p timers is described in detail at Nick Gammons Timers & Counters page, and I realized that I could tweak the method from ‘Timing an interval using the input capture unit’ (Reply #12) so that it recorded only the initial rise of an RC circuit.  This is essentially the same idea as his capacitance measuring method except that I’m using D8’s external pin change threshold rather than a level set through the comparator. That should be somewhere around 60% of the rail, but the actual level doesn’t matter so long as it’s consistent between the two consecutive reference & sensor readings. It’s also worth noting here that the method  doesn’t require an ICU peripheral – any pin that supports a rising/ falling interrupt can be used. (see addendum for details) It’s just that the ICU makes the method more precise which is important with small capacitor values.

Using Nicks code as my guide, here is how I setup the Timer1 with ICU:

float referencePullupResistance=10351.6;   // a 1% metfilm measured with a DVM
volatile boolean triggered;
volatile unsigned long overflowCount;

ISR (TIMER1_OVF_vect) {  // triggers when T1 overflows: every 65536 system clock ticks
overflowCount++;
}

 

ISR (TIMER1_CAPT_vect) {     // transfers Timer1 when D8 reaches the threshold
sleep_disable();
unsigned int timer1CounterValue = ICR1;       // Input Capture register (datasheet p117)
unsigned long overflowCopy = overflowCount;

if ((TIFR1 & bit (TOV1)) && timer1CounterValue < 256){    // 256 is an arbitrary low value
overflowCopy++;    // if “just missed” an overflow
}

if (triggered){ return; }  // multiple trigger error catch

finishTime = (overflowCopy << 16) + timer1CounterValue;
triggered = true;
TIMSK1 = 0;  // all 4 interrupts controlled by this register are disabled by setting zero
}

 

void prepareForInterrupts() {
noInterrupts ();
triggered = false;                   // reset for the  do{ … }while(!triggered);  loop
overflowCount = 0;               // reset overflow counter
TCCR1A = 0; TCCR1B = 0;     // reset the two (16-bit) Timer 1 registers
TCNT1 = 0;                             // we are not preloading the timer for match/compare
bitSet(TCCR1B,CS10);           // set prescaler to 1x system clock (F_CPU)
bitSet(TCCR1B,ICES1);          // Input Capture Edge Select ICES1: =1 for rising edge
// or use bitClear(TCCR1B,ICES1); to record falling edge

// Clearing Timer/Counter Interrupt Flag Register  bits  by writing 1
bitSet(TIFR1,ICF1);         // Input Capture Flag 1
bitSet(TIFR1,TOV1);       // Timer/Counter Overflow Flag

bitSet(TIMSK1,TOIE1);   // interrupt on Timer 1 overflow
bitSet(TIMSK1,ICIE1);    // Enable input capture unit
interrupts ();
}

With the interrupt vectors ready, take the first reading with pins D8 & D9 in INPUT mode and D7 HIGH. This charges the capacitor through the reference resistor:

//========== read 10k reference resistor on D7 ===========

power_timer0_disable();    // otherwise Timer0 generates interrupts every 1us

pinMode(7, INPUT);digitalWrite(7,LOW);     // our reference
pinMode(9, INPUT);digitalWrite(9,LOW);     // the thermistor
pinMode(8,OUTPUT);digitalWrite(8,LOW);  // ground & drain the cap through 300Ω
LowPower.powerDown(SLEEP_30MS, ADC_OFF, BOD_ON);  //overkill: 5T is only 0.15ms

pinMode(8,INPUT);digitalWrite(8, LOW);    // Now pin D8 is listening

set_sleep_mode (SLEEP_MODE_IDLE);  // leaves Timer1 running
prepareForInterrupts ();
noInterrupts ();
sleep_enable();
DDRD |= (1 << DDD7);          // Pin D7 to OUTPUT
PORTD |= (1 << PORTD7);    // Pin D7 HIGH -> charging the cap through 10k ref

do{
interrupts ();
sleep_cpu ();                //sleep until D8 reaches the threshold voltage
noInterrupts ();
}while(!triggered);      //trapped here till TIMER1_CAPT_vect changes value of triggered

sleep_disable();           // redundant here but belt&suspenders right?
interrupts ();
unsigned long elapsedTimeReff=finishTime;   // this is the reference reading

Now repeat the process a second time with D7 & D8 in INPUT mode, and D9 HIGH to charge the capacitor through the NTC thermistor:

//==========read the NTC thermistor on D9 ===========

pinMode(7, INPUT);digitalWrite(7,LOW);     // our reference
pinMode(9, INPUT);digitalWrite(9,LOW);     // the thermistor
pinMode(8,OUTPUT);digitalWrite(8,LOW);  // ground & drain the cap through 300Ω
LowPower.powerDown(SLEEP_30MS, ADC_OFF, BOD_ON);

pinMode(8,INPUT);digitalWrite(8, LOW);    // Now pin D8 is listening

set_sleep_mode (SLEEP_MODE_IDLE);
prepareForInterrupts ();
noInterrupts ();
sleep_enable();
DDRB |= (1 << DDB1);          // Pin D9 to OUTPUT
PORTB |= (1 << PORTB1);    // set D9 HIGH -> charging through 10k NTC thermistor

do{
interrupts ();
sleep_cpu ();
noInterrupts ();
}while(!triggered);

sleep_disable();
interrupts ();
unsigned long elapsedTimeSensor=finishTime;   //this is your sensor reading

Now you can determine the resistance of the NTC thermistor via the ratio:

unsigned long resistanceof10kNTC=
(elapsedTimeSensor * (unsigned long) referencePullupResistance) / elapsedTimeReff;

pinMode(9, INPUT);digitalWrite(9,LOW);pinMode(7, INPUT);digitalWrite(7,LOW);
pinMode(8,OUTPUT);digitalWrite(8,LOW);  //discharge the capacitor when you are done

The integrating capacitor does a fantastic job of smoothing the readings and getting the resistance directly eliminates 1/2 of the calculations you’d normally do. To figure out your thermistor constants, you need to know the resistance at three different temperatures. These should be evenly spaced and at least 10 degrees apart with the idea that your calibration covers the range you expect to use the sensor for. I usually put loggers in the refrigerator & freezer to get points with enough separation from normal room temp. – with the thermistor taped to the surface of a si7051.  Then plug those values into the thermistor calculator provided by Stanford Research Systems.

Just for comparison I ran a few head-to-head trials against my older dithering/oversampling method:

°Celcius vs time [1 minute interval]  si7051 reference [±0.01°C resolution] vs. Pin-toggle Oversampled 5k NTC vs. ICUtiming 10k NTC.   I’ve artificially separated these curves for visual comparison, and the 5K was not in direct contact with the si7051 reference, while the 10k NTC was taped to the surface of chip – so some of the 5ks offset is an artifact. The Timer1 ratios delver better resolution than 16-bit (equivalent) oversampling in 1/10 the time.

There are a couple of things to keep in mind with this method:

si7051 reference temp (C) vs 10k NTC temp with with a ceramic 106 capacitor. If your Ulong calculations overflow at low temperatures like the graph above, switch to doing the division before the multiplication. Also keep in mind that the Arduino will default to 16bit calculations unless you set/cast everything to unsigned long.  Or you could make you life easy and save the elapsedTimeReff & elapsedTimeSensor values to your data file and do the calculations later in Excel.

1) Because my Timer1 numbers were small with a 104 cap I did the multiplication before the division. But keep in mind that this method can easily generate values that over-run your variables during calculationUlong MAX is 4,294,967,295  so the elapsedTimeSensor reading must be below 429,496 or the multiplication overflows with a 10,000 ohm reference. Dividing that by our 8mHz clock gives about 50 milliseconds. The pin interrupt threshold is reached after about one rise-time constant so you can use an RC rise time calculator to figure out your capacitors upper size limit. But keep in mind that’s one RC at the maximum resistance you expect from your NTC – that’s the resistance at the coldest temperature you expect to measure as opposed to its nominal rating.  (But it’s kind of a chicken&egg thing with an unknown thermistor right?  See if you can find a manufacturers table of values on the web, or simply try a 0.1uF and see if it works).  Once you have some constants in hand Ametherm’s Steinhart & Hart page lets you check the actual temperature at which your particular therm will reach a given resistance.  Variable over-runs are easy to spot because the problems appear & then disappear whenever some temperature threshold is crossed. I tried to get around this on a few large-capacitor test runs by casting everything to float variables, but that lead to other calculation errors.

2) This method works with any kind of resistive sensor, but if you have one that goes below ~200 ohms (like a photoresistor in full sunlight) then the capacitor charging could draw more power than you can safely supply from the D9 pin.  In those cases add a ~300Ω resistor in series with your sensor to limit the current, and subtract that value from the output of the final calculation. At higher currents you’ll also have voltage drop accross the mosfets controlling the I/O pins (~40Ω on a 3.3v Pro Mini), so make sure the calibration includes the ends of your range.

There are a host of things that might affect the readings because every component has a temperature, aging, and other coefficients, but for the accuracy level I’m after many of those factors are handled by the ratio-metric nature of the method. It’s almost independent of value of the capacitor, so you can get away with a cheap ceramic even though it’s value changes dramatically with temperature and age. (I’d still use at least an X7R or, a plastic film cap for measuring colder temps) Last year I found that the main system clock was variable to the tune of about 0.05% over a 40°C range, but that shouldn’t matter if the reference and the sensor readings are taken close to each other. Ditto for variations in your supply. None of it matters unless it affects the system ‘between’ the two measurements.

The significant digits from your final calculation depend on the RC rise time, so switching to 100k thermistors increases the precision. Processors with higher clock speeds should also boost your resolution.  You can shut down more peripherals with the PRR to use less power during the readings as long as you leave Timer1 running.  I’ve also found that cycling the capacitor through an initial charge/discharge cycle (via the 300Ω on D8) after long sleep intervals improved the consistency of the readings. That capacitor shakedown might be an artifact of the ceramics I was using but reading your reference twice at the start of each run might be a good way of eliminating errors that arise from the pre-existing conditions, though you’d also be heating up that resistor so it’s a bit of a trade-off.

Will this method replace our pin-toggled oversampling?  Perhaps not for something as simple as a thermistor since that method has already proven itself in the real world, and I don’t really have anything better to do with A6 & A7. And oversampling still has the advantage of being simultaneously available on all the analog inputs, while the ICU is a more limited resource.  Given the high resolution that’s potentially available with the Timer1/ICU combination, I might save this method for sensors with less dynamic range.  I already have some ideas there and, of course, lots more testing to do before I figure out if there are other problems related to this new method.  I still haven’t determined what the long-term precision is with our Pro Mini clones, and the WDT experiment taught me to be cautious about counting those chickens.


Addendum:   Using the processors built in pull-up resistors

After a few successful runs, I realized that I could use the internal pull-up resistor on D8 as my reference; bringing it down to only three components. Measuring the resistance of the internal pull-up is simply a matter of enabling it and then measuring the current that flows between that pin and ground. I ran several tests, and the Pro Mini’s the internal pullups were all close to 36,000 ohms, so my reference would become 36k + the 300Ω resistor needed on D8 to safely discharge of the capacitor between cycles. I just have to set the port manipulation differently before the central do-while loop:

PORTB |= (1 << PORTB0);     //  enable pullup on D8 to start charging the capacitor

A comparison of the two approaches:

°Celcius vs time: (Post-calibration, 0.1uF X7R ceramic cap)  si7051 reference [±0.01°C] vs. thermistor ICU ratio w 10k reference resistor charging the capacitor vs. the same thermistor reading calibrated using the rise time through pin D8’s internal pull-up resistor. The reported ‘resistance’ value of the NTC themistor was more than 1k different between the two methods, with the 10k met-film reference providing values closer to the rated spec. However the Steinhart-Hart equation constants from the calibration were also quite different, so the net result was indistinguishable between the two references in the room-temperatures range.

The internal pull-up probably isn’t a real resistor. More likely its a very thin semiconductor channel that simply acts like one, and I found the base-line temperature variance to be about 200 ohms over my 40°C calibration range. And because you are charging the capacitor through the reference and through the thermistor, the heat that generates necessarily changes the changes those values during the process.  However when you run a calibration, those factors get absorbed into the S&H coefficients provided you let the system equilibrate during the run.

As might be expected, all chip operation time affects the resistance of the internal pull-up, so the execution pattern of the code used for your calibration must exactly match your deployment code or the calibration constants will give you an offset error proportional to the variance of the internal pull-up caused by the processors run-time. Discharging the capacitor through D8, also generates some internal heating so those (~30-60ms) sleep intervals also have to be consistent.  In data logging applications you can read that reference immediately after a long cool-down period of processor sleep and use the PRR to reduce self-heating while the sample is taken.  Code consistency is always important with time based calibrations no matter how stable your reference is.

Another issue was lag because that pull-up is embedded with the rest of the chip in a lump of epoxy. This was a pretty small, with a maximum effect less than ±0.02°C/minute and I didn’t see that until temperatures fell below -5 Celsius.  Still, for situations where temperature is changing quickly I’d stick with the external reference, and physically attach it to the thermistor so they stay in sync.


Addendum:   What if your processor doesn’t have an Input Capture Unit?

With a 10k / 0.1uF combination, I was seeing Timer1 counts of about 5600 which is pretty close to one 63.2% R * C time constant for the pair.  That combination limits you to 4 significant figures and takes about 2x 0.7msec per reading on average.  Bumping the integrating capacitor up to 1uF (ceramic 105) multiplies your time by a factor of 10 – for another significant figure and an average of ~15msec per paired set readings.  Alternatively, a 1uF or greater capacitor allows you to record the rise times with micros()  (which counts 8x slower than timer1) and still get acceptable results. (even with the extra interrupts that leaving Timer0 running causes…) So the rise-time method could be implemented on processors that lack an input capture unit – provided that they have Schmitt triggers on the digital inputs like the AVR.

void d3isr() {
triggered = true;
}

pinMode(7, INPUT);digitalWrite(7,LOW);     // reference resistor
pinMode(9, INPUT);digitalWrite(9,LOW);     // the thermistor
pinMode(8,OUTPUT);digitalWrite(8,LOW);  // ground & drain the cap through 300Ω
LowPower.powerDown(SLEEP_30MS, ADC_OFF, BOD_ON);  // 5T is now 1.5ms w 10k

pinMode(8,INPUT);digitalWrite(8, LOW);    // Now pin D8 is listening

triggered = false;
set_sleep_mode (SLEEP_MODE_IDLE);  // leave micros timer0 running for micros()
unsigned long startTime = micros();
noInterrupts ();
attachInterrupt(1, d3isr, RISING); // using pin D3 here instead of D8
DDRD |= (1 << DDD7);          // Pin D7 to OUTPUT
PORTD |= (1 << PORTD7);    // Pin D7 HIGH -> charging the cap through 10k ref
sleep_enable();

do{
interrupts ();
sleep_cpu ();
noInterrupts ();
}while(!triggered);

sleep_disable();
detachInterrupt(1);
interrupts ();
unsigned long D7riseTime = micros()-startTime;

Then repeat the pattern shown earlier for the thermistor reading & calculation. I’d probably bump it up to a ceramic 106 for the micros method just for some extra wiggle room. The method doesn’t really care what value of capacitor you use, but you have to leave more time for the discharge step as the size of your capacitor increases.

‘No-Parts’ Temperature Measurement with Arduino Pro Mini

328p processor System Clocks & their Distribution pg26

Most micro-controllers use a quartz crystal oscillator to drive the system clock, and their resonant frequency is reasonably stable with temperature variations. In high accuracy applications like real time clocks even that temperature variation can be compensated, and last year I devised a way to measure temperature by comparing a 1-second pulse from a DS3231 to the uncompensated 8Mhz oscillator on a Pro Mini. This good clock / bad clock method worked to about 0.01°C, but the coding was complicated, and it relied on the ‘quality’ of the cheap RTC modules I was getting from fleaBay – which is never a good idea.

But what if you could read temperature better than 0.01°C using the Pro Mini by itself?

Figure 27-34:  Watchdog Oscillator Frequency vs. Temperature. Pg 346 (Odd that the frequency is listed as 128kHz on pg55?)  Variation is ~100 Hz/°C

The 328P watchdog timer is driven by a separate internal oscillator circuit running at about 110 kHz. This RC oscillator is notoriously bad at keeping time, because that on-chip circuit is affected by external factors like temperature. But in this particular case, that’s exactly what I’m looking for.  The temperature coefficient of crystal resonators is usually quoted at 10–6/°C and for RC oscillation circuits the coefficient is usually somewhere between 10–3/°C to 10–4/°C.  There’s plenty of standard sensors don’t give you a delta that large to play with!

To compare the crystal-driven system clock to the Watchdogs unstable RC oscillator I needed a way to prevent the WDT from re-starting the system.  Fortunately you can pat the dog and/or disable it completely inside its interrupt vector:

volatile boolean WDTalarm=false;
ISR(WDT_vect)
{
wdt_disable();  // disable watchdog so the system does not restart
WDTalarm=true;    // flag the event
}


SLEEP_MODE_IDLE
leaves the timers running, and they link back to the system clock.  So you can use micros() to track how long the WDT actually takes for a given interval.  Arduinos Micros() resolution cannot be better than 4 microseconds (not 1 µs as you’d expect) because of the way the timer is configured, but that boosts our detectable delta/° by a factor of four, and the crystal is far more thermally stable than the watch-dog. It’s worth noting that timer0 (upon which micros() depends) generates interrupts all the time during the WDT interval, in fact at the playground they suggest that you have to disable timer0 during IDLE mode sleeps. But for each time interval, the extra loops caused by those non-WDT interrupts create a consistant positive offset, and this does not affect the temperature related delta.

WDTalarm=false;
// Set the Watchdog timer                    from: https://www.gammon.com.au/power
byte interval =0b000110;  // 1s=0b000110,  2s=0b000111, 4s=0b100000, 8s=0b10000
//64ms= 0b000010, 128ms = 0b000011, 256ms= 0b000100, 512ms= 0b000101
noInterrupts
();
MCUSR = 0;
WDTCSR |= 0b00011000;    // set WDCE, WDE
WDTCSR = 0b01000000 | interval;    // set WDIE & delay interval
wdt_reset();  // pat the dog
interrupts ();
unsigned long startTime = micros();
while (!WDTalarm)  {    //sleep while waiting for the WDT
set_sleep_mode (SLEEP_MODE_IDLE);
noInterrupts ();  sleep_enable();  interrupts ();  sleep_cpu ();
sleep_disable();  //processor starts here when any interrupt occurs
}
unsigned long WDTmicrosTime = micros()-startTime;  // this is your measurement!

The while-loop check is required to deal with the system interrupts that result from leaving the micros timer running, otherwise you never make it all the way through the WDT interval. I haven’t yet figured out how many interrupts you’d have to disable to get the method working without that loop.

To calibrate, I use my standard refrigerator->freezer->room sequence for a repeatable range >30°.  Since the mcu has some serious thermal lag, the key is doing everything VERY SLOWLY with the logger inside a home made “calibration box” made from two heavy ceramic pots, with a bag of rice between them to add thermal mass:

1sec WDT micros() (left axis) vs si7051 °C Temp (right axis) : Calibration data selected from areas with the smallest change/time so that the reference and the 328p have equilibrated.

If you use reference data from those quiescent periods, the fit is remarkably good:

si7051 reference temperature vs 1 sec WDT micros() : A fit this good makes me wonder if the capacitor on the xtal oscillator is affected the same way as the capacitor in the watchdogs RC oscillator, with the net result being improved linearity. In this example, there was a constant over-count of 100,000 microseconds / 1-second WDT interval.

I’m still experimenting with this method, but my cheap clone boards are delivering a micros() delta > 400 counts /°C with a one second interval – for a nominal resolution of ~0.0025°C.  Of course that’s just the raw delta.  When you take that beautiful calibration equation and apply it to the raw readings you discover an inter-reading jitter of about 0.1°C – and that lack of precision becomes the ‘effective’ limit of the resolution.  It’s going to take some serious smoothing to get that under control, and I’ll be attacking the problem with my favorite median filters over the next few days. I will also see if I can reduce it at the source by shutting down more peripherals and keeping an eye on stray pin currents.

Noise from si7051 reference (red)  vs Cal. equation applied to raw WDT micros readings (blue). 

Doubling the interval cuts the the noise and the apparent resolution in half, and if you are willing to wait around for the watchdogs 8-second maximum you can add an order of magnitude. Of course you could also go in the other direction: a quarter second WDT interval would deliver ~100 counts/°C, which still gets you a nominal 0.01°C though the jitter gets worse. Note that you can’t use the ‘b’ coefficients from one interval to the next, because of the overhead caused by the non-WDT interrupts. That “awake time” must also be contributing some internal chip heating.

The si7051 reference sensor needs to be held in direct physical contact with the surface of the mcu during the room->fridge->freezer calibration; which takes 24 hours. Since my ref is only ± 0.1ºC accuracy, calibrations based on it are probably only good to about ± 0.2ºC.

There are a few limitations to keep in mind, the biggest being that messing with WDT_vect means that you can’t use the watchdog timer for it’s intended purpose any more. The other big limitation is that you can only do this trick on a voltage regulated system, because RC oscillators are affected by the applied voltage, though in this case both oscillators are exposed to whatever is on the rail, so a bit more calibration effort might let you get away with a battery driven system.

Self-heating during normal operation means that this method will not be accurate unless you take your temperature readings after waking the processor from about 5-10 minutes of power-down sleep. The mass of the circuit board means that the mcu will always have significant thermal lag. So there is no way to make this method work quickly and any non-periodic system interrupts will throw off your micros() reading.

Every board has a different crystal/capacitor/oscillator combination, so you have to re-calibrate for each one. Although the slopes are similar, I’ve also found that the raw readings vary by more than ±10k between different Pro Minis for the same 1sec WDT interval, at the same temperature. The silver lining there is that the boards I’m using probably have the cheapest parts available, so better quality components could boost the accuracy , though I should insert the usual blurb here that resolution and accuracy are not the same thing at all.  I haven’t had enough time yet to assess things like drift, or hysteresis beyond the thermal lag issue, but those are usually less of a problem with quality kit. If your board is using Y5V caps it probably won’t go much below -15°C before capacitor failure disrupts the method.

It’s also worth noting that many sleep libraries, like Rocketscreem’s Lowpower lib, do their own modifications to the watchdog timer, so this method won’t work with them unless you add the flag variable to their modified version of the WDT_vect.  To add this technique to the base code for our 1-hour classroom logger, I’ll will have to get rid of that library dependency.

Where to go from here:

  1. Turning off peripherals with PRR can save power and reduce heating during the interval.
  2. Switching from micros(), to timer based overflows could increase the time resolution to less than 100 ns;  raising nominal thermal resolution.
  3. Clocking the system from the DS3231’s temperature compensated 32khz output could give another 100 counts/°C and improve the thermal accuracy. My gut feeling is the noise would also be reduced, but that depends on where it’s originating.

Despite the limitations, this might be the best “no-extra-parts” method for measuring temperature that’s possible with a Pro Mini, and the method generalizes to every other micro-controller board on the market provided they have an independent internal oscillator for the watchdog timer.

Addendum:

As I run more units through the full calibration, I’m seeing about 1 in 3 where a polynomial fits the data better for the -15 to +25°C range:

si7051 reference temperature vs 1 sec WDT micros() : a different unit, but both clone boards from the same supplier

This is actually what I was expecting in the first place and I suspect all the fits would be 2nd order with a wider range of calibration temperatures. Also, this is the raw micros output – so you could make those coefficients more manageable by subtracting the lowest temperature reading from all those above. This would leave you with a numerical range of about 16000 ticks over 40°C, which takes less memory and is easier for calculations.

And just for fun I ran a trial on an unregulated system powered by 2xAA lithium batteries.  Two things happened: 1) the jitter/noise in the final Temperature readings more than doubled – to about 0.25°C  and 2) calibration was lost whenever the thermal mass of the batteries meant that the supply was actively changing – regardless of whether the mcu & reference had settled or not:

Red is the Si reference [left axis], Green is the calibration fit equation applied to the WDT micros() [left], and blue is the rail voltage supplied by 2xAA lithium batteries [right axis] (Note: low voltage spikes are caused by internal housekeeping events in the Nokia 256mb SD cards)

Addendum 2019-02-26

This morning I did a trial run which switched from micros() to timer1 overflows, using code from Nick Gammon’s Improved sketch using Timer 1. This increased the raw delta to almost 5000/°C, but unfortunately the width of the jitter also increased to about 500 counts. So I’m seeing somewhere near ±0.05°C equivalent of precision error – although my impression is that it’s been reduced somewhat because Timer1 only overflows 122 times per second, while the Timer0/micros had to overflow 100k times. So changing timers means less variability from the while-loop code execution. Next step will be to try driving the timer with the 32khz from the RTC…

Addendum 2019-02-27

So I re-jigged another one of Nicks counting routines which increments timer1 based on input from pin D5, using the WDT interrupt to set the interval. Then I enabled the 32.768 kHz output from a DS3231N and connected it to that pin. This pulse is dead-dog slow compared to the WDT oscillator, so I extended the interval out to 4 seconds.  This long-ish sample time only produced a delta of about 40 counts/°C.

Si7051 reference temp vs Timer1 counts of 32kHz output from DS3231N  (based on data selected from quiescent periods)

There wasn’t enough data to produce high resolution, but my thought was that since the DS3231N has temperature compensated frequency output, it eliminates the xtal as a variable in the question of where the jitter was coming from.  This approach also causes only 2-3 overflows on timer1, so the impact of code execution is further reduced.  Unfortunately, this experiment did not improve the noise situation:

DS3231 32khz clock tics vs 4sec WDT interval Raw reading jitter during a relatively quiescent period.

That’s about 8 counts of jitter in the raw, which produces readings about ±0.1C away from the central line.  That’s actually worse than what I saw with the Xtal vs WDT trials, but the increase might be an artifact of the pokey time-base.  The smoking gun now points squarely at variations in the WDT oscilator output as the source of the noise.

That’s kind of  annoying, suggesting it will take filtering/overhead to deliver better than about 0.1°C from this technique, even though higher resolution is obviously there in the deltas. The real trick will matching the right filter with all the other time lag / constraints in this system. Still, extra data that you can get from a code trick is handy, even if it sometimes it only serves to verify that one of your other sensors hasn’t gone squirrely.

—> just had a thought: oversampling & decimation eats noise like that for breakfast!
Just fired up a run taking 256 x 16ms samples (the shortest WDT interval allowed) with Timer1 back on the xtal clock. Now I just have to wait another 24 hours to see if it works…

Addendum 2019-02-28

OK: Data’s in from oversampling the WDT vs timer1 method.  I sum the the timer1 counts from 256 readings (on a 16msec WDT interval) and then >>4 to decimate. These repeated intervals took about 4 seconds of sampling time.

si7051 reference temperature vs 256x Oversampled Timer 1 reading on 16 msec WDT interval: Fit Equation

This produced 750 counts/°C for a potential resolution of 0.0013°, but as with the previous trials, the method falls down because the jitter is so much larger:

Variability on 256 Timer1 readings of 16msec WDT interval : During quiescent period

100 points of raw precision error brings the method back down to a modest ‘real’ resolution of only ±0.066°C at best.  The fact that this variability is so similar to the previous trials, and that oversampling did not improve it, tells me that the the problem is not noise – but rather the WDT oscillator is wandering around like a drunken sailor because of factors other than just temperature.  If that’s the case, there’s probably nothing I can throw at the problem to make it go away.

Several people pointed out that there is another way to measure temperature with some of the Atmel chips, so I decided to fire that up for a head-to-head trial against the WDT method.  Most people never use it because the default spec is ±10°C and it only generates 1 LSB/°C correlation to temperature for a default resolution of only 1°C.  Some previous efforts with this internal sensor produced output so bad it was used as a random seed generator.

But heck, if I’m going through the effort of calibration anyway, I might be able to level the playing field somewhat by oversampling those readings too:

si7051 reference temperature  °C  vs  4096 reading oversample of the internal diode: Fit equation

Even with 4096 samples from the ADC, this method only delivered ~75 counts /°C. But the internal diode is super linear, and the data is less variable than the WDT:

Variability from 4096 ADC readings of the internal reference diode : During quiescent period

Five counts of raw variability means the precision error is only  ±0.033°C  (again, this becomes our real resolution, regardless of the raw count delta) .  So even after bringing out the big guns to prop up the WDT, the internal reference diode blew the two-oscillator method out of the water on the very first try.

volatile uint16_t adc_irq_count;

ISR (ADC_vect)
{
adc_irq_count++;   //flag to track how many samples have been taken
}

 

internalDiodeReading=0;
adc_irq_count = 0;
unsigned long sum = 0;
unsigned int wADC;
ADMUX = (_BV(REFS1) | _BV(REFS0) | _BV(MUX3)); // Set the 1.1v aref and mux for diode
ADCSRA |= _BV(ADSC);  // 1st conversion to engage settings
delay(10);    // wait for ADC reference cap. to stabilize
do
noInterrupts ();
set_sleep_mode( SLEEP_MODE_ADC ); // Sleep Mode just to save power here
sleep_enable();
ADCSRA |= _BV(ADSC);  // Start the ADC
do{
interrupts();
sleep_cpu();      // Sleep (MUST be called immediately after interrupts()
noInterrupts(); // Disable interrupts so while(bit_is_set) check isn’t interrupted
} while (bit_is_set(ADCSRA,ADSC));  // back to sleep if conversion not done 
sleep_disable();  interrupts(); // Enable interrupts again
wADC = ADCW;  // Reading “ADCW” combines both ADCL & ADCH
sum += wADC;   // Add new reading to the total
} while (adc_irq_count<4096);  //sets how many times the ADC is read
ADCSRA &= ~ _BV( ADIE ); // No more ADC interrupts after this
internalDiodeReading=(sum >> 6); //decimation turns sum into an over-sampled reading

 

For now, I think I’ve kind of run out of ideas on how to make the WDT method more precise..? Oh well – It’s not the first time I’ve tried to re-invent the wheel and failed (and it probably won’t be my last….) At least it was a fun experiment, and who knows, perhaps I will find a better use for the technique in some other context.

I’ll spend some more time noodling around with the internal diode sensor and see if I can wrestle better performance out of it since it’s probably less vulnerable to aging drift than the WDT oscillator.  I’m still a bit cautious about oversampling the internal diode readings because the math depends on there being at least 1-2LSB’s of noise to work, and I already know the internal 1.1v ref is quite stable..?  I threw the sleep mode in there just to save power & reduce internal heating, but now that I think about it the oversampling might work better I let the chip heat a little over the sampling interval – substituting that as synthetic replacement for noise if there is not enough. The other benefit of the diode is that it will be resilient to a varying supply voltage, and we are currently  experimenting with more loggers running directly from batteries to see if it’s a viable way to extend the operating lifespan.

Addendum 2019-04-05

So I’ve kept a few units perking along with experiments oversampling the internal diode. And on most of those runs I’ve seen an error that’s quite different from the smoothly wandering WDT method. The internal diode readings have random & temporary jump-offsets of about 0.25°C:

si7051 (red) reference vs internal Diode (orange) over sampled 16384 reads then >>10 for heavy decimation.

These occur all all temperatures from +20 to -15C,  and I have yet to identify any pattern. The calculation usually returns to a very close fitting line after some period with the offset with about 50% of the overall time with a closely fit calibration. This is persistent through all the different oversampling intervals and stronger decimation does not remove the artifact – it’s in the raw data. No idea why…yet.

Easy 1-hour Pro Mini Classroom Datalogger [Build Update: Feb 2019]

Dupont jumper variant of the 2019 Classroom Pro Mini Data Logger from the Cave Pearl project: This version uses solderless dupont jumpers to reduce assembly time to about 1 hour

It’s only been a couple of weeks since the 2019 classroom logger was released, and we’re already getting feedback from instructors saying the extra soldering that we added to that build creates a resource bottleneck that might prevent some of them from using it:

“Our classroom has just two soldering stations, and the only reason there are two is I donated my old one from home. So we simply don’t have the equipment to build the logger you described. And even if we did, some of my students have physical / visual challenges that prevent them from working with a soldering iron safely…”

Or goal with the new design was to give students their first opportunity to practice skills that are vital for field research. However helping people do science on a budget is also important – so that feedback sent us back to the drawing board.  After a little head scratching we came up with a version that combines the Dupont jumpers we used in 2016, with this more flexible flat-box layout. In the following video, I assemble one of these ‘minimum builds’ in approximately one hour.  To put that in perspective, the soldered version takes 2 – 2.5 hours.

Note: After you’ve seen the video to get a sense of where you are headed, its usually much better to use the photos (below) when building your logger. Videos make it look easier than it actually is when you are just starting out. 

This variation of the 3-component logger is optimized for quick assembly so the soldering has been reduced to just pin headers and bridging the I2C bus.  An instructor could easily do that ahead of time with about 15 minutes of prep per unit, leaving the solder-less steps for their students. Wire connections are made simply by twisting stripped ends together and clamping them under screw terminals.

This time reduction involves a few trade-offs, and the bringing the I2C bus to A2&A3 leaves only two analog ports readily accessible ( although A6 & A7 are still available). Removing the regulator & battery voltage divider adds ~30% more operating lifespan, but it also forces you to deal with a changing rail voltage as the Lithium AA batteries age. That daily variation is quite small, but for quantitative comparisons on monthly scales you may need to correct for the change in Vcc (included in the data file automatically) over time.  Another proviso is that you have to add a few more components to the part list from the soldered build:

This pushes the cost for a complete unit close to $20 (before adding sensors) and the required lithium AA batteries are also more expensive.

Pro Mini Prep:

First, test your pro mini board with the blink sketch

Remove two SMD resistors with iron

Remove the voltage regulator with snips. few regs tolerate output voltage with no input.

Add pin headers

Bridge I2c bus connections with a resistor leg.

Connect A4->A2, A5->A3. Then disable digital inputs on A2&A3 in software.

Screw-Terminal Component Stack:

Add 3 layers to extend past the pins. On DeekRobot boards the two ground terminals are connected.

Gently rock the Pro Mini back & forth until the pins are fully inserted.

IMPORTANT! Align RX&TX corner. The GND points on the ST board may be interconnected & should match ProMini’s GND pins.

Remove unused pin headers to make room …

…for the SD adapter

Remove bottom 3 resistors – leave the top one in place!

Separate Dupont Cable wires & click into housing

Color Pattern: Black GND, Purple MISO, Brown SCl, Orange MOSI, Grey CS, and Red Vcc

Attach SD module to the Screw Terminal board

Measure, cut & strip the SD modules 4 SPI bus wires

Attach Grey D10, Orange D11, Purple D12, Brown D13

Add three jumper wires to the SD modules power line, one with male end pin

Add 3 wires to the power line & heat-shrink for strain relief

Short Jumper recruits the orphan capacitor at Vin

Longer red jumper bridges power to other side of the board

Add two extra wires to the black GND bundle, one with male Dupont end pin.

The completed Pro Mini, ST board & SD module stack. Those Red & Blk jumpers should be a bit longer…

Note: You could connect the battery holder lead wires directly into the multi-wire Vcc & GND bundles: skipping the 2 jumpers to the other side of the ST shield.  But those jumpers provide extra Vcc/GND points & the ability to change the battery holder later if you have a battery leak.

RTC module:

Remove two resistors from the RTC board with the tip of your soldering iron

The DS3231 modules often have flux residue – remove this with 90% isopropyl

 

Cable shroud retainer clips facing up & no wire on the RTC’s 32K output.

First tape layer

Tape stage 2

Write the date on the coincell with a black marker

Final Assembly:

Attach stack & RTC to housing.

Trim wht & yellow I2C wires  & add extra jumper 2 each

Attach yellow beside Vcc connection, then white

The extra jumpers on Vcc, GND, & both I2C lines…

…get patched over to the breadboard so you can add sensors

RTC power line joins the short red jumper on Vin

Some the box bottoms have slight bowing. If something doesn’t stick, add another layer of tape.

GND & blue RTC alarm line to D2

Tape 2xAA battery holder down. Trim wires to length. Use 30lb Mounting Tape for the battery holders.

Battery wires joint the black and red jumpers from other side of the terminal board

Connections now complete except for the indicator LED

5050 RGB LED on pins D3=GND, D4-Bl, D5-Gr, D6-Red

Since this build is a variation of the soldered version, so you can test your new unit using the procedures at the end of that post. Also see that tutorial for a description of how we attach external sensors with a cable gland passing through the housing. Of course, for an indoor classroom project you could simply drill small a hole through the lid and stick the sensor/module on top of the housing with double sided tape. Remember that breadboard connections are very easy to bump loose, so once you have your prototype circuit working, re-connect the components directly to the screw terminals before real-world deployments.

Using the logger for experiments:

Logger mounted on a south-facing window and held in place with double sided tape. The top surface of the housing is covered with two layers of white label-maker tape to act as a diffuser.

Many types of sensors can be added to this logger and the RTC has a temperature register which automatically gets saved with the starter script. The transparent enclosure also makes it easy to do light-based experiments. Grounding the LED through pin D3 allows it to be used as both a status indicator, and as a very capable light sensor.   The human eye is maximally sensitive to green light so readings made with that LED channel approximate a persons impression of overall light levels.  Photosynthesis depends on blue and red light, so measurements using those two color channels can be combined for readings that compare well to the photosynthetically active radiation measurements made with “professional grade” sensors. In fact Forest Mimms (the man who discovered the light sensing capability of LEDs) has shown the readings from red LED’s alone are a good proxy for total PAR.  Photoperiod measurements also have important implications for plant productivity.

The code for using LEDS sensors is from the Arduino playground. This polarity reversal technique does not require the op-amps that people typically use to amplify the light sensing response but it does rely on the very tiny parasitic capacitance inside the LED. (~50-300pF) This means that the technique works better when the LED is connected directly to the logger input pins rather that through the protoboard (because breadboards add stray capacitance) . We have integrated this into the starter script which you can download from GITHUBThat method was used to generate this light exposure graph with a typical 5mm RGB LED, with a 4k7Ω limiter on the common ground connected to pin D3:

Red, Green & Blue channel readings from the indicator LED in the logger photo above.  The yellow line is from an LDR sensor the same unit, that was over-sampled to 16-bit resolution. The sensor has a logarithmic response and the left axis on the graph is a time- based measurement where more light hitting the LED sensor results in a lower number. The blue channel is somewhat less sensitive than the red & green, but that actually gives you more useful information.  LED’s work well with natural light, but their sharp frequency sensing bands can give you trouble with the odd spectral distribution of some indoor light sources. You may be able to use a floating point mapping function like fscale to linearize your data – depending on the range of output from your particular LED.

Characterizing light absorption and re-emission is one of the primary climate science techniques. For example, measuring light intensity just after sunset with LEDs inside a heat-shrink tube pointed straight up can provide a measure of suspended particles in the stratosphere. An “ultra bright” LED has more than enough sensitivity to make collumnated readings, and on bright sunny days you usually have to place the LED-sensor beneath a good thickness of white diffusing material to prevent it from being over-saturated.  Older LEDs that emit less light can sometimes be easier to work with because they are less sensitive, so the readings do not go to zero in high-light situations. Other sensor experiments are possible with LED’s in the IR spectrum which can be used to detect total atmospheric water vapor. Chlorophyll fluorescence is another interesting application, and the response of plants to UV is fascinating.

One thing to watch out for is that full sun exposure can cook your logger, so consider adding a bit of reflective film to protect the electronics. Add a couple of desiccant pouches to control condensation.  You might need to add a few holes to the housing as tie-down points:

Pro Mini Logger Project for the Classroom [ EDU Jan: 2019 ]

“Instrumentation is a central facet of student, amateur and professional participation in science. STEM education, recruitment of scientists and experimental research are thus all hampered by lack of access to appropriate scientific hardware. Access restrictions occur because of: 1) lack of capital to purchase or maintain high-cost equipment, and/or 2) the nature of proprietary ‘black box’ instrumentation, which cannot be fully inspected, understood or customised…
…In addition to reducing opportunities for people to engage with science, this lack of access to appropriate hardware restricts scientist’s creativity in experimental designs.”

From: Journal of Open Hardware
Expanding Equitable Access to Experimental Research and STEM Education

by Supporting Open Source Hardware Development


Last year’s intense deployment schedule focused on getting more sensors into the field, which left little time for development of new approaches to the logger itself.  Now that everything is settling into the school term routine, it’s time to update the “classroom edition” of the Cave Pearl Logger with feedback from three years in the trenches: 

The 2016 build achieved it’s goal reducing construction time, but it was low on important skills like soldering. Limited lab time meant that something had to give if we want students to “pay the iron price” for their data, so we’ve added a pre-made enclosure box. Though it’s not as robust as the PVC housings, it provides more room inside the housing. Past student projects have required things like 555’s, ADS1115 modules, display screens, etc. and the proto-board will make it easier to integrate those additional components.

PARTS & MATERIALS

TransparentSinglePixl
Bill of Materials: $18.35
Plano 3440-10 Waterproof Stowaway Box
Usually cheaper at Amazon as “add-on” items.  $4.96 at Walmart and there are a selection of larger size boxes in the stowaway series. 6″ Husky storage bins are an alternative option.
$5.00
4Pin 24AWG IP65 Black Waterproof Cable Connector OD 4mm
Better quality version is available at Adafruit for $2.50 each, wBL-RED-Wht-Yel colors used here for the I2C bus.
$1.00
M12 IP68 Nylon Cable Gland
Adjustable for 3mm-6mm diameter. You need two for the build. Make sure they include O-rings.
$1.00
3/4″ Schedule 40 PVC Cap
Diameter will depend on the size of your sensor breakout board. Get ones with FLAT ends.
$1.00
White 170 Tie-Points Prototype Breadboard
Available in other colors.
$0.60
Pro Mini Style clone 3.3v 8mHz
Get the ones with A6 & A7 broken out at the back edge of the board.
$2.20
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 (CHECK: some of them have the GND terminals interconnected)  Have a few small flat head screw drivers handy.  
$1.05
DS3231 IIC RTC with 4K AT24C32 EEprom (zs-042)
Some ship with CR2032 batteries already installed.  These will pop if you don’t disable the charging circuit!  
$1.25
CR2032 lithium battery  $0.40
4 poles/4 Pin 2.54mm 0.1” PCB Universal Screw Terminal Block Connector
These things look “open” when they are “closed”, and you need a very small screw driver to open them.
$0.40
SPI Mini SD card Module for Arduino AVR
Buy the ones with four ‘separate’ pull-up resistors so that you can remove them.
$0.50
Sandisk or Nokia Micro SD card 256mb-512mb 
 Test used cards from eBay before putting them in service. Older Nokia cards have much lower write currents in the 50-75mA range. This is less than half the current you see on more common larger sized cards.
$2.00
3×1.5V AAA Battery Batteries Holder w Wire Leads
The Pro Mini regulator will handle battery packs holding from 3 to 8 AA or AAA batteries. If you are using alkaline AAA batteries, changing this to a 4xAA battery holder doubles the run time.
$0.40
Common Cathode Bright RGB LED 5mm 
( & 4k7 limit resistor) 
$0.05
3M Dside Mounting Tape10MΩ resistors & 3MΩ resistors, 22awg silicone wireheader pins, etc… $0.50
Donation to Arduino.cc
If you don’t use a ‘real’ Pro Mini 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!
$1.00
Comment:   You might need some extra parts to get started:                (not included in the total above)
2in1 862D+ Soldering Iron & Hot Air station Combination
a combination unit which you can sometimes find as low as $40 on eBay.
Or get the Yihua 936 iron alone for about $25.
$50.00
3.3V 5V FT232 Module
  ***Be sure to set the UART module jumpers to 3.3v before using it!*** and you will also need a USB 2.0 A Male to Mini B cable.
$2.75
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.
$1.00

Connection Diagram:

This logger uses the same three components described in the paper from 2018, but we now connect those core modules via a screw-terminal expansion shield, rather than soldering them directly to the pins:
 
COMPONENT PREPARATION

Watch through the videos as a complete set first so you know where you are going, and then use the images below the videos to remind you of the key steps while you do the assembly; It usually goes much faster working from a photo where you can see all the connections at once. The times listed are estimates for people with soldering experience. If you’ve never built a circuit before, then taking 3-4x that long is completely normal. Don’t worry about it – you will be surprised how much faster you get with a little practice!

Screw Terminal board:  (~40min)  or ( 5min with pre-assembled board)

Don’t forget to measure the values of the resistors before soldering that voltage divider. You will need those values to calculate the battery voltage based on the ADC readings from pin A6.

These screw terminal boards are designed for an Arduino Nano, but if you orient the board to the Tx/Rx pins, the labels on digital side of the shield will be correctly aligned with the Pro Mini:

The most common beginner errors at this stage are crooked headers & not heating the pad/pins long enough for solder to flow properly.  This is often because students are trying to use an iron tip that has “gone dry” so the heat is not transferring properly to the pins. You must protect soldering iron tips with fresh solder every time you put it in the stand to prevent oxidation. Tip Tinner can sometimes restore those burnt tips.   {Click images for larger versions}

Common soldering errors – which are easily fixed by re-heating with more solder & flux.

Divider runs between GND & RAW Vin, with output to A6 pin.

Always apply conformal coating in a well ventilated space, such as a fume hood.

It is better to err on the side of using a little too much heat, because partial connections to the screw terminals will cause you no end of debugging grief later: Cold solder joins can “sort of” work “sometimes”, but cause mysterious voltage drops over those points because they can act like randomly variable resistors in your circuit.  Note: This voltage divider uses meg-Ω size resistors and takes >1 second to charge the capacitor when the unit is first powered on – so you can’t take the first battery reading until that much time has passed.

The Pro Mini Board:   (~40 min)

~5-10% of the cheap Pro Mini clones from eBay are flaky, and it is quite annoying to discover one of those that after you have assembled a logger. So test your board with the blink sketch before you remove pin13 LED resistor.  These limit resistors move around from one manufacturer to the next, so you might have to go 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 in place:

Test w blink sketch!

You can skip the reset pins at this stage, or you can pull those pins out of the plastic rails later with pliers, or simply cut them off.

SCL & SDA jumpers to the 2 extra pins on the digital side.

Connect A6-7 & GND to analog side pins with the leg of a scrap resistor.

(Note: Credit goes to Brian Davis for the idea of using “extra header pins” when patching to the unused to screw terminals.)    

The SD Card Adapter:   (~15 min)

This SD card adapter comes with small surface mount pull-up 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 pull-ups on the Atmel328P processor, so those physical resistors on the breakout board can also be removed. Leave the top-most resistor of the four in place to pull up the unused DAT1 & DAT2 lines.  This keeps those unused pins on the SD card from floating, which can draw excess current.

Only remove the bottom three pullup resistors. keep the top one
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

Attach SD adapter & Pro mini to the Screw Terminal Board:  (~20 min)

Label the Vcc & GND connections with a colored marker, then insert the Pro Mini into the headers on the screw-terminal shield. Be careful not to bend the pins – especially the “extension” pins at the back of the board. It’s easy to connect the board in the wrong orientation at this step. The voltage divider on the bottom of the screw terminal shield aligns with the ANALOG side of the Promini board. 

Label the Vcc & GND connections.

Then affix the SD adapter board to the top of the Pro Mini with a slight overhang, so that the jumper wires align with the screw terminals below. Trim the wires about 1cm past the edge of the board to provide enough stripped wire for the terminal connection.

The limit resistor for the common cathode RGB can range from 4k7 to > 30kΩ

LED on pins D4-D6, & GND. NOTE: Grounding the LED through D3 lets you use the LED as a sensor and support for this is included in the base code.  Also see: multiplexing wD9 GND

Add layers until the tape extends beyond the pins. This might require 3 layers of tape.

The RTC Module:  (~15min)

The simplest modification to these DS3231 RTC boards is to remove the charging circuit resistor and power LED limit resistor from the circuit board (the red squares in the first picture).  A non-rechargeable CR2032 coin cell battery will supply the RTC with power for many years of operation.  Note: The 32K pin is not connected, and does not get a jumper wire. The four screw terminals go on the same side as the battery holder.

rtc1

An alternative to the 4-pin 0.1” screw-terminal block is to solder the I2C pass-through wires directly to the module.

Cutting the RTCs Vcc pin reduces sleep current by ~40% – this step is completely OPTIONAL!

Add an extra layer of foam tape over the smaller 4K eeprom chip, so that the thickness matches the top surface of the DS3231 chip.  The RTC board already has 4.7kΩ pull-ups on the SDA (data) and SCL (clock) lines so you will not need to add them to the bus.  This module also has a 4.7k pull-up on the SQW alarm line.  (Adding the screw terminals to the small cascade port on the RTC module is another creative idea from Brian D.)

The Plano Stowaway housing: (~10 min)

In these photos, I’ve tapped some threads into the housing for the cable gland, but that is entirely optional. Glands much larger than PG7 (12mm) will not fit in the available space in that corner.  Strategically placed holes in the clips provide zip-tie locations to secure your logger.

Holes for Zip-tie securing

ASSEMBLING THE LOGGER PLATFORM:  (~30 min)

Using double sided tape to hold the parts inside the housing (rather than traditional stand-offs) makes this stage of the build remarkably quick.  Adding male Dupont pins allows you to join internal and external wires via the breadboard.  Be sure to use wires that are long enough to reach the mini breadboard. 

Bowing on some boxes can reduce the contact patch on the battery holder, if so try adding another layer of  tape, or upgrade to 30LB.

You can “reactivate” spent desiccant packs with a few zaps in the microwave – if they have indicator beads.

Connecting external sensors to the housing:

It’s worth mentioning the breadboard contacts are notoriously sensitive to vibration, etc. Once your testing stage is complete, and your prototype is working as it should, bring those  sensor wires directly over to the screw terminal connections for a more secure connection.  Also remember to put protective tape over any sensor ports that need to remain open before potting those sensor boards in epoxy. Otherwise you might clog the sensor by accidentally letting a drop fall into it.

Your Logger is ready!    (~2 to 2.5 hours)

Now you can test your new logger to confirm all the connections are working:

1. Test the LED – Edit & upload the default blink sketch, changing the pin numbers each time to match your RGB LED connections.

2. Scan the I2C bus – with the scanner from the Arduino playgound. The eeprom on the RTC module is at address 0x56 or 57 and the DS3231 should show up at address 0x68. If you don’t see those two devices when you run the scan, there is something wrong with your RTC or the way it’s connected.

3. Test the EEprom on the RTC module – We’ve updated BroHogan’s original code from the playground to this tester script. You may have to change the I2C address at the start of the code based on the numbers shown during your I2C bus scan.

An alternative parts arrangement for the classroom logger that makes room for a larger 4xAAA battery holder and DUPONT style connectors. It’s easy to move things around to suit your own projects, and rotating the breadboard gives you room for larger battery holders & longer operating times. Note: For most 1.5V alkaline batteries, (voltage-1)*200 will give you the approximate percentage of total capacity remaining.

4. Set the RTC time, and check that the time was set – There are dozens of good Arduino libraries you could use to control the DS3231, and there is a script over at TronixLabs.com that lets you set the clock without a library. The trick with Tronix’s “manual” method is to change the parameters in setDS3231time(second, minute, hour, dayOfWeek, dayOfMonth, month, year);  to about 1-2 minutes before the actual time, and then wait to upload that code till about 10-15 seconds before your computers clock reaches that time (to compensate for the compiler delay). Open the serial window immediately after the upload finishes, and when you see the time being displayed, upload the examples>blink sketch to remove the clock setting program from memory – otherwise it will keep resetting the RTC every time the Arduino re-starts.  [ Note: hour  in 24-hour time, & year with two digits eg: setDS3231time(30,42,21,4,26,11,14);  ]

5. Check the SD card is working with Cardinfo – Changing chipSelect = 4; to chipSelect = 10;
Note that this logger requires the SD card to be formatted as fat16, so most 4GB or larger High Density cards will not work. Most loggers only generate 5 Kb of data per year anyway.

These breadboard connections are really vulnerable to vibration, so for quick back-yard tests of a rough prototype I sometimes add a tiny spot of hot-melt glue to stabilize the components. Breadboards add about 2-4pF of capacitance for side by side rows so the rule of thumb is you shouldn’t run protoboard circuits much faster than 1 MHz.

6. Check the sleep current – With an SD card inserted in the logger, upload this Pro Mini datalogger starter script with no changes (note: that code requires you to install three libraries as well). Then connect an ammeter between the positive battery connection and the Vin screw terminal (you will need an extra wire to do this) and run the logger from the AAA or AA battery pack.  After the initialization sequence has finished, the reading on the screen (in milliamps) is your loggers sleep current.

AAA cells usually provide about 1000 milliamp-hours of power to their rated 0.8v, but we are only using half of that from alkaline batteries with a regulator input cutoff up at 3.6v.  So dividing 500 by your loggers sleep current gives you a rough estimate of your loggers operating life (in hours).  For a more accurate estimate, you can use one of the Battery Life Calculators on the web with 250ms @5mA for your sample time.  Changing the power supply to 4xAAA cells lets you bring alkalines down to 0.9v/cell, extracting almost the entire 1000 mAh. Lithium AAAs deliver almost their entire capacity above 1.2v/cell so 3xAAA lithiums yield almost 1000 mAh capacity even with the 3.6v input cut-off.

Thermal response of 3x AAA’s (mV left axis) vs Temp (°C right axis) in the standard voltage-regulated classroom build running the starter code from GithubThe 90mv drops caused by SD card “controller housekeeping events” at 20°C  increase to ~220mV as temps near -15°C. These periodic high drain (100-200mA) events would likely trigger the low-voltage shutdown before anything else in the loggers normal duty cycle. (Note: Battery readings taken AFTER SD save, Sample interval: 1 min, sleep current for this test unit: 0.22mA )

Its worth noting that the starter program we’ve provided captures an ambient temperature record from the RTC in 0.25°C increments (example right).  New sensor readings can be added to the log file by inserting:
file.print(YourSensorVariableName);
followed by a separator file.print(“,”);
in the main loop before you close the datafile on the SD card. Then change the text in the dataCollumnLabels[] declaration to add headers to match your new data. This starter script automatically generates required data files on the SD card at startup, and tracks the rail voltage for those adventurous enough to run without a regulator. SD memory is electrically more complex than the Pro Mini processor, with some using a 32 bit arm core.

Once you have your logger running, you might want to review our tutorial on adding sensors to your data logger. And after that you’ll find a few more advanced I2C sensor guides on this site as well (…and the list is constantly growing) We’ve also developed methods to add 5110 LCD & OLED screens to the Cave Pearl Loggers using the fewest system resources. These screens are easily accommodated on the proto-board in this build.

For people wanting to take their skills farther, you can explore the gritty details of how we optimize these loggers for multi-year deployments in the 2018 Sensors article (free to download). The research loggers are a more challenging build, that fits inside an underwater housing made from PVC pipe

Addendum: Power Management (Optional)

On the standard build described above, the Pro-Mini’s MIC5205 power regulator should deliver  sleep currents below 0.25 mA (Pro Mini ~0.05 mA + sleeping SDcard ~0.05-0.09 mA + RTC ~0.09 mA). That should reach several months operation on 3 AAA cells before the batteries reach the regulators 3.4v input cut-off.  I usually have regulated loggers go into shutdown mode at ~3.6v to reduce the chance of leaks, because alkaline batteries often spill their guts when they reach 1v/cell.

There are a couple of relatively simple modifications to the basic logger that more than double the operational time – but they both come with important implications you should understand fully before adding them to your project. The RTC mod is relatively safe, but running a datalogger from a raw battery supply is not for the faint of heart. 

1) Cutting the VCC leg on the DS3231 chip forces the RTC to run from the coin-cell. >Set Bit 6 of CONTROL_REG 0x0E to enable wake alarms from the backup battery!

2) Remove the regulator: few LDO’s have reverse current protection & most regs leach 30-90 uA if the voltage on output line is  higher than on their input

2) Then connect the positive wire from a 2X LITHIUM AA battery pack directly to the Vcc rail on the Pro Mini

The DS3231 RTC was designed to handle power supply failures by switching over to the backup coin-cell battery, and it enters a special 3uA “timekeeping mode” to use less power in that situation. However the chip is still fully capable of generating alarms (provided you set the Battery-Backed Square-Wave Enable bit of CONTROL_REG 0x0E to 1) , and of responding to the I2C bus at 400 mHz. So if you cut the main power leg on the RTC you reduce the loggers sleep current by almost 0.1mA (~40%). The trade-off is that your loggers operation is now entirely dependent on the 200mAh CR2032 coincell to keep the clock delivering wake-up alarms when you are also asking it to deal with pulsed loads in the 80 uA range every time you communicate over the I2C bus. The RTC also can not generate temp-corrected frequency outputs on the 32kHz pin when operating from the coin cell.

Here drops of hot glue secure the RTC coin cell battery against accidental resets.

Another quid pro quo here is that coin-cell holders occasionally lose contact very briefly under vibration, so if you cut the Vcc input leg – add  a  0.1 μF capacitor (ceramic 104) across the coin-cell holder pins. That will give you about 80 ms coverage, which should be longer than the holder will lose contact. Otherwise a hard bump can reset the RTC back to its Jan 01 2000 default.  The wake-up alarms usually continue after that kind of reset, however fixing an entire years worth of time series data based on your field notes is a pain in the backside.  Real world installations often involve this kind of rough handling, so I prefer a more advanced RTC modification that keeps two power lines feeding the RTC.  But for more “gentle” deployments, simply cutting the chip’s vcc leg & adding a couple of drops of hot glue OK. Write the installation date on the coin cell with a black marker. I do this to all of my batteries now…

For loads in the 0.1mA range, the MIC5205 is less than 60% efficient. So the other modification is to remove the voltage regulator and run the entire system from 2x LITHIUM AA batteries.  Lithium batteries (like the Energizer L91) have two characteristics that make them well suited to this approach: 1) they have an extremely flat discharge curve, yielding >75% of their power before the voltage falls below 1.5v/cell and 2) a pair of brand new lithium cells gives a combined voltage right at 3.6 volts. If you look at any of the SD card manufacturers’ specifications, they all specify a voltage range of 2.7v to 3.6v.  At 8 MHz the ATmega328P processor on the ProMini supports voltage levels between 2.7 V and 5.5 V. Both of these ranges overlap beautifully with the lithium cell’s discharge curve.

Here I’ve done both modifications to the basic build, and brought the sleep current (with no sensors) from an unmodified starting point of 228μA, down to 80μA. Most of that remaining power is due to the sleeping SD card, since the Pro Mini only draws about 5μA in deep sleep mode. Using only 1/2 of the 3000 mAh capacity of a typical AA Lithium pack would keep a logger that sleeps at 0.1mA alive for more than a year, and my rough estimate is that the RTC mod will get you at least twice that much time from a new Cr2032 cell – with a typical 5-15 minute sampling interval.      [NOTE: I’m using an EEVblog uCurrent here to display the μA sleep current on a DVM as milivolts. It’s an exceedingly useful tool that lets you read sleep currents into the nano-amp range without adding the burden voltage you’d see from putting your meter directly into the circuit ]

Even with one less cell powering the system, getting rid of the MIC5205 yields a 25-30% reduction in sleep current for a typical build. While this is very close to the reduction you get from changing to a more efficient regulator like the MCP1702, the other cool thing about this mod is that the processor on the Pro Mini can take a reading of it’s rail voltage by comparing it to the 1.1v internal band-gap reference. So all you need is a little bit of code, and you can keep track of your rail=battery level without a voltage divider.  Unlike alkaline batteries, lithium cells have very little voltage droop with brief loads below 100mA, but it’s still a good idea to protect your SD card from potentially data corrupting brief low voltage spikes with a capacitor.  The formula for figuring out how much the voltage across a capacitor will drop is  ΔV = current(A)*time(sec)/C(farads) – but you need to decide how much your supply can drop to know how big to make the capacitor.

Adding a 47uF (or larger) on the microSD rails should keep the shortest transients under control, and once the system reg. has been removed running a jumper between Vcc & Raw recruits the orphan (10uF tantalum) capacitor from the input side of the (now removed) regulator.  Always check the supply voltage before the data saving begins and perform a low voltage shutdown when the lithium pack reaches 2875mv. (ie: at least 150mv above the SD’s 2.7v safe writing minimum). Generate a new data file every couple of weeks so only the last one is vulnerable during the write process. 

2x LITHIUM AA’s (mV left axis) vs Temp (°C right axis) supplying power to an unregulated build running the same code from GithubThe key observation is that the initial 50mv supply voltage variation recorded at 22°C increases  ~100mV at temps near -15°C. For Lithium chemistry batteries with flat discharge curves, the voltage-drop under load is often a better indicator of the remaining battery capacity than the cell voltage. (Readings taken AFTER SD CARD SAVE, Sampling every 1 min, overall sleep current 0.17mA   Note: the 200mv baseline drop on the cells is slightly exaggerated here because low temps increase the bandgap slightly) 

Of course everything has a price, and removing the regulator means you’ve not only lost your reverse voltage protection – you also need to think about how all the components in your system will respond to a decreasing supply voltage over time. Ratiometric analog circuits using the rail as Aref handle this well, but even if you want to use the 1.1v bandgap ref – you already know what the rail voltage is, so you can easily throw a compensation factor into the calculation.  The real problem is that when 2.7 – 3.6v is listed on the spec sheet for a digital sensor – that’s no guarantee the readings will be consistent when the supply voltage changes.  You could have very different error percentages at the high & low end and ambient temperature fluctuations could push your batteries through significant voltage changes every single day (lithium cells are more resistant to this than alkaline).  Testing is the only way to find out if your sensors can handle the variation, and if you don’t have time for that it might be worth keeping that voltage regulator on board despite the reduced lifespan. For field units, I usually replace the MIC5205 with a more efficient MCP1700 regulator because I want all the data consistency I can get.  Another thing to keep in mind when you are hoping for a really low-power build is leakage currents through leftover flux – it’s always worth the time to get your parts squeaky-clean during construction.

Addendum 2019-02-21:

A teacher friend asked us for a version that was less dependent on soldering because they didn’t have enough money for a complete classroom set of equipment. So we’ve worked out a “minimum build” of the classroom logger that uses crimped Dupont jumpers to reduce assembly time to about 1 hour (before sensors):

We’ve also added support to the starter code for using the indicator LED as a light sensor, but this requires that you ground the indicator LED’s common cathode through pin D3, rather than directly to GND as shown in the tutorial above. Then you can gather three-channel light level data like this:

Readings from an RGB LED deployed outside in our back yard on an overcast day with two light snow fall events. The yellow line is from an LDR sensor in the same logger that was over-sampled to 16-bit. 

If you go down that rabbit-hole, you might want to calibrate your diy sensor output against the NOAA observations for your area, which includes hourly data for thousands of stations. The data includes temperature, dew point (from which you can compute relative humidity), wind speed and direction, and you can calculate solar position.

 

Tutorial: Adding the SSD1306 OLED Screen to an Arduino Logger (without a library)

An SSD1306 OLED screen mounted on a climate station build.

This is the third installment in a series on adding output screens to Cave Pearl Data loggers. It builds on the Nokia 5110 LCD tutorial and the post describing how I store fonts in the Arduino’s internal EEprom to save program memory – so you might want to have a look at those two posts before diving in here.

While the Nokia 5110’s are a cheap, low-power option with great visibility in full sun, they reacted badly to pressure directly on the LCD surface.  Since this project deploys loggers in an underwater environment I went looking for something more robust and the SSD1306 OLED’s caught my attention. (@ ~$3.50 on eBay) These little screens are showing up in hacked toyscompasses, GPSanalog meters, ECG’s, and theres even a tiny oscilloscope project at Hackaday. But those applications typically use the u8g2 library which is fantastic for graphic output, but also quite memory intensive; what I need is a bare bones solution that uses the fewest system resources on a unit that’s already near memory limits. Though these 0.96 inch displays are quite small, 128×64 pixels lets you render several lines of readable text.

While the I2C variants of this screen are easy to use, the SPI version lets me re-purpose unused analog lines to drive this display without interfering with the sensor or SD card bus because it can be driven via shift-out on any pins that are available. Powering the screen from A0 brings the screen down to zero current while the logger is sleeping, and also lets me get rid of the Reset & Cable Select lines. The only thing to watch out for is that you bring all the control lines low when you cut power so that reverse bias doesn’t end up “back powering” the screen controller.

Connecting the OLED:                                {Click any image to see larger versions.}

The data sheet for the SSD1306 controller specifies that the reset input needs to be LOW, during initialization, after which the pin should be HIGH for normal operation. To achieve this low -> high  transition, I tie the Reset line to the middle of an RC bridge made from a 104 (0.1uF) capacitor and a 10k resistor. When you add the 25k inside the 328p used to pull A0 high, you get a 63.2% time constant of about 3.5ms.

Begin by tinning the pins, and bending them 90 degrees for alignment with the RC bridge which also connects to the CS pin.  Once that delayed rise circuit is soldered in place, also jumper the incoming GND line over to Cable Select. You would not do this if the display was on the normal hardware SPI bus, because it would exclude other devices on the SPI bus. But since we are using a separate set of wires for the display we can get away with this trick.

The connections for the SSD1306 OLED display are the same as the ones for the Nokia 5110: (D0=clock, DC=command, D1=data in)  Note that I’m not using the 3.3v rail from the pro-mini,  and YOU MUST BE USING A 3.3V ARDUINO FOR THIS WIRING TO WORK UNLESS YOUR OLED BOARD IS 5V TOLLERANT.  Some of the breakouts  from eBay are OK at 5v, and some are not – check THIS before you buy if you are using a 5V Arduino.  (Note that  most breakouts sold by Adafruit include regs & level shifters to handle both voltages.)

When all the incoming lines have been added to the board, thread them through the housing and verify that the screen is still working. The analog A0-A5 lines on an Arduino can be re-purposed as digital I/O so I usually break them out with a 6-pin Deans Micro Plug (incl. GND & Vcc)  In this case the screens Vcc line is being re-routed to pin-powering from A0 so the pro-mini’s rail voltage is not used.  Cut away or heat-shrink over the unused Vcc pass-through on the screen side of the connector (which is still visible & exposed in the photo on the right) as you don’t want it accidentally contacting something later on inside the housing.

After the operation test, cork the pass-through hole with a bead of plumbers epoxy putty  about the size of your fingertip. Wrap that putty so that it is on both sides of the wires before smoothing it down on the housing.  (The sensor cap shown below has four housing penetrations sealed by this method)  On the top of the sensor cap, wrap a grape-sized lump of well worked putty around the wires on the back of the screen. Then carefully press the screen down into the PVC well so that the putty compresses into a support pillar that holds the screen as near level as possible. The putty hardens in about 10 minutes and then you can pour the first layer of potting epoxy. Here I’ve used Loctite HYSOL E-60NC, with a 50ml applicator & MA6.3-21s mixing nozzles. The air space for the reflective back light forced me to use clear epoxy with the 5110 LCD screens, but since these OLED’s emit their own light, you can bring that black epoxy right to the edge of the display – covering the board & any ugly soldering… 🙂  Check the epoxy over the next couple of hours and pop any bubbles that rise to the surface with a pin. (unless you have a vacuum chamber)

Always TEST before potting!

Let that initial layer of epoxy cure for 24 hours, then drill a 7/32″ hole at a safe distance away from the screens edge. Mount an RGB indicator led inside the housing and seal it in place with plumbers putty. At this point you can simply add a second layer of clear epoxy to protect the screen, but in our experiments with the Nokia LCDs, seawater caused a serious fogging problem.  So I recommend a top surface of more chemically resistant material for that kind of application. Here I’ve used a 1/4″ thick plexiglass disk (~$0.35 each on eBay)
First apply a thin layer of clear epoxy over the screens surface so that it self-levels to thickness of about 1-2mm. Then hold the edge of the acrylic disk and carefully tilt it over the epoxy film with an even contact edge that moves across the disk slowly enough to let the air escape.  Don’t make that first layer of clear epoxy too thick or it will over-top the edge when the plexi settles into place and this will form blobs on the surface. Once the acrylic is in place, you can slowly add drops of epoxy on the side to bring the level up to match the edge.  Give the clear epoxy another 24 hours to cure.

The display draws more current as you increase the number of pixels turned on.  As a point of reference: the screen output shown above (generated from the code on Github ) draws 3.4 mA.  And I haven’t seen any nasty current surges that might exceed the pin limit when the display is first powered up.

The shore hardness of the plexiglass is not much higher than cured epoxy, but the optical clarity is considerably better. The photos in this post don’t capture it well, but this mounting method really shows off the razor-sharp output you get from an OLED, and it’s readable from just about any angle.  For surface loggers a 1/8″ disk should provide sufficient protection, but for the underwater units I will use 1/4″ thickness to resist the compression at depth. I won’t be able to test those underwater versions in the real world for a few months, but if the acrylic fails I will change to  a stiffer glass overlay.

A few might wonder why I added an indicator LED when the logger now has full display capability. There are plenty of situations where brief LED pips communicate the progression of duty cycle events that are too fast and too numerous for screen output. I also use the LED as a noise generator when I want to squeeze higher resolution from the ADC through oversampling.  These days I add an NTC thermistor to every build to capture ambient temperature.

The Code Example on Github uses a cascading method to break text strings up for single character rendering with : oledWriteString–>oledWriteCharacter–>oledWriteData  and I’ve co-opted that with a new split-character method to print larger numbers to the screen in a two-pass method.  The shift-out code for those functions is basically the same as that used for the Nokia 5110. I went over all that in some detail in the earlier posts so I wont re-fry those beans here. The key thing to keep in mind is that this new screen driving script uses EEPROM.read which assumes you’ve already loaded the font definition(s) into your Arduinos internal eeprom with this helper utility.  That utility loads a ‘reduced’ font set to leave 500 bytes of space for file header data in the EEprom as well. (so the 5×7 font is caps only, and large characters are #’s only).  If you don’t need that level of memory optimization in your build, you can switch back to simply storing the screen fonts in program memory;  just transfer the PROGMEM based string->character->data functions (and the font arrays) over from in the NOKIA 5110 code example.

The housekeeping functions that take care of  initializing the screen, clearing the memory, and setting the XY position are more complex for the OLED than those for the PCD8544 Nokia because the SSD1306 controller has more operating parameters.  But the overall approach transferred easily between the two controllers and I suspect this method could be re-used with most of the larger SPI OLED screens on the market (like the SH1106) provided they stay below the pin current limit on A0.

Time to go shopping!  🙂

Addendum 20181019:

A helpful commenter has informed me that it’s possible to try a similar approach with I2C screens because you can bit-bang I2C devices over any two spare wires.  Now that we’ve pulled off this isolation trick with SPI, I’ll look into bang’n as a way to shut down high current I2C sensors when they are not in use.  I’d want those off the regular I2C bus because it necessarily has a few “always on” devices that might not respond well to a lump of dead wood hanging off the same wires.

Addendum 20190423:

A year long experiment in OLED burn-in. Significant dimming of the most used pixels after 4 weeks, but the screens were still operational after one year.

Starting Points & Ideas for your Arduino STEM Curriculum

Arduino Starter Kits can be assembled from $10 to $100 per seat depending on the complexity of your course. We generally order parts about a six weeks beforehand and then spend a day pulling it all together into “ready to go” lab kits at the start of term.

Over the last decade the open source Arduino platform has been embraced by STEM educators, and there are a growing number of pay-per-use resources available with pre-made lesson plans, etc. (eg: becauselearning.com While most welcome viable business models in the sector, it struggles against the problem of ever shrinking education budgets. Where the rubber meets the road you are faced with the stark reality that many teachers now have to pay for teaching materials with their own money:

“Classroom teachers spent an average of $468 out of pocket on classroom supplies and equipment in the last year — amounting to nearly 1 percent of an average teacher’s salary in the United States. Nearly eight in 10 teachers — 77 percent — spent “at least” $200, with some as high as $5,000, according to the latest results of an annual survey.”

Teachers also have to teach themselves first, which is always a hard sell to the admins.  With those things in mind, I’ve started this list of links to STEM learning resources, curriculum, and activities for Arduino.  Unlike more formalized lists of this type, the focus here will be on creative IDEAS and resources that teachers can access for FREE ( eg: WeTeachNYC’s Gr9 lesson plans , textbooks online, etc.) For the most part

This page will grow over time as I find more material.


Arduino project IDEAS:

Any teacher worth their salt already knows how to make lesson plans, so the tough part is finding a theme that really motivates your students.  If you are looking for science project ideas, it wouldn’t hurt to browse through a few commercial data logger websites sites to see how people use loggers in the real world. Then search through the Arduino sensors forum and see if someone has already posted helpful information about the application your students find interesting.  Though the Cave Pearl Project is focused on environmental monitoring, you shouldn’t overlook the other cool things that people do with Arduinos for more information on integrating sensors (eg: building instruments like the TC1 slinky seismometer) Browsing through the Arduino project hub gives you some sense of the range.  A good number of artists create interactive pieces by adding motion, sensing, LEDs & sound. Wear-able projects are also pretty groovy and you are never too old for some fun & gamesOthers create simple robots with their Arduinos, and there are plenty of body/wheel/motor kits to get you rolling. Drones get all the media attention, but I think underwater ROV’s are also interesting.

There are lots of great maker resources to search through if can appreciate their sense of humor (though you might want to avoid clock projects 🙂  Intructables is heaving with Arduino projects which you can find simply by searching for “Arduino” + “subject”.  If you find an Arduino book that sounds interesting, there is a good chance that there are sample projects on the web from the book that you can review.  GPS tracking opens up interesting possibilities and the folks over at the RIFFLE project have been pulling that location data out of digital camera photos, with their data logger hanging from a kite.  So really, the sky is the limit . . . or maybe not even that . . . commander Sparkles


Most ” Discovering Arduino ” resources follow a pattern something like this:

Introduction of the Arduino board. (hardware)
Introduction of the Arduino programming environment and the structure of a script. (software)
Introduction of the breadboard. (hardware)
Blinking the internal LED at pin 13. (software)
Connecting a LED to Arduino using a breadboard. (hardware)
Using a digital output pin to blink the LED. Using multiple digital output pins & leds (software)
Pulse-width modulation for fading LEDs. (software)
Connecting a button to the Arduino, with de-bouncing cap/resistor combination. (hardware)
Programming  to support the button input , if/then/else conditional behavior. (software)
Introducing the serial monitor for text output of events (software)
Connecting analog sensors to the Arduino with voltage dividers. (hardware)
Capturing sensor readings with the ADC and storing them in a variable. (software)
Using the serial plotter for live display of sensor output (software)
Advanced programming concepts (e.g., for/while loops, counters, switch/case). (software)
Connecting digital bus sensors with pull-up resistors (usually I2C or OneWire ) (hardware)
Including code libraries to so you can read data from those digital sensors (software)

As you can see, learning the Arduino platform is like climbing a ladder, where each step you take toward understanding the hardware is matched by one learning how to write code.

Where To begin:
The instructables beginners guide is a good place to start, as is Udemy’s free Learn the Basics Arduino Tutorial. Actually instructables has been busy building a range of free beginners classes on subjects from the internet of things to 3D printingetc.  There are plenty of other “Getting started” videos available with  another free video course offered at the Programming Electronics Academy (also see their other Youtube videos). Many of these courses require some kind of registration, and given the nature of their business you can expect a fair amount of self promotion messages to be peppered throughout. And finally, don’t overlook the official Arduino example tutorials that come built into the IDE. There are some great learning examples in there like the Tone Pitch follower with tutorials by Massimo Banzi himself.

Special Mention:
Be sure to check out Jeremy Blum’s Arduino Tutorials which are essentially a complete course on the Arduino;  all the more impressive because he did the entire thing as a one-man-band while he was still a student.  In my opinion,  the best quality videos available for Arduino are the ones created by Jeff Feddersen & Tom Igoe for the ITP program at NYU, though there’s a lot to wade through, and some of those tutorials might be pitched at too high a level for beginners. Paul McWhorter also has an extensive tutorial series on youtube.

And don’t forget to search for the many new videos that people have posted. Youtube has grown into a universal self-teaching tool and we’ve entered the game with clips from our fieldwork, and step-by step data logger build tutorials.

Arduino in a Nutshell is a free e-book resource worth looking into, as is the Programming Guide from instesre.org. And though I’m not sure if they are still a going concern, the old Earthshine Starter kit manual PDF can still be found floating around the internet. If e-books like that are your thing, and you are willing to shell out a few bucks, there are sometimes good Humblebundle deals, though those are often in weird combinations of topics, and the individual books also available the Make website.

Sparkfun is also a great place to look for teacher resources.

It’s a lot to wade through, but the Adafruit tutorial list  is another one of the best resources out there. Just be aware that they have developed their own library “system”, so sometimes their tutorials are tailored to that.

Tronixstuff has a large number of  specific hardware tutorials when you are ready to go further with your Arduino projects, and there are a host of cool Arduino projects to dig through at instructables site. I really believe that you can improve engagement and understanding by providing hands-on experience with real data, but there are plenty of other practical things you can do with the same basic setup.

If you google around, you can find curriculum documents, individual lesson plans, and other resources all over the place, like for example this conductivity lab over at teachengineering.org or this beginners set from Arduino 101.  The challenge is that most of the sites were developed for a different curriculum than yours, so first figure out what you want to tackle, then go sifting through the tutorial sites for material that matches your learning outcomes. Otherwise you will just get buried in the shear volume of it all.

If you want to abstract away the entire IDE interface for younger students, there are a few visual programming tools out there for the Arduino like Visuino, or MIT’s Scratch, for which there are plenty of tutorials on youtube.

Going further:
There is also a long set of more detailed videos at Makecourse.com. Though it’s a bit dry, All About Circuits has a complete textbook online [see: Vol. I – Direct Current (DC) ] And if you really want to dig deep, several universities like Stanford, MIT & Berkeley have made full electronics courses available, though that goes well beyond the Arduino landscape. There is a good walk through the sub-components that make up an UNO at Rheingold Heavy’s build an Arduino From Scratch series.  And I’d be remiss if I didn’t mention the Mini-notebook series that that Forest Mims wrote back in the 70’s.


Using Social Media to find resources:
Like Pinterest, Reddit has grown into one of the most useful social media sites for leveraging other peoples knowledge to help you find useful resources.  I’ve compiled a short list of places that might be good starting points.  You can also find material with the right hashtags on twitter such as #DIYscience.  But social media like Twitter/Facebook/etc can easily waste as much of your time as it saves, so finding good material is directly related to how particular you are about following people who actually contribute resources to the community (as opposed to those who are merely talking about it)

A few Teacher & Maker sub-reddits:

Teaching & Education
/r/ScienceTeachers/ – Chemistry, Biology, Physics, Astronomy, and General Science for K-12
r/matheducation
r/education -news articles about America’s education system, from Pre-K through PhD
r/teaching

Electronics:
/r/arduino – Arduino and compatibles. Lots of projects posted, discussion leans more towards programming with dabbling in circuit design

/r/electronics – About electronic circuit design and occasionally embedded systems
/r/raspberry_pi – Discussion about the Raspberry Pi & /r/raspberrypi
/r/embedded – Similar to /r/arduino and /r/raspberrypi but not platform specific
/r/3Dprinting – 3D printers

Others that instructors may find interesting:
/r/diy – The granddaddy of them all. Largely focused on home improvement but has content from just about everything.
/r/crafts – Sewing, knitting, scrapbooking, kids crafts, etc. and /r/craftit – Smaller version of /r/crafts
/r/somethingimade – Largely arts & crafts related, occasional woodworking or self made website posted
/r/maker – For people who make things. Doesn’t seem to be very active.
/r/woodworking – 50/50 between using power tools and hand tools. Wide variety of projects posted from simple to “they must be professionals”
/r/metalworking – Stuff you can do with metal
/r/welding – Welders, machinists and all other enthusiasts of joining two things together
/r/outstruments – Musical instrument making
/r/lego – Lego is made of ABS plastic and you can use a tiny dab of ABS plumbing solvent (nasty stuff!) to weld it together into a custom bullet-proof housing for your Arduino project. Great for internal scaffolding too.  also see /r/AFOL – Custom designed LEGO creations.


YouTubers on Science & Technology:

Like the other flavors of social media, YouTube can give a boost to your STEM lessons, provided you don’t go down that rabbit hole until after you already have clear lesson outcomes in mind.  It’s hard to pick a favorite, but Ben Krasnow @ Applied Science might take the title because he does incredible things without the over-the-top wow-yuck factor that the media seems to feel is the only way to make science interesting.  When Ben wants an electron microscope – he builds one.  Super Conductor? ditto. Plasma tube? easy-peasy.

Life, the universe, & everything:
Veritasium – An element of truth – videos about science, education, and anything else
Kurzgesagt – In a Nutshell – finding a new way to end the human race with every video
SciShow – delves into popular scientific subjects with lots of flash for younger audiences
Physics Girl – Physics videos for every atom and eve
Vsauce – Michael Stevens combines discussions of science and philosophy
BrainCraft – Vanessa Hill explains why we humans act the way that we do
ASAPscience  -explains topics in science with their trademark kinetic typography and drawings
minutephysics -physicist Henry Reich explains physics concepts simply  in a few minutes
It’s Okay To Be Smart
Steve Mould

People who put things together:
Kevin Darrah
Julian Ilett – the Bob Ross of makers
Electronic Basics by GreatScott
Adam Savage’s Tested
The Thought Emporium

People who take things apart:
bigclivedotcom – random take apart videos from expensive toys to cheap junk from China

People who explain how things work:
Engineerguy
Real Engineering
Practical Engineering
3Blue1Brown:  Brilliant animated introductions to complex math subjects

The Do-ers:
Smarter Every Day-engineer Destin Sandlin films himself doing his own experiments
Mark Rober
Cody’sLab
The Slow Mo Guys
Mike Boyd

The Story Tellers:
Curious Droid (Paul Shillito)
LindyBeige
Knowing Better
The Good Stuff
TedX Talks
Today I Found Out

The incomparable Colinfurze.  Close tie for the #1 spot because Colin is kind of like Ben Krasnow’s unhinged alter-ego from some parallel dimension where humans have very short lifespans.  Like ArduinovsEvil , and the site-which-must-not-be-named, this highly inappropriate material is best viewed at 1am when you’ve already killed off any high-function brain cells with a 10-hour exam marking marathon. (ie: Don’t show Colin’s videos to impressionable young minds unless you want to be fired, and never show content from AvE, and IFLS without serious vetting)


Other inspiring links:

What’s the Maker Movement and Why Should I Care?

The Maker Movement in K-12 Education: A Guide to Emerging Research

Progressive Education and The Maker Movement

TED Talk – Massimo Banzi (the primary founder of Arduino) – How Arduino is Open-Sourcing Imagination

Hans Rosling as an advocate for a “fact-based worldview” with his amazing bubble charts.

Using Arduino’s Internal EEprom to Store Data & Screen Fonts

This tutorial is the second in a series on adding displays to expand the capability of the Arduino data loggers described in our SENSORS paper earlier this year. As more of those DIY units go into the field, it’s become important that serial #s & calibration constants are embedded inside the files produced by each logger.  One can always hard-code that information, but with multiple sensors and screens for live output, space is getting tight:

This prompted me to look at the ATmega328p’s internal EEprom as a potential solution. The EEprom can only store one byte per location, so saving numbers larger than 255 requires you to slice them up and store them in consecutive memory locations. That takes two locations for a the “high byte” and a “low byte” of an int, and four memory locations for longs & floats. That’s a little clunky but it mirrors the way you read & write high-bit registers on I2C sensors, so there are lots of code examples out there to follow.  Piece by piece approaches also require memory pointers for retrieval and re-assembly of your variables.

A more elegant method is to roll them into a single ‘struct’  and store that with a generic function, but even with read_block & write_block I’d still be tweaking the code for each logger, since they often have dramatically different sensor combinations.  I wanted a more generic “cut & paste” method that would handle file headers and variable boiler plate info ( contact emails, etc.  ) without me having to update a bunch of pointers.  The real clincher was realizing that the equation constants generated by my thermistor calibration procedure had too many significant figures to store in an Arduino float variable anyway.

A char array provided the simplest way to achieve the flexibility I was after, and I wrapped that up into a little “EEprom loader” utility:             (Note: Github link at the end of this post)

#include <EEPROM.h>
#define PADLENGTH 1024
char eepromString [PADLENGTH+1];    //+1 to leave room for null terminator

void setup(){
strcpy(eepromString," ");
strcat(eepromString, "\r\n");      // a carriage return in Excel
strcat(eepromString,"#198, The Cave Pearl Project");
strcat(eepromString, "\r\n");
strcat(eepromString,"Etime=(raw*iSec)/(86400)+\"1/1/1970\"");
// NOTE: \ is an escape which lets you put "special" characters into the string 
strcat(eepromString, "\r\n");
strcat(eepromString,"Tres=(SeriesOhms)/((((65536*3)-1)/Raw16bitRead)-1)");
strcat(eepromString, "\r\n");
strcat(eepromString,"1M/100k@A7(16bitP32@1.1v),A=,-0.0003613129530,B=,0.0003479768279,C =,-0.0000001938681482");
strcat(eepromString, "\r\n");                 
// following this pattern, simply add more lines as needed (up to max 1024 characters)

// This fills the remaining unused portion of eepromString with blank spaces:
int len = strlen(eepromString);  // strlen does not count the null terminator
memset(&eepromString[len],' ',PADLENGTH-len);

// Now write the entire array into the EEprom, one byte at a time:
for (int i = 0; i < 1024; i++){ 
EEPROM.update(i, eepromString[i]); 
}
}

void loop() {
// nuthin here...
}
 

This is just a bare-bones example I whipped up, and it’s worth noting that strcat will overflow if you try to add more characters than eepromString can hold.  You can check your count at sites like lettercount.com  or try using snprintf() as an alternative without that problem. Since this is a ‘run-once’ utility, I haven’t bothered to optimize it further.  Bracketing calibration numbers with a comma on each side makes them directly usable when the CSV data file is loaded into Excel later because they end up inside their own cell.  It’s a good idea not to avoid putting EEPROM.write codes inside the main loop, or could accidentally burn through the limited write cycles of your internal EEprom. EEPROM.update is the safer than EEPROM.write, because it first checks the content of each memory location, and only updates it if the new information to be stored is different.

With the data stored in the EEprom, it only takes a few lines to transfer that information to the SD card when a new file gets created:

char charbuffer[0]=" ";  //a one character buffer
file.open(FileName, O_WRITE | O_APPEND);
for (int j = 0; j < 1024; j++) {
 charbuffer[0] = EEPROM.read(j);
 file.write(charbuffer[0]);
 }
file.close(); 

The spaces used to pad out the array so that it fills all 1024 bytes of the EEprom do create an extra blank line in the file, but that’s a pretty harmless trade-off for this level of code simplicity.

What else can we store in the EEprom?

In the post covering how to drive a Nokia5110 LCD using shiftout, I went into some detail on the way the fonts for that screen were created and displayed. Three simple cascading functions (originally from Julian Ilett) let you send any string of ASCII characters to the screen.

void LcdWriteString(char *characters)
{
while(*characters) LcdWriteCharacter(*characters++);
} 
void LcdWriteCharacter(char character)
{
for(int i=0; i<5; i++){
LcdWriteData(pgm_read_byte(&ASCII[character - 0x20][i])); 
}
LcdWriteData(0x00);            //one row of spacer pixels between characters
} 
void LcdWriteData(byte dat)
{
digitalWrite(DCmodeSelect, HIGH);    // High for data 
digitalWrite(ChipEnable, LOW);  
shiftOut(DataIN, SerialCLK, MSBFIRST, dat);  // transmit serial data 
digitalWrite(ChipEnable, HIGH);
} 

I modified that code that slightly with reduced font-set arrays stored in PROGMEM and introduced a method for displaying larger numbers on screen by printing the upper and lower parts of each number in two separate passes.  PROGMEM requires pgm_read_byte to get the data out of the 2-D arrays for printing.

Now, with a little bit of juggling, a “loader” script can store that font data in the 328P’s internal EEprom by converting the two dimensional font array into a linear series of memory locations:

const byte ASCII[][5] PROGMEM =  
{
{0x00, 0x00, 0x00, 0x00, 0x00} // 20 
,{0x00, 0x00, 0x5f, 0x00, 0x00} // 21 !
,{0x00, 0x07, 0x00, 0x07, 0x00} // 22 "
,{0x14, 0x7f, 0x14, 0x7f, 0x14} // 23 #
,{0x24, 0x2a, 0x7f, 0x2a, 0x12} // 24 $
,{0x23, 0x13, 0x08, 0x64, 0x62} // 25 %
,{0x36, 0x49, 0x55, 0x22, 0x50} // 26 &
,{0x00, 0x05, 0x03, 0x00, 0x00} // 27 '
,{0x00, 0x1c, 0x22, 0x41, 0x00} // 28 (
,{0x00, 0x41, 0x22, 0x1c, 0x00} // 29 )
,{0x14, 0x08, 0x3e, 0x08, 0x14} // 2a *
,{0x08, 0x08, 0x3e, 0x08, 0x08} // 2b +
,{0x00, 0x50, 0x30, 0x00, 0x00} // 2c ,
,{0x08, 0x08, 0x08, 0x08, 0x08} // 0x2d (dec 45) (-) in row 13 of source array
,{0x00, 0x60, 0x60, 0x00, 0x00} // 2e .
//...etc, a complete font is in the array, but I only store a 46 character "caps only"
// subset in the eeprom ranging from (-) at array row 13 to capital (Z) =array row 59
// this takes 235 bytes of EEprom memory storage (addresses 0-234)
} 

// move that sub-set of the font (13 to 59) from PROGMEM into the 328's internal EEprom
currentIntEEpromAddress=0;   // each letter is constructed from 5 byte-collumns of data
for(int i=13; i<59; i++){    // so each i value forces a 5-byte jump in EEprom address
for(int j=0; j<5; j++){      // while j value counts through each individual column
charbuffer=pgm_read_byte(&ASCII[i][j]);  // i&j used separately in the array
currentIntEEpromAddress=(((i-13)*5)+j);  // i&j combined for the EEprom address
EEPROM.update(currentIntEEpromAddress,charbuffer); //update to save unnecessary writes
}

Once the font has been transferred into the internal EEprom by the loader program, I only need to make two small changes to the original display functions so they pull that font from the EEprom. Note the calculation trick (character-0x2d) which uses the ASCII code for each character to calculate where the first of the five bytes is located for that character:

void LcdWriteCharacter(char character)
{
for(int j=0; j<5; j++){
LcdWriteData(EEPROM.read(((character -0x2d)*5)+j)); 
//we subtract 0x2d because the zeroth position in EE memory is (-) = ASCII(0x2d)
}
LcdWriteData(0x00);            
} 
// (character - 0x2d)*5 jumps 5 bytes at a time through the EEprom address space
// Add a fixed offset for other fonts stored at higher locations in the memory: 
// eg: LcdWriteData(EEPROM.read(235+((character -0x2d)*5)+j)); 
// the big#fonts are 11 byte-columns wide, so calc = (Offset+((character -0x2d)*11)+j)

This adds some delay, but because the Nokia 5110 is already dead-dog slow it’s not even noticeable.  A similar mod gets applied to the split print functions for the big number font, and moving both to the EEprom still leaves a very serviceable 500 characters for file header info.  The trick to stacking different kinds of information in the EEprom is figuring out what the resulting address offsets are so each reading function starts at the correct memory address. You skip over the memory locations holding the font data when transferring that file header text.

With fonts stored in EEprom, the remaining Nokia 5110 functions compile to just a little over 400 bytes of program storage and 10 bytes of dynamic (excluding EEPROM.h, which gets called by some of my other libraries even if the screen is not present)  That’s with three duplicate copies of each LCD function because I’ve used a set for the standard size font, and two more for printing the large numbers in upper & lower rows.  With a bit more optimization I could get that down to about 200 bytes, which I suspect is probably the smallest memory footprint achievable for adding live data output to my loggers.

I’ve posted a ‘EEprom loader utility’ which demonstrates the dual Font/Text approach at our Project’s GitHub repository  so people can modify it to suit their own projects. On loggers with a number of DS18b20 sensors, I’ll use a similar approach to store the sensor bus addresses, which I usually store in two dimensional arrays very similar to those shown here for screen fonts.