/* 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 // LCD with I2C library #include #include // 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; }