I was poking around in the JeeLabs RF12 library recently (now part of JeeLib) and noticed that it now supports the ATtiny microcontrollers – it’s what the new JeeNode Micro uses, which got me thinking about even smaller, simpler wireless temperature sensor modules again. If you aren’t familiar with the Atmel TinyAVR range of microcontrollers they are the little brother of the ATmega that is used in our old friend the Arduino, smaller and lower cost, they are ideal for simple applications like this. The Arduino platform doesn’t support the ATtiny out of the box but thanks to the arduino-tiny project that is easily fixed, more on that later.
The version of the TinyAVR that I’m using here is the ATtiny84, it’s a 14 pin DIP package with 8KB flash, 512 byte RAM, up to 11 GPIO pins, 8 with ADC, and support for SPI, plenty to easily support the RFM12B and a temperature sensor. Using the ATtiny84 has allowed me to build my smallest (4cm square excluding the 2 x AA batteries), simplest and cheapest wireless temperature sensor yet which I’ve dubbed the TempTX-tiny, only 3 components are used, the ATtiny84 microcontroller itself, the RFM12B transceiver and a temperature sensor.
This is how the ATtiny84′s pins translate to the standard Arduino pin labels, as you can see most of the pins are multi-purpose.
The reduced RAM of the ATtiny84 meant ditching the familiar DS18B20 digital temperature sensor that I’ve used previously as the onewire and DallasTemperature libraries pushed the code size over the 8KB flash limit. Initially I considered trying to trim the libraries down to the minimum required to read a single sensor in degrees C but soon dropped that idea and went for a cheap analogue sensor instead (although I’ve found something that might work for the DS18B20 now so watch out for a follow up post see update below for using the DS18B20 instead).
There are a few options for analogue sensors but the Analog Devices TMP36 seemed perfect, it is a TO-92 packaged (like the DS18B20) precision analogue temperature sensor with a range of -40°C to 125°C and runs from 2.7V – 5.5V with a 0.05 mA current draw. The TMP36 doesn’t require any external calibration and outputs the temperature as an analogue voltage in mV that is linearly proportional to the temperature in degrees celsius, with a scale factor of 10mV per degree and an offset of 500mV to allow for negative temperatures, eg. a voltage of 725mV equals a temperature of 22.5C and 475mV would equal a temperature of -2.5
This makes it incredibly simple to read the temperature, only requiring a few lines of code. The sensor’s output can be connected straight to one of the ATtiny’s analogue to digital converter (ADC) inputs where we can read it with the Arduino analogRead function. To get a consistent output from the ADC the ATtiny needs a fixed voltage to use as a reference (the aref), by default the supply voltage would be used but as the completed circuit will be running from 2 x AA batteries that will gradually decrease over time as the batteries deplete, fortunately the ATtiny also has an internal 1.1V reference voltage that we can use by setting analogReference(INTERNAL). This seems to be pretty stable but isn’t an accurate 1.1V so we will need to calibrate this later on if we want accurate readings. For now lets assume it is 1.1V
The resulting reading from the ADC will be a 10-bit value, ie. 1024 steps ranging from 0 to 1.1V, to turn this figure into mV we just need to multiply it by the reference in mV divided by 1024, we can then remove the 500mV offset and divide the result by 10 (the sensors 10mV per degree resolution) to give us the temperature in degrees celsius.
So in setup() we need to do:
analogReference(INTERNAL); // Set the aref to the internal 1.1V reference
and then in the loop:
tempReading = analogRead(tempPin); // Get the reading from the sensor
double voltage = tempReading * (1100/1024); // Convert reading from sensor into millivolts
double temperatureC = (voltage – 500) / 10; // Convert to temperature in degrees C.
Where tempPin is the pin the TMP36 Vout is connected.
This gives us temperatureC as a temperature in degrees celsius. In the final code I’m actually taking 11 readings, throwing the first away and then taking an average of the remaining 10 to help filter out any noisy readings. See further down for a link to the full code.
As with the previous TempTX design I’ve powered the sensor from a pin on the microcontroller so that it can be turned on only when getting a reading. +Vs is connected to Pin 12 (as Arduino D9) and Vout to Pin 13 (as Arduino A0)
Connecting the RFM12B to the ATtiny84
As I said earlier, the RFM12B on the ATtiny84 works with an unmodified JeeLib library, it just needs to connected as follows. The ATtiny’s internal oscillator also needs to be set to 8Mhz, I’ll cover how to do that further on.
Since a couple of people have asked, here is how I am mounting RFM12B transceivers to the stripboard. The best method I’ve found so far is to solder short lengths of wire to the 8 contacts required on the RFM12B first and then fit it to the stripboard. Soldering the wire to the module is very fiddly.
Complete stripboard layout for the TempTX-tiny
Here is the layout for the complete board, as usual I’ve tried to keep it neat rather than going for the smallest footprint by crossing links etc.
The complete code is available on Github here, it will read the temperature and battery voltage once a minute and transmit them via the RFM12B. As with my other temperature sensors this transmission is picked up by my wireless graphical displays and a Nanode which then uploads it to my emoncms installation.
For the voltage measurement I managed to get the code Tinker SecretVoltmeter code that I’ve used previously to work with the ATtiny84 by changing the registers for the MUXs.
Getting the code onto the ATtiny
As I mentioned at the start the Arduino IDE doesn’t support the ATtiny microcontrollers out of the box but this is where the arduino-tiny project comes in, this set of “cores” allows the Arduino platform to be extended to work with the ATtiny84, ATtiny85, and ATtiny2313 microprocessors. To install arduino-tiny you just need to create a directory under your Arduino sketchbook directory called hardware and unzip the latest arduino-tiny from here into it. Now restart the Arduino IDE and you should see the new ATtiny entries under the Tools > Board menu.
There is no bootloader and the ATtiny doesn’t have the hardware serial of the ATmega so we’re limited to ICSP (In Circuit Serial Programming) to get the code onto the ATtiny but don’t worry, you don’t need a dedicated ICSP device, an Arduino can provide that function perfectly well.
Using an Arduino as an ICSP programmer
To use an Arduino as an ICSP you need to load the ArduinoISP sketch on the Arduino and connect the SPI pins and reset between it and the ATtiny. A version of the ArduinoISP sketch is included in the examples directory with the Arduino IDE but it seems to be broken, download this one instead and load it onto your Arduino in the normal manner.
Now connect the Arduino to your ATtiny as follows:
The first thing we need to do is set the ATtiny’s internal oscillator to 8MHz (required for the RF12 support), to do this you need to go to Tools > board and select “ATtiny84 @ 8MHz (internal oscillator;BOD disabled)” and then use Tools > Burn Bootloader. Note that this isn’t actually burning a bootloader to the ATtiny, it is just using this function to set the AVR fuses to configure the oscillator.
Now download my TempTX-tiny code from Github. Load the TempTX_tiny.ino sketch in the Arduino IDE and then upload in the normal manner, the ArduinoISP sketch running on the Arduino will upload it on to the ATtiny. You will probably get the following message twice:
avrdude: please define PAGEL and BS2 signals in the configuration file for part ATtiny84
but this can be safely ignored, as long as it says “Done uploading” at the end then you are good to go.
If you get some errors about “expected identifier or ‘(‘ before ‘double’“ then you need to comment out the following line in ~/sketchbook/hardware/tiny/cores/tiny/wiring.h
#define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))
If you get “not in sync” or “protocol error” then try reducing the baud rate in the ArduinoISP sketch and in hardware/arduino/programmers.txt (under your Arduino IDE directory) as mentioned here, I had to do this to get it to work.
I was hoping to get away without this given that the TMP36 doesn’t need calibrating but I found that the readings were consistently out by a few degrees. This seems to be mostly due to the inaccuracy of the internal voltage reference as mentioned above, it seems to be stable but not an accurate 1.1V. I thought about using the battery voltage as the reference now I can measure that but of course that relies on the internal reference too so isn’t accurate either, fine for a low battery notification but no good for getting accurate temperature readings. I couldn’t find a simple way of determining the internal reference on the ATtiny84 so what I did was temporarily modify the code to transmit the analogue reading instead of the temperature, ie. change temptx.temp = temperatureC * 100; to temptx.temp = tempReading; and then placed the sensor next to a known accurate sensor to use as a reference (one of my DS18B20 based sensors). I then logged a series of temperatures from the reference sensor and the corresponding analogue readings from the TMP36 and did the following calculation for each reading to calculate the reference voltage.
aref = (reference_temp x 10 + 500) / analogue_reading x 1024
Then I added the results from all the samples up and divided by the number of samples to get an average. This gave me an aref value of 965.047318612. I divided this by 1024 and used the resulting value of 0.942382812 as my calibrated conversion factor for the mV reading, eg.
double voltage = tempReading * 0.942382812;
This has given me accurate results over a range of temperatures.
Parts List for the TempTX-tiny
Stripboard min 15 holes x 16 rows (40x40mm)
TMP36 Analogue temperature sensor
14 Pin DIP socket
2 AA Battery holder
Wire for links
Wire for antenna (165mm for 433MHz or 82mm for 868MHz)
Update – using the DS18B20
After the comment by ‘Martin 2′ below I realised I’d made a silly mistake when I thought the onewire and Dallas temperature libraries were making the code too big for the ATtiny84, I had looked at the compiled code size in the Arduino IDE before ordering the bits for this and hadn’t yet installed the arduino-tiny cores so was doing it with the board type set as Duemilanove for which it comes in over 8k. Switch to the ATtiny and it’s well under. Could have saved myself a lot of faffing around with the TMP36 if I’d realised that. Oh well, it’s all for fun at the end of the day.
To use the DS18B20 the only change required for the hardware is to fit the DS18B20 in place of the TMP36 (in the reverse orientation to the TMP36) and add a 4k7 resistor across the data (DQ) and VDD pins, there is enough space on the previous design to fit this so the changes are minimal. My previous code for the ATmega version works fine it just needs the change for the SecretVoltmeter code and sensor pins, a modified copy it available on GitHub here. You will also need to make a one line change to OneWire.h in the Onewire library, see this post for details.