An ATtiny based Wireless Temperature Sensor

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.

RFM12B ATtiny84
IRQ Pin 5
SDO Pin 7
SDI Pin 8
SCK Pin 9
SEL Pin 3

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 Code

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:

Arduino ATtiny84
D13 Pin 9
D12 Pin 8
D11 Pin 7
D10 Pin 4
3.3/5V Pin 1
GND Pin 14

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)
ATtiny84 Microcontroller
RFM12B transceiver
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.

40 thoughts on “An ATtiny based Wireless Temperature Sensor

  1. Martin, Looks great, like that you are using a coin cell to power it, I’ve been thinking about the same thing.

    The stripboard layouts are just drawn in Inkscape. I looked at a few options for dedicated programs but they all seemed a bit limited.

  2. True_North – I haven’t used Voipuser for a while myself but I know the website was victim of a hard disk crash late last year, the service is itself is still running I think or at least it was. Dean had said it will be back at some point but I don’t know any more than that.

  3. Which AtTiny84 did you choose? I can see that there is more than one kind at Farnell. One of them can take a supply voltage of 1.8V to 5.5V whereas the two others (2.7V to 5.5V) only differ on CPU speed.

  4. Hi Martin,

    That’s interesting, I’ll have to look at it again as I was having trouble getting it to work and you don’t seem to be doing anything different to what I tried, it’s essentially the same as I did for the ATmega version as far as I can see. Wonder if it is down to a difference in library versions.

    I made some progress on reading the DS18B20 without using the OneWire or DallasTemperature libraries, got it working on an ATmega but it was failing on the ATtiny for some reason I never got to the bottom of. Not looked at it again since.


  5. I am working on an ATtiny84 based gas sensor and I would like to use the RFM12B to transmit data from the outside unit to a close by indoor arduino. How should I go about hooking up the RFM12B to the arduino and coding it to receive the transmissions? I am using a data logger shield to capture the information.

    Thank you for your very detailed project.

  6. There is a diagram here showing how the RFM12B should be connected to an Arduno with the pin numbers:

    However the Arduino must be running on 3.3V (the RFM12B can’t handle 5V) or you must use a voltage regulator to power it and resistors to create voltage dividers on the SEL, SCK and SDI pins. When I built my OpenEnergyMonitor setup I used the Jeelabs breakout board for the RFM12B, there is a schematic for that here that should help:

    The code I am using on the receiving end is below, this runs on a Nanode (Arduino like board with ethernet) and just relays it to my openenergymonitor emoncms installation. It should give you an idea of what you need to do though.

  7. Thank you! I am planning on using the plans for that breakout board to hook up my RFM12B to the Arduino. Also, I am building my code based off of your Nanode code as well as some of the example codes that came with the Jeelibs library. However, instead it will upload the data to an adafruit data logger shield/ SD card

  8. I’m starting to use an ATTINY84 for a similar purpose, talking to an RFM12B and a DS18B20. I’m running into compile errors and wonder if anyone can shed any light on it?

    /applications/arduino-1.0.1/hardware/tools/avr/bin/../lib/gcc/avr/4.3.2/../../../../avr/lib/avr25/crttn84.o:(.init9+0x2): relocation truncated to fit: R_AVR_13_PCREL against symbol `exit’ defined in .fini9 section in e:/dropbox/paul/applications/arduino-1.0.1/hardware/tools/avr/bin/../lib/gcc/avr/4.3.2/avr25\libgcc.a(_exit.o)

    BlinkTest_ino.cpp.o: In function `readVcc()’:
    C:\Users\PAULCH~1\AppData\Local\Temp\build2242680371036286222.tmp/BlinkTest_ino.cpp:120: relocation truncated to fit: R_AVR_13_PCREL against symbol `__divmodsi4′ defined in .text.libgcc section in /applications/arduino-1.0.1/hardware/tools/avr/bin/../lib/gcc/avr/4.3.2/avr25\libgcc.a(_divmodsi4.o)

  9. I want to do a similar project, but I was thinking of using the RFBee to be the core of the entire device. Thanks for sharing your project, I got a lot of the information I need for mine from it.

  10. Hi Nathan,
    first of all thanks for the wonderful blog. I was so inspired that i at once built the stripboard module. But the source i have downloaded cannot be compiled. I only have errors, although i have downloaded the libs from the urls you have given in your source. I am using the Arduino IDE 1.03. I also tried IDE 1.02, 1.01, 023 and IDE 022 with those libs but still no success. Can you give me a tip? Thank you.

  11. Hi Nathan,
    thanks for very much for your answer. These are the errors:

    /Applications/ In function `__vector_default’:
    (.vectors+0x2): relocation truncated to fit: R_AVR_13_PCREL against symbol `__vector_1′ defined in .text.__vector_1 section in core.a(WInterrupts.c.o)
    /Applications/ In function `__vector_default’:
    (.vectors+0x16): relocation truncated to fit: R_AVR_13_PCREL against symbol `__vector_11′ defined in .text.__vector_11 section in core.a(wiring.c.o)
    /Applications/ relocation truncated to fit: R_AVR_13_PCREL against symbol `main’ defined in .text.main section in core.a(main.cpp.o)
    /Applications/ relocation truncated to fit: R_AVR_13_PCREL against symbol `exit’ defined in .fini9 section in /Applications/
    TempTX_Nathan.cpp.o: In function `readVcc()’:
    /Applications/TempTX_Nathan.ino:117: relocation truncated to fit: R_AVR_13_PCREL against symbol `delay’ defined in .text.delay section in core.a(wiring.c.o)
    /Applications/TempTX_Nathan.ino:121: relocation truncated to fit: R_AVR_13_PCREL against symbol `__divmodsi4′ defined in .text.libgcc section in /Applications/
    TempTX_Nathan.cpp.o: In function `loop’:
    /Applications/TempTX_Nathan.ino:79: relocation truncated to fit: R_AVR_13_PCREL against symbol `digitalWrite’ defined in .text.digitalWrite section in core.a(wiring_digital.c.o)
    /Applications/TempTX_Nathan.ino:80: relocation truncated to fit: R_AVR_13_PCREL against symbol `delay’ defined in .text.delay section in core.a(wiring.c.o)
    /Applications/TempTX_Nathan.ino:86: relocation truncated to fit: R_AVR_13_PCREL against symbol `__mulsf3′ defined in .text.fplib section in /Applications/
    /Applications/TempTX_Nathan.ino:86: relocation truncated to fit: R_AVR_13_PCREL against symbol `__fixsfsi’ defined in .text.fplib section in /Applications/
    /Applications/TempTX_Nathan.ino:87: additional relocation overflows omitted from the output

  12. Hi Nathan,
    sorry to bother you with such nonsense. I must have overhead that post. After installing the patch compiler works perfect. So now i have to build a transmitter. Do you have any similar simple solution?

  13. Oops i think i was a bit confused. Okay your sensor board is the transmitter. I’ll have to build a receiver. I think i’ll go for the RFM2Pi or put a RFM12B on a Arduino Shield and try to program it as a receiver.
    BTW what program do you use for drawing the strip board layouts?

  14. Thanks for the write up. I found this when I was trying to investigate variations in the internal voltages of the ATTiny range.
    I realise you did this a while ago, but I’ve written a calibration code for ATTiny, which might be useful to you or others.
    To use it you upload the code to your device, apply a known voltage to the input and it calcualtes the internal reference. This is then stored into EEPROM as a milliVolt reading.
    This can then be read when you upload new code and can help improve measurement accuracy.
    I found the 2.56V reference was actually 2.473V on my IC, which is within specifications, but meant all my caluclations were incorrect.
    Its in a blog post here:

    Cheers, Matt.

  15. Hi Nathan, This is a good project and I am about to do it for myself. However, I compared your connection diagram to the pin connection specified on this page( and I have a question. The page indicated nIRQ as OPTIONAL but you connected it. It also specified nRES as REQUIRED but you did not connect it. Can you please tell me why you made the decision? Thank you.

  16. Hi David,

    I’m using the Jeelib library which is interrupt driven so needs the nIRQ line.

    nRES is actually listed in the Hope RF datasheet as optional, it can be pulled low to reset the module. I can see why pulling it high might make sense to avoid the potential of unwanted resets but I’ve never seen that happen and none of the other designs I’ve seen using the RFM12B have done that.


  17. I use various ATTiny uCs for projects. Affordable, low power, and incredibly powerful, esp considering their price and accessability.

    So, when using the Arduino-as-ISP sketch, I found that it was handy to fab a shield, which removes misplaced wires from the equation later on (remember, I’m using different models of ATTiny – so the programming signals go to different pins). On the simple shield (the first revision of which was on perfboard), you’re providing the cap for the reset disable for the host Arduino, and pulling all the signals from the appropriate Arduino pins to a 6-pin ICSP header on the shield. There are additional ICSP headers on the shield labelled for individual ICs (ATTiny85, ATTiny84, ATMega328, etc), with each of those wired off to the appropriate pin on a 28 pin ZIF (Zero Insertion Force) socket.

    To program an ATTiny85 in the ZIF, all I need to do is set the processor into the ZIF socket, pull the lever to lock it in place, then take a 6-pin cable from the main ICSP socket to the ATTiny85 ICSP header – the signals from the host Arduino-as-ISP are then routed to the correct pins, and I can start the upload.

    That alone would be fine, but in fact, this simple shield can also be used to flash uCs in-circuit. I put the 6-pin ICSP header on all of my projects, which allows me to take the ICSP cable from the shield and plug it into a PCB, and flash directly.

    Using a ZIF (Zero Insertion Force) socket instead of a standard DIP friction socket means the ICs don’t get abused being stuffed into a standard socket (no matter how careful you are, eventually, the thin bottom portion of a DIP pin breaks off, or you fold a pin underneath, etc). Of course, the processor is subjected to this if you’re sticking it into a breadboard or a socket on your PCB, but the ZIF cuts the wear and risks in half.

    Having an ICSP header on your projects though greatly simplifies things, since you needn’t remove the uC from your project in the first place.

    In support of breadboarding, I even fabbed a few small ICSP adapters for use on breadboards – the 6-pin header on a perfboard with 6 pins out at DIP-6 spacing, so they bridge the centre rib on a breadboard. From there, one can jumper wires to the uC on the breadboard, which stay even when the breadboard is someplace separate from the Ardunino-as-ISP. I can use the ICSP plug from the shield to program it without dealing with having to reconnect the dangling wires to the correct places on the Arduino-as-ISP. A better implementation would be a carrier PCB for the uC, with the ICSP directly on that carrier – NO wires to route, no extra bridges consumed on the breadboard to support the ICSP. The same carrier PCB could house an external crystal for the uC if appropriate.

    Another added benefit of the ICSP: the bottom two pins (if viewed with pin1 in the “upper left”) are RESET and GND – you can short these two together to reset your uC.

  18. The ZIF adapter for different kinds of chips sounds useful. I made one myself but it was much simpler and just for the ATtiny84, with my later boards I started soldering the ATtiny directly to the PCB and use an IC test clip to program it which also allows connection of a USB-UART adapter for debugging, I like this because it keeps the board minimal although the clips can be hard to find at a sensible price:

    On my latest PCB (blog post coming soon) I’ve used a SMT ATmega328 and I’ve put an FTDI header on for day to day programming and for the initial flashing of the bootloader I used a pogo pin jig which saved having to make space for a dedicated ICSP header: I’m going to knock one of these up for the FTDI pins too.

  19. Hi Natthan.
    I ended here after using muy own arduino board as a sensor transmitter. Battery life was short (I had power led and voltage regulator). Now that your project is mature. How far did your batteries went with attiny 3v sensor? Assuming 2 AA for max mhA.

  20. Hi Xavier,

    22 months and counting for an ATmega328 based sensor, started in December 2011 and now at 2.74V

    20 months so far for an ATtiny84 version started in February 2012 and now at 2.98V

    Both these started with early code that wasn’t very optimised so should be slightly better now and the first one is a long way from the base station and has to retry quite often. 2+ years should be doable.

    From my tests the DS18B20 will probably start to drop out once the voltage gets below 2.7V.


  21. Thank you for this post! Im sketching order list for parts for 5 or 10 nodes. Is it feasible to use nRF24l01+ in this setup? its cheaper than rfm12b. Let me know what (if any) are potential problems, so i could research it further. Thank you!

  22. Nathan after loosing some hair with random hang ups (LCD on Uno + sensor + rfm12b drains sram).. I’ve moved to Uno + ethernet shield + rfm12b + sensor and I’m playing with nodejs and emoncms.. When I see those 100$ weather stations in shops I feel sorry for future owners. Thanks for this great post!!

  23. This is totally what I’ve been looking for! I was thinking Zigbee, but that would make things cost quite a lot more. I was thinking of using a DHT11 or DHT22 in order to have a very similar sensor that harvests both temperature and humidity data. Have you considered that? Or no use for it?

  24. @Tom, I did some trials with the nRF24L01+ here:
    It works fine with the ATtiny84 but you would probably want to do a specific board for it rather than botch one onto a TinyTX like I did. The range is nowhere as good as the RFM12B but not as bad as I’d been led to believe.

    @cubeconvict, I’ve used the DHT22 on a few nodes, it works well, more here: and latest code here:

    I didn’t like the DHT11 though, the humidity sensing seems poor and it will only return integer values.

  25. Do you have a link to the receiver you built? I’m looking to use the RFM69 connected to a UNO and build your suggestion for sensor nodes.

Leave a Reply

Your email address will not be published. Required fields are marked *

Notify me of followup comments via e-mail. You can also subscribe without commenting.