Friday, March 9, 2018

Review and Test of the MH-Z14A NDIR CO2 Sensor

The MH-Z14A measures CO2 concentrations using absorption of infra-red light. These devices are available on eBay, Amazon and other places from about $20.

Ignore the capacitor across the power pins - this was a failed attempt by me to improve the reliability of the analog outputs.

My initial impressions of the module were pretty favourable. It looks pretty space-age with its gold coated chamber and 4 LEDs in the corner of the fabric window that allows the air to enter. These LEDs blink once a second as the readings are taken.

You can find a datasheet for the module here.

The device is available configured for different ranges of CO2 concentration. This module uses the most common range of 0 to 5000ppm.


The module actually has three interfaces - described in the datasheet.

  • Analog output - proportional to the ppm of CO2 range of 0.4 to 2V for full range.
  • PWM output - very slow PWM over the period of a second, with the HIGH duration being proportional to the ppm of CO2.
  • 9600 baud 3V/5V TTL UART interface. You send a message saying you want a reading, it responds with a reading.


The module has lots of pins on it, most of which are duplicates. I just stuck to the main 0.1inch pitch 2 x 6 connector on the right. Note that the pin numbering on the board reflects the evolution of various connectors added through versions of the boards, and is therefore a bit odd.

First Use

My first experiment was just to power-up the board (5V to pin1, GND to pin 2) with a multimeter on the analog output (pin 4).

As soon as it powered up it was obvious that every second or so, the LEDs behind the fabric would light up and the current would surge to around 100mA. This was very short in duration so I expect the average power consumption of this unit is actually pretty low.

The analog output on pin 4 indicated 0.85V using the datasheet the ppm is calculated as:

ppm = (Vout - 0.4) * (5000 / 1.6)

In this case a ppm of 1406. This was wrong, I was in a big room with the door open and not breathing on the sensor, so I would expect a reading of around 400ppm.

It looked like the device was not calibrated. Or, calibrated in a somewhat unhealthy factory.


You can calibrate by sending a serial message (see later) or by connecting pin 8 to GND for at least 7 seconds. Using the pin 8 methods, the new voltage reading was a much more believable 0.52V indicating a plausible 375 ppm.

After huffing on the sensor for a bit, it was easy to get the readings to go up and then work their way back down to ambient levels after about 5 minutes.

Its looking good!


The next step was to hook the device up to an Arduino Uno so that I could compare the reading methods and also do a bit of logging.

Here is the test script that displays the readings from all three output methods in the Arduino Serial Monitor once every 10 seconds.

#include <SoftwareSerial.h>

const int analogPin = A0;
const int pwmPin = 9;

const long samplePeriod = 10000L;

SoftwareSerial sensor(10, 11); // RX, TX

const byte requestReading[] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
byte result[9];
long lastSampleTime = 0;

void setup() {
  pinMode(pwmPin, INPUT_PULLUP);

void loop() {
  long now = millis();
  if (now > lastSampleTime + samplePeriod) {
    lastSampleTime = now;
    int ppmV = readPPMV();
    int ppmS = readPPMSerial();
    int ppmPWM = readPPMPWM();

int readPPMV() {
  float v = analogRead(analogPin) * 5.0 / 1023.0;
  int ppm = int((v - 0.4) * 3125.0);
  return ppm;

int readPPMSerial() {
  for (int i = 0; i < 9; i++) {
  //Serial.println("sent request");
  while (sensor.available() < 9) {}; // wait for response
  for (int i = 0; i < 9; i++) {
    result[i] = sensor.read(); 
  int high = result[2];
  int low = result[3];
    //Serial.print(high); Serial.print(" ");Serial.println(low);
  return high * 256 + low;

int readPPMPWM() {
  while (digitalRead(pwmPin) == LOW) {}; // wait for pulse to go high
  long t0 = millis();
  while (digitalRead(pwmPin) == HIGH) {}; // wait for pulse to go low
  long t1 = millis();
  while (digitalRead(pwmPin) == LOW) {}; // wait for pulse to go high again
  long t2 = millis();
  long th = t1-t0;
  long tl = t2-t1;
  long ppm = 5000L * (th - 2) / (th + tl - 4);
  while (digitalRead(pwmPin) == HIGH) {}; // wait for pulse to go low
  delay(10); // allow output to settle.
  return int(ppm);

Here is a sample of the results from the Arduino Serial Monitor.

The first column is the analog reading, the middle column from the PWM interface and the right column from the serial interface.

The analog readings are all over the place. I don't think this interface is useable.

UPDATE: The analog output actually appears to be unbuffered. Using my DMM which has a Zin of about 10MΩ the voltage readings were within a couple of % of the serial readings. It was the relatively low Zin of the Arduino ADCs sample and hold messing things up. So, a unity gain op-amp buffer should do the trick.

The PWM interface is pretty consistent with the UART interface, although every so often there will be a bad reading.

The Serial interface is clearly the winner and if you implement the checksum algorithm specified in the datasheet I would expect 100% reliable readings.


To give the module a long term test, I set it running overnight in out bedroom with the following somewhat worrying results.

The y-axis is ppm of CO2 and the x-axis time (1 sample per minute).

I used the PWM measurement method and you can see the frequent misreadings (perhaps 1 in 50). But the trend is pretty good, showing the ppm rising well above 4000ppm, before the bedroom door was opened in the morning and the readings fell back to ambient values of 400ppm or so. Yes, we were all still alive!

The high overnight readings seem worryingly high for me, but at the moment, I do not have a calibrated sensor to compare with.


The sensor module's Serial interface works well and the PWM interface acceptably but the analog interface is only useful if buffered.

A serial interface that it both 5V and 3V compatible makes the module suitable for use with a Raspberry Pi or Arduino without the need for level conversion.

I cannot comment on the accuracy of the sensor without comparison to a trusted device. When I get one, I will come back here for an update.

Tuesday, December 5, 2017

Bluetooth Control of a micro:bit and motor using Bitty

Bitty is a suite of software that allows you to use your smartphone as an interface to your micro:bit. The software comes in two parts. A resident program that you download from here as a HEX file and copy onto your micro:bit and an App that you install on your smartphone (search your app store for Bitty Blue.

Here's a video of me using Bitty Blue to turn a MonkMakes Relay for micro:bit on and off and hence control a motor.

The Bitty Blue app has a load of other fun things to do with your micro:bit, so do try them out as well.

Bitty also have a range of other apps for things such as:

  • Turning your micro:bit into a wireless data logger (for which a Sensor for micro:bit might be handy)
  • Bluetooth control of a Kitroniks robot.
  • Controlling lighting
  • An audio prank
  • even a Christmas tree bauble

Monday, October 16, 2017

How to Get Started in Electronics

I sometimes get asked how to get started in electronics and so at Maker Faire New York 2017 I gave a short talk on this topic. In particular I looked at how hobby electronics now almost inevitably starts with the use of a microcontroller board like the micro:bit or an Arduino or a single board computer like the Raspberry Pi. The question then arises, which do you pick?

Here are the slides from this presentation. You can also find a video of it here (although there was a lot of noise).

Saturday, April 1, 2017

Introducing the MonkMakesDuino

Yet another Arduino clone, but his time a bit different in that is is designed with education and breadboard use as its main goal.

Its on KICKSTARTER now, so take a look if you are interested.
The main features are:
  • Breadboard friendly - clearly labelled pins
  • Arduino Uno compatible (Board Type: Arduino Uno in the Arduino IDE)
  • Low-cost simple design
  • Built in USB interface with high quality drivers available (CP2102)
  • Polyfuse over-current protection
  • 5V USB powered
  • Buffered 'L' LED on pin 13
You get the same pins as the Arduino Uno (more or less) but in a single line that fits over one edge of the breadboard, leaving the rest of the breadboard free for your project.

Friday, March 24, 2017

Logging Multimeter Readings with a Digitek DT-4000ZC on a Mac

I bought one of these multimeters a few months ago on eBay, to be able to automate the timed taking of readings using a multimeter.

Given the price, I wasn't expecting much, but it works pretty well.

Being a Mac user, I am used to either using software in a Windows XP virtual machine, or searching out alternative software for a device. I this case, I found that a Python library has already been written to decode the multimeter's strange display-based protocol. The protocol actually transmits the state of each of the segments on the display - an interesting problem to code.

To get this going, you will need to:

  1. Install Python on my Mac 
  2. Download the TP4000ZC library
  3. Unzip it and then in the directory containing the library python files you just downloaded, create a new file called 'dmm.py' and paste the following code into it:

import tp4000zc
import time
port = '/dev/tty.wch ch341 USB=>RS232 1420'

dmm = tp4000zc.Dmm(port)

last_time = 0
period = 1.0

  while True:
    val = dmm.read()
    t = time.time()
    if t > last_time + period:
      last_time = t
      value = val.numericVal

The multimeter is supplied with an RS232 interface, so I bought a cheap USB to RS232 adapter. You may need to install drivers for this, and then you have to work out what the device name is and modify the line of dmm.py that starts 'port =' to match the device name for your USB adapter.

Run the program, and readings will start to appear.

MacBook:tp4000_dmm si$ sudo python dmm.py 


To draw charts and analyze the data, just select the readings from the terminal and then paste them into a spreadsheet.

Thursday, March 23, 2017

Raspberry Pi 2 vs 3 Current Consumption During Boot

I just wanted to share this fairly uninteresting result in case its useful to anyone.

I measured the current drawn from a 5V power supply on both a Raspberry Pi 2 (with Realtek WiFi dongle) and a Raspberry Pi 3 (with built in WiFi) to get an idea of the current consumption while these two boards booted up.

So, with no further ado, here are the results.

Rather as expected - much the same, both settling down to an 'idle current' of around 300mA, booting in under 30 seconds.

In answer to MrMobileWill, to take the readings, I used the following setup.

I sacrificed a micro USB lead, stripping the wires to get the black and red power wires. I then put my multimeter in current mode in series between my bench power supply (set to 5V) and the Raspberry Pi.

This multimeter has an RS232 serial interface that I connected to my Mac using a USB to serial interface (cheap off eBay).

The software setup is a little complicated. So See my next post for details on how I did this.

Sunday, January 22, 2017

On Switch for Raspberry Pi

When you gave finished using your Raspberry Pi, you should really shut it down, otherwise, its possible to corrupt the SD card image which would mean you having to reinstall Raspbian. Having shut-down your Raspberry Pi you can get it to boot up again by unplugging and plugging the USB lead, but a neater solution is to add a reset button to your Raspberry Pi.

Most models of Raspberry Pi have a connector just for this purpose.


You will Need

* 2 pin header
* A push button with female header connectors.

For the push button, you can either use a scavenged reset switch from an old PC, or, as I have here, a MonkMakes Button, which is sold as a twin-pack on Amazon.com or Amazon.co.uk.


First of all, you need to identify the two holes in your Raspberry Pi board that you need to make connections to. These are circled in yellow in the figures below, for the Raspberry Pi models that I have. The location of this has moved with pretty much every Raspberry Pi release. The two pins will either be labelled RUN or P6

Raspberry Pi 3 model B

Raspberry Pi 2 model B

Raspberry Pi model B ver 2

Raspberry Pi Zero

Raspberry Pi A+

Having found where the pin headers need to go, if you have soldering equipment, then solder the pin header into the socket as shown here:

If you want to avoid soldering, then you can do so as follows. First push the plastic part of the header pin down as shown below:

Then push the header pin through from the underside of the board and push the header sockets of the MonkMakes Button into the header pins so that the Pi is the filling in a header sandwich. As long as the switch is not moved around too much, it should stay connected and make a reasonable contact, but soldering is better if you can.

Shutting Down and Starting

To test out your modification, power up your Raspberry Pi and then shut it down, by selecting Shutdown from the raspberry menu.

Then select the option Shutdown.

After a while the screen will close down and the Pi will go into a 'halt' mode, where it uses minimal power and is basically in standby mode.

Now to start your Pi, all you need to do is press the button and it will start to boot up!

If you liked this article, you will probably like my Raspberry Pi books: