สวัสดีครับ ห่างหายไปนานสำหรับบทความ ในช่วงวิกฤตการณ์แบบนี้ เราต่างรู้ดีว่าควรต้องอยู่บ้าน แต่ตราบใดที่บางอาชีพยังคงไม่มีมาตรการจัดการ บริษัทก็ยังคงประกอบการปกติ หรือเป็นผู้ที่ไม่ได้อยู่ในข่ายที่ได้รับเงินเยียวยา อาจต้องออกไปข้างนอกบ้าง หรือเป็นการออกไปซื้อของมาตุนไว้กินที่บ้าน ทุกๆนาทีที่อยู่นอกบ้านก็เพิ่มความเสี่ยงที่จะติดเชื้อ แม้ว่าจะใส่หน้ากากอนามัย แต่เชื้อไวรัสสามารถลอยตัวอยู่และติดกลับบ้านมาบนผิวหนังได้ การทำความสะอาดอย่างทั่วถึงด้วยการอาบน้ำจึงเป็นวิธีที่ดีที่สุด แต่หากเราออกบ่อยกว่า 1 ครั้งแบบผมนั้น ตัวคงเปื่อยแน่นอน
ในบทความนี้เลยเสนอโปรเจคไว้ทำเสริมทักษะโดยการใช้งาน NodeMCU คู่กับปั้มน้ำ 12v จ่ายแรงดันน้ำออกไปยังหัวพ่นหมอก เพื่อพ่นทำความสะอาดร่างกายหลังจากกลับมาจากนอกบ้านครับ
อุปกรณ์ที่ต้องใช้
1. โครงสร้าง PVC ออกแบบได้ตามใจชอบ โดยขนาดที่กำลังดีอยู่ที่ 90cm x 90cm x 200cm (งบไม่เกิน 1000 บาท)
2. ตู้ควบคุมขนาดปานกลาง (~250 บาท)
3. ระบบน้ำ (ประมาณ 850 บาท)
4. อุปกรณ์อิเล็กทรอนิกส์
5. น้ำยาฆ่าเชื้อที่ใช้กับผิวหนังได้ Dettol antiseptic disinfectant (สำคัญ *** สังเกตให้ดีต้องมีมงกุฎด้านบนฉลาก ถึงจะสามารถใช้กับผิวหนังคนได้) (1600B)
โดยหลักการการทำงานจะใช้ Nodemcu ในการรับค่าเซนเซอร์ Infrared ที่ติดตั้งอยู่ใต้ตู้ Control เพื่อสั่งให้มอเตอร์เริ่มทำงานโดยเปิด Mosfet relay ที่เวลานั้น และดับมอเตอร์ลงเมื่อเวลาผ่านไป 30 วินาที หรือเปิดปิดมอเตอร์โดยการกดปุ่มกดติดกดดับในกรณีต้องการชำระล้างเป็นเวลานานกว่า 30 วินาที
การเลือกปั้มควรเลือกที่เหมาะสมกับหัวพ่นหมอกที่มี โดยปกติหัวพ่นหมอกสแตนเลสจะจ่ายน้ำออกประมาณ 2 ลิตร/ชั่วโมง (l/h) ซึ่งหากปั้มเราที่ทำงานที่แรงดัน 12v มีอัตราการจ่ายน้ำที่ 4 ลิตร ต่อนาที (l/min) หรือ 240 l/hr เราอาจต้องใช้หัวพ่นหมอกจำนวนมากเพื่อไม่ให้เกิดความดันในท่อน้ำเกิน ซึ่งแน่นอนว่าการเพิ่มหัวหมอกนั้นเกินความจำเป็นไปมาก เราจึงจำเป็นต้องลดอัตราการไหลจากการแรงดันจากเดิม 12v ลงมาเหลือเพียงค่าๆนึงที่ทำให้ปั้มน้ำยังทำงานได้และไม่สั่นเพราะความดันเกินในระบบท่อน้ำนั่นเอง
โดยการลดแรงดันจะใช้ Motor speed control มาคั่นไว้ระหว่างมอเตอร์แหละ mosfet relay ซึ่ง mosfet relay จะถูกต่อกับแหล่งจ่ายไฟ 12V และควบคุมโดย Nodemcu นั่นเอง
โดยผังการต่อวงจรเป็นดังนี้
Code การทำงานจะแบ่งไฟล์ออกเป็น 7 ไฟล์ดังนี้
1. anti_covid_control.ino
#include <Arduino.h> #include "config.h" #include "led.h" #include "driver.h" #include "sensor.h" #include "wifi_util.h" #include "state_machine.h" void setup() { drive_init(); console_init(); led_init(); sensor_init(); wifi_init(); Serial.println("Started"); } void loop() { wifi_handle(); if (drive_but_pressed()) { drive_set(HIGH); led_set_r(true); led_set_g(false); } else { if ( sensor_get() && !st_is_holding()) { st_set_holding(); } if (st_is_holding()) { // Serial.println("Drive"); drive_set(HIGH); led_set_g(false); led_flash(LED_R, 500); } else { // Serial.println("Not Drive"); led_set_r(false); led_flash(LED_G, 500); drive_set(LOW); } } }
2. config.h
#define HOLD_SEC 30 void console_init() { Serial.begin(115200); }
3. driver.h
#define DRIVE_PIN 13 #define DRIVE_BUT D1 void drive_init() { pinMode(DRIVE_PIN, OUTPUT); pinMode(DRIVE_BUT, INPUT_PULLUP); digitalWrite(DRIVE_PIN, LOW); } static inline bool drive_but_pressed() { return !digitalRead(DRIVE_BUT); } static inline void drive_set(const bool& val ) { digitalWrite(DRIVE_PIN, val); }
4. led.h
/** * Pin out */ #define LED_R D6 #define LED_Y D0 #define LED_G D4 #define LED_ON HIGH #define LED_OFF LOW void led_init() { pinMode(LED_R, OUTPUT); pinMode(LED_Y, OUTPUT); pinMode(LED_G, OUTPUT); digitalWrite(LED_R, LED_OFF); digitalWrite(LED_Y, LED_OFF); digitalWrite(LED_G, LED_OFF); } static inline void led_set_r(const bool& val) { digitalWrite(LED_R, (val ? LED_ON : LED_OFF)); } static inline void led_set_y(const bool& val) { digitalWrite(LED_Y, (val ? LED_ON : LED_OFF)); } static inline void led_set_g(const bool& val) { digitalWrite(LED_G, (val ? LED_ON : LED_OFF)); } static inline void led_flash(const int& pin, const int& interval) { bool last_state = 0; uint32_t t_now = millis(); last_state = ((t_now % (uint32_t)interval*2) < interval); digitalWrite(pin, last_state); }
5. sensor.h
#define SENS_PIN D5 #define SENS_DETECTED false #define SENS_UNDETECTED true void sensor_init() { pinMode(SENS_PIN, INPUT); } static inline bool sensor_get() { static int val_count = 0; bool val = digitalRead(SENS_PIN); if(val == SENS_DETECTED) { val_count ++; }else{ val_count = 0; } if(val_count > 20) { val_count = 0; return true; } return false; }
6. state_machine.h
int32_t last_trig = -31000; static inline void st_set_holding() { last_trig = millis(); } static inline bool st_is_holding() { uint32_t t_now = millis(); if( (int32_t)t_now - last_trig < HOLD_SEC*1000 ) { return true; }else{ return false; } }
7. wifi_util.h
/***********************| * WiFI สำหรับ ESP8266 | ***********************/ #include <ESP8266WiFi.h> #include <ESP8266mDNS.h> #include <WiFiUdp.h> #include <ArduinoOTA.h> /************************| * รหัส WiFi ที่ต้องเชื่อมต่อ | ************************/ char* ssid = "Kanuengnit_kiss2G"; char* pwd = "3213213213"; #define ALWAY_CONNECT true /************************| * ตั้งค่า Static IP ตรงนี้ | ************************/ IPAddress local_IP(192, 168, 1, 56); IPAddress gateway(192, 168, 1, 1); IPAddress subnet(255, 255, 255, 0); IPAddress primaryDNS(8, 8, 8, 8); void wifi_ota_init() { ArduinoOTA.onStart([]() { String type; if (ArduinoOTA.getCommand() == U_FLASH) { type = "sketch"; } else { // U_FS type = "filesystem"; } // NOTE: if updating FS this would be the place to unmount FS using FS.end() Serial.println("Start updating " + type); }); ArduinoOTA.onEnd([]() { Serial.println("\nEnd"); }); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { Serial.printf("Progress: %u%%\r", (progress / (total / 100))); }); ArduinoOTA.onError([](ota_error_t error) { Serial.printf("Error[%u]: ", error); if (error == OTA_AUTH_ERROR) { Serial.println("Auth Failed"); } else if (error == OTA_BEGIN_ERROR) { Serial.println("Begin Failed"); } else if (error == OTA_CONNECT_ERROR) { Serial.println("Connect Failed"); } else if (error == OTA_RECEIVE_ERROR) { Serial.println("Receive Failed"); } else if (error == OTA_END_ERROR) { Serial.println("End Failed"); } }); ArduinoOTA.begin(); Serial.println("Ready"); Serial.print("IP address: "); Serial.println(WiFi.localIP()); } void wifi_init() { Serial.println("Checking wifi req."); pinMode(0, INPUT_PULLUP); led_set_y(true); delay(2000); if(digitalRead(0) == LOW || ALWAY_CONNECT) { Serial.println("WiFi requested."); WiFi.config(local_IP, primaryDNS, gateway, subnet); WiFi.begin(ssid, pwd); while(WiFi.status() != WL_CONNECTED) { led_set_y(false); delay(100); led_set_y(true); delay(100); } Serial.println("WiFi started ! IP = " + WiFi.localIP().toString()); wifi_ota_init(); }else{ Serial.println("WiFi not requested."); } led_set_y(false); } void wifi_handle() { ArduinoOTA.handle(); }
สังเกตว่าในโค้ดส่วน wifi_util.h จะมีฟังก์ชัน OTA รองรับสำหรับการอัพโหลดไร้สายด้วย เราจึงสามารถอัพโหลดโปรแกรมเวอร์ชันใหม่ๆ ผ่าน wifi ได้โดยไม่ต้องไปเปิดตู้ Control ทุกครั้งครับ สำหรับรายละเอียดเพิ่มเติมสามารถหาอ่านได้จาก บทความ OTA ได้เลย
ในส่วนของการผสมน้ำยาฆ่าเชื้อ เราจะใช้น้ำยา Dettol antiseptic disinfectant ในอัตราส่วน 1 ml ต่อ น้ำ 200 ml ตามสูตรสำหรับการอาบน้ำ ซึ่ง 1 ฝาของน้ำยาขนาด 5L มีปริมาตร 21ml ดังนั้นการผสมน้ำจะใช้ 4200 ml หรือประมาณ 4.2kg ต่อน้ำยา 1 ฝา กรณีของผมใช้เป็นถังเล็ก 10l จึงเลือกใช้เป็น 2 ฝา ต่อน้ำ 8.4kg นั่นเองครับ โดยเราควรวางถังน้ำไว้สูงกว่าปั้มเล็กน้อย เพื่อให้ไม่มีอากาศค้างในระบบท่อ
แต่หากใครไม่ถนัดด้านการเขียนโปรแกรม หรือไม่เคยใช้มาก่อนและไม่ได้วางแผนจะนำไปต่อยอด ก็สามารถใช้ Timer แทน Nodemcu ในการสั่งการหน่วงเวลาที่เปิดปั้มได้เช่นกัน
หวังว่าโปรเจคนี้จะให้แรงบรรดาลใจในการคิดค้นเครื่องมือสู้ภัย Covid-19 มีคำถามหรือข้อเสนอแนะประการใดสอบถามเข้ามาทางไลน์ร้านได้เลยครับ
ธนบดี บุหลันศรีชาติ
ผู้เขียน