Mark Clowes

Index - Vision CO2 Monitor and Python Comms Example

2023-12-09
I bought a Vision CO2 monitor a few years ago. It also shows temperature and relative humidity. It's a well built and designed device that I expect to last a very long time (brochure claims 10+ year life expectancy). I borrowed this pic from their webpage:

What they don't show you on their webpage is the device pulled apart.

The CO2 sensor is a Cubic CM1106 using non-dispersive infrared (NDIR), which is an excellent quality component. This probably contributes to the £100 (ex vat) price point. The screen is a simple LCD but I expect will have excellent longevity. The temperature sensor is tucked away on a separate leg in the corner to try to keep it thermally isolated.

Software (Windows only) is available to download from their website that lets you control certain aspects of operation such as screen colour and brightness. The software will chart up to the last 30 days worth of data. The data can also be downloaded as a csv which seems to be limited to 4096 records at 15 minute intervals, actually giving just over 42.5 days of data. The lack of a realtime clock means the data seems to be relative to power on time.

It's unfortunate they do not publish a specification for talking to the device, as that means you cannot use it with say a Raspberry Pi. So I reverse engineered the system (it shows up as a serial device when connected via USB) and built a simple python3 implementation of the most important functions (read current live values and a fresh-air CO2 calibration). This could be extended to log the data into your own database or do other clever actions such as turn on an extractor fan when relative humidity becomes high.

When the Vision CO2 monitor is connected to a Raspberry Pi it shows up as /dev/ttyUSB0.

import serial
ser = serial.Serial('/dev/ttyUSB0', 115200, bytesize=8, stopbits=1)

print("Sending datacmd")
datacmd = b'\x02\x00\x00\xFF'
ser.write(datacmd)

response = ser.read(13)
co2ppm = str(int.from_bytes(response[5:7], byteorder='little'))
temperature = str(int.from_bytes(response[7:9], byteorder='little')/10)
relhum = str(int.from_bytes(response[9:11], byteorder='little')/10)

print('Response: ' + response.hex())
print('CO2 ppm : ' + co2ppm)
print('Temp ' + u'\N{DEGREE SIGN}' + "C : " + temperature)
print('RH%     : ' + relhum)

answer = input("Calibrate (y/n)? ")
if answer.lower() == "y":
    # calibrate the CO2 sensor to fresh air
    print("Sending calibratecmd")
    calibratecmd = b'\x02\x03\x01\x00\xFB\x02\x03\x01\x00\xFB'
    ser.write(calibratecmd)

ser.close()