จากช่วงต้นปี เกิดวิกฤตฝุ่นพิษ PM2.5 ทำให้ทางผมได้ทดลองใช้ เซ็นเซอร์วัดฝุ่น และ M5Stack ทำต้นแบบเครื่องวัดฝุ่นขึ้นมาง่ายๆ ตอนนี้ถึงเวลาทำให้สมบูรณ์แล้ว
- รวมโมดุล จับเอา Sensor และ Battery ทั้งหมดใส่เข้าไปในเคสเดียวกัน ด้วย 3D Printer ขึ้นรูปมา ดังภาพ โมดิฟาย M5Stack โมดุลเดิมนิดหน่อยครับ
- แก้ไข UI ใหม่ครับ ให้เป็นระเบียบมากขึ้น ช่วงแรกยังไม่คุ้นกับ M5Stack เลยทำให้แสดงผลได้ก่อน ในตอนนี้ทางผมปรับปรุง UI ใหม่ และแก้ไขเรื่องกระพริบ และ ผมคิดว่า Code ใหม่น่าจะช่วยให้คนที่สนใจ ต่อยอดงานอื่นๆ ได้ง่ายขึ้นครับ
#include <M5Stack.h> #include <HardwareSerial.h> //const uint8_t PMS_RX=16, PMS_TX=17; HardwareSerial pmsSerial(2); // UART2 on GPIO16(RX),GPIO17(TX) #define GFXFF 1 #define FF9 &FreeSans9pt7b #define CF_OL24 &Orbitron_Light_24 #define CF_OL32 &Orbitron_Light_32 #define CF_RT24 &Roboto_Thin_24 #define CF_S24 &Satisfy_24 #define CF_Y32 &Yellowtail_32 struct pms7003data { 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 displayCode { int bg_color; int text_color; int AQI; String Code; }; struct pms7003data data; struct displayCode displayCode_t; void setup() { M5.begin(); // our debugging output Serial.begin(115200); // sensor baud rate is 9600 pmsSerial.begin(9600); M5.Lcd.setFreeFont(CF_RT24); M5.Lcd.setTextDatum(MC_DATUM); M5.Lcd.fillScreen(TFT_BLACK); M5.Lcd.setTextColor(TFT_WHITE); M5.Lcd.drawString("LOADING DATA", M5.Lcd.width()/2, M5.Lcd.height()/2, GFXFF); } int old=-1; void loop() { if (readPMSdata(&pmsSerial)) { displayCode_t = PM25AQI(data.pm25_env); // update bg color if (old != displayCode_t.bg_color) { M5.Lcd.setFreeFont(CF_RT24); // Select the font M5.Lcd.setTextSize(0.5); M5.Lcd.fillScreen(displayCode_t.bg_color); M5.Lcd.setTextColor(displayCode_t.text_color, displayCode_t.bg_color); M5.Lcd.setTextDatum(ML_DATUM); M5.Lcd.drawString("PM2.5(AQI)", 5, 15, GFXFF); M5.Lcd.setTextDatum(MC_DATUM); M5.Lcd.drawString(displayCode_t.Code, M5.Lcd.width()/2, M5.Lcd.height()/2+40, GFXFF);// Print the string name of the font M5.Lcd.setFreeFont(FF9); M5.Lcd.setTextPadding(0); M5.Lcd.drawString("PM1.0", 40, 195, GFXFF); M5.Lcd.drawString("PM2.5", M5.Lcd.width()/2, 195, GFXFF); M5.Lcd.drawString("PM10", 280, 195, GFXFF); } M5.Lcd.setFreeFont(FF9); M5.Lcd.setTextPadding(40); M5.Lcd.setTextSize(1); M5.Lcd.drawNumber( data.pm10_env, 40, 220); M5.Lcd.drawNumber( data.pm25_env, M5.Lcd.width()/2, 220); M5.Lcd.drawNumber( data.pm100_env, 280, 220); M5.Lcd.setFreeFont(CF_OL32); M5.Lcd.setTextDatum(MC_DATUM); M5.Lcd.setTextSize(2); M5.Lcd.setTextPadding(120); M5.Lcd.drawNumber( displayCode_t.AQI, M5.Lcd.width()/2, M5.Lcd.height()/2-20); printTest(); //debug old = displayCode_t.bg_color; } } displayCode PM25AQI(int reading) { struct displayCode display_t; display_t.text_color = TFT_WHITE; if (reading <= 25) { display_t.bg_color = TFT_BLUE; display_t.Code = "GOOD"; display_t.AQI = reading; } else if ( (reading >= 26) && (reading <= 37) ) { display_t.bg_color = TFT_GREEN; display_t.Code = "Moderate"; display_t.AQI = map(reading,26,37,26,50); } else if ( (reading >= 38) && (reading <= 50) ) { display_t.bg_color = TFT_GREENYELLOW; display_t.Code = "unhealthy"; //unhealthy for kid display_t.AQI = map(reading,38,50,51,100); } else if ( (reading >= 51) && (reading <= 90) ) { display_t.bg_color = TFT_ORANGE; display_t.Code = "very unhealthy"; //very unhealthy display_t.AQI = map( reading,51,90,101,200 ); } else if (data.pm25_env >= 91) { display_t.bg_color = TFT_RED; display_t.AQI = map( reading,91,200,201,510 ); display_t.Code = "Hazardous"; //Hazardous } return display_t; } void printTest() { // reading data was successful! Serial.println(); Serial.println("---------------------------------------"); Serial.println("Concentration Units (standard)"); Serial.print("PM 1.0: "); Serial.print(data.pm10_standard); Serial.print("\t\tPM 2.5: "); Serial.print(data.pm25_standard); Serial.print("\t\tPM 10: "); Serial.println(data.pm100_standard); Serial.println("---------------------------------------"); Serial.println("Concentration Units (environmental)"); Serial.print("PM 1.0: "); Serial.print(data.pm10_env); Serial.print("\t\tPM 2.5: "); Serial.print(data.pm25_env); Serial.print("\t\tPM 10: "); Serial.println(data.pm100_env); Serial.println("---------------------------------------"); Serial.print("Particles > 0.3um / 0.1L air:"); Serial.println(data.particles_03um); Serial.print("Particles > 0.5um / 0.1L air:"); Serial.println(data.particles_05um); Serial.print("Particles > 1.0um / 0.1L air:"); Serial.println(data.particles_10um); Serial.print("Particles > 2.5um / 0.1L air:"); Serial.println(data.particles_25um); Serial.print("Particles > 5.0um / 0.1L air:"); Serial.println(data.particles_50um); Serial.print("Particles > 10.0 um / 0.1L air:"); Serial.println(data.particles_100um); Serial.println("---------------------------------------"); } 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; }
เพิ่มเติมตอนนี้ มีน้องใหม่ สำหรับ Node32Lite แสดงผลได้เหมือนกัน แค่ต้องเหนื่อยทำเองครับ เดี่ยวจะแชร์ กันในวันหลัง