CheapWeather: Sensor node design

The design

The ESP8266 changed the game for making this project happen.  Even more, the Arduino support for it had made building it easier through the standard libraries that I already knew how to use.  Let’s go back to the list I had initially made of what I wanted sensors to tell me:the

  • When my furnace or AC was on
  • What the temperature at the thermostat was when it went on
  • What the temperature in each room was, throughout the day
  • What the temperature outside was, to know the delta between inside and outside temps.
  • A temp sensor for every room, or at least the rooms that count
  • A humidity sensor, only really need one of these within the house
  • An outside station that captures
    • light, to know when the clouds have overcast
    • temp
    • humidity
    • barometric pressure (maybe?  nice to have?)
  • The battery level at each station.

While I’m at it, I’ll throw in some nice-to-have features:

  • solar power for the outside node
  • multi sensor per node so that I can have one sensor out in the garage that senses ambient temperature but also freezer internal temps so I can know if my freezer is dying.  Also multi sensor so I don’t have to have two nodes in one area.

The long laundry list of features you want it to have is how most projects start.  I’ve learned that you have sort that list and come up with the “minimum viable product”.  This means that you prioritize the features you really need at the cost of putting off the others until later. This isn’t all bad.  At work we use Agile as our way of organizing our priorities.  When we have good ideas, they go into our backlog.  We don’t take on good ideas right away, we stick to what we’ve said we’d accomplish in our current sprint (a sprint is a “work unit” of time, so for us, it’s every 2 weeks).  One good side-effect of it is that sometimes good ideas are not necessary.  We find that over time when we’ve solved the necessary issues, sometimes those good ideas that got stuck in the backlog have become irrelevant in light of the new knowledge about the problems we’ve been solving.  Sometimes those good ideas ripen with age, and you add more information to them as you think about them while you work on other items.  That way, when you finally get around to implementing that good idea, if it’s still useful to have, it’s matured and is ready to be put into action.

So looking at the laundry list of items, the minimum viable product that should come first would need to have at least these features:

  • What the temperature at the thermostat was when it went on all the time (if a regular node was put on top of the thermostat)
  • What the temperature outside was, to know the delta between inside and outside temps.
  • A temp sensor for every room, or at least the rooms that count that measured throughout the day
  • An outside station that captures
    • temp
    • light (for knowing if the sun is shining or not)
  • The battery level at each station.

This should satisfy the immediate problem of knowing what temperatures my rooms are at throughout the day.  The battery level should help me keep an eye on when the batteries die so that I can change them quickly and keep getting data.  A side effect of having the battery knowledge will also be getting data on how much life one sensor can get out of a pair of AA cells.  Should I keep this system going long term, I will need to write a script that pulls from the database and notify me if any of the nodes stop reporting.  But that’s another nice to have feature that can wait.

My initial design also included the need to configure each without having to reprogram it.  This meant some kind of system of being able to change it’s config through the serial port.  For a while I’ve been doing shell-like interfaces to all of my projects, where you can connect up using a serial cable, and talk to the system.  This enables me to dump registers through functions I’ve coded up, or do any number of things needed to either be useful, or debug.  Mostly these are primitive serial based shells.  And I do mean primitive.  Hardly any processing of the command line, very little flexibility to be used between projects.  I decided to factor out the shell functionality and start building it into a library that I can use across projects without having to munge it for every one.  I was inspired by some of the structure of ChibiOS’s shell, in that it used function pointers to tie a string for the command to the function responsible for it.  Similarly, in my SimpleShell library, I require a table of strings and function pointers to be handed in.  This table allows you to create commands that you can then call from the serial shell.  SimpleShell is as platform independent as I could get, and will not read or write to any input/output streams directly, but instead will only take a string (like the unprocessed command line slurped in via a Serial.readUntilByte() call) and execute a command, or return NOTFOUND.  The command functions return a ExecStatus type, and it’s up to the person using the library to make something of the return codes.

I was contemplating how to put the node into a “command mode”.  I needed to figure this out because the ESP8266 wakes up from a sleep mode via reset and not where it left off, so i was not guaranteed (at least that I could find) that any characters that I typed on the serial terminal while it was asleep would trigger the Serial object to show that it had data in it’s FIFOs when it woke.  I decided that I’d use a weak pull up on one of the pins and read it on startup.  If the pin was pulled low, then I would go into a “Command Path”, which would put the sensor into a loop that read the serial object and process it through the SimpleShell object.  The other code path, the “Sensor Path” would merely start up, read the sensors, send the data and go back to sleep.

Of course, a command mode needs commands.  The sensor node is coded with commands that fit into two categories: config commands, and interactive commands.  The config commands do what you’d expect, they set the persistent EEPROM data that the system uses when it powers up and connects to send it’s data.  The interactive commands allow a user to test the node.  Things like sleep, sending data, getting sensor data are all commands that invoke the code paths that would be invoked by the sensor path.   The interactive command set also included things that would dump registers, or show config data.  This is how SimpleShell can help debug problems in a system where there isn’t a in-system-debugger readily available.

As for the dev process, I had a solderless breadboard setup that allowed me to plugin modules and program them pretty quickly.  If I do more with these modules, I’ll most likely create a protoboard version of this circuit, so that I will not wear out the area on my solderless breadboards.

20161225_093643I like this particular version of protoboard from RadioShack.  I haven’t found any that are close.  I’ve always thought that I’d have to scrape off the lacquer layer that is on top of the copper, but it turns out it’s not lacquer, it’s a type of flux.  It keeps the copper from oxidizing while not in use, and you can solder straight through it no-problemo.  These boards are definitely nice for prototyping.  I was able to get two sensors out of each one, with a bit left over.

The temp sensor is an Atmel AT30TS74 which is an LM75 clone that is a broad voltage range part (1.8v to 5.5v).  I don’t know why I hadn’t initially thought about it, but with two AA batteries I wasn’t going to be operating at 3v all of the time.  I would need to go down to the 1.8V that they say the ESP8266 can go to.  This sensor can do that. In my experience now that I’ve had enough time for most of my modules to expire a set of batteries, they tend to last down to 2.3v before giving up completely.  With a sleep interval of 5 minutes, it took about three months for my modules to get to 2.3v.

The assembled module was really simple.  Just a bit of wire, the temp sensor, the ESP module and the battery holder.  20160612_001023 20160612_164839

All of this amounts to a sensor node that can be configured easily, report temperatures, and sleep until the next time interval.  The system sleeping every 5 mintues means I can get about 3 months on a pair of AA batteries.  So far, it’s been a success.


Ah, EEPROM.  What a pain in the neck.  EEPROM is normally easy in the AVR Arduino environment but doesn’t work as advertised in the ESP8266 environment.  I found that in the ESP8266 Arduino environment (and maybe in the SDK as well) writing to any other location than the first location in EEPROM would not save the data.  It took me coding an EEPROM shadow object to save data in locations other than 0x00.  Normally this is a bad idea because you have to use up the same amount of RAM for the shadow copy of data as the EEPROM size.  This means that for my 1kB sized EEPROM, I’m eating into my runtime RAM space by the same amount, solely for the purpose to keep config data around.  This isn’t too much of a problem on the ESP8266 though, because it has about 80kB of RAM, with about 40kB usable by user programs.  Most of what I’m doing here won’t go over the 40kB limit.  In fact, most projects I can see applying this microcontroller to won’t go over that limit.  Since the EEPROM is really an emulation of EEPROM and stores all of your data in the flash memory chip, if memory does get tight the library for the ESP8266 allows you to use just what you need.  Only need 32 bytes?  The shadow will only take 32 bytes in RAM.  Any time you set data on part of the shadow, it writes out the entire shadow area to the EEPROM.  While it’s ugly, and I hate writing out the whole shadow each time, I’ve found that it works and I’m not writing config data too often.

[UPDATE 03/01/17: the problem mentioned in the next paragraph about programming these was largely to do with the breadboard circuit I had been using.  After creating a more permanent programming solution, all of the suspect modules programmed and booted fine.  The comment about the voltage regulators and the bum LM75s still stands… ]

Speaking of flash, that reminds me about programming them.  Ugh.  Flash.  Re-flash.  Flash again.  Register dump.  Reset.  Re-flash.  Sometimes it takes, sometimes it doesn’t.  Sometimes it takes after four times.  Sometimes never.  Sometimes, it doesn’t boot, it just keeps register dumping to the screen.  Beware of getting parts off eBay cheap.  My friend Dan always chuckles and shakes his head at me when I mention any of these troubles.  “When will you ever learn?  Stop wasting your time and spend a little extra for known working parts…”.  They’re cheap, so if you’re going to get cheap, you’re going to have to deal with the potential 50% garbage parts that you get.  Marginal, or swept off the floor.  Or marginal in construction.  Any way you cut it, there’s a percentage of your cheap parts that will be trash.  About 25% of my modules have issues.  About 100% of a set of LM75 sensors don’t work, period.  They tie the clock line low, and sayonara, no talkey.  A few of a set of 3.3v regulators are trash.  You can input voltage all day long and they don’t output 3.3v, they regulate it to 0v.

If you’re using a serial cable without the RTS line being tied to reset, this means you have to hold the part in reset until the proper time and then release it so the bootloader comes alive just when the esptool utility is needing it to.

The SDK isn’t as straightforward as I’d like.  I had accidentally moved a line of code for turning the ADC pin toward the VCC rail.  This made it stop working, but it wasn’t as obvious why.  Eventually I figured it out, and it was back to reading the battery voltage.

Also, this is one of my first major lead-free ventures.  Beware of your solder joints.  While you can’t easily tell anymore if they’re cold joints or not, you can with an ohm meter.  I don’t know if the pins weren’t taking to the solder, or what, but the joint i had on the VCC was 16kohms.  No wonder it was rebooting, it wasn’t getting the current it needed and would brown out, which meant once it hit a certain voltage on the battery, it would reset loop until it burnt out a set.

Next up, the design of the outside sensor node.

You can find the code for this at GitHub: