Reading Temperature and Humidity from Plantower PMS5003T

by Achim Haug

We use the PMS5003T version for our outdoor monitor Open Air which includes an integrated temperature and humidity sensor. If you use the Arduino Sketch from the AirGradient Arduino library you need to patch the PMS library to get these values. Please follow these steps to patch the PMS library.

  1. In the Arduino IDE, install the library called: ““pms” by Markusz Kakl version 1.1.0”
  2. Inside the Arduino folder, open a directory called “libraries” and then open the PMS folder “PMS_Library”
  3. Insider the src folder, open the file called “PMS.h” and replace it with below content:
#ifndef PMS_H
#define PMS_H

#include "Stream.h"

class PMS
{
public:
static const uint16_t SINGLE_RESPONSE_TIME = 1000;
static const uint16_t TOTAL_RESPONSE_TIME = 1000 * 10;
static const uint16_t STEADY_RESPONSE_TIME = 1000 * 30;

static const uint16_t BAUD_RATE = 9600;

struct DATA {
// Standard Particles, CF=1
uint16_t PM_SP_UG_1_0;
uint16_t PM_SP_UG_2_5;
uint16_t PM_SP_UG_10_0;

// Atmospheric environment
uint16_t PM_AE_UG_1_0;
uint16_t PM_AE_UG_2_5;
uint16_t PM_AE_UG_10_0;

// Raw particles count (number of particles in 0.1l of air
uint16_t PM_RAW_0_3;
uint16_t PM_RAW_0_5;
uint16_t PM_RAW_1_0;
uint16_t PM_RAW_2_5;
uint16_t PM_RAW_5_0;
uint16_t PM_RAW_10_0;

// Formaldehyde (HCHO) concentration in mg/m^3 - PMSxxxxST units only
uint16_t AMB_HCHO;

// Temperature & humidity - PMSxxxxST units only
int16_t AMB_TMP;
uint16_t AMB_HUM;
};

PMS(Stream&);
void sleep();
void wakeUp();
void activeMode();
void passiveMode();

void requestRead();
bool read(DATA& data);
bool readUntil(DATA& data, uint16_t timeout = SINGLE_RESPONSE_TIME);

private:
enum STATUS { STATUS_WAITING, STATUS_OK };
enum MODE { MODE_ACTIVE, MODE_PASSIVE };

uint8_t _payload[50];
Stream* _stream;
DATA* _data;
STATUS _status;
MODE _mode = MODE_ACTIVE;

uint8_t _index = 0;
uint16_t _frameLen;
uint16_t _checksum;
uint16_t _calculatedChecksum;

void loop();
char Char_PM2[10];
};

#endif
  1. Then save this file and open “PMS.cpp” and replace it with below code:
#include "Arduino.h"
#include "PMS.h"

PMS::PMS(Stream& stream)
{
  this->_stream = &stream;
}

// Standby mode. For low power consumption and prolong the life of the sensor.
void PMS::sleep()
{
  uint8_t command[] = { 0x42, 0x4D, 0xE4, 0x00, 0x00, 0x01, 0x73 };
  _stream->write(command, sizeof(command));
}

// Operating mode. Stable data should be got at least 30 seconds after the sensor wakeup from the sleep mode because of the fan's performance.
void PMS::wakeUp()
{
  uint8_t command[] = { 0x42, 0x4D, 0xE4, 0x00, 0x01, 0x01, 0x74 };
  _stream->write(command, sizeof(command));
}

// Active mode. Default mode after power up. In this mode sensor would send serial data to the host automatically.
void PMS::activeMode()
{
  uint8_t command[] = { 0x42, 0x4D, 0xE1, 0x00, 0x01, 0x01, 0x71 };
  _stream->write(command, sizeof(command));
  _mode = MODE_ACTIVE;
}

// Passive mode. In this mode sensor would send serial data to the host only for request.
void PMS::passiveMode()
{
  uint8_t command[] = { 0x42, 0x4D, 0xE1, 0x00, 0x00, 0x01, 0x70 };
  _stream->write(command, sizeof(command));
  _mode = MODE_PASSIVE;
}

// Request read in Passive Mode.
void PMS::requestRead()
{
  if (_mode == MODE_PASSIVE)
  {
    uint8_t command[] = { 0x42, 0x4D, 0xE2, 0x00, 0x00, 0x01, 0x71 };
    _stream->write(command, sizeof(command));
  }
}

// Non-blocking function for parse response.
bool PMS::read(DATA& data)
{
  _data = &data;
  loop();
  
  return _status == STATUS_OK;
}

// Blocking function for parse response. Default timeout is 1s.
bool PMS::readUntil(DATA& data, uint16_t timeout)
{
  _data = &data;
  uint32_t start = millis();
  do
  {
    loop();
    if (_status == STATUS_OK) break;
  } while (millis() - start < timeout);

  return _status == STATUS_OK;
}

void PMS::loop()
{
  _status = STATUS_WAITING;
  if (_stream->available())
  {
    uint8_t ch = _stream->read();

    switch (_index)
    {
    case 0:
      if (ch != 0x42)
      {
        return;
      }
      _calculatedChecksum = ch;
      break;

    case 1:
      if (ch != 0x4D)
      {
        _index = 0;
        return;
      }
      _calculatedChecksum += ch;
      break;

    case 2:
      _calculatedChecksum += ch;
      _frameLen = ch << 8;
      break;

    case 3:
      _frameLen |= ch;
      // Unsupported sensor, different frame length, transmission error e.t.c.
      if (_frameLen != 2 * 9 + 2 && _frameLen != 2 * 13 + 2)
      {
        _index = 0;
        return;
      }
      _calculatedChecksum += ch;
      break;

    default:
      if (_index == _frameLen + 2)
      {
        _checksum = ch << 8;
      }
      else if (_index == _frameLen + 2 + 1)
      {
        _checksum |= ch;

        if (_calculatedChecksum == _checksum)
        {
          _status = STATUS_OK;

          // Standard Particles, CF=1.
          _data->PM_SP_UG_1_0 = makeWord(_payload[0], _payload[1]);
          _data->PM_SP_UG_2_5 = makeWord(_payload[2], _payload[3]);
          _data->PM_SP_UG_10_0 = makeWord(_payload[4], _payload[5]);

          // Atmospheric Environment.
          _data->PM_AE_UG_1_0 = makeWord(_payload[6], _payload[7]);
          _data->PM_AE_UG_2_5 = makeWord(_payload[8], _payload[9]);
          _data->PM_AE_UG_10_0 = makeWord(_payload[10], _payload[11]);

        // Total particles count per 100ml air
          _data->PM_RAW_0_3 = makeWord(_payload[12], _payload[13]);
          _data->PM_RAW_0_5 = makeWord(_payload[14], _payload[15]);
          _data->PM_RAW_1_0 = makeWord(_payload[16], _payload[17]);
          _data->PM_RAW_2_5 = makeWord(_payload[18], _payload[19]);
          _data->PM_RAW_5_0 = makeWord(_payload[20], _payload[21]);
          _data->PM_RAW_10_0 = makeWord(_payload[22], _payload[23]);

          // Formaldehyde concentration (PMSxxxxST units only)
          _data->AMB_HCHO = makeWord(_payload[24], _payload[25]) / 1000;

          // Temperature & humidity (PMSxxxxST units only)
          _data->AMB_TMP = makeWord(_payload[20], _payload[21]);
          _data->AMB_HUM = makeWord(_payload[22], _payload[23]);
        }

        _index = 0;
        return;
      }
      else
      {
        _calculatedChecksum += ch;
        uint8_t payloadIndex = _index - 4;

        // Payload is common to all sensors (first 2x6 bytes).
        if (payloadIndex < sizeof(_payload))
        {
          _payload[payloadIndex] = ch;
        }
      }

      break;
    }

    _index++;
  }
}

  1. Then save this file. Completely quit the Arduino IDE and restart it.
  2. If you haven’t done so, install the AirGradient library from the Arduino library manager
  3. Then open the example called “DIY_OUTDOOR_C3” and adjust/flash the code
This is an Ad for our Own Product

AirGradient DIY Air Quality Kits

Build your own open source air quality sensor measuring PM2.5, CO2, TVOC, Temperature and Humidity. Easy to build, accurate and affordable!

Learn More