จากช่วงต้นปี เกิดวิกฤตฝุ่นพิษ 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 แสดงผลได้เหมือนกัน แค่ต้องเหนื่อยทำเองครับ เดี่ยวจะแชร์ กันในวันหลัง

chang

ชื่อ “ช้าง” ส่วนมากเขาจะเรียกว่า “พี่ช้าง” แล้ว มีความสนใจทางเทคโนโลยีทางคอมพิวเตอร์ อิเล็กทรอนิกส์ และ หุ่นยนต์ เป็นทั้งนักคิด นักประดิษฐ์ ชอบทดลองเล่น จนเดี่ยวนี้รู้สึกว่าจะเล่นมากกว่ามืออาชีพไปสักแล้ว

Leave a Reply