K-log: DIY Temperature logger with STM32F103, microSD card and DS18B20.

K-log temperature logger.

The idea of the device I'm about to share with you was born out of events completely irrelevant to microcontrollers or any technology whatsoever. This may lead you to believe that I'm only going to share an idea, but I'm a tinkerer, not motivational speaker - scroll down for schematics and sources!

So, here's how it went down: the group of biology students was researching galls. Gall, they explained to me in layman's terms, is an abnormal outgrowth on plant's leaves. They've been used for centuries to make ink and that's actually all they're known for. These students' research was aimed to find out if these galls really are so useless/malevolent in every case, or can even be beneficial (i.e. symbiotic) to their host.
Typical gall. Taken from Wikipedia.

To do that they needed to take periodic measurements of certain parameters in specific locations (e.g. humidity around the plant, temperature inside the plant etc..). They've figured it all out, I've been told, but logging the temperature inside the gall. The device must be autonomous and its sensor must be tiny to fit inside. The nearest candidate they found was Maxim's temperature logging iButtons:

IP67 autonomous temp. loggers. Can be bought in bulk for next to nothing and thrown around to know the temperature of EVERYTHING!

Now, they are small and autonomous, but they were looking for something with even smaller sensor, something like this:
Can be found  here, but for all intents and purposes it's unobtainium for mere students😃

As it always happens, one thing led to another and I agreed to make a temperature-logging autonomous device with a tiny sensor for them (deciding to make a tutorial on how to make a useful piece of hardware in the process).

So, here's the tutorial on building a ready-for-customer temperature logger from scratch with inexpensive and easily obtainable parts:

Part 1. Parts.

The most important part-picking decision concerned temperature sensor. It has to be small and easily procured, right? Two choices that distinctly fit the bill immediately came to mind: thermocouples and DS18B20. IC temperature sensor is a lot bulkier than a droplet of two metals fused together, still, I picked DS18B20 after briefly considering the interface circuitry required for both:

Interface between uC and temp. sensors: left - thermocouple, right - ds18b20. Roughly scaled against one another.

External resistor can be omitted when wire connecting the sensor is relatively short or environment is doesn't have much interference, in which case internal pullup esistor can be used instead. The rest was easy to decide, so here goes:
  1. DS18B20 temperature sensor. You can get one in metal package, or a bare IC. I used the latter, because I hope to come up with something smaller than this steel cylinder encasing it.
    DS18B20 in waterproof package

  2. Blue pill. Inexpensive and versatile board with STM32F103C8T6 as its core.

  3. STLink v2 programmer. You'll need it to upload your code to STM32 and debug it.

  4. microSD memory card + adapter for it. We'll convert microSD to SD adapter into microSD to microcontroller one. You can omit this part and just opt for an adapter board for microcontrollers (especially if you don't want to go further than a prototype) - less soldering, but maybe less fun.
    Adapter for microSD card.
    microSD adapter board for microcontrollers.

  5. Pushbutton, some jumper wires (these or these), one 4.7k resistor.

  6. For end product you'll additionally need:
  • Two toggle or rocker switches (both SPST - one to switch operating mode and another as a power switch)
    Left to right: power switch, mode switch.

  • Enclosure-mount connector for temperature sensor. I used this circular 4-pin connector (something similar online can be found here):
    Body marking says XS9K4P.

    This connector is pure awesomeness. It looks like the plug is locked into place after being screwed into receptacle, but it is actually self-locking. You have to pull the outer 'collar' part of a plug to unlock it, otherwise it won't budge.

  • Coin cell battery. How big depends on what kind of autonomous time you expect to get from this logger, I'm using CR2477 (25mm diameter, 7mm height, 1Ah on average) to get as much as years. The standard CR2032 (20mm diameter, 3mm height, 220mAh on average) won't last as long, but it's encountered more frequently in daily life.

  • Polarized 3-pin connector to configure temperature logger via UART. I'm going to be using old familiar Molex KK series pin header that everyone knows simply as 'motherboard fan connector':

  • Load switch IC with at least one channel. You're going to need it to switch off your memory card to conserve power. DS18B20 consumes so little current it can be switched from IO pin. Load switch is not a part that every tinkerer has in their part bin, but luckily I had one that I ordered a long time ago as a sample from Texas Instruments. Well, your time to shine's finally come, little buddy, after years of laying in a dusty box!
    TSP22960 (datasheet)

    You can order one from distributor like Mouser, Farnell, etc..
  • Miscellania like standoffs and tiny screws and 3d-printed enclosure. I'll just leave the picture of all the parts I had (excluding the lid of the enclosure with indicator LED and mode switch associated with it) prior to assembling first revision of this device.

Part 2. Prototyping.

First off, some document links that might help:

With a device as relatively complex as this one, you gotta prototype to get your feet wet with all this technology. Temperature logger will involve FAT filesystem access via SPI, temperature sensor access via OneWire, usage of real-time clock with battery backup. Got to admit, I never even as much as dabbled in any of those before, at least on STM microcontrollers.

Let's cobble up something that can read temperature and write it to memory card upon a click of a pushbutton - using only jumper wires! Why solder something as impermanent as this?
That's the prototype

Assemble it according to this schematic and connect STLink programmer (you can have a peek on this post for programmer connections):

By the way, have you noticed something peculiar in this jumble of wires and electronics? I'm talking about SD adapter turned microSD-microcontroller interface with just a 2.54mm socket header:

Got the idea for this gimmick from this blog post. It's not pretty, but still better than nothing in a pinch - certainly better than waiting for a slow boat to China to bring you an SD card adapter.

Thanks you, Duncan the owner of  Shepherding electrons, for this clever and unconventional idea.

What comes next is writing POC software for this thing. This proved to be unexpectedly challenging, in terms of firmware size and software.

When it comes to writing software for STM32, the choice of IDEs and APIs is abundant as ever, which IMO creates more confusion than conclusiveness. This reminds me of any shelf dedicated to one product in any supermarket around the world. Want to write your code somewhere and be able to debug it? We have CooCox CoIDE, IAR-EWARM, Keil MDK-ARM, Hitex HiTop5, ARM Development Studio and that's only the most popular ones. Now that you've picked an IDE, you'll additionally have to choose between libraries they have available to make your life even easier (ugh).
Arranged from least overhead to most, they are: CMSIS, StdPeriph and HAL. The difference is decently described on StackExchange. As usual, I chose something in-between to find a perfect balance between being buried under datasheets and reference manuals and making one line of code equivalent to 10kB in flash memory.

After long hours of staring at the shelf, you decide that you no longer need the shampoo. You'd rather get qualified mental health help.

To write, upload and debug my code I used OpenSTM32 Workbench. It's an Eclipse-based IDE for STM micros and it's cross-platform. You can refer to my previous tutorial to learn how to set it up in Linux to debug and upload in one click.

STM32F103C8T6 pack a hefty 64k of flash memory, and it looks like a whole lot of memory to someone with experience of programming 8bit microcontrollers in plain C or assembly code. In realm of 32bit and STM-provided libraries, merely including stdlib.h and time.h and using standard strftime function to pretty-print your datetime structure can eat up a whopping 10k of flash!

Final prototype firmware that just logs temperature to SD card every 2 seconds as soon as the button is pressed is sized about 57k  - and the final device will require much more code! Thankfully, later I found out how to overcome this problem, you'll find the solution below in Coding section.

The OpenSTM32 project for prototype can be downloaded here.

Upload it, wait for 2 quick blinks of onboard LED. Then, press the button on PB11, the LED will blink rapidly as soon as the press is 'seen' on microcontroller (yes, I f*kken love to blink LEDs). Release the button and temperature logging will start with the period of 2 seconds.

To end logging process, press the button again until you see rapid blinks. Actually, ending the logging process properly is really important - unplugging memory card or powering off your device in the middle of write operation may lead to complete obliteration of FAT filesystem.

Logged temperature is recorded in tab-separated files (.csv, though technically it's not a comma-separated file) and can be accessed after just plugging your memory card to a card reader on your PC:

Each log file is marked with creation date - it's year 1970 because I didn't set up RTC counter.

The reason I went with CSV files to store the logs is that you can import CSV files into any spreadsheet program - just make sure to pick Tab as the only record-separating symbol (actually I couldn't find this option in latest MS Excel, but I'm sure it's still there... somewhere...) Each log entry has a timestamp against it.

CSV file is easily imported into any spreadsheet software.

Part 3. K-log temperature logger. Final device.

There are several things to consider about our final device, and flash size is one of them.

All the code that you write is placed in flash memory which is 64kB on Blue pill, and our little prototype's code nearly depleted this limit. Keeping in mind that prototype lacks a couple of vital functions that I had in store for K-log, I naturally expected that I'll have to cut memory consumption wherever I can to stay within 64k.

While browsing the forums related to STM32 programming, I accidentally found a silver bullet for those hefty firmwares. Just adding a couple of linker flags to the project configuration cuts firmware size almost in half. To do that for your project, go to Project properties -> C/C++ build -> Setting, then pick [All configurations] in Configuration dropdown, then add these flags under MCU GCC Linker -> Micellaneous -> Linker flags:

--specs=nano.specs -specs=nosys.specs

It's weird that OpenSTM32 developers didn't include these flags to every project by default though...

Just look at the difference in sizes of the same project built with and without the flags:
No flags - 58k.
With flags - 36k.

Another thing is power saving. To understand the order of magnitude of power-saving works, I settled on a plan to measure the current consumption in the prototype first. You'll only have to do two things to get ready for that: desolder the power LED from Bluepill and improvise a current-measuring jig like this:

Prototype's code would hog up around 7.5mA during temperature reading and SD write operations, and a whopping 5.5mA when it's just waiting!

Current consumption while measuring and writing to SD.
Current consumption during waiting.

According to this battery life calculator that's only about 30 hours of uptime if it's powered from CR2032 (remember, one of the most important goals was to build a miniature device).

Average CR2032 capacity is 200-240mAh.

K-log will spend most of the time waiting to make a measurement (specifically my 'customers' wanted to measure temperature twice a day), hence the current consumption we want to minimize is idle time consumption.

STM32F10x value line controllers come equipped with 2 modes for moderate to extreme power saving - STOP and STANDBY mode. To read more about those, refer to STM32F10x reference manual here .

Let's now upload some code to see how much we can save during idle time:
  • STOP mode:
    milliamps here, barely legible
    That's nice! But let's see if we can do even better...
  • STANDBY mode:
    Awesome! More that two-hundredfold decrease in current consumption compared with no power saving! Incidentally, our microcontroller can wake up from this mode with RTC interrupt, which is exactly what we need.
Let's check our battery life calculator again:

A little over 8 month of work on CR2032 cell, provided it's not waking up too often.

Eight month is just great for just about any autonomous device! However, I wanted the user to NEVER ever face the need to remove the cover and change the battery, so I went for a bigger battery: CR2477(25mm diameter, 7mm height, 1Ah on average) compared to CR2032 (20mm diameter, 3mm height, 220mAh on average). That increased device's lifetime to 3.3 years approximately.


Having settled all those concerns, I set out to make an enclosure for K-log.

I could've made an ugly enclosure out of a soapbox like I always do, but this special device called for a neat packaging. Luckily for me, I have a friend that knows his way around 3d modeling and has a 3d printer installed at home, and also happened to have an interest in this temperature logger (those biology students actually spoke to him first, he then "delegated" this temperature logger to me).

Well, it was time to make it a joint project with my new accomplice!

Screenshots of first revision's STL files are given below:

Very much work in progress - the battery holder just holds the battery, the wires have to be connected with tape or something like that.

The lid holds a slider switch that switches between measurement and configuration modes, as well as an indicator LED.

Some pics of revision 1 enclosure fleshed out:

A Bluepill board for scale.
Elements worth noting in this model include threaded inserts that hold the top and snap-fit connector that holds Blue pill.
M4 10mm. threaded inserts is what I had handy. You should get M3 inserts here - 100pcs bag is little over 5 USD.

I'll show how to hot-plug those inserts later, meanwhile, have the links to STL files of K-log enclosure top and bottom parts (rev1).


 You'll have to take care of one more thing before diving into assembling this. SD card module is designed to be used with 5V-powered boards, hence the 3.3V regulator and a buffer chip on it. One has to desolder the regulator and short out Vin and Vout pads like this to make it work on 3V powered device:

The assembly process can commence now - according to the schematics:

As you can see, UART is broken out on J4(5) molex connector.  It will be used to set date and time on logger, as well as time between temperature measurements. For personal convenience I put together an adapter to connect this directly to a standard FT232RL adapter (or cheaper CH340) (it corresponds to wires between J4 and J6 on schematic drawing):

To be frank, I didn't assemble the logger outright. Knowing the fickle beasts those microcontrollers are, I was prepared to solder all the stuff together just to learn that this particular peripheral can't be connected to this particulal pin(s) due to the lack of a certain capacity (i.e. pin can't be turned off during sleep mode, or can't perform as an analog input or PWM input).

As a consequence, I assembled a sort of preliminary device without any care to soldering technique to test some scenarios from temperature logger's planned daily work:

Load switch was too tiny so I incorporated it onto an adapter board to use it

Some modifications had to be made: I learned that you needed pulldown resistors for load switch inputs because they start floating during deep sleep mode, 100 Ohm resistor with red LED pulled too much current and I had to halve the current with 220 Ohm one.

About 20mA to log temperature, and additional 40mA for a mere LED...

Well, now I was ready to make K-log come true. Some WIP pics following below:
Snug fit for mode switch.
Bottom view, hole on the right-hand side is for LED

A closeup of UART connector. Only 3 standoffs are used to minimize the amount of space occupied:

Everything soldered together. The only thing left to to is cram all this in so little room:

Every part that goes inside is there now. It's probably a good idea to test everything again at this point, this jumble of wires may contain a couple of shorts:

Outside view:

Time to program your device (scroll down for Coding section) and screw on the lid. You're going to need a heat source for that. Plugging threaded inserts sounds easy enough: heat it up and plug it in. The trick is to avoid overheating:

About 30 seconds on open flame is enough.
Red-hot slug, on the other hand, will melt the crap out of your enclosure. Don't do that.

Plug it carefully, making sure it's strictly upright at all times.

Cut away the plastic residue on the top and it's ready!

Now your very own K-log is ready:

One more thing that I only noticed when I had to use my logger: it's totally impossible to understand what mode you're in and how to switch to another mode. Black felt pen quickly resolved my doubts:


Some useful lessons were learned during development of firmware for STM32F103 microcontroller.
  • For instance, I faced the following problem: when using FatFS to interface a memory card, it would work until power to SD card was cycled (i.e. switched off during idle time and on again during measurement and writing). After that any attempt to access filesystem resulted in FR_NO_FILESYSTEM error code. This software problem, as it later turned out, had a simple solution I haven't found anywhere online:

    The thing is, when controller wakes up from sleep mode, the variables are retained and hardware SPI is considered initialized when, in fact, it has to be reinitialized after power cycle. Commenting out the code highlighted in screenshot above does the trick. Note that this doesn't apply to STANDBY mode as uC resets upon wakeup.
  • The fact well known to every embedded developer is CPU frequency is proportional to current draw. Core frequency for StdPeriph is defined in system_stm32f10x.c. It's 72MHz by default, but for my low-power application I defined it to HSE frequency (on-board 8MHz crystal):

    This considerably reduced power consumption. Just look at the current draw of the same code (configuration mode) clocked at 8 and 72MHz:


Let's not forget about the software that will upload settings to K-log via serial port. I chose to write it in Java, simply because it's the only language that I'm any good at☺.

UI is implemented with JavaFX - quite a sleek look!

Without further ado I'll just share the Java source code (IntelliJ IDEA project, although considering it uses maven it can be assembled with mvn clean install command or packaged into a single executable JAR with mvn clean compile assembly:single), alongside with full source code for Bluepill.

Final thoughts

One thing I haven't managed to do with STM32 is to reduce current consumption to datasheet-indicated levels. K-log currently consumes 6mA in config. mode and 30mA while logging, which is fine, but what really ticks me off is 25 microamps consumed in STANDBY mode. It should be about 2-5 microamps and I would be grateful for any advice on how to reduce this tenfold engorgement on my coin cell.

I'd like to encourage my readers to replicate this device and share the pictures and ideas☺. I'd love to see how yours turned out.