MiCS-VZ-89T a low power air quality sensor

A project log for Portable environmental monitor

A handheld, battery powered, sensor array unit for environmental monitoring focused mostly on air quality using a global infrastructure.

Radu MotisanRadu Motisan 07/31/2015 at 18:395 Comments

Putting everything together also requires checking everything separately first. So I did with the MiCS-VZ-89T sensor, and here is the story, also presented on my blog: MiCS-VZ-89 Air Quality Sensor

A low power sensor that can measure at the same time both carbon dioxide (CO2) and Volatile Organic Compounds (tVOC) is probably too good to be true. Yet the MiCS-VZ-89 does this, among some other appealing features like 3.3V supply (in the T variant), I2C communication, calibration-free high sensitivity and a small size factor. Its price is also a plus.
I got the chance to give it a closer look while designing the Portable Environmental Monitor project for this year's Hackaday Prize .
My circuit's I2C line was already serving a BMP180 temperature and barometric pressure sensor, but the MiCS_VZ_89T got along just fine. Using the datasheet, I was able to find the I2C Address (set to 0x70), its commands and parameters. The precious air quality data is returned as a 6 bytes array:

3.2. 0b00001001: Get VZ89 status:
This command is used to read the VZ89 status coded with 6 bytes:
D1 (8bits) represent the CO2-equivalent signal value [13..242].
D2 (8bits) represent the VOC-short signal value [13..242].
D3 (8bits) represent the VOC-long signal value [13..242].
D4 (8bits) represent the 1st byte of raw sensor resistor value (LSB).
D5 (8bits) represent the 2nd byte of raw sensor resistor value.
D6 (8bits) represent the 3rd byte of raw sensor resistor value (MSB).

Full details in this datasheet: MICS-VZ-89-I2C-specs-rev-A .
The code implementation is as follows:

  1. #define VZ89_ADDR (0x70<<1) //0x70 default I2C address
  2. //registers
  3. #define VZ89_CMD_SETPPMCO2 0x8 // This command is used to send a ppmCO2 value from an analyser to the VZ89 in order to recalibrate its outputs.
  4. #define VZ89_CMD_GETSTATUS 0x9 // This command is used to read the VZ89 status coded with 6 bytes:
  5. ..
  6. // i2c read
  7. void VZ89::readmem(uint8_t reg, uint8_t buff[], uint8_t bytes) {
  8. uint8_t i =0;
  9. i2c_start_wait(VZ89_ADDR | I2C_WRITE);
  10. i2c_write(reg);
  11. i2c_rep_start(VZ89_ADDR | I2C_READ);
  12. for(i=0; i<bytes; i++) {
  13. if(i==bytes-1)
  14. buff[i] = i2c_readNak();
  15. else
  16. buff[i] = i2c_readAck();
  17. }
  18. i2c_stop();
  19. }
  20. void VZ89::readRaw(uint8_t rawData[6]) {
  21. static uint8_t buff[6];
  22. memset(buff, 0, sizeof(buff));
  23. //read raw temperature
  24. readmem(VZ89_CMD_GETSTATUS, rawData, 6);
  25. }

Where read raw returns the 6 bytes carrying what we need. A quick test gave me the three bytes starting at 13 right away, and spraying a little butane from a lighter showed the numbers change.
All good. Except I didn't have a way to interpret them into actual gas concentrations. I contacted the manufacturer, and they replied (in less than 24hours), indicating where I can find the information. The interpretation of the I2C data can be seen here: Preliminary Datasheet MiCS-VZ-86 and VZ-89 rev 6 (page 3).
The instructions were translated into code as follows:

  1. void VZ89::readRaw(uint8_t rawData[6]) {
  2. static uint8_t buff[6];
  3. memset(buff, 0, sizeof(buff));
  4. //read raw temperature
  5. readmem(VZ89_CMD_GETSTATUS, rawData, 6);
  6. }
  7. /*
  8. During “Functional Test Mode” only “Raw sensor” and “VOC_short” data are available. “VOC_short” is
  9. an image of sensor reactivity and can then be used for functional test.
  10. Out of this initial period, the device will have the I2C data CO2 equivalent [ppm] and tVOC equivalent
  11. referred to the isobutylene sensitivity unit [ppb].
  12. D1:Data_byte_1: CO2_equ: [13…242] -> CO2_equ [ppm] = (D1 -13) * (1600/229) + 400
  13. D2: Data_byte_2: VOC_short [13…242]
  14. D3: Data_byte_3: tVOC: [13…242] -> tVOC [ppb] = (D3 -13) * (1000/229)
  15. D4: Data_byte_4: Raw sensor first byte (LSB)
  16. D5: Data_byte_5: Raw sensor second byte
  17. D6: Data_byte_6: Raw sensor third byte (MSB) -> Resistor value [W] = 10*(D4 + (256*D5) + (65536*D6))
  18. return CO2 equivalent [ppm] and tVOC equivalent referred to the isobutylene sensitivity unit [ppb].
  19. *
  20. */
  21. bool VZ89::read(float *co2, uint8_t *reactivity, float *tvoc) {
  22. uint8_t data[6];
  23. readRaw(data);
  24. if (data[0] < 13 || data[1] < 13 || data[2] < 13) return false;
  25. // convert data to meaningful values
  26. *co2 = (data[0] - 13) * (1600.0 / 229) + 400; // ppm: 400 .. 2000
  27. *reactivity = data[1];
  28. *tvoc = (data[2] - 13) * (1000.0/229); // ppb: 0 .. 1000
  29. //uint32_t resistor = 10 * (data[3] +256 * data[4] + 65536 * data[5]);
  30. return true;
  31. }

And the result produced the CO2 concentration as ppm, respectively the tVOC as ppb:

Get the code

Mics-vz-89t code is released as open source. Get it here or on Github.


paul grant wrote 03/02/2017 at 14:17 point

Hi, I'm trying to use this device but only get zeros for data! Can you help?

  Are you sure? yes | no

Radu Motisan wrote 11/02/2015 at 23:06 point

the MICS-VZ-89 is having stability issues and the manufacturer did very little to help. Readings would degrade in just a few days! So I moved to a better sensor.

  Are you sure? yes | no

Andrei Stoica wrote 07/08/2016 at 19:49 point

First of all thanks for documenting the entire process and also this issue. I have 2 questions related to it. First do you recommend a supplier to get the MICS-VZ-89 for personal projects (1-2 pcs)? And second what sensor did you move to? 

  Are you sure? yes | no

Radu Motisan wrote 07/15/2016 at 12:44 point

Salut Andrei si mersi de mesaj. I have direct contacts at SGX, but I also have one supplier in Taiwan, which I could point you to. Drop me an email for that. I moved to the Bosch BME680.

  Are you sure? yes | no

James Cannan wrote 11/01/2015 at 11:58 point

Thanks for documenting this. Did you have to leave the Mics-vz-89t to warm up for 15min before you got stable results?

  Are you sure? yes | no