Fab Academy 2019 - Lucas Lim
My Final Project Programming & Testing of Electronics

My Final Project Phases :
Project
Conception
Project
Planning
2D & 3D Design,
3D Print & Laser Cut
Electronics Design
and Fabrication
Programming &
Testing of
Electronics
Project
Presentation



Pin connections





Programming my board








My code :

Following is the minimum code to run my dust sensor.

However I continue to explore and added in some improvements to my original working code. These improvements are commentted off, like serial communication to PC and LED. While testing the serial output to my PC via FTDI adaptor, it still need more work and testing.

/*
  Title: [For Arduino] PMS5003_dust_sensor_ver3.0.ino
  Author: Lucas Lim 
  Date Created: 16/5/2019
  Last Modified: 14/6/2019
  Purpose: Receive data from PMS Dust Sensor Module
           and display PM2.5 and PM10 results to LCD display module. 
           with I2C adaptor.
           Sound buzzer if pre-defined unhealthy limit is reach or exceeded.
           (Optional) Output result to PC via serial communication.           
  PS : The code for the PMS5003 dust sensor module was adapted and modified 
       from the orignal code by Lady Ada published on
       https://learn.adafruit.com/pm25-air-quality-sensor/arduino-code
*/

// I2C library
#include < Wire.h>

// LCD with I2C library
#include < LiquidCrystal_I2C.h>

#include < SoftwareSerial.h>

// variable declaration
// const value will not change
//const int ledpin=2;          // LED connected to PD2 on Arduino pin 2(not use)
const int buzzpin=5;         // buzzer connected to PD5 on Arduino pin 5
const int pms5003_RX = 17;    // PMS5003 dust sensor Rx, on Arduino pin 17
const int pms5003_TX = 16;    // PMS5003 dust sensor Tx, on Arduino pin 16(not use)
int pms_interval_time=2000; // PMS5003 delay time between taking next reading, 1000 = 1sec


SoftwareSerial pmsSerial(pms5003_RX, pms5003_TX);

LiquidCrystal_I2C lcd(0x3F, 16, 2);  // set the LCD address to 0x3F for a 16 chars and 2 line display

 
void setup() {
  // our debugging, output to serial on PC
  //Serial.begin(115200);
 
  // sensor baud rate is 9600
  pmsSerial.begin(9600);

  Wire.begin();
  
  // Initialize the LCD, must be called in order to initialize I2C communication
  lcd.begin();

  // Turn on the backlight
  lcd.backlight();

  // Display to LCD starting up message (not use)
  //lcd.setCursor(0, 0);  
  //lcd.print("Staring up");
  //delay(3000);
  // Clear the LCD
  //lcd.clear();

  // Buzzer
  pinMode(buzzpin,OUTPUT);

  // LED - for power status indicator (not use as LCD display is lit up already)
  //pinMode(ledpin,OUTPUT);
}
 
struct pms5003data {
  uint16_t framelen;
  uint16_t pm10_standard, pm25_standard, pm100_standard;
  uint16_t pm10_env, pm25_env, pm100_env;
  uint16_t particles_03um, particles_05um, particles_10um, particles_25um, particles_50um, particles_100um;
  uint16_t unused;
  uint16_t checksum;
};
 
struct pms5003data data;
    
void loop() {
  if (readPMSdata(&pmsSerial)) {
    // reading data was successful!
    //Serial.println();
    //Serial.println("---------------------------------------");
    //Serial.println("Concentration Units (standard)");
    //Serial.print("\t\tPM 2.5: "); Serial.print(data.pm25_standard);
    //Serial.print("\t\tPM 10: "); Serial.println(data.pm100_standard);
    //Serial.println("---------------------------------------");


    //Output PM2.5 and PM10 measurements to LCD display
    //LCD first row display PM10
    lcd.setCursor(0, 0);  
    lcd.print("PM10 :");
    lcd.setCursor(8, 0);  
    lcd.print(data.pm100_standard);

    //LCD second row display PM2.5
    lcd.setCursor(0,1);
    lcd.print("PM2.5 :");
    lcd.setCursor(8, 1);  
    lcd.print(data.pm25_standard);


    // Sound off buzzer if PM2.5 or PM10 read the 'moderate polluted range'
    //   Good :                PM10 (1-50) PM2.5(0-30)
    //   Satisfactory :        PM10 (51-100) PM2.5(31-60)
    //   Moderately polluted : PM10 (101-250) PM2.5(61-90)
    //   Poor : PM10 (251-350) PM2.5(91-120)
    //   Very poor : PM10 (351-430) PM2.5(121-250)
    //   Severe : PM10 (430+) PM2.5(250+)

    if (data.pm100_standard >= 251) {
      tone(buzzpin, 1000, 10000);        //turns on buzzer, tone( pin number, frequency in hertz, in milliseconds)
      //Serial.print("Buzzer sounded. PM10 reach or exceed limit!");
      lcd.setCursor(0, 0);  
      lcd.print("PM10 :");
      lcd.setCursor(8, 0);  
      lcd.print(data.pm100_standard);
      lcd.setCursor(20, 0);  
      lcd.print("!!!");
      } else {
      noTone(buzzpin);
      }

    if (data.pm25_standard >= 91) {
      tone(buzzpin, 1000, 10000);       //turns on buzzer, tone( pin number, frequency in hertz, in milliseconds)
      //Serial.print("Buzzer sounded. PM2.5 reach or  limit!");
      lcd.setCursor(0,1);
      lcd.print("PM2.5 :");
      lcd.setCursor(8, 1);  
      lcd.print(data.pm25_standard);
      lcd.setCursor(20, 1);  
      lcd.print("!!!");
      } else {
      noTone(buzzpin);
      }
  }

  // Wait for xx seconds before PMS5003 take next reading
  delay(pms_interval_time);

}
 
boolean readPMSdata(Stream *s) {
  if (! s->available()) {
    return false;
  }
  
  // Read a byte at a time until we get to the special '0x42' start-byte
  if (s->peek() != 0x42) {
    s->read();
    return false;
  }
 
  // Now read all 32 bytes
  if (s->available() < 32) {
    return false;
  }
    
  uint8_t buffer[32];    
  uint16_t sum = 0;
  s->readBytes(buffer, 32);
 
  // get checksum ready
  for (uint8_t i=0; i< 30; i++) {
    sum += buffer[i];
  }
 
  /* debugging
  for (uint8_t i=2; i< 32; i++) {
    Serial.print("0x"); Serial.print(buffer[i], HEX); Serial.print(", ");
  }
  Serial.println();
  */
  
  // The data comes in endian'd, this solves it so it works on all platforms
  uint16_t buffer_u16[15];
  for (uint8_t i=0; i< 15; i++) {
    buffer_u16[i] = buffer[2 + i*2 + 1];
    buffer_u16[i] += (buffer[2 + i*2] << 8);
  }
 
  // put it into a nice struct :)
  memcpy((void *)&data, (void *)buffer_u16, 30);
 
  if (sum != data.checksum) {
    //Serial.println("Checksum failure");
    return false;
  }
  // success!
  return true;
}
  


Video of working PM2.5/PM10 dust sensor

Duration : 1 min.
Please switch on your audio speaker.



Reflection :

Areas of improvement :
1) To take reading at 10 minutes or longer interval.
2) Take a few readings and average out the readings before displaying to the LCD screen.


Files :

My code :
PMS5003_sensor_with_LCD_display.ino



References :

- Arduino Code | PM2.5 Air Quality Sensor | Adafruit Learning System