Introduction: TempBug: Internet-connected Thermometer

Last January, we had some trouble with the heat in my office. Specifically, the kind of trouble wherein the heat is not on, you turn it up, and it still not on. This went on for more than a few days, and finally ended a day or two after we got an email announcing that the heat was broken and speculating that it had probably been down for a few days. My teammates and I laughed a bit at this - we knew exactly when the heat had stopped working. We had a continuous record of the temperature in the office going back months, with 10-minute resolution.

You can do this too, and it's quick, cheap, and easy! This little gadget is built around an electric imp, and you can push the data from the imp out to anywhere you want. In my case, I found it handy to push the data to Sparkfun's Data Stream service, which stores the data for free. Even better, I can use's Sparkfun Data Stream frontend to get nice graphs of the data I've collected.

This project takes about an hour to two hours, if you've never done a project with an electric imp before, and when you're done you'll have a thermometer that you can toss anywhere with WiFi and collect data for months to years on a single battery, depending on how often you check the temperature.

Step 1: Gather the Parts

Here are the parts you'll need to build your internet-connected thermometer:

  1. An electric imp (currently available on Sparkfun and Adafruit) - $30
  2. An electric imp breakout board (again, Sparkfun or Adafruit) - $12.50
  3. A 9V battery - $2.00
  4. A 9V battery clip, like this one from Sparkfun (we're just going to cut the end off of it, so the connector doesn't matter) - $1.25
  5. A big capacitor, like this one on Digikey (220 uF, 50V). Make sure it's at least rated to 16V if you're going to connect it to a 9V battery, or you'll have a bad time. - $0.40
  6. A 100kΩ resistor, preferably 1% tolerance, like this one on Digikey. - $0.10
  7. An NTC (negative-temperature-coefficient) Thermistor. I used this one on Digikey. It needs to have a room-temperature resistance equal to your resistor (#6) - this one is 100kΩ at room temperature. - $3.25

Parts total: $49.50

You'll need some tools and supplies, as well:

  1. Soldering iron
  2. Solder
  3. A bit of foam tape or other thick, double-sided tape for securing the battery to the breakout board
  4. Angle cutters / wire cutters
  5. Wire strippers
  6. A smart phone for using BlinkUp to put the imp on your wifi network
  7. A computer to program the imp

Alright! Let's get started!

Step 2: Wire It Up

Check out the breadboard diagram in the photos to see how we'll be wiring up your tempbug.

First, connect the battery clip to the breakout board.

  1. If your battery clip has a connector on the end, cut it off. Strip about 1/8" off the end of each wire and twist the strands to keep them together. If you don't want long leads hanging off your tempbug, you'll want to cut the wire to about 2" long or a little less.
  2. Secure your electric imp breakout board to your work surface and heat up your soldering iron.
  3. One pad at a time, use the soldering iron to heat the "P+" and "P-" battery pads on the breakout board. For each pad, when it gets hot, flow a good-sized bead of solder onto the pad. Take care not to short the pads together.
  4. Place the red lead from the battery clip against the bead of solder on the "P+" pad and heat the pad and wire together with the soldering iron. When the solder flows, hold the parts together and remove the iron. Let the solder cool, then let go.
  5. Repeat previous step with black lead and "P-" pad. Refer to photos to see how this should be arranged. Note that you should follow the label on the board, not right vs. left, as your breakout board may have the pads on opposite sides.

Connect the Capacitor

When the imp turns on its WiFi transmitter, it can draw a lot of current (up to 400 mA, during initial calibration) from your power supply. To keep this sudden current draw from "drooping" the power supply and browning out your tempBug, we've got a BIG HONKING CAP. We'll connect the capacitor in parallel with the battery, the battery will charge the capacitor to the same voltage as the battery, and if the imp suddenly demands more current than the battery can dish out, the capacitor will pitch in to help out.

  1. The leads on the capacitor go through the breakout board at "VIN" and "GND" which are next to each other along the bottom edge of the board. Note that the negative side of the capacitor is marked with a big stripe with a "-" sign in it, and should go to ground
  2. One pad at a time, use the soldering iron to heat the wire and the pad, then flow some solder onto the joint and remove the iron.
  3. When you're all secure, trim the excess from the capacitor leads with your angle cutters or wire cutters.

Connect the Resistor

  1. The 100kΩ resistor can go either way around and is connected between pin9 and 3V3 on the electric imp breakout board.
  2. Bend the wire leads to fit and push them through the holes marked "3V3" and "Pin9".
  3. Just as you did with the capacitor leads, solder ONLY THE 3V3 SIDE to the breakout board. The thermistor needs to share the Pin9 pad with the resistor, so don't solder that side yet!

Connect the Thermistor

  1. Just like the resistor, the thermistor is a passive component and can go either way around. If you bought the same one as me, the wire ends come pre-stripped. Push one end through the Pin9 hole and the other through the hole marked "Pin8".
  2. Now, solder the resistor and thermistor leads together through the Pin9 pad.
  3. Lastly, solder the last thermistor lead into the Pin8 pad.

Alright, all wired up! Let's take a look at what you just built.

Step 3: How's This Thing Work?

The operating principle behind the tempbug is really simple. All we're doing here is making a resistive divider using a resistor and a thermistor, and measuring the voltage in the middle using an Analog-to-digital converter in the electric imp.

Wait, what's a thermistor?

A thermistor is a temperature-dependent resistor; it has a specific resistance at room temperature, and its resistance varies in a known proportion with the temperature of the component. In our case, we've selected a NTC, or negative-temperature-coefficient thermistor; the resistance of the part will decrease as temperature increases.

Got it, but what's the deal with the resistor?

Together, the resistor and thermistor form a resistive divider; the two resistors, wired up in series (end-to-end) have an effective resistance equal to the sum of their individual resistances. This determines the amount of current that flows through them: V=I*R (ohm's law), V=3.3V (from the imp breakout board's 3.3V on-board power supply), R = the 100kΩ + the thermistor's resistance at the current temperature -> now we know how much current is flowing through the circuit.

Going back to Ohm's Law, we know that the voltage across the thermistor = I*R, where I is the current through the divider (they're in series, so both components see the same current), and R is the current resistance of the thermistor. Therefore, if we can measure the voltage at pin 9, we can solve for the resistance of the thermistor. Given the thermistor's resistance, we can determine the temperature of the part.

Hey wait, the other side of the thermistor isn't connected to ground!

Right you are. It's connected to pin 8. This is a sneaky trick we'll use to save the battery. Current flowing through the resistive divider drains the battery, so we'll only let the current flow when we want to measure the temperature. We do this by programming pin8 to be a digital output. When we set pin 8 high, the voltage drop across the divider is 0V, so no current flows. When we want to take a measurement, we drive pin 8 low and sink current from the divider through the imp.

How do we actually do the measuring?

To measure the voltage across the thermistor, we set up pin 9 as an analog-to-digital converter, which allows us to take an accurate measurement of the analog voltage on the pin using the imp. Check out the software in a few steps to see how that's done.

Step 4: Power Up!

Ok, enough math, let's get to the actual temperature-measuring part.

Connect the battery

To secure the battery to the breakout board, a little square of foam tape will do the trick. Cut a small piece off with a pair of scissors and stick the sticky side to the back of the breakout board. Peel back the backing film, and stick the battery on, making sure to keep it nice and square - this stuff is sticky! Once you've got it stuck in place, go ahead and connect the battery clip.

Check the jumper

Make sure the jumper on your breakout board is set to connect the middle pin and the "BAT" pin. Check the photos if you're not sure. If your imp doesn't power up when you click it into the socket, double-check the jumper.

Plug in the imp

Insert your imp into the socket on the breakout board, and it should start to blink red or orange (unless you've connected it before on this network, in which case it will just connect again). Congrats! You're powered up and ready to blink up and program.


If you haven't already registered as an electric imp developer, you'll need to do that now. Head to to sign up (it's free). You'll also need to download the electric imp app on your smart phone, which is also free - just search for "electric imp". Log into the app with the same account credentials you used to register at

Once you're logged in, add your wireless network to the list of networks in the electric imp app. If you're on iOS, it will prompt you to allow it to use the network you're already on, and you'll just need to add the password yourself. To add a new network manually, select "other network" and type in your SSID and password. Check the "save this network" button!

Once you've added the network, you're ready to configure the device with BlinkUp. Make sure the imp is powered up. If it's powered up but stopped blinking, reset the power by "push-pushing" the card (push once to disconnect, count to 3, push again to reconnect) so you can be sure it's listening for the BlinkUp signal.

Hold the screen of your phone against the top edge of the imp and press "send BlinkUp" in the electric imp app. The screen on your phone will begin to blink quickly for about 10 to 20 seconds, then stop. The imp should blink green once ("got it!"), then start to blink red/orange ("found the network, connecting to the electric imp server"). After a second or so, the imp should be able to phone home to the electric imp server, and will start to blink green. You're connected. Excellent.

Now it's time to program the imp! Head to the next step.

Step 5: Setting Your Imp Firmware

Point your browser to again and log in. This takes you back to the IDE window.

If you've connected this imp breakout board before (you pro), it will pop back up in the IDE under the model it was last a part of. If this is a brand-new breakout board, it will appear in the IDE's left-hand nav panel under "New Devices", at the top of the panel. You'll see a little number in this panel showing the number of new devices there waiting to be configured - probably just one!

Click "New Devices" to see the list of new devices. Again, if you've never configured this device before, it won't have a name, so it will default to a long hex string (this device's Device ID). Click on the small gear next to the device name to open up the settings for the device. This is where you can name the device and assign it a model. Go ahead and name it whatever you like, and create a new model by typing a name for your new model in the model drop-down and pressing enter. When you hit "save changes", you'll be taken to the code window for this new model, and you'll see your new device is now listed under that model in the left-hand nav.

A model has two parts: an agent and a device. The device firmware is simply the code that runs on the imp itself. The agent is that device's partner - a tiny server that runs in the electric imp cloud. The device and agent can send information back and forth, and the agent can talk to the rest of the internet and has its own URL. You get to write code for both of them.

... Or you can use the code that's already written for you :)Head to github, and notice that there are two files here - one for the agent and one for the device. Accordingly, there are two windows for you in the IDE - one will say "agent" and the other "device". Copy the correct code into each window.

If you hit "Build and Run" now, you'll see the device read the temperature and then go to sleep, but the agent will have an error when it tries to log the data to Sparkfun because you haven't added your Sparkfun account yet. Let's do that next. If you did do this, and your device has gone to sleep for 15 minutes, you can disconnect and reconnect power (by simply pulling and re-inserting the jumper) to get the imp to wake up again.

Let's connect your new thermometer to Sparkfun to log the data, then we can come back and take a look at what makes the firmware work.

Step 6: Connecting to Sparkfun Data Stream

To connect your thermometer to Sparkfun, you'll first need to create a new Sparkfun Data Stream. Head to and click on "Create" on the right side of the page.

You'll need to fill in a few details about your new stream: a title, a description, and some data fields. What you call the data fields is important: make sure you create a field called "temp". The electric imp code is set up to post the temperature data with this field name, and if the fields don't match, your code will throw errors. You can create other fields if you want, too, but the only one you need (and the only one your code is already set up to use) is "temp".

Once you've filled in the details and clicked "save", you'll be taken to a page with the keys to your new stream. Save the public and private keys from this page. You'll need to add these keys to your electric imp agent code in order to tell your TempBug which Sparkfun Data Stream to post the data to. Save the stream URL you configured, too - it'll be a quick and easy place to see your data arrive in the Sparkfun stream.

Paste your keys into lines 6 and 7 in the agent back in the electric imp IDE:


Click "Build and Run" in the electric imp IDE to deploy the code that now includes your keys. Your thermometer is probably asleep, so if you want to test it right away, do a quick push-push (push the imp card to eject it from the SD socket, count to 3, push it in to the socket again) to reboot the imp, and you should see the logs report a successful post to Sparkfun!

2015-01-30 09:15:05 UTC-8[Status]Device Booting; 5.14% program storage used
2015-01-30 09:15:05 UTC-8[Agent]PUSH: 200 - 1 success
2015-01-30 09:15:05 UTC-8[Device]sleeping until 1422639001000 2015-01-30 09:16:51 UTC-8[Status]Device disconnected

Point your browser at the Stream URL you configured when you set up your data stream to see the data logged in the Sparkfun stream. Of course, you'll probably want a graph. Good news: that's easy.

Head to and you'll find a page that asks for your Sparkfun stream public key. Paste the public key in here and hit enter, and the page will build you a graph of your temperature data!

Let's head back to the code editor and take a look at how the imp is working this magic.

Step 7: Digging Into the Code

Imp firmwares are written in an object-oriented language called squirrel, which looks and feels a lot like javascript. Squirrel runs in a virtual machine on top of the imp OS inside the imp, so runtime errors won't knock your device offline, and you have access to a lot of nice OS-based features, like buffered serial input and easy one-line APIs to set GPIOs or communicate with peripheral devices.

To take a look at all the things you can do in your imp firmware, check out the electric imp API reference and electric imp worked examples page. To see which pins can do what, check out the imp pin mux.

Our code here is pretty simple. Here's our basic operating procedure:

  • Set some constants
  • Define a class for our temperature sensor
  • Instantiate our class to create a temperature sensor object
  • Read the Temperature Sensor
  • Send the datapoint to the agent
  • Schedule ourselves to wake up in a bit and do it all again

The constants on lines 8 and 9 look a little ominous, but they're simple. These are the parameters for your thermistor - you can find them on the datasheet for the thermistor you're using. If you're using the thermistor I linked to earlier, you can even keep the parameters already set here.

Lines 20 to 71 are a class definition - this is basically a set of instructions for how to do various important things with a thermistor, like read the temperature. This comes straight from github - it's a nice, modular piece of code so that it can easily be reused.

After that, we just need to define our high-level logic. On lines 79 to 83, we configure the two pins we're using. Remember from when we built the device - one of the pins is anenable pin, which will allow current to flow through the thermistor when it is set low. The other pin is our analog input pin, which we'll use to read the voltage across the thermistor.

// Configure Pins
// pin 8 is driven high to turn off temp monitor (saves power) or low to read
therm_en_l <- hardware.pin8;
// pin 9 is the middle of the voltage divider formed by the NTC - read the analog voltage to determine temperature
temp_sns <- hardware.pin9;

On Line 86, we call on our class definition from earlier to create a thermistor object. This object takes in the constants we set earlier, and has methods that allow us to read the temperature in Celsius or Fahrenheit.

// instantiate our thermistor class
myThermistor <- thermistor(temp_sns, b_therm, t0_therm, 10, false);

Lines 88 to 96 are where the magic happens - we enable the sensor, take a reading, send it to the agent, and disable the sensor again to save battery.

local id = hardware.getdeviceid();
local datapoint = {
    "id" : id,
    "temp" : format("%.2f",myThermistor.read_f()),

And lastly, schedule a wakeup in fifteen minutes. We'll go to deep sleep in the mean time, saving the battery:

//Sleep for 15 minutes and 1 second, minus the time past the 0:15
//so we wake up near each 15 minute mark (prevents drifting on slow DHCP)
imp.onidle( function() { 
    //server.sleepfor(1 + 15 - (time() % (WAKEINTERVAL_MIN*15)));
    server.sleepfor(1 + WAKEINTERVAL_MIN*60 - (time() % (WAKEINTERVAL_MIN*60))); 

That's the device firmware - let's take a look at the agent. You've already seen part of the agent - we set the Sparkfun Public Key and Private Key lines 6 and 7 of the agent earlier. Below that, we have another class definition; this one defines methods for interacting with a Sparkfun Data Stream.

Lines 62 to 65 are a really interesting bit of code - this is the hook that the agent uses to receive new temperature data from the device. This is done by registering an agent "callback" with agent.on. This says, "when the device sends you an event called "temp", call the following function with the data marked with that tag". Looking back at line 95 of the device firmware, we see that we sent data marked "temp" with a function called "agent.send". This is where that data went!

device.on("data", function(datapoint) {
    local resp = stream.push({"temp": datapoint.temp});
    server.log(format("PUSH: %i - %s", resp.statuscode, resp.body));

Lastly, we see the agent do two things when it first starts running. The first: it sends a log message letting us know that it started and is ready to get data. Second, it instantiates a Sparkfun Data Stream object with the class we defined earlier, letting us use the methods of those classes to send data to Sparkfun as it comes in.

The agent doesn't restart or sleep; it starts the first time the device boots with this model, and continues to run forever, handling each new datapoint when it comes in by calling the "device.on" callback. It's the device's brain in the cloud!

That's the firmware! Enjoy your new Internet-connected thermometer.


KimM171 (author)2017-01-12

I'm getting the error "cannot convert the string" on this line:

assert(typeof(data == "table"));

I know this is trivial, but I'm trying to understand it. I think the parentheses are misplaced and it should be:

assert(typeof(data) == "table");

but I still get the error "cannot convert the string". Any idea why?

BryanS24 (author)2015-07-20

This device looks to fit my needs exactly, but does anyone know what the accuracy is at temperatures of -20 degrees Celsius +/- 15 degrees C.?

EngrDanny (author)BryanS242015-07-22

The accuracy would be dependent on the thermistotr you select. The IMP and database provide .01 degree resolution using the agent and device code.

hugoimp (author)EngrDanny2016-09-26

If you're looking for serious accuracy, then thermistors aren't necessarily the best way to go. You should look at digital sensors instead, which are pre-calibrated - there's going to be a new environmental sensor demo board for the imp to replace the current one on Amazon shortly...

JohnS607 (author)2016-02-17

Hello! I'm planning on doing this project to keep track of temperature in our office. I can only find a 10K thermistor on adafruit. Would this work if I use a 10k resistor with it but do everything else the same?

Link to the thermistor:

Thank you!

hugoimp (author)JohnS6072016-09-26

Late reply but: yes, you can. The measurement is ratiometric so as long as the bias resistor matches the thermistor 25C temperature you're all set.

hugoimp (author)2016-09-26

Hi all,

I'm afraid Tom went to go and make rockets at SpaceX so he can't answer questions on tempbug anymore, but you can always go to the imp forums at to post questions on things like

jrwst36 (author)2016-01-10

Sorry if this has already been asked, but can you tell me how to push two pieces of data to at once. I'm using a temp - humidity sensor. I've figured out how to log once piece of data but not both at once. Can you tell me how to modify the stream.push function to send two pieces of data a once (temp and humidity)?



I'd like to bump jrwst36's comment. This Instructable is awesome, though I think it would be even more awesome if 2 thermistor temperatures were pushed to Sparkfun. That would help code newbies (like me) a lot!

hodjon (author)2016-01-17

My temp bug stopped working and I an trying to reconnect. When I point the imp to my phone it "gathers data" but then notifies me the the connect was unsuccessful....what am I doing wrong?

GigemTodd (author)2015-11-30

Howdy Tom!

I love this project! It does exactly what I need. The construction went very smooth. I am sending the data to spark fun and then graphing it via, very cool. I have but one problem. The temp that the bug is sending is incorrect. I left the default Fahrenheit read function, but I am getting 150-151 as the readings. Do you think I damaged the thermistor when installing?

GigemTodd (author)GigemTodd2015-12-07

I bought a new thermistor, the data sheet b_therm value was slightly different (4540). I unsoldered the original thermistor and installed the new one, being very cautious to not overheat the wires during installation. The unit worked, but the temp was still incorrect. Now it reported 141.x as the reading. After watching the graph for several days it appears the temp is off approximately 69 degrees fahrenheit. So, like any knowledge-less hack, I modified the read_f() function to subtract 69 from the calculated value for degrees in fahrenheit. Yeah, so this is giving me what is "about" the right temp. But even I (as an Aggie and avid practioneer of Aggie Engineering) know there is nothing useful or reliable going on here. Might anyone have some insight as to what I am missing.

davidmonaghan (author)2015-10-16

The breakout board I got from Sparkfun did not have jumper terminals; luckily I had some around to solder on myself...

Ploopy (author)2015-08-05


BryanS24 (author)2015-07-20

This device looks to fit my needs exactly, but does anyone know what the accuracy is at temperatures of -20 degrees Celsius +/- 15 degrees C.?

EngrDanny made it! (author)2015-07-07

Awesome tutorial, how do you graph the data?

Also, the time stamp on the Sparkfun Stream is 3 hours ahead of the device. Any thoughts?

NeilB9 (author)2015-06-24

Hi there. It's fantastic. So straightforward, worked first time and tells me exactly how (and if) my solar PV panels are heating my hot water through an ImmerSUN device. I didn't thin k it wasm but this Tempbug proves it is doing exactly as it should. I have it monitoring the hot-water pipe leading out of the tank. I would really like to also be able to monitor the 9v battery voltage too, so I know when to change the battery. I have tried to follow Lathyrus's code, but I get completely lost. and very cold feet! Are there any simple lines of code I can insert into Tombrew's code that will give me this, without breaking what already works? If it ain't broke ...! Thank you all. I am alos using Luke Beno's excellent to see the data logged in Sparkfun, as is no longer available. Brilliant!

MariusV1 (author)2015-06-16

Hi there and thanks for the tutorial. I built the TempBug on a breadboard and when it runs I get nothing in the datastream. I the code, I only changed the b_therm value (4600 for my thermistor) and obviously, the keys from sparkfun.

The log shows something like:

expecting: batteryvoltage, temp

2015-06-16 20:45:03 UTC+3[Status]Device connected

2015-06-16 20:45:03 UTC+3[Status]Device disconnected

2015-06-16 20:45:03 UTC+3[Device]sleeping until 1434477601000

2015-06-16 20:45:03 UTC+3[Agent]PUSH: 400 - 0 batteryvoltage missing from sent data.

Any ideas on where I could start troubleshooting?

Thanks in advance,


MariusV1 (author)MariusV12015-06-18

Well ... this is just random. Now I soldered the parts on the breakout board and I get single digit values :|

MariusV1 (author)MariusV12015-06-16

UPDATE: The cod will work only if I remove the "batteryvoltage" tag from sparkfun. But now I'm getting huge values for my temp in celsius ... 150 degrees

dalekb (author)2015-05-30

This is the first time I've ever made anything like this. I found the instructions to be very detailed and helpful. But I'm not sure why I can't get my Tempbug to work. Here is an example of the idea log I am getting:

2015-05-30 23:16:14 UTC-7[Status]Device connected
2015-05-30 23:16:15 UTC-7[Device]ERROR: the index 'device' does not exist
2015-05-30 23:16:15 UTC-7[Device]ERROR: at main:63
2015-05-30 23:16:20 UTC-7[Status]Device disconnected
2015-05-30 23:16:20 UTC-7[Status]Device connected
2015-05-30 23:16:20 UTC-7[Device]ERROR: the index 'device' does not exist
2015-05-30 23:16:20 UTC-7[Device]ERROR: at main:63
2015-05-30 23:16:25 UTC-7[Status]Device disconnected

Can you help me understand what I may have done wrong and how I might be able to fix it?

alpha0101 (author)2015-04-20

Could you use a 9v wallwart or a USB wall charger instead of a battery(you could use a small solar panel and run indefinetly) ? And, could you print the data to an excel sheet? Could you manage several of these and have them all printed in one excel?

This Seems more practical for me at least. Im no expert but these conversions dont seem very difficult.

lathyrus (author)2013-10-07

Wow! Despite not having soldered anything for about 30 years, I was able to build this thermometer without any real difficulty in a couple of hours, and I understand how it works! Thanks Tombrew for the excellent instructions. The only problem I had was that the guy in Maplins gave me the wrong size fixed resistor, so the first time I built it the reading in my livingroom was a chilly -159 degrees C... but with the correct 100k Ohm resistor everything is fine. I also couldn't get blinkup to work with my Sony Android phone so I had to borrow my wiffe's iPhone to do the setup. And getting Xively (Cosm) to draw graphs was tricky until I figured out that I needed a MASTER API key, available under the Settings tab.

lathyrus (author)lathyrus2014-07-14

Follow-up: The tempbug has been running smoothly in my attic for 9 months now. My first 9 volt battery lasted only a month, checking the temperature every 15 mins. So I changed the interval to 2 hours and my second battery lasted 6 months. I also noticed that the first temperature reading in each cycle is a bit higher than the others (by about 0.5 degree, I don't know why) so I modified the code to ignore the first reading and average the next 10:

2014-07-14 06:00:08 UTC+1[Device]ignore: voltage: 3.315 temp: 19.4 C2014-07-14 06:00:08 UTC+1[Device]loop: 0 voltage: 3.313 temp: 18.9 C2014-07-14 06:00:08 UTC+1[Device]loop: 1 voltage: 3.311 temp: 18.4 C2014-07-14 06:00:08 UTC+1[Device]loop: 2 voltage: 3.311 temp: 18.2 C2014-07-14 06:00:08 UTC+1[Device]loop: 3 voltage: 3.311 temp: 18.1 C2014-07-14 06:00:08 UTC+1[Device]loop: 4 voltage: 3.313 temp: 18.2 C2014-07-14 06:00:08 UTC+1[Device]loop: 5 voltage: 3.309 temp: 18.3 C2014-07-14 06:00:08 UTC+1[Device]loop: 6 voltage: 3.311 temp: 18.2 C2014-07-14 06:00:08 UTC+1[Device]loop: 7 voltage: 3.313 temp: 18.2 C2014-07-14 06:00:08 UTC+1[Device]loop: 8 voltage: 3.311 temp: 18.6 C2014-07-14 06:00:08 UTC+1[Device]loop: 9 voltage: 3.311 temp: 18.1 C

Building and programming the Imp was so much fun that I bought 2 more and made a phone-controlled electric gate opener, and a gizmo for forcibly rebooting my Wowwee Rovio...

Thanks again Tombrew for the great intro and instructions!

raydlettca (author)lathyrus2014-09-23

how did you change to a 2 hour interval? was it in the code inserted into electric imp? if so where?

lathyrus (author)raydlettca2014-09-23

Here's the complete Device and Agent code that I'm using. It logs the temperature on odd-numbered hours (1 a.m., 3. a.m. etc). It uploads to Xively the temperature (Celsius), WiFi signal strength, battery voltage, and lag (how long it took to do the measurement cycle).

//KW tempbug device code v2 20nov2013
// April with a 100k resistor from 3V3 to pin9 and 100k NTC Thermistor from pin9 to pin8
// pin8 TEMP_READ_EN_L - drive low to enable temp reading (great for batteries!)
// pin9 ANALOG NTC value
// benchmarking runtime: first tick with hardware.micros
local tick = hardware.micros();
// turn on WiFi powersave to reduce power consumption when awake
// WiFi is not used until line 44 (no communication with agent until end of program)
// Configure Pins
// pin 8 is driven high to turn off temp monitor (saves power) or low to read
// pin 9 is the middle of the voltage divider formed by the NTC - read the analog voltage to determine temperature
// all calculations are done in Kelvin
// these are constants for this particular thermistor; if using a different one,
// check your datasheet
const b_therm = 4450;
const t0_therm = 298.15;
// turn on the thermistor network
// gather several pin9 ADC and battery voltage readings and average them
// (just takes out some noise)
local val = 0;
local v_high = 0;
//skip first measurement (always low)
local v_high_now=hardware.voltage();
local v_therm = v_high_now * val_now / 65535.0;
local r_therm = 100000.0 / ( (v_high_now / v_therm) - 1);
local ln_therm = math.log(100000.0 / r_therm);
local t_therm = (t0_therm * b_therm) / (b_therm - t0_therm * ln_therm) - 273.15;
local t_str = format("%.01f", t_therm);
local batt_str = format("%.03f",v_high_now);
server.log("ignore: voltage: " +batt_str +" temp: " +t_str+" C");
//then measure 10 times
for (local i = 0; i < 10; i++) {
val_now =;
v_high_now = hardware.voltage();
v_therm = v_high_now * val_now / 65535.0;
r_therm = 100000.0 / ( (v_high_now / v_therm) - 1);
ln_therm = math.log(100000.0 / r_therm);
t_therm = (t0_therm * b_therm) / (b_therm - t0_therm * ln_therm) - 273.15;
t_str = format("%.01f", t_therm);
batt_str = format("%.03f",v_high_now);
server.log("loop: " +i +" voltage: " +batt_str +" temp: " +t_str+" C");
val += val_now;
v_high += v_high_now;
val = val/10;
v_high = v_high / 10.0;
// turn the thermistor network back off
// scale the ADC reading to a voltage by dividing by the full-scale value and multiplying by the supply voltage
local v_therm = v_high * val / 65535.0;
// calculate the resistance of the thermistor at the current temperature
local r_therm = 100000.0 / ( (v_high / v_therm) - 1);
local ln_therm = math.log(100000.0 / r_therm);
local t_therm = (t0_therm * b_therm) / (b_therm - t0_therm * ln_therm) - 273.15;
// format into a string for the string output port
local t_str = format("%.01f", t_therm);
server.log("Current temp is "+t_str+" C");
// update the current battery voltage with a nicely-formatted string of the most recently-calculated value
local batt_str = format("%.03f",v_high);
server.log("Battery Voltage is "+batt_str);
// benchmarking runtime
local tock = hardware.micros();
server.log(format("Read cycle took %d us", (tock-tick)));
local lag = ((tock-tick)/1000000.0);
local lag_str = format ("%.06f",lag);
//log WiFi signal strength
function ReportRSSI() {
local rssi = imp.rssi();
if (rssi < -87) {
server.log("WiFi signal is: " + rssi + " dBm (0 bars)");
else if (rssi < -82) {
server.log("WiFi signal is: " + rssi + " dBm (1 bar)");
else if (rssi < -77) {
server.log("WiFi signal is: " + rssi + " dBm (2 bars)");
else if (rssi < -72) {
server.log("WiFi signal is: " + rssi + " dBm (3 bars)");
else if (rssi < -67) {
server.log("WiFi signal is: " + rssi + " dBm (4 bars)");
else {
server.log("WiFi signal is: " + rssi + " dBm (5 bars)");
return rssi;
local rssi = ReportRSSI();
//attempt to send it to Xively...
agent.send("Xively-temp", t_str); // send data to Xively
agent.send("Xively-lag", lag_str); // send data to Xively
agent.send("Xively-battery", batt_str); // send data to Xively
agent.send("Xively-rssi", rssi); // send data to Xively
// wake up every 2 hours on odd-numbered hours (prevents drifting on slow DHCP)
imp.onidle( function() {
local secsToEvenHour = (1+ 120*60 - (time()%(120*60)) );
local secsToOddHour = secsToEvenHour - (60*60);
if (secsToOddHour < 0) {secsToOddHour += (120*60);}
local snoozelength = secsToOddHour;
server.log("snoozing for " + (snoozelength / 60) + " minutes and " + (snoozelength % 60) + " seconds" );
// full firmware is reloaded and run from the top on each wake cycle, so no need to construct a loop
##################### CUT HERE ######################
//KW tempbug agent code v2 20nov2013
//********************BEGIN XIVELY********************
//Code written by @beardedinventor modified for use by Joel Wehr
API_Key <- "pdyL6p9fp9zVtakiNqFGTaKGXPwqnMuxCdfM7258b1D8D6LI"; //Type your Xively API Key
Feed_ID <- "1282406454" //Type your Feed ID
/// Channel_ID <- "thermistor"; //Type your Channel ID -- modified by KW - see bottom of program
Xively <- {}; // this makes a 'namespace'
class Xively.Client {
ApiKey = null;
triggers = [];
constructor(apiKey) {
this.ApiKey = apiKey;
* method: PUT
* IN:
* feed: a XivelyFeed we are pushing to
* ApiKey: Your Xively API Key
* OUT:
* HttpResponse object from Xively
* 200 and no body is success
function Put(feed){
local url = "" + feed.FeedID + ".json";
local headers = { "X-ApiKey" : ApiKey, "Content-Type":"application/json", "User-Agent" : "Xively-Imp-Lib/1.0" };
local request = http.put(url, headers, feed.ToJson());
return request.sendsync();
* method: GET
* IN:
* feed: a XivelyFeed we fulling from
* ApiKey: Your Xively API Key
* OUT:
* An updated XivelyFeed object on success
* null on failure
function Get(feed){
local url = "" + feed.FeedID + ".json";
local headers = { "X-ApiKey" : ApiKey, "User-Agent" : "xively-Imp-Lib/1.0" };
local request = http.get(url, headers);
local response = request.sendsync();
if(response.statuscode != 200) {
server.log("error sending message: " + response.body);
return null;
local channel = http.jsondecode(response.body);
for (local i = 0; i < channel.datastreams.len(); i++)
for (local j = 0; j < feed.Channels.len(); j++)
if (channel.datastreams[i].id == feed.Channels[j].id)
feed.Channels[j].current_value = channel.datastreams[i].current_value;
return feed;
class Xively.Feed{
FeedID = null;
Channels = null;
constructor(feedID, channels)
this.FeedID = feedID;
this.Channels = channels;
function GetFeedID() { return FeedID; }
function ToJson()
local json = "{ \"datastreams\": [";
for (local i = 0; i < this.Channels.len(); i++)
json += this.Channels[i].ToJson();
if (i < this.Channels.len() - 1) json += ",";
json += "] }";
return json;
class Xively.Channel {
id = null;
current_value = null;
{ = _id;
function Set(value) {
this.current_value = value;
function Get() {
return this.current_value;
function ToJson() {
local json = http.jsonencode({id =, current_value = this.current_value });
return json;
client <- Xively.Client(API_Key);
//********************END XIVELY********************
device.on("Xively-temp", function(v) {
channel1 <- Xively.Channel("temperature");
feed1 <- Xively.Feed(Feed_ID, [channel1]);
device.on("Xively-lag", function(v) {
channel1 <- Xively.Channel("lag");
feed1 <- Xively.Feed(Feed_ID, [channel1]);
device.on("Xively-rssi", function(v) {
channel1 <- Xively.Channel("WiFi");
feed1 <- Xively.Feed(Feed_ID, [channel1]);
device.on("Xively-battery", function(v) {
channel1 <- Xively.Channel("battery");
feed1 <- Xively.Feed(Feed_ID, [channel1]);

Great. Thanks! How would I have it run every 6 hours starting at 8:00 am? I can see where to do it every 6 hours - just 60 *6. But how would I make it start the 6 hour cycle at 8:00 am, 2:00 pm, 8:00 pm, or 2:00 am?

lathyrus (author)lathyrus2014-09-23

Here are the graphs on Xively for 3 months

lathyrus (author)lathyrus2014-09-23

And here's what my code writes to the Electric Imp IDE log window every 2 hours.

jared.shakespeare (author)2015-02-13

Is there a way to send the remaining battery life data as well as the temperature reading?

StuartH2 (author)2015-02-10

This is really great and the simple to follow instructions got me up and running very easily. I'm trying to take this to the next level now and I've created an amazon cloud instance with running. I'm not a great coder, certainly not for squirrel, and I was wondering what I need to change in the IMP agent code to redirect to my own cloud instance running rather than sparkfun. Any help much appreciated.

Ryan121978 (author)2014-10-13

Thanks for making this easy to build! but I'm having a problem Xively isn't allowing any new accounts now. so i have this but have no where to sent the info. i tried to use another site but the code wont work with the way its wired. i was wondering if you have any ides what i can do. thanks for any help you can give!

tombrew (author)Ryan1219782014-10-20

Wondered if that would happen. has a nice graphing front end for devices like this. They built their front end on top of Sparkfun's data store. It looks pretty good - I should get around to switching my examples all over to it. Try it out here:

kenny.carlsson (author)tombrew2014-11-06

Hi, I just found a blog entry about temperature logging using the Electric Imp and using a graph from ThingSpeak here:

I'm a novice at programming, but do you think it would be possible to reuse the code from Slickstreamer in the Tempbug project?

ThingSpeak graph.png
tombrew (author)kenny.carlsson2015-01-30

Absolutely, though I haven't dug into slickstreamer at all. If you can send HTTP requests to post data, then the imp can do it!

tombrew (author)Ryan1219782015-01-30

Just updated this instructable to use Sparkfun and, so no more Xively troubles!

BenC4 (author)2014-09-11

What kind of wireless range does the imp have?

tombrew (author)BenC42015-01-30

It's very good! Better than your phone or computer; it has the same WiFi chip in it your phone probably has, but the antenna isn't surrounded by a battery and a metal frame.

lathyrus (author)BenC42014-09-23

Similar to a laptop's wifi card. My Imp in the attic and the router is in the hallway 2 floors below it on the opposite side of the house. When the Imp's battery begins to run out, the WiFi signal it detects drops, and when if falls below about -93 dBm it can't connect anymore.

Pizzadragon (author)2015-01-06

hah, finally read through all the comments and my Qs are answered. Thanks again for the writeup. This will be perfect for my situation!

Pizzadragon (author)2015-01-06

wow! awesome instructions! I was planning on hacking sth together with an arduino I've got hibernating in a drawer since last xmas, but you won me over with this tutorial.

going to set this up in a utility room at home. I already set up a raspi webcam for visual access, now for temp readings. I've got water pipes that are at risk of freezing in severe weather and this puppy will help me set up alarm notifications for my computer and phone when the temp gets too low.

After that I plan on adding a wifi connected water sensor with notification to monitor for leaks.

If I have an outlet nearby I assume that I can simply use a usb power supply for the board instead of the battery, right?

I assume squirrel enables the option for sending temp readings to anywhere I like, right? I'm building a simple website with a dashboard of info from a bunch of devices, like a couple home security webcams i made with raspis and probably two or three of these tempbugs. Got any advice on whether SQLite would be a good choice or another database? Or perhaps I can tap into the Xively API to feed my webpage dashboard...

Thanks for a great project idea!

achary (author)2014-12-11

hello sir i need to build a wake up signal with int pin with other exisiting pins through pushbutton, like pin1 can be wake by pin2 and pin5 via pushbutton pressed..

raydlettca (author)2014-09-24

I had the thermometer up on Xively yesterday, but was having trouble with measuring temperature. Then I tried to modify the code, made a mess, and then decided to start from scratch and got rid of everything on electric imp. I started by attempting to relink via BlinkUp and can't get it to link up. Any thoughts on how to get BlinkUp to work again?

tombrew (author)raydlettca2014-09-29

You actually don't need to BlinkUp again; you can just edit the code in the model you've already created. When you hit "build and run" in the IDE, the code you have in the IDE is built and sent to the imp, wiping out the previous code.

BlinkUp is only needed when initially configuring or changing the WiFi credentials on the imp. As long as you're in the same Electric Imp account and on the same WiFi network you were on before, there's no need to do it again.

If you've lost your WiFi configuration and are now having trouble using BlinkUp to get it back, you can take a look at the documentation and tips on BlinkUp here:

KRS Test (author)2014-08-24

Thanks for a great list of instructions. You really have done an outstanding job making this manageable for a for someone with limited tech skills!

I have been able to setup the code and the integration to Xively but am having trouble with the readings. The readings remain steady at say 50 for quite a while then spike up to 120 or plunge to -119. Sometimes they stay at say -10 for an extended period of time. Attached is a picture of the Xively graph.

Have tested the readings by placing the E-Imp next to hot and cold items but there is little fluctuation in the readings.

No doubt I have made a simple error somewhere but am struggling to find it.

For the moment the power supply is by USB from a laptop. Am planning on soldering on the battery once the readings work properly.

Any feedback or suggestions would be appreciated.

Thanks again!

Electric Imp output.jpg
tombrew (author)KRS Test2014-09-29

Sounds like a flaky connection somewhere in your thermistor circuit. Go back over your solder joints and make sure they're all holding together properly. It might be a good idea to look up a tutorial on soldering if you're unfamiliar; I've covered the basics here but a more in-depth of cold solder joints and how to avoid them might save you some time.

raydlettca (author)2014-09-22

Thanks for the instructions, there were one thing that was not mentioned in putting this together, the 2 pin jumper. I temporarily made one from wire. One problem that I am having is I am not getting temperature reads. The request log is running properly - there are request and responses - but nothing shows up in the graph to the left. Any thoughts?

lathyrus (author)raydlettca2014-09-23

Try to write the temperature as a server.log() statement in the Imp IDE first, to make sure the thermistor is working correctly, before you try to draw graphs in Xively. Uploading data to Xively is tricky - I eventually got something to work, but I don't really understand how it works!

lenny2020 (author)2014-05-14

I tried to build it--great, thorough, easily understandable instructions, but I am not getting any info posted to Xively. I copied the info for lines 7 and 8, and although the project seems to cycle as expected, the log states:

Posted to Xively: 120.21, got return code: 56, msg: <html>

<head><title>400 Bad Request</title></head>

body bg color="white">

<center><hi>400 Bad Request</hi></center>




I am getting no data posted, and the Request log sits empty with the "waiting for requests area" just cycling. Where have I screwed up? I could send you any additional information you request, if needed

tombrew (author)lenny20202014-05-14

Hi Lenny,

Sorry, that sounds frustrating! Let's look over your code on a direct message.