Update 01/04/21: I’ve noticed some typos in the code examples which I’ve corrected.
A bit of background
I’m building a reflow oven to assemble the small projects I’m working on. I could just hand solder everything but that’s quite time-consuming. I’ve bought a 12L 1200W mini oven for around £60 from eBay which I’m intending to use. This is quite a popular option with hobbyists and hackers. The other is to modify a cheap Chinese reflow oven but the price for these start from £150 in the UK I’m having to count the pennies.
My first aim is to create a reflow oven that just works. It doesn’t need to be pretty – it just needs to work. In its most basic form you really only need three things – (1) A heat source, (2) a temperature sensor and (3) a controller.
I’ve not started anything with the controller yet but I like the design used by Adafruit’s EZ Make Oven which doesn’t require you to take the oven apart but unfortunately the outlet they use doesn’t come in a UK version so I’m still working on that.
The first thing I’m working on is getting the temperature sensor to report back the temperature inside the oven using a type K thermocouple.
I followed this guide at ABElectronics to set up my Raspberry Pi to communicate with the MCP9600 via I2C. There are common bus speeds with I2C and the MCP9600 datasheet states that it is rated up to 100 kbit/s (otherwise known as standard mode) so I set this as the bus speed on the Raspberry Pi as well.
Pimoroni have a really good GitHub repository for the MCP9600 which allows you to get up a running in a few lines of code – I found the guide here the best. If you’ve connected your thermocouple the wrong way round you’ll find the measurements move in the opposite direction to the actual temperature which is a quick and easy sense check.
I wanted to plot my temperature measurements in realtime so was working my way through a tutorial on SparkFun to do this using Matplotlib. I got to the point where I was plotting sensor data on a graph and was finding strange temperature readings.
import time import datetime as dt import matplotlib.pyplot as plt from mcp9600 import MCP9600 # Create figure for plotting fig = plt.figure() ax = fig.add_subplot(1, 1, 1) xs =  ys =  # Initialize communication with MCP9600 mcp9600 = MCP9600(i2c_addr=0x66) mcp9600.set_thermocouple_type('K') # Sample temperature every second for 10 seconds for t in range(0, 20): # Read temperature (Celsius) from MCP9600 temp_c = round(mcp9600.get_hot_junction_temperature(), 2) print(temp_c) # Add x and y to lists xs.append(dt.datetime.now().strftime('%H:%M:%S.%f')) ys.append(temp_c) # Wait before sampling temperature again time.sleep(0.2) # Draw plot ax.plot(xs, ys) # Format plot plt.xticks(rotation=45, ha='right') plt.subplots_adjust(bottom=0.30) plt.title('MCP9600 Temperature over Time') plt.ylabel('Temperature (deg C)') # Draw the graph plt.show()
Interspersed between what were normal temperature readings values of 16.06°C would be reported back.
I initially thought I was polling the device too frequently with the line
time.sleep(0.2) and that was causing the problem so I tried the script with 1s and 3s intervals but I got the same results.
Trying again from first principles
My initial thoughts were that the library created by Pimoroni was causing the problem so started to write my own using smbus2 and created the script below.
(A helpful tip for anyone who tries this themselves is you’ll find yourself constantly converting between Binary, Decimal and Hexadecimal systems – this online converter by ABElectronics will save you a lot of time).
from smbus2 import SMBus import time # Define registers addresses from datasheet HOTJUNCTION = 0x00 # Hot junction temperature measurement DEVIDREV = 0x20 # Device ID/Revision THERMSENSCONFIG = 0x05 # Thermocouple type and filter config # Define registers values from datasheet type_k_no_filter = 0x00 # Thermocouple type 'k' and no filter config ADDR = 0x66 # Address of MCP9600 device # Read Device ID & Revision with SMBus(1) as i2cbus: dev = i2cbus.read_i2c_block_data(ADDR, DEVIDREV, 2) print(dev) # Write thermocouple and filter configuration data with SMBus(1) as i2cbus: i2cbus.write_byte_data(ADDR, THERMSENSCONFIG, type_k_no_filter) # Get hot junction (compensated) temperature reading with SMBus(1) as i2cbus: val = i2cbus.read_i2c_block_data(ADDR, HOTJUNCTION, 2) print(val)
Even with this script however around 50% of the time I was still getting a reported temperature reading of 16.06°C.
At this point I decided that I needed to use my PicoScope and see what was happening on the I2C bus.
Interrogating the bus
I soldered a header onto the Breakout Garden HAT, connected some probes onto the SDA and SDC lines of the bus and set up the scope to analyse the I2C protocol. There a good explainer here from PicoScope on how to do this if you have one.
I stuck with using smbus2 as it would give me granular control of what was going across the bus and put together a short script that just interrogated the device for its device ID and revision number.
from smbus2 import SMBus import time # Define registers addresses from datasheet DEVIDREV = 0x20 # Device ID/Revision ADDR = 0x66 # Address of MCP9600 device # Read Device ID & Revision with SMBus(1) as i2cbus: dev = i2cbus.read_i2c_block_data(ADDR, DEVIDREV, 2) print(dev)
Looking at the scope traces there didn’t appear to be anything unusual. You can see the correctly addressed data packet requesting the device information followed by the device sending that information back.
As everything seemed to be working I then added to the code the next step before reading a temprature measurement which was to set the thermocouple type.
from smbus2 import SMBus import time # Define registers addresses from datasheet DEVIDREV = 0x20 # Device ID/Revision THERMSENSCONFIG = 0x05 # Thermocouple type and filter config # Define registers values from datasheet type_k_no_filter = 0x00 # Thermocouple type 'k' and no filter config ADDR = 0x66 # Address of MCP9600 device # Read Device ID & Revision with SMBus(1) as i2cbus: dev = i2cbus.read_i2c_block_data(ADDR, DEVIDREV, 2) print(dev) # Write thermocouple and filter configuration data with SMBus(1) as i2cbus: i2cbus.write_byte_data(ADDR, THERMSENSCONFIG, type_k_no_filter)
When I looked at the scope traces for the new script I started to see something strange. The ID and revision number returned by the device was changing. You can see this in the scope trace below. The first value returned is
01000000 01000000 (Hex:
0x4040) while on the second attempt the value returned is
01000000 00010010 (Hex:
I wouldn’t expect this information to change so I dug into the datasheet to see if I had missed something.
Digging into the datasheet
According to the datasheet the device ID is carried in bits 15-8. Both times my device reported this back as
0x40) which is correct as per the datasheet. So far so good.
The device revision is carried in the remaining bits with the upper nibble (bits 7-4) containing the major revision and the lower nibble (bits 3-0) containing the minor revision. The datasheet states that the initial release was indicated by a major revision of ‘1’ and a minor revision of ‘0’ so when put together with the Device ID gives
01000000 00010000 (Hex:
This differs from both results returned by my device
0x4012 so as recommended by the device datasheet I had to look at MCP9600 Rev. A – Silicon Errata and Data Sheet Clarification and it’s here that I found my solution.
Problems with the silicon
The Silicon Errata and Data Sheet Clarification document lists every revision of the device that has been released and explains what issues were fixed in each. Fortunately this was a short document but reading through it I came across a section titled “Intermittent I2C Read Command Clock Stretching Failure”.
Devices that suffer from this problem may fail to Clock Stretch, tSTRETCH, which is used when the device needs more time to respond, and outputs the previously transmitted data.
You can see this happening when I request the device ID and revision where the first value I got back was
01000000 01000000 (Hex:
0x4040). This is also not a valid revision number. On the second attempt the value I got back was
01000000 00010010 (Hex:
0x4012) which is a valid revision number and one that suffers from this problem – it’s fixed in devices
This also explains why I was getting temperature readings of 16.06°C. The hot-junction register returns two bytes and when the second byte is the same as the first
00000001 00000001 the temperature is reported as 16.0625°C or (16.06°C when rounded to two decimal places).
Frustratingly the suggested workaround of repeating the read instruction until the second byte differs from the first is not very practical as this problem happens around 50% of the time, definitely not what I’d called intermittent.
As this device is the only thing my Raspberry Pi is communicating with and I don’t necessarily need a high-speed interface for my application a second solution is hinted at.
According to the errata document this problem typically only happens at bus speeds greater than 85 kHz. If I slowed mine down to 50 kHz this should fix the problem, no?
Slowing everything down
When following this guide on ABElectronics I had set the bus speed to 100 kHz. In order to reduce it to 50 kHz I had to edit the following parameter in the
After restarting my Raspberry Pi I ran my last script again.
The first thing to note is that the bus speed had been slowed down to 50 kHz which was a good sign – I wasn’t sure if this value would take as it is not a typical I2C speed.
Looking at the device ID and revision the value returned is
01000000 00010010 (Hex:
0x4012) which is correct. Success!
I ran this script several times and each time got the right response from the device so it looks like the problem is fixed.
When I now try and read the temperature using both the Pimoroni library and the smbus2 script I get consistent measurements.
It took me a good week of evenings to get to the bottom of this issue – reading through datasheets, setting up my scope, looking at the traces. I feel like this is something I was only able to solve given all the equipment I had available to me.
I feel that if companies like Adafruit, SparkFun and Pimoroni are going to be selling breakout boards based on this device they need to provide and bit more information when it comes to using them. It was only after identifying the problem from the datasheet that I found an unassociated page on the Adafruit website which said there might be an issue with this device.
There also appears to be other issues with using the I2C bus on various versions of the Raspberry Pi, here I’m using the Raspberry Pi 3 Model B+. Just something to be aware of in future.
If you’re having problems using the MCP9600 I hope you found this article useful.