Simple and cheap temperature logger: v2.1 [UPDATE: EEPROM in BOM corrected (SST25WF080) ]

Hi everyone!

It’s been too long since the last update. But it’s there! The last version of the temperature logger, the hardware and the source code.



The changes since the last version:


  • New footprint for the PIC18F26J50, easier to solder.
  • Only 0603 or bigger resistors/capacitors.
  • The serial EEPROM is now a SST25WF080, still 8Mb, but easier to source.
  • Some components price-optimized (USB connector, switch, 3.3V reg).


  • Each logger can get a number, shown in the mass storage device drive name: simply add an asterisk (*) followed by the number you want to assign to the logger (between 0 and 65535) in the config.txt file (after the logging period), save the file and format the logger.
  • Bug fixes in the FAT12 functions, but the logging space is still limited to the half of the EEPROM.
  • Other bug fixes (month and year change, added robustness). The memory and the EEPROM are now scanned when you plug the logger back, to get the last data and reconstruct the filesystem in the case of a battery failure.

Temperature Logger 2.1: Schematics and basic BOM

The Altium files

The source code (based on Microchip Applications libraries -Device – Mass Storage – SD Card data logger- MPLABX)

I also made a program/debug adapter (especially useful for debugging):


USB-Temperature-Logger-Programming-tool1This is the “office” version of my previous peg-adapter 😉

A lot of people were asking me if I was selling these loggers. Unfortunately, no. The design and functionalities are sexy, but I don’t have the capacity to launch a production on my own. And if I had do make only a small batch, the price would be totally uncompetitive.

A big thanks for all the people who donated a little something to motivate me!! (or to help me be more ashamed of the lack of updates)

All the contents (except the parts of Microchip’s code in the source) are under the Creative commons license, Attribution – Share Alike – Non Commercial.

Creative Commons License
Simple temperature logger: v2.1 by Jean Wlodarski is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.

Simple and cheap temperature logger: New update coming

I’m working on a new revision of the temperature logger: new PCB and new firmware. It’s easier to solder, as the resistors/caps are now 0603 instead of 0402 and the PIC package is now a SSOP28 (instead of QFN).
The sleep current is smaller than 10uA and below 1mA when measuring the temperature (plus a few mA every 20-or-so logs, when writing to EEPROM).

The EEPROM is a SST25WF080 (still 1,8V 8Mb), as the previous Atmel memory is not available any more.

Some 3D renderings of the PCB:

Simple and cheap temperature logger 2.1

The USB connector is a Multicomp MC32604, cheaper than the previous MCKUSBX-SMT2AP1S-W30. (I couldn’t find a suitable 3D model for the PCB rendering)

The ISCP connector for programming the firmware  is SMT but fits the Microchip’s ICD header:

The new firmware has functions for the new EEPROM, a low battery indication, more robust logging and USB update capabilities. Plus a lot of bugfixes and optimizations.

Now, to complete the firmware, I have to build at least one logger, which means ordering the PCB and the components. That’s why I added a “donate” button to this blog (on the top right side of the page, just under the title), especially I don’t earn any money with this blog and content is free (and will stay free, of course).

If my work helped you, if you find it interesting, if you want more articles and more projects, dropping a few coins will motivate me and help me to materialize all the projects waiting in my head. 🙂

Stay tuned for more!


Designing a simple and cheap temperature logger. Part 7: Schematics, PCB and source code.

I decided to publish all this project under Creative Commons license (Attribution, Derivatives, Non commercial, ShareAlike). The source code contains a FAT12 filesystem that can be reused to make custom flash drives for other projects.

USB Temperature Logger

(PCB version 1.2, hence the misaligned 3.3V regulator to include a diode. Corrected in the V2.0)

USB Temperature Logger 2

USB Temp Logger Schematics

You’ll find the schematics here: Simple USB temperature logger schematics

The Altium files (including a routed PCB) there: Simple USB temperature logger Altium files

The source code (based on Microchip Applications libraries -Device – Mass Storage – SD Card data logger- MPLABX): Simple USB temperature logger source code

The source code could be improved to increase the robustness of the logger (especially in the case of battery power failure while on logging mode). The PCB allows the monitoring of the battery voltage, it just needs to be implemented in the firmware.

I hope my work will be useful to someone!

Don’t hesitate to contact me (mail in the About section) for questions or comments!

Designing a simple and cheap temperature logger. Part 6: Video!!

I just finished developing my USB temperature logger and shot a video showing how it works. Here are the key figures:

  • Up to two months of logging with a single coin cell battery.
  • 30 000 timestamped temperature measurements.
  • Logging period from every 5 seconds to every 24 hours.
  • 0.5°C accuracy, 0.06°C resolution.
  • No driver, no software and no admin rights needed!
  • Compatible with any computer with USB port.
  • Automatic time synchronization with the computer.
  • Low cost design.

When plugged to a computer’s USB port, the logger appears as a normal USB drive. The temperature logging period is changed by editing a config.txt file. When this file is saved, the logger reads the periodicity (in seconds), synchronizes its clock to the computer’s date and time and starts to flash the LED to indicate it’s ready to log. Once removed from USB, the logging is started by pushing the button (the LED blinks three times). For every temperature measurement, the LED flashes.

Once plugged back to USB, the file DATA.CSV will contain all the temperature measurements and the corresponding date and time, TAB separated.

A future development could include a Li-Ion USB rechargeable battery, battery life monitoring, low power – long period logging mode.

If you’re interested by this project, don’t hesitate to contact me. Share if you like it!

Measuring small currents in battery-powered systems

This is a simple method for measuring small currents (μA/nA).

I wanted to see how much current my temperature logger is consuming while in various sleep modes for the various components on the board. I have a good multimeter but unfortunately, its smallest DC current range is 400mA. Even if the burden voltage isn’t that big, it’s impossible to measure anything below 10μA.

As I’m not lucky enough to own one of Dave Jones’ μCurrent I tried to find an other method. I inspired myself of a Microchip App note (AN1416).

The idea is to power a circuit with a charged capacitor and measure the discharge time, thus the current:

I = C*(Vd/t)

Where I is the current(A), C the capacity(F), Vd the voltage drop(V) and t, the time (s).

The method can be easily set up on a breadboard and it’s ideal for measuring small currents that don’t change over time (typically, microcontroller’s sleep modes):


Measuring small currentsIt’s better to use a nomally-opened switch, so a push on it will disconnect the power supply and allow the capacitor to discharge.


Measuring small currents breadboardIn my setup, I’m using a LM317 voltage regulator to have 3,00V (coin cell battery voltage) at the Vd point when my microcontroller is connected and the switch closed.

At the same time I open the circuit with the switch, I start a timer and usually stop it when the capacitor voltage dropped 0,2V  (2,8V at the capacitor)

For example, with a capacitor of 6600uF (measured), a Vd of 0,2 Volts and 50 seconds to reach it:

I=6600*(0,2/50) = 26,4μA

The value of the capacitor can be adjusted, so you don’t need to wait too long when dealing with nA currents. ex:

Vd=0,1V ; C=10uF ; t=30s : I=33nA

And so on..

To make sure the capacitor internal leakage is not affecting the measure too much, repeat it with no load connected to see how fast the current is dropping. It also allows you the see if the voltmeter impedance isn’t too high.

As soon as I get my temperature logger new PCB, I’ll measure the current in sleep / deep sleep mode for the PIC18F26J50, the EEPROM, the temperature sensor and I’ll post them here.

Designing a simple and cheap temperature logger. Part 5: New prototype PCB.

It has options to be powered by a Li-Ion battery (rechargeable via the USB port, with a MCP73831), as well as by a CR2032 coin cell. There’s an other option for the 1,8V EEPROM SDO line pull-up voltage adaptation.

All the options are selectable by fitting or not 0 Ohm resistors and adding the dedicated components.

In the Li-Ion version, it’s supposed to fit into a case like that (but with a hole for a button and a LED). It’s 19mm x 40mm.

The blue connector is the ICSP program port. I designed it to be “off-snappable”, as it’s connected only by three bits of PCB, where the signals pass.

USB Temperature Logger - Prototype 2 - Face


USB Temperature Logger - Prototype 2 - Back

NB: Some of my 3D models aren’t exact (like the battery socket, the 3D model is SMT instead of the through hole I have)


Here, an other PCB, Li-Ion only. Just for fun, I wanted to see how small the PCB (19mm x 38mm) can be routed with the components on the same face. On the bottom, there’s only a contact-style connector for ICSP:

Mini USB Temperature Logger - Prototype 3 - Face


Mini USB Temperature Logger - Prototype 3 - Back

The components names couldn’t fit on the top side, so I put them on the bottom.

The PCB could maybe get even smaller with a smaller 3,3V regulator, in a SOT23 package instead of the SOT223. The 32,768KHz crystal could also use a smaller package. But here, I hit the limits of the 2 layers, 6mil/6mil routing and I’m not sure some tracks won’t interfere each other.

The prototype on the first two images is the one I’ll be using to continue the software development.

Designing a simple and cheap temperature logger. Part 4: The junk in the trunk.

I somehow managed to solve the problem of the serial flash memory write block size vs. the erase block size. I just had to write a custom FAT12 filesystem..

As seen before, the Temperature Logger will act like an USB memory stick. There will be at least two files. One with the time stamped temperature measurements and one file allowing the user to set the logger’s configuration.

The configuration file must be accessible by any OS and modifiable. It will configure the temperature measurement period, from one every second till one every 24h (86400 seconds), in seconds. It’s also used to synchronize the PIC’s real time clock and calendar to the computer’s current time and date.

So far, it looks like that:

Setting the temperature logging period, in seconds
Don't forget to add a "#" before the number:

Pretty simple. When it’s saved, the PIC will read the number following the “#” and the time and date of the file modification.

The FAT filesystem is divided into five sections, each of them divided into sectors:

  • The Master Boot Record
  • The Boot Record
  • The FAT (doubled)
  • The Directory
  • The Data

When a file is written, its content will be placed in the Data section. The name and other information will be in the Directory. The FAT will then describe where the different data pieces of the file are located on the disk.

I took the standard sector size of 512 bytes (the size of the PIC’s buffer and the double of the EEPROM’s write block).

The MBR, the Boot Sector and the Directory are 512B from the OS point of view and 4KB on the EEPROM. I need five FAT pages to map the capacity of my EEPROM. Here’s a map of my filesystem:

FAT12 MicrochipAll the sectors occupied by the filesystem sections are 4K wide.

(The help.txt file contains the basic explanations on how to start/stop temperature acquisition and how to erase the data.csv file)

I modified the Microchip’s SD-SPI.c file to be able to talk to my memory, instead of a SD card. The main functions are:

BYTE MDD_SDSPI_SectorRead(DWORD sector_addr, BYTE* buffer)
BYTE MDD_SDSPI_SectorWrite(DWORD sector_addr, BYTE* buffer, BYTE allowWriteToZero)

They both take the address of the EEPROM sector to be written to, in LBA format (ie. the sector numbers) and translate them to the real EEPROM address. The buffer is the 512B PIC’s memory zone to write to the EEPROM or to fill with the EEPROM data.

An if statement chooses the address shift. Everything that’s below the DATA.CSV file will be 12-bits shifted ($02 becomes $2000) and the 4KB zone will be erased before any write occurs. If the write or read is in the zone where the temperature measurements are, the shift will be relative to the DATA.CSV address ($2E000), and looks like:

Address = Address - $2E
Address = Address << 9
Address = Address + $2E000

Which means, for example, that if we read or write the third sector of DATA.CSV, the function will be called with $30 and will result in the EEPROM address of $2E400, which is the third sector of the EEPROM after $2E000.

Of course, any write to this zone by the OS has to be forbidden.

For this, the Microchip USB Mass Storage Device driver uses the same functions as above, but in the usb_function_msd.c file. It uses an other buffer, msd_buffer, 512B too.

Every time the computer OS wants to read or write into the EEPROM, the data is cached in this buffer. Two functions are doing that:

BYTE MSDReadHandler(void)

BYTE MSDWriteHandler(void)

In each of these functions, I inserted a test to know what and where the OS wants to read or write.

In the case of a read, I’m not doing a lot for now (the MDD_SDSPI_SectorRead function already handles it).

For the writes, I first test if the OS is writing the data of config.txt file. If yes, I search for the period string and write the data to the memory. If the OS writes the File Dierctory, I read out the new date of the config.txt file but I write the original contents of the File Directory in the EEPROM instead of the new ones.

For any other write from the OS, I just answer the write was OK, but don’t write the actual data to the EEPROM.

This way, I’m sure the OS won’t fill the memory with anything else but the right data. It’s especially true for OSX, who wants to write Spotlight, Trash and info files in any USB stick it finds.

Here’s a simplified diagram of the modified part of SectorWrite function:

  • if msd_buffer contains the config.txt start and end string -> Extract the period, write the buffer to the config.txt file address.
  • if msd_buffer contains the File Directory -> Extract the time and date from config.txt, if it’s newer than the original date. Erase the msd_buffer and fill it with the original contents, minus the date and time fields. Write the buffer to the File Directory address.
  • in any other case, perform a dummy write.

That way, we save a lot of space in the EEPROM. If it would have been divided into 4KB sectors, with only the first 512B usable, we would have ended with a ≈100KB disk ((($FFFFF-$2E000)/$1000)*$200). Now, even if some space is wasted for the filesystem, we have a ≈840KB drive ($FFFFF-$2E000). It means something like 30 000 timestamped temperature measurements.

Some precisions about the FAT:

The FAT is used to keep track of every file on the disk. Each of the FAT entries matches a sector on the disk (that’s why my FAT is 5 sectors long, we need 5*(512/1,5) bytes to map our 1648 sectors). Here’s my FAT12 capture, as it comes after initialization:

FAT - FAT12 - Temperature Logger - FormatedThe first two entries are sector 0 and sector 1. They’re reserved. That’s why the data zone is located starting $2C000 (each sector is $1000 bytes before the DATA.CSV file and the FAT ends at $0C000).

The three next entries are $FFF,$FFF and $FFF which means that the sector contains one complete file (config.txt, help.txt and data.csv). (It’s easy to understand why small files are wasting space: Every file has to occupy at least one sector, even if it’s smaller than the size of the sector)

When a file is spread on more than one sector, its FAT entry will have the next sector address.

Unfortunately, the entries are coded in 12 bits values, and in big-endian format. If we want to write xyz for the sector 4 and XYZ for the sector 5, we have to write:

Yes, could be easier (it actually is, with FAT16 and FAT32, but our memory is too small to use those formats)

Here’s an example with the data.csv file spreading on 15 sectors (512*15 Bytes wide):

FAT12 - 19 sectors

If we decode the 12-bit addresses, it gives:

FAT12 - Decoded

There is one file in sector 2. One in sector 3. The third file starts on sector 4 and goes till sector 18.

I made a function to add entries every time a buffer full of temperature logs is written into the EEPROM. The 12-bit address management looks like that:

if(FatSector & 1) //Odd Sector Number
 FATEntryPtr = FatSector-1;
 FATEntryPtr = FATEntryPtr + (FATEntryPtr>>1); // *1,5
 FatDWordA.w[0] = ( (FatSector+1) & 0xFFF);
 FatDWordA.w[1] = ( (FatSector) & 0xFFF);
 //We have:  0x yz 0X YZ and we want:
 //          00 yz Zx XY
 FatDWordB.v[0] = ((FatDWordA.v[1]&0x0F)<<4) | ((FatDWordA.v[0]&0xF0)>>4);
 FatDWordB.v[1] = ((FatDWordA.v[3]&0x0F)) | ((FatDWordA.v[0]&0x0F)<<4);
 FatDWordB.v[2] = FatDWordA.v[2];
 //The FatDWordB Contains what we want to write to the FAT, for the current //Sector and the next one.
 msd_buffer[FATEntryPtr] = FatDWordB.v[2];
 msd_buffer[FATEntryPtr+1] = FatDWordB.v[1];
 msd_buffer[FATEntryPtr+2] = FatDWordB.v[0];
 msd_buffer[FATEntryPtr+3] = 0xFF; //And write the end of the file for the //next sector entry
 msd_buffer[FATEntryPtr+4] = 0x0F;
else //Even Number
 FATEntryPtr = FatSector;
 FATEntryPtr = FATEntryPtr + (FATEntryPtr>>1); // *1,5
 //We want to write the next sector address in the current one, plus the //end of the file on the next sector
 FatDWordA.w[0] = 0x0FFF;    //End of the file for the next sector
 FatDWordA.w[1] = ( (FatSector+1) & 0xFFF); //In the current sector: the next //sector address.
 //We have:  0x yz 0X YZ and we want:
 //          00 yz Zx XY
 FatDWordB.v[0] = ((FatDWordA.v[1]&0x0F)<<4) | ((FatDWordA.v[0]&0xF0)>>4);
 FatDWordB.v[1] = ((FatDWordA.v[3]&0x0F)) | ((FatDWordA.v[0]&0x0F)<<4);
 FatDWordB.v[2] = FatDWordA.v[2];
 //The FatDWordB Contains what we want to write to the FAT, for the current //Sector and the next one.
 msd_buffer[FATEntryPtr] = FatDWordB.v[2];
 msd_buffer[FATEntryPtr+1] = FatDWordB.v[1];
 msd_buffer[FATEntryPtr+2] = FatDWordB.v[0];

I’m using the msd_buffer to save the FAT during temperature acquisition (and the gDataBuffer to save the temperature logs) between two writes to the EEPROM, when on battery power.

Every time a gDataBuffer is full, it’s written to the EEPROM in the data zone and a new FAT entry is added to the msd_buffer. Once this one is full, it’s written to the EEPROM at the FAT address.

The variable FatSector contains the current sector in the FAT. FatDWordA and FatDWordB are temporary Double-Word variables used to compute the three-bytes-12bits FAT entries.

This is MCC18 PIC18 code, based on Microchip USB and MSD libraries. I wanted to share this snippet, as I couldn’t find any good PIC code example on the internet. I’m sure it could be optimized and simplified, but like that, it’s maybe easier to understand how it works.

I think it’s all for now. My next step will be to consolidate my program and code the power-managed temperature acquisition. I already have the Temp. sensor, EEPROM, USB and RTCC working.

I also have to make a new PCB for the logger, something more USB-key shaped, with options for a rechargable battery. It should be my next blog post.

Stay tuned and as always, don’t hesitate to report any error.