Introduction: Thermochromic Temperature & Humidity Display - PCB Version

About: I am a physicist, part time maker and electronics enthusiast. My projects revolve mainly around daily-use items, toys and decoration with a focus on unconventional mechanisms and high standard of design.

A while ago a did a project called Thermochromic Temperature & Humidity Display where I built a 7-segment display out of copper plates that were heated/cooled by peltier elements. The copper plates were covered with a thermochromic foil that changes color with temperature. This project is a smaller version of the display which instead of peltiers uses a PCB with heating traces as suggested by user DmitriyU2 in the comments section. Using a PCB heater allows for a much simpler and more compact design. The heating is also more efficient which leads to a faster color change.

Watch the video to see how the display works.

Since I had a few PCBs left I am also selling this display on my Tindie store.


  • Heater PCB (see my GitHub for Gerber files)
  • Control PCB (see my GitHub for Gerber files and BoM)
  • DHT22 sensor (e.g.
  • 3D printed stand (see my GitHub for stl file)
  • Thermochromic Adhesive Sheet, 150x150 mm, 30-35°C (SFXC)
  • M2x6 bolt + nut
  • 2x pin header 1x9, 2.54 mm (e.g.
  • 2x SMD board connector 1x9, 2.54 mm (e.g.

Step 1: Designing the Heater PCB

The heater PCB was designed in Eagle. The PCB dimensions are 100x150 mm because 150x150 mm is the standard size of the thermochromic sheets I used. At first I made a sketch of the segments in Fusion360 which was saved as dxf and then imported into Eagle. The segments have milled gaps in between them and are only connected by small bridges. This improves the thermal insulation of the individual segments and therefore allows faster heating and reduces 'thermal crosstalk'. The segments were filled with PCB traces on the top layer (seen in red) using the meander tool in Eagle. I used a track width and spacing of 6 mil which is the minimum size that can be manufactured by PCBWay without extra costs. Each trace is meandered between two vias which are then connected to the pins via the bottom layer (seen in blue) using much thicker 32 mil traces. All segments share a common ground.

I did not do any calculations for the heating power required for a certain temperature rise nor did I calculate the expected resistance of a segment. I figured that any adjustment of the heating power can be made by using a PWM signal with varying duty cycle. I later found that the segments heat reasonably fast when powered through the 5V USB port using a ~5% duty cycle. The total current when heating all 17 segments is about 1.6 A.

All board files can be found on my GitHub.

Step 2: Designing the Controller PCB

To control the PCB heater I choose an SAMD21E18 MCU which I also used in my GlassCube project. This microcontroller has enough pins to control all 17 heater segments and read out the DHT22 sensor. It also has native USB and can be flashed with Adafruit's CircuitPython bootloader. A micro USB connector was used as power supply and for programming the MCU. The heater segments are controlled by 9 dual channel MOSFETs (SP8K24FRATB). These can handle up to 6 A and have a gate threshold voltage <2.5 V so they can be switched by the 3.3 V logic signal from the MCU. I found this thread very helpful to help me design the heater control circuit.

I ordered the PCBs from PCBWay and the electronic parts separately from Mouser and assembled the PCBs myself to save costs. I used a solder paste dispenser placed the parts by hand and soldered them with an infrared IC heater. However, due to the relatively large amount of components involved and the required rework this was quite tedious and I am considerng using an assembly service in the future.

Again the board files can be found on my GitHub. There you can find an improved version of the PCB which uses an USB-C connector instead of micro USB. I also corrected the spacing of the thru holes for the DHT22 sensor and added a 10-pin connector for easier flashing of the bootloader via J-Link.

Step 3: CircuitPython Bootloader

At first, I flashed the SAMD21 with a UF2 bootloader based on Adafruit's Trinket M0. The bootloader had to be slightly modified because the Trinket has an LED connected to one of the pins that I use for heating. Otherwise this pin will go high for a short time after boot and heat the connected segment with full power. Flashing the bootloader is done by connecting a J-Link to the MCU via the SWD and SWC ports. The whole process is described in detail on the Adafruit website. After installing the bootloader the MCU is recognized as a flash drive when connected via the micro USB port and subsequent bootloaders can be simply installed by dragging a UF2 file onto the drive.

As a next step I wanted to install a CircuitPython bootloader. However, since my board uses many pins that are not connected on the Trinket M0, I first had to slightly modify the board configuration. Again there is a great tutorial for this on the Adafruit website. Basically, one just has to comment out a few ignored pins in the mpconfigboard.h and then recompile everything. The custom bootloader files are also available on my GitHub.

Step 4: CircuitPython Code

After the CircuitPython bootloader has been installed you can just program the board by saving your code as a file directly to the USB flash drive. The code I wrote reads out the DHT22 sensor and then alternately displays the temperature and humidity by heating the corresponding segments. As already mentioned the heating is done by switching the MOSFETs with a PWM signal. Instead of configuring the pins as PWM outputs, I generated a "fake" PWM signal with a low switching frequency of 100 Hz in the code using delays. To further lower the current consumption I do not switch on the segments simultaneously but sequentially as shown in the schematic above. There are also a few tricks to make the heating of the segments more even. First of all the duty cycle is a bit different for every segment. For example the dash of the "%" sign needs a much larger duty cycle because of its higher resistance. Also I found that segments which are surrounded by many other segments need to be heated less. In addition, if a segment was heated in the previous "run" the duty cycle can be reduced in the next. Finally, the heating and cooling time is adapted to the ambient temperature which is conveniently measured by the DHT22 sensor. To find reasonable time constants I actually calibrated the display in a climate chamber that I luckily have access to at work.

You can find the full code on my GitHub.

Step 5: Assembly

    The assembly of the display is rather easy and can be divided in the following steps

    1. Solder female pin headers to heater PCB
    2. Attach self-adhesive thermochromic sheet to heater PCB
    3. Solder DHT22 sensor to controller PCB and fasten with M2 bolt and nut
    4. Solder male pin headers to controller PCB
    5. Connect both PCBs and place in 3D printed stand

    Step 6: Finished Project

    I am quite happy with the finished diplay which is now constantly running in our living room. The goal of making a smaller, simpler version of my original thermochromic display was definitely achieved and I would like to thank user DmitriyU2 once more for the suggestion. The project also helped me to improve my PCB design skills in Eagle and I learned about the use of MOSFETs as switches.

    One could maybe further improve the design by making a nice enclosure for the PCBs. I am also thinking about making a digital clock in the same style.

    If you like this project you can just remake it or buy it on my Tindie store. Also consider voting for me in the PCB design challenge.

    PCB Design Challenge

    Judges Prize in the
    PCB Design Challenge