AirGradient ONE เป็นผลิตภัณฑ์ของ AirGradient Co. Ltd. ซึ่งเป็นอุปกรณ์สำหรับการตรวจวัดคุณภาพอากาศภายในอาคาร นอกจากจะสามารถวัดค่าอุณหภูมิและความชื้นของอากาศแล้วก็ยังมีความสามารถในการวัดค่าอื่น ๆ ได้อีกหลายค่า เช่น CO2, TVOC, NOx และ PM เป็นต้น ซึ่ง AirGradient ONE รุ่นที่รีวิวนี้เป็นรุ่นล่าสุด (9-th generation) ผู้ผลิตปรับให้เป็น open-source แล้ว มีทั้ง source code และ Schematic/PCB ให้เราดาวน์โหลดไปศึกษาและพัฒนาต่อเองได้ รวมถึงมีไฟล์โมเดลสามมิติของตัวกล่องใส่อุปกรณ์ซึ่งสามารถนำไปพิมพ์สามมิติได้เอง
เปิดกล่อง
เวอร์ชันที่ได้รับมาทดสอบครั้งนี้เป็นแบบชุดคิท (kit) ซึ่งจะต้องมีการประกอบอุปกรณ์เองบางส่วน อุปกรณ์ทั้งหมดถูกบรรจุมาในกล่องกระดาษลูกฟูก มีการ์ดข้อความขอบคุณจากผู้ผลิตซึ่งมี QR-code ไปยังเว็บไซต์ที่แสดงละเอียดการประกอบอุปกรณ์ ภายในกล่องมีอุปกรณ์ชิ้นใหญ่ที่สุดคือกล่องแสดงผลซึ่งมีกระดาษลูกฟูกหุ้มไว้อีกชั้น ตัวกล่องแสดงผลนั้นแยกส่วนด้านบนและด้านล่าง ยังไม่ได้ขันสกรู เมื่อเปิดฝาออกมาพบแผงวงจรหลักที่มีการบัดกรีอุปกรณ์ที่จำเป็นและติดตั้งมอดูลตรวจวัดอนุภาคในอากาศมาให้เรียบร้อยแล้ว อุปกรณ์ที่เหลือประกอบด้วยเซ็นเซอร์ 3 ตัวใส่รวมกันมาในถุงซีล, ไขควงและสกรู, สาย USB Type-C และ adapter 5V 2A ดังภาพต่อไปนี้
รายละเอียดของอุปกรณ์
- กล่อง (แยกฝาด้านบนและด้านล่าง)
• ควบคุมการทำงานด้วยไมโครคอนโทรเลอร์ ESP32-C3 Mini
• จอแสดงผลแบบ OLED ขนาด 1.3 นิ้ว ความละเอียด 128×64 พิกเซล
• RGB LED สำหรับแสดงสถานะการทำงาน จำนวน 11 ดวง - เซ็นเซอร์
▪ 1 x Plantower PMS5003 PM Sensor
▪ 1 x Senseair S8 CO2 Sensor
▪ 1 x SHT4x Temperature and Humidity Sensor Module
▪ 1 x SGP41 TVOC / NOx Sensor Module - อื่น ๆ
▪ 1 x 5V 2000mA Adaptor
▪ 1 x 90 degree USB Type-C cable
▪ 4 x M1.8×10 Torx T6 Screws
▪ 1 x Torx T6 Screw driver
การประกอบ
การประกอบชุดคิทนี้ทำได้ง่าย เว็บไซต์ของผู้ผลิตแสดงรายละเอียดการประกอบไว้ให้อย่างดี ซึ่งโดยรวมแล้วเรามีเซ็นเซอร์ที่ต้องติดตั้งเองเพียงแค่ 3 ชิ้น ดังภาพด้านล่างนี้
- Senseair S8 Module เป็นเซ็นเซอร์สำหรับการวัดค่า CO2 สามารถเสียบลงบอร์ดได้ง่าย ไม่ต้องระวังการสลับทิศเนื่องจากจำนวนขาของแต่ละฝั่งไม่เท่ากัน
- SGP41 เป็นเซ็นเซอร์วัดค่าสารประกอบอินทรีย์ระเหยง่าย (Total volatile organic compounds : TVOC) และไนโตรเจนออกไซด์ (NOx) โดยรุ่นที่รับมาทดสอบครั้งนี้จะใช้ soildering mask สีน้ำเงิน ให้เรานำไปเสียบลงในช่องที่สกรีนว่า I2C3
- SHT4X สำหรับการวัดอุณหภูมิและความชื้นของอากาศ โดยรุ่นที่รับมาทดสอบครั้งนี้จะใช้ soildering mask สีม่วงแดง (margenta) นำไปเสียบในช่องที่สกรีนว่า SHT1
- PMS5003 เป็นเซ็นเซอร์สำหรับการวัดปริมาณอณุภาคในอากาศ PM 1.0, PM2.5, และ PM 10 มอดูลนี้ใช้ connector แบบ JST ซึ่งสินค้าตัวอย่างที่ได้รับมานั้นติดตั้งลงบนแผงวงจรหลักเรียบร้อยแล้ว เพียงแต่ตรวจสอบว่าสายนั้นแน่นดีแล้วหรือยัง
จุดที่ผู้ใช้ควรระวังคือการติดตั้ง SGP41 กับ SHT4X เนื่องจากมีขนาดและจำนวนขาเท่ากัน อีกจุดหนึ่งคือต้องตรวจสอบทิศทางการเสียบขาเซ็นเซอร์ลงบอร์ดให้ถูกต้อง ดังตัวอย่างในภาพด้านล่างนี้
เมื่อตรวจสอบการติดตั้งอุปกรณ์เรียบร้อย ก็เสียบสาย USC Type-C ที่ด้านหลังกล่องเพื่อเปิดการทำงานได้ โดยฝากล่องด้านหลังนั้นก็มีการออกแบบช่องสำหรับร้อยสาย cable เพื่อความเป็นระเบียบไว้ให้ด้วย
หลังจากเสียบสายและหน้าจอ OLED ด้านหน้าติด แสดงข้อความ แสดงว่าการติดตั้งทุกอย่างเรียบร้อย สามารถปิดฝากล่องให้เข้าที่และไขสกรูฝากล่องให้แข็งแรง ก็พร้อมสำหรับการทดสอบการใช้งานในขั้นต่อไป
การใช้งานครั้งแรก
เมื่อเปิดใช้งาน หน้าจอแรกจะแสดงข้อความให้เรากดปุ่มเพื่อกำหนดรูปแบบของหน่วยวัดต่าง ๆ เราสามารถกดปุ่มนี้ได้ผ่านทางช่องเล็ก ๆ ด้านหลังกล่อง ถ้าเราต้องการตั้งค่าก็ให้กดปุ่มนี้ค้างไว้จนหน้าจอแสดงข้อความเหมือนในภาพด้านล่างนี้ หลังจากนั้นให้เรากดปุ่มสั้น ๆ เพื่อสลับรูปแบบของหน่วยวัด ซึ่งเราสามารถกำหนดได้ทั้งหมด 4 แบบ ดังนี้
- อุณหภูมิ : °C, อนุภาคในอากาศ : ug/m3
- อุณหภูมิ : °C, อนุภาคในอากาศ : US AQI
- อุณหภูมิ : °F, อนุภาคในอากาศ : ug/m3
- อุณหภูมิ : °F, อนุภาคในอากาศ : US AQI
เมื่อตั้งค่าที่ต้องการแล้วให้กดปุ่มค้างไว้เพื่อบันทึกค่าและรีสตาร์ทระบบให้เริ่มทำงานใหม่อีกครั้ง
หน้าจอต่อมาจะเป็นการรอการเชื่อมต่อ WiFi ในการใช้งานครั้งแรก เราจะต้องกำหนดค่า SSID และรหัสผ่านก่อนจึงจะใช้งานได้ โดยขั้นแรกเราจะต้องจด serial number ที่แสดงขึ้นมาบนหน้าจอไว้ จากนั้นจึงใช้คอมพิวเตอร์หรือสมาร์ทดีไวซ์ค้นหาและเชื่อมต่อ WiFi Hotspot ของอุปกรณ์ซึ่งใน firmware ที่ได้รับมาจะมีชื่อ hotspot เป็น AG-xxxxxx
โดยที่ xxxxxx
เป็นชื่อ serial number ของอุปกรณ์เรา เมื่อเชื่อมต่อกับ WiFi ของอุปกรณ์แล้วให้เราตั้งค่า SSID และรหัสผ่านของเครือข่ายที่เราต้องการ เสร็จแล้วจึงกดบันทึกค่า
หลังจากนั้นก็จะเข้าสู่หน้าหลักของอุปกรณ์ ไฟ LED ด้านบนจะแสดงสีตามระดับคุณภาพอากาศ หน้าจอ OLED จะแสดงข้อมูลจากเซ็นเซอร์ตามหน่วยที่เรากำหนดค่าไว้ ระบบสามารถทำงานได้ปกติแม้ว่าจะไม่มีการเชื่อมต่ออินเตอร์เน็ต แต่ถ้าเชื่อมต่อได้ระบบจะส่งข้อมูลไปยังเครื่องแม่ข่ายของผู้ผลิตที่กำหนดไว้
เมื่อตรวจสอบจาก serial monitor พบว่าข้อมูลจากเซ็นเซอร์จะถูกนำมารวมกับ RSSI ของ WiFi แล้วจัดรูปแบบเป็นข้อมูล JSON
ก่อนที่จะส่งไปยัง server ของผู้ผลิตซึ่งมี URL เป็น http://hw.airgradient.com/sensors/airgradient:xxxxx/measures
โดยที่ xxxxx
คือ serial number ของอุปกรณ์ เมื่อทดสอบการส่ง HTTP POST
ด้วย PostMan
พบว่าส่งข้อมูลไม่สำเร็จ เครื่องแม่ข่าย response เป็น sensor 'airgradient:xxxxxx' unknown
จึงคาดว่าจะน่าจะเกิดจาก AirGradient Dashboard ของทางฝั่ง server ยังไม่ได้ตั้งค่าให้รับข้อมูลจากอุปกรณ์นี้ ดังนั้นถ้าหากเราต้องการใช้ AirGradient Dashboard ทดสอบการทำงาน เราก็จำเป็นต้องดำเนินการขั้นต่อไปให้เรียบร้อย
เชื่อมต่อกับ AirGradient Dashboard
การใช้งาน AirGradient Dashboard เพื่อรับและแสดงค่าจากอุปกรณ์นั้นทำได้ด้วยการเข้าไปที่ยูอาร์แอล https://app.airgradient.com/onboarding/welcome เพื่อสมัครสมาชิกแล้วเข้าสู่ระบบให้เรียบร้อย จากนั้นจึงเพิ่มข้อมูลต่าง ๆ เช่น ชื่อสถานที่, serial number ของอุปกรณ์ให้เรียบร้อย เสร็จแล้วจะเข้าสู่หน้า Dashboard ได้ หลังจากนั้นจึงรีเซ็ตอุปกรณ์หรืออาจจะรอสักครู่จะพบว่าการรับส่งข้อมูลทำได้สำเร็จ หน้าต่าง Dashboard สามารถแสดงค่าจากอุปกรณ์ได้ถูกต้อง
การแฟลชโปรแกรม
ผู้ผลิตแจ้งในเว็บไซต์ว่าสามารถเราสามารถแฟลชโปรแกรมได้ 2 แนวทาง แนวทางแรกคือการแฟลชด้วยการใช้เว็บเบราเซอร์ อีกทางหนึ่งคือทำผ่าน Arduino IDE โดยการรีวิวครั้งนี้เราจะทดลองใช้วิธีที่สอง และเนื่องจากผู้ผลิตไม่ได้กำหนดเวอร์ชันของ Arduino IDE ไว้ ดังนั้นการทดสอบนี้จะใช้ Arduino IDE 1.8.13 ซึ่งมีติดตั้งไว้ในคอมพิวเตอร์ของผมเรียบร้อยแล้ว โดยเราสามารถดาวน์โหลด source-code ได้จาก GitHub ของผู้ผลิต ขยายไฟล์ให้เรียบร้อย เสร็จแล้วใช้ Arduino IDE เปิดไฟล์ ONE_V9.ino ขึ้นมา สำหรับการเลือกบอร์ดใน Arduino IDE นั้น ผู้ผลิตให้กำหนดพาธ Additional Boards Manager ตามภาพด้านล่างนี้ เสร็จแล้วจึงเลือกบอร์ดเป็น Lolin C3 Mini
สำหรับรายชื่อของ library ที่จำเป็นนั้นจะอยู่บริเวณส่วนบนของไฟล์ ONE_V9.ino ซึ่งในเวอร์ชันที่รีวิวนี้มีระบุ library ที่จำเป็นไว้ 6 โปรแกรม ดังนี้
- WifiManager by tzapu, tablatronix tested with version 2.0.11-beta
- U8g2 by oliver tested with version 2.32.15
- Sensirion I2C SGP41 by Sensation Version 0.1.0
- Sensirion Gas Index Algorithm by Sensation Version 3.2.1
- Arduino-SHT by Johannes Winkelmann Version 1.2.2
- Adafruit NeoPixel by Adafruit Version 1.11.0
ซึ่งการติดตั้งนั้นก็จะทำเหมือนการติดตั้ง library ของ Arduino IDE นั่นคือเราสามารถใช้ Library Manager ในการจัดก็ได้ แต่การรีวิวนี้ผมใช้การค้นหาและนำไฟล์ต่าง ๆ ไปวางในโฟล์เดอร์ Libraries ของ Arduino ด้วยตนเอง
ในกรณีของผม เมื่อทดลองคอมไพล์ก็พบว่าคอมไพล์ไม่ผ่านเนื่องจากขาด library บางตัวไป ซึ่งพบว่าต้องการ library ต่อไปนี้เพิ่ม
- PMS Library by Markusz Kakl Version 1.1.0
- S8 UART by Josep Comas Version 1.0.0
- U8g2 by oliver Version 2.32.15
- Sensirion Core by Sensirion Version 0.6.0
หลังจากนั้นจึงพบปัญหาอีกประการหนึ่งเกี่ยวกับข้อผิดพลาดใน PMS Library ซึ่งผู้ผลิตแจ้งไว้ใน Blog ว่าอุปกรณ์ชุดนี้ใช้ PMS5003T ดังนั้นเราจำเป็นต้องแทนที่โค๊ดทั้งหมดในไฟล์ PMS.h
และไฟล์ PMS.cpp
ด้วยโค๊ดใหม่ด้านล่างนี้
ไฟล์ PMS.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
#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 |
ไฟล์ PMS.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
#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++; } } |
เมื่อทดสอบการคอมไพล์อีกครั้ง พบว่าอุปกรณ์ไม่สามารถเชื่อมต่อ WiFi ได้ ซึ่งเมื่อตรวจสอบพบว่าโค๊ดต้นฉบับได้กำหนดชื่อ SSID กับ password ไว้ ซึ่งหลังจากทดลองเปลี่ยนให้ถูกต้องแล้วคอมไพล์ใหม่ พบว่าทำงานได้ตามปกติ
ความเป็น Open-source
source-code โหลดได้จาก GitHub ซึ่งนอกจากจะมี firmware ของกล่องนี้แล้ว ยังมีของรุ่นอื่นและเวอร์ชันอื่น ๆ ให้ศึกษาเพิ่มเติมได้ และเนื่องจากมี source code อยู่ในมือ การทดสอบต่อมาจึงเป็นการทดลองส่งข้อมูลไปยัง server อื่น โดยในที่นี้จะใช้เครื่อง server ของที่ทำงานในการรับข้อมูล โดยการแก้ไขตัวแปร APIROOT
บริเวณด้านบนของโค๊ดให้เป็น URL ที่เราต้องการ จากนั้นจึงปรับปรุงฟังก์ชัน sendToServer(
) ให้มีการส่งค่า hardware id เพิ่มเข้าไปอีกหนึ่งรายการ และปรับปรุงตัวแปร POSTURL
ให้เหมาะสมกับโค๊ดฝั่ง server ที่เตรียมไว้ เสร็จแล้วคอมไพล์และ upload ก็พบว่าระบบสามารถรับส่งข้อมูลได้ดี
การทดสอบเขียนโปรแกรมอีกอย่างหนึ่งจะเป็นทดลองเปลี่ยนรูปแบบการใช้ LED แสดงสถานะตอนเปิดเครื่องก่อนเข้าหน้าแสดงผลหลัก จากเดิมที่ไม่มีการแสดงผลเปลี่ยนมาเป็นเพิ่มลูกเล่นนิดหน่อยด้วยการสุ่มสี LED ทั้ง 11 เป็นเวลาสั้น ๆ ก่อนจะทำงานตามคำสั่งเดิมต่อไป การแก้ไขก็ทำได้ง่ายเนื่องจากโปรแกรมใช้มอดูล NeoPixel ในทำงาน โดยในการทดลองนี้เราจะลองควบคุมการทำงานได้้ด้วยการเพิ่มโค๊ดตัวอย่างไปนี้ลงไปบริเวณส่วนท้ายของฟังก์ชัน setup()
ซึ่งเมื่อรันแล้วพบว่า LED ทั้ง 11 ดวงมีการแสดงสีเปลี่ยนไปได้ตามที่ต้องการ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
void setup() { ... int i; int r, g, b; for(i = 0; i < 11; i++) { pixels.setPixelColor(i, pixels.Color(0, 0, 0)); delay(1); } pixels.show(); for(i = 0; i < 32; i++) { for(j = 0; j < 11; j++) { r = random(0, 256); g = random(0, 256); b = random(0, 256); pixels.setPixelColor(j, pixels.Color(r, g, b)); } delay(50); pixels.show(); } for(i = 0; i < 11; i++) { pixels.setPixelColor(i, pixels.Color(0, 0, 0)); } pixels.show(); } |
นอกจาก source code สำหรับการศึกษารายละเอียดฝั่งซอฟต์แวร์แล้ว ผู้ผลิตยังมีข้อมูล schematic diagram และ PCB ให้ดาวน์โหลดไปศึกษาฝั่งของฮาร์ดแวร์ได้ด้วย โดยที่เราสามารถเปิดได้ด้วยโปรแกรม KiCad ดังภาพต่อไปนี้
นอกจากที่กล่าวมาแล้ว ผู้ผลิตยังได้ให้ไฟล์ชิ้นงานสามมิติจำนวนทั้งหมด 3 ชิ้น ประกอบด้วยฝาด้านบน ฝาด้านล่าง และช่องสำหรับเสียบอุปกรณ์ ในรูปแบบไฟล์ STL และ STP ซึ่งในตัวอย่างนี้ผมทดลองเปิดไฟล์ STL ด้วยโปรแกรม MeshLab และทดลองพิมพ์ชิ้นงานสามมิติออกมาหนึ่งชิ้น ได้ผลลัพธ์เป็นชิ้นงานที่น่าพอใจ ดังภาพต่อไปนี้
สรุป
AirGradient ONE โดยรวมแล้วอุปกรณ์ที่ได้รับมารีวิวครั้งนี้ทำงานได้ดี อาจจะพบปัญหาการอ่านค่าจากเซ็นเซอร์ได้ยังไม่เสถียรในช่วงเริ่มเปิดอุปกรณ์แต่เมื่อผ่านไปสักครู่ ระบบจะอ่านค่าและส่งค่าไปยัง server ปลายทางได้อย่างสม่ำเสมอ นอกจากตัวอุปกรณ์แล้วจุดที่น่าสนใจอีกอย่างหนึ่งคือผู้ผลิตมีแนวคิด “1% for the planet” หรือการแบ่งยอดขายจำนวน 1% ให้กับงานด้านการอนุรักษ์และฟื้นฟูทรัพยากรธรรมชาติ รวมถึงในเว็บก็ยังมีส่วนที่นำเสนอบทความที่เกี่ยวข้องกับงานวิจัยทางด้านสิ่งแวดล้อม ซึ่งถือเป็นสิ่งที่ดีที่ผู้ผลิตแสดงความสนใจกับกิจกรรมทางด้านนี้ด้วย
สุดท้ายนี้ผมต้องขอขอบคุณ AirGradient ที่ส่งอุปกรณ์มาให้รีวิวในครั้งนี้ และสามารถสั่งซื้อ AirGradient ONE เวอร์ชันที่ประกอบและทดสอบการทำงานแล้ว (fully assembled and tested) เวอร์ชัน 9 นั้นจำหน่ายราคา $195 หรือประมาณ 9,600฿ ที่เว็บไซต์ของผู้ผลิต
I am an assistant professor in surveying engineering and geographic information systems at Rambhai Barni Rajabhat University. My primary research areas include digital image/audio processing, digital photogrammetry, AI, IoT, and UAV. I am open to other subjects as well.