ผู้เขียนบทความ : 165404140091 นายอนุวัตร์ ไตรเวทย์ และ 165404140072 นายเทพพิทักษ์ เกรียงไกรฉัตร COE#15
คณะวิศวกรรมศาสตร์ สาขาวิศวกรรมคอมพิวเตอร์
วิชา 04-513-201 การโปรแกรมคอมพิวเตอร์ขั้นสูง 1/2566
1. ความเป็นมา
ในปัจจุบันปัญหาสุขภาพเป็นภัยที่พบได้ตลอดในคนทุกยุคและทุกวัย ส่งผลให้คนส่วนใหญ่ต้องได้รับยา เพื่อแก้ไขปัญหาสุขภาพรวมถึงรักษาโรคประจําตัวต่างๆ แต่ปัญหาที่พบส่วนใหญ่ของผู้ที่ต้องได้รับยาคือมักจะลืมบริหารยาซึ่งอาจมาจากสาเหตุหลายประการ เช่น อาชีพที่มีเวลาในการทํางานไม่เป็นเวลา เป็นผู้สูงอายุที่อาจจะจําไม่ได้ว่าแพทย์สั่งยาไว้ อย่างไรและมีวิธีใช้ยาที่ถูกต้องแบบใด ทําให้ผู้ป่วยได้รับยาไม่ตรงตามที่แพทย์สั่งหรือบริหาร ยาไม่ตรงเวลา ซึ่งอาจส่งผลให้ผู้ป่วยไม่ได้รับการรักษาจากยาได้เต็มประสิทธิภาพเท่าที่ควร
เนื่องจากเกิดปัญหาดังกล่าว จึงได้ทําระบบการแจ้งเตือนการรับประทานยาขึ้นมา เพื่อที่จะได้รับประทานยาได้ตรงตามเวลา เพิ่มความสะดวกแก่ผู้ดูแลและผู้สูงวัยให้รับประทานยาได้ตรงเวลา อย่างสม่ําเสมอ
2. วัตถุประสงค์
2.1. เพื่อศึกษาและเข้าใจการเขียนโปรแกรมด้วยภาษา C++ เพื่อใช้ควบคุมระบบแจ้งเตือนการรับประทานยา
2.2. เพื่อพัฒนาและออกแบบระบบแจ้งเตือนการรับประทานยา
2.3. เพื่อนำระบบแจ้งเตือนการรับประทานยา ไปใช้งานจริง
3. ขอบเขต
3.1 ระบบสามารถทำตามเงื่อนไขที่กำหนดได้
3.1.1 เมื่อรับประทานยาตามเวลาที่กำหนด
3.1.2 เมื่อไม่ได้รับประทานยาตามเวลาที่กำหนด
3.2 ระบบสามารถสั่งการผ่าน blynk ได้
3.3 ระบบสามารถส่งภาพและข้อความไปยังไลน์ได้
4. ประโยชน์ที่คาดว่าจะได้รับ
4.1 ได้ศึกษาและเข้าใจการเขียนโปรแกรมด้วยภาษา C++ เพื่อใช้ควบคุมระบบแจ้งเตือนการรับประทานยา
4.2 สามารถพัฒนาและออกแบบระบบแจ้งเตือนการรับประทานยาได้
4.3 สามารถนำระบบแจ้งเตือนการรับประทานยา ไปใช้งานจริง
5. ความรู้ที่เกี่ยวข้อง
5.1 Blynk
คือ แอปพลิเคชันสำเร็จรูปที่ใช้สำหรับงานที่เกี่ยวกับอินเทอร์เน็ตของสรรพสิ่ง โดยขั้นตอนแรก หากยังไม่เคยสร้าง account มาก่อน ให้ไปที่ Create new account โดยเมื่อสร้างเสร็จแล้วจะได้หน้าตาแบบนี้
หลังจากนั้นให้สร้าง Template ให้ไปที่ My Devices จะมีปุ่มให้เราสร้าง New Template ตั้งชื่อ Template กรณีนี้เราใช้ Medication reminder system ในการตั้งชื่อ จึงเลือก Hardware เป็น ESP32 และเลือก Connection type เป็น WiFi
จากนั้นให้เราไปตั้ง Datastreams เพื่อตั้งค่า pin ที่จะใช้งานสำหรับ Template ตัวนี้ ให้กดที่ New Datastream แล้วทำการเลือก pin ที่ต้องการใช้งาน กรณีนี้เราจะใช้เป็น Virtual Pin ตัวอย่างนี้ เราจะใช้ pin V1 เป็นตัวควบคุมของเซนเซอร์ซ้าย V2 เป็นตัวควบคุมของเซนเซอร์กลาง และ V3 เป็นตัวควบคุมของเซนเซอร์ขวา เป็นค่า Voltage จากนั้นให้ตั้งค่าอื่นๆตามความเหมาะสม แล้วกด Create
จากนั้นให้ไปที่เมนู Automations เพื่อเปิดใช้งานการตั้งเวลาอัตโนมัติ ผ่าน Blynk Application โดยการเปลี่ยน Type Of Automation ให้เป็น Switch และ ตั้ง Condition กับ Action ให้เป็น on ดังภาพ
จากนั้นให้ไปยังเมนู My Device แล้วเลือก Device ที่ได้สร้างไว้ จากนัน้ให้ไปยัง Device info เพื่อทำการบันทึก FIRMWARE CONFIGURATION โดยจะนำไปใช้ในการตั้งค่า ESP32 ในขั้นตอนอื่น ดังภาพ
จากนั้นให้ไปตั้งค่า BLYNK Application ภายในโทรศัพท์ ให้มำการกดไปยัง Template ที่ได้สร้างไว้ แล้วกดปุ่มประจะขวาล่างของแอปพลิเคชั่นเพื่อเพิ่มปุ่มสำหรับทดสอบการทำงาน โดยวิธีเพิ่มปุ่มนั้น สามารถทำได้โดยการกดเครื่องหมาย + ด้านขวาบนของแอปพลิเคชั่น ดังภาพ
โดยในที่นี้ เราจะใช้ button เป็นตัวควบคุม จากนั้นให้ตั้งค่าปุ่มโดยให้ data stream เป็นตัวเดียวกับขา virtual pin ที่เราได้ตั้งค่าไว้ในเว็บไซท์ ในส่วนของ settings ให้เปลี่ยนจาก push เป็น switch และสร้าง button ให้แก่ virtual pin แต่ละตัว ดังภาพ
จากนั้นให้กลับไปหน้าหลักแล้วเลือกเมนู automations แล้วกด add automation เพื่อเพิ่มการทำงานอัตโนมัติ โดยในที่นี้ให้เลือก schedule แล้ว ตั้งเวลาตามต้องการ ดังรูป
จากนั้น กด Add Action และกด Control Device เลือก Device ที่เราได้ทำการสร้างไว้ และเลือกปุ่ม Virtual Pin ที่ได้สร้างไว้ก่อนหน้า โดยให้ 1 Automations ควบคุมเพียง 1 Virtual Pin จากนั้นให้สร้าง Automations สำหรับ Virtual Pin แต่ละตัว ดังภาพ
5.2 Line Notify
ในส่วนนี้จะใช้ line notify ในการส่งภาพและข้อความ เพื่อให้ทราบว่าผู้ป่วยได้ทานยาหรือไม่ โดยเราต้องนำ Line Token มาใส่ในโค้ด โดยการสร้าง Line token สามารถทำได้โดยการ เช้าเว็บไซท์ Line Notify แล้วเข้าสู่ระบบด้วยไลน์ เมื่อเข้าสูระบบแล้วให้กดลูกศรขวาบน แล้วกดที่ Mypage
จากนั้นให้กด Generate Token ตั้งชื่อ bot สำหรับการแจ้งเตือนและสามารถเลือกได้ว่าจะให้แจ้งเตือนในกลุ่มหรือส่วนตัวได้ โดยในที่นี้จะเลือก 1 on 1 chat with line notify ดังภาพ
เมื่อกด Generate Token แล้ว จะมี Token มาให้ ซึ่งสามารถดูได้ครั้งเดียวเท่านั้น แนะนำให้บันทึกไว้ เราจะนำ Line Token ไปใช้ใน ขั้นตอนอื่น ดังภาพ
5.3 อุปกรณ์และวงจรของ ระบบแจ้งเตือนการรับประทานยา
ประกอบด้วย ESP32 ,ESP32-CAM ,เซ็นเซอร์อินฟราเรด ,หลอด LED ,สายไฟ ,สวิตชิ่งขนาด 5V 5A ,DFpalyer mini ซึ่งจะใช้ร่วมกับ SD card ที่บรรจุไฟล์เสียงจำนวน 3 ไฟล์ (ในส่วนของการตั้งค่า SD card นั้น ต้อง format เป็น FAT32 และต้องมีขนาดไม่เกิน 32 GB) ,ตัวขยายเสียง และลำโพง ซึ่งจะนำมาประกอบรวมกัน ดังภาพ
5.4 โค้ดการทำงาน
เริ่มต้น เราต้องกำหนดตัวแปรสำหรับข้อมูล Blynk Template ที่เราได้ทำไว้ก่อนหน้านี้ และนำ Line Token มาใส่ จากนั้นนำไฟล์ library ของอุปกรณือื่นๆมาใส่ ในส่วนนี้จะรวมไปถึง library ของ Blynk และ Line notify ด้วย
#define BLYNK_TEMPLATE_ID "---------------"
#define BLYNK_TEMPLATE_NAME "--------------"
#define BLYNK_AUTH_TOKEN "---------------------------------------" // token blynk
#define LINE_TOKEN "---------------------------------------------------" // Token line
#define BLYNK_PRINT Serial
#include <WiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h> // library ที่ใช้
#include <TridentTD_LineNotify.h>
#include "HardwareSerial.h"
#include "DFRobotDFPlayerMini.h"
จากนั้น ให้กำหนดขาของอุปกรณ์ต่างๆ ที่ต่อเข้ากับ ESP32 และกำหนด ชื่อ WIFI และ รหัสผ่าน
char ssid[] = "Ton1"; // ชื่อ wifi
char pass[] = "0630795411"; // รหัส wifi
#define led_left 13 // led ซ้าย D13 to led
#define led_middle 12 // led กลาง D12 to led
#define led_right 14 // led ขวา D14 to led
#define sensor_left 33 // เซนเซอร์ ซ้าย D33 to VOUT
#define sensor_middle 32 // เซนเซอร์ กลาง D32 to VOUT
#define sensor_right 35 // เซนเซอร์ ขวา D34 to VOUT
#define camera_capture 5 // ถ่ายรูป D5 to IO14
ขั้นตอนต่อไปให้กำหนดตัวแปรต่างๆ สำหรับนำไปคำนวนเวลา
int repeater_time = 1 ; // ตัวแปร เวลาตัวเตือนซ้ำ ให้แก้ให้ตรงกับ repeater_time +( เวลาที่ต้องการ(นาที))
unsigned long startTime = 0; // ตัวแปร เวลาเริ่มทำงานฟังก์ชั่น
unsigned long elapsedTime = 0; // ตัวแปร เวลาทำงานที่ผ่านไปฟังก์ชั่น
unsigned long lastSensorCheckTime = 0; // ตัวแปร เก็บค่าเวลาหลังจากหากล่อง/ขวดยาไม่เจอ
const unsigned long sensorCheckInterval = 1 * 1 * 15 * 1000; // ตัวแปร ตั้งเวลาเช็คขวด/กล่องยา
const byte RXD2 = 16; // ตัวแปร ขา rx ของ dfplayer mini
const byte TXD2 = 17; // ตัวแปร ขา tx ของ dfplayer mini
จากนั้นจะสร้าง Object เพื่อนำไปใช้ควบคุมอุปกรณ์ภายในโค้ด
HardwareSerial dfSD(1); // ใช้ขา UART RX,TX channel 1 (16,17)
DFRobotDFPlayerMini player; // สร้าง object เพื่อใช้ควบคุม
BlynkTimer timer;
ในส่วนของ Void Setup() จะเป็นการตั้งค่าตัวอุปกรณ์ในช่วงที่เริ่มทำงานของอุปกรณ์ ในส่วนนี้ประกอบไปด้วย การกำหนด upload speed การเชื่อต่อ blynk line token การกำหนดหน้าที่ของขาของอุปกรณ์ต่างๆ และตรวจเช็คการทำงานของ DFplayer mini จากนั้นปรับระดับเสียงของลำโพง ในที่นี้จะปรับไว้ที่ 25 (สามารถปรับได้จาก 1-30)
void setup() {
Serial.begin(115200); // กำหนดค่า upload speed ของ serial monitor เป็น 115200 (max) และตั้งค่า input , output ขาต่างๆ
Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass); // อ่าน token blynk และ wifi
LINE.setToken(LINE_TOKEN); // อ่าน token และ line
pinMode(led_left, OUTPUT);
pinMode(led_middle, OUTPUT);
pinMode(led_right, OUTPUT);
pinMode(sensor_left, INPUT); //กำหนด input-output ของอุปกรณืต่างๆ
pinMode(sensor_middle, INPUT);
pinMode(sensor_right, INPUT);
pinMode(camera_capture,OUTPUT);
dfSD.begin(9600, SERIAL_8N1, RXD2, TXD2); // 16,17 // กำหนดค่า upload speed ของ dfplayer เป็น 9600 (max) และตั้งค่า input , output ขาต่างๆ
if (player.begin(dfSD)) // ทดสอบว่าเชื่อมต่อกับ dfplayer mini ได้หรือไม่ และตั้งเสียง (0 ถึง 30)
{
Serial.println("OK");
player.volume(25); //ปรับเสียงของลำโพง (1-30)
}
else
{
Serial.println("Connecting to DFPlayer Mini failed!");
}
}
ต่อไปจะเป็นการกำหนดฟังก์ชันการทำงานหลักตัวแรก โดยจะตั้งชื่อว่า Left_Med หลักการทำงานคือ เมื่อเรียกใช้งาน จะเรียกใช้งาน dfplayer ให้เล่นเสียงแจ้เตือนรับประทานยา สั้งให้ LED ติดค้างไว้ จากนั้นจะกำหนดตัวแปรสำหรับเก็บสถานะการเปลี่ยนแปลงของสถานะ LED เพื่อใช้ในการดำเนินเงื่อนใข จากนั้นกกำหนดตัวแปรสำหรับการเตือนการรับประทานยาซ้ำ และกำหนดตัวแปรสำหรับเทียบเวลา
void Left_Med() { // ฟังก์ชั่น เตือนกินยากล่อง/ขวดซ้าย
player.play(1); // เล่นเสียงเตือน และตั้งตัวแปรเก็บค่าการเปลี่ยนแปลงของ LED ซึ่งเมื่อทำงานจะเริ่มที่ 1 เสมอ และตัวแปร เก็บสถานะของ (LED LOW,HIGH)
int led_left_StateChanges = 0; // นับจำนวนครั้งที่สถานะของ LED ซ้ายเปลี่ยนแปลง
int led_left_State = LOW;
int repeater_time = 1; //ตัวแปรสำหรับ
digitalWrite(led_left, HIGH); // เปิด LED ซ้าย // รอเป็นเวลา 8 วินาที
startTime = millis();
หลังจากนั้นจะเข้าสู้ loop while(true) โดยเริ่มแรกจะอ่านค่าจากเซนเซอร์ ในที่นี้หากเซนเซอร์ไม่สามารถตวจจับกล่องยาได้ สถานะจะเป็น HIGH จากนั้นหากเซนเวอร์เปลี่ยนสถานะไปจากเดิม หรือมีการหยิบกล่องยา ตัวแปรที่เก็บค่าสถานะของ LED จะะเปลี่ยนการทำงานไปทำอีกเงื่อนไข โดยจะสั่งให้ DFplayer mini เล่นเสียงชื่นชม และจะสั่งให้esp32 cam ถ่ายรูปส่งไปยังไลน์ และออกจาก loop while(true) เป็นอันจบการทำงาน
while (true) { // ในลูปนี้จะทำงานจนกว่าจะเจอคำสั่ง break;
int sensor_left_status = digitalRead(sensor_left); // อ่านสถานะของเซนเซอร์ซ้าย
if (sensor_left_status == HIGH) {
digitalWrite(led_left, LOW); // ปิด LED
} else {
digitalWrite(led_left, HIGH); // เปิด LED
}
if (digitalRead(led_left) != led_left_State) { // ตรวจสอบการเปลี่ยนแปลงของสถานะ LED และเพิ่มค่านับ
led_left_StateChanges++;
led_left_State = digitalRead(led_left);
}
if (led_left_StateChanges < 2) {
digitalWrite(led_left, HIGH);
}
if (led_left_StateChanges >= 2) { // เมื่อยกหรือวางยา ไฟจะดับ
player.play(2); // เล่นเสียงที่ 2
digitalWrite(led_left, LOW); // ปิด LED ซ้าย
Med_taken(); // เรียกใช้ฟังก์ชั่น Med_taken()
digitalWrite(camera_capture, HIGH); // เปิดกล้องถ่ายรูป
delay(1000); // รอเป็นเวลา 3 วินาที เพื่อลดค่าความผิดพลาด (กันเหนียว)
digitalWrite(camera_capture, LOW); // ปิดกล้องถ่ายรูป
delay(3000); // รอเป็นเวลา 5 วินาที
break; // ออกจากลูป
}
ต่อไปจะเป็นส่วนของการแจ้งเตือนซ้ำและเงื่อนไขกรณีไม่รับประทานยาในเวลาที่กำหนด ซึ่งทั้งสองยังคงอยู่ใน loop wihle(true) หลักการทำงานคือ กำหนดตัวแปร elasp time (เวลาที่ผ่านมาแล้ว) มีค่าเท่ากับ เวลาที่เริ่มทำงานอุปกรณ์นำมาลบกับเวลาที่เริ่มเรียกใช้ฟังก์ชัน
โดยเงื่อนไข หาก เวลาที่ผ่านมา มากกว่าหรือเท่ากับ repeater_time(ตัวเตือนซ้ำ) คูณกับ 25 และคูณกับ 1000 อีกที ซึ่งจะได้ ค่า millisec หากเข้าเงื่อนไข (ในกรณีนี้จะเล่นเสียงเตือนทุกๆ 25 วินาที และจะเตือนทั้งหมด 3 ครั้ง) จะสั่งให้ DFplayer mini เล่นเสียงเตือนซ้ำ และเพิ่มตัวคูณที่จะใช้เล่นเสียงครั้งต่อไป
ในเงื่อนไขต่อไป หาก เวลาที่ผ่านไป เกิน 1 นาที จะสั่งให้หลอด LED ดับและเรียกใช้ฟังก์ชั้น Med_Not_Taken() เพื่อส่งข้อความไปยังไลน์แล้ว break ออกจาก loop while(true) เป็นอันจบการทำงาน
elapsedTime = millis() - startTime;
if (elapsedTime >= repeater_time * 25 * 1000 ) { // หากไม่หยิบยาในเวลาที่กำหนด จะเล่นเสียงเตือนซ้ำจนกว่าจะหมดเวลา
player.play(1); // เล่นไฟล์เสียงที่ 1 // รอเป็นเวลา 8 วินาที
repeater_time += 1; // เพิ่มเวลาเตือนอีกครั้ง ต้องตั้งให้เหมือนกับที่กำหนดไว้ก่อนหน้า
}
if (elapsedTime >= 1 * 60 * 1000) { // หากหมดเวลาจะออกจากลูป
digitalWrite(led_left, LOW); // ปิด LED ซ้าย
Med_Not_taken();
delay(1000);
break; // ออกจากลูป
}
}
}
ซึ่งในส่วนของฟังก์ชั้นของเซนเซอร์ตัวตรงกลางและด้านขวา จะทำงานในรูปแบบเดียวกัน เพียงแค่เปลี่ยนชื่อเล็กน้อย
//---------------------------------------------------------------------เนื่องจากฟังก์ชั่นอีก 2 อันทำงานเหมือนกันแค่คนละชื่อ จึงขอข้าม -----------------------------------------------------------------------------//
void Middle_Med() { // ฟังก์ชั่น เตือนกินยากล่อง/ขวดกลาง
player.play(1);
int led_middle_StateChanges = 0;
int led_middle_State = LOW;
int repeater_time = 1;
digitalWrite(led_middle, HIGH);
startTime = millis(); // Record the start time
while (true) {
int sensor_middle_status = digitalRead(sensor_middle);
if (sensor_middle_status == HIGH) {
digitalWrite(led_middle, LOW); // Turn off
} else {
digitalWrite(led_middle, HIGH); // Turn on
}
// Check for LED state changes and increment the counters
if (digitalRead(led_middle) != led_middle_State) {
led_middle_StateChanges++;
led_middle_State = digitalRead(led_middle);
}
if (led_middle_StateChanges < 2) {
digitalWrite(led_middle, HIGH);
}
if (led_middle_StateChanges >= 2) {
// Turn off both LEDs
player.play(2);
digitalWrite(led_middle, LOW);
Med_taken();
digitalWrite(camera_capture, HIGH);
delay(1000);
digitalWrite(camera_capture, LOW);
delay(3000);
break; // Exit the loop
}
elapsedTime = millis() - startTime;
if (elapsedTime >= repeater_time * 25 * 1000) {
player.play(1);
repeater_time += 1;
}
if (elapsedTime >= 1 * 60 * 1000) {
digitalWrite(led_middle, LOW);
Med_Not_taken();
delay(1000);
break;
}
}
}
void Right_Med() {
player.play(1); // ฟังก์ชั่น เตือนกินยากล่อง/ขวดขวา
int led_right_StateChanges = 0;
int led_right_State = LOW;
int repeater_time = 1;
// Initially, turn LED on
digitalWrite(led_right, HIGH);
startTime = millis(); // Record the start time
while (true) {
int sensor_right_status = digitalRead(sensor_right);
if (sensor_right_status == HIGH) {
digitalWrite(led_right, LOW); // Turn off
} else {
digitalWrite(led_right, HIGH); // Turn on
}
// Check for LED state changes and increment the counters
if (digitalRead(led_right) != led_right_State) {
led_right_StateChanges++;
led_right_State = digitalRead(led_right);
}
if (led_right_StateChanges < 2) {
digitalWrite(led_right, HIGH);
}
if (led_right_StateChanges >= 2) {
// Turn off both LEDs
player.play(2);
digitalWrite(led_right, LOW);
Med_taken();
digitalWrite(camera_capture, HIGH);
delay(1000);
digitalWrite(camera_capture, LOW);
delay(3000);
break; // Exit the loop
}
elapsedTime = millis() - startTime;
if (elapsedTime >= repeater_time * 25 * 1000) {
player.play(1);
repeater_time += 1;
}
if (elapsedTime >= 1 * 60 * 1000) {
digitalWrite(led_right, LOW);
Med_Not_taken();
delay(1000);
break; // Exit the loop
}
}
}
ในส่วนของ void loop() จะมีคำสั่ง Blynk.run() และ timer.run() เพื่อเริ่มใช้งาน Blynk จากนั้นจะมีในส่วนของการเช็คขวดยา โดยมีหลักการคือ กำหนดตัวแปร current time ที่เป็นตัวเทียบเวลาปัจจุบัน แล้วนำมาลบกับ เวลาที่เซนเซอร์เช็คล่าสุดที่หากล่องยาไม่เจอ (โดยจะมีค่าเป็น 0 เมื่อเริ่มใช้งานเครื่อง) ว่ามีค่ามากกว่าหรือเท่ากับ เวลาเช็คยาหายหรือไม่ (ในกรณีนี้จะเช็คทุกๆ 15 วินาที) หากกล่องยาหายเป็นเวลาชั่วครู่ จะน้ำเวลานั้นมาแทน เวลาที่เซนเซอร์เช็คล่าสุดที่หากล่องยาไม่เจอ เพื่อนำมาเทียบในครั้งถัดไป
และมีอีกเงื่อนไขซ้อนไว้ นั่นคือเงื่อนไข หากเซนเซอร์ตัวใดตัวหนึ่ง ไม่สามารถตรวจหากล่องยาได้ จะสั่งให้ DF player mini ทำการส่งเสียงแจ้งวางยากลับที่เดิมและส่งข้อความไปยังไลน์ ส่วนอีกเงื่อนไขคือ การเชื่อต่อ WIFI โดยอัตโนมัติ หาก WIFI มีปัญหา จะเรียกใช้ฟังก์ชั่น reconnectWiFi()
void loop() {
Blynk.run(); // รัน Blynk เพื่อให้แอปพลิเคชัน Blynk ทำงาน
timer.run(); // รัน timer
unsigned long currentTime = millis(); // บันทึกเวลาปัจจุบัน
int leftSensorStatus = digitalRead(sensor_left);
int middleSensorStatus = digitalRead(sensor_middle);
int rightSensorStatus = digitalRead(sensor_right);
if (currentTime - lastSensorCheckTime >= sensorCheckInterval) { // เช็คเวลาที่เหมาะสมในการตรวจสอบสถานะของเซนเซอร์
if (leftSensorStatus == HIGH || middleSensorStatus == HIGH || rightSensorStatus == HIGH) { // ตรวจสอบสถานะของเซนเซอร์ทางซ้าย กลาง และขวา
LINE.notify("ไม่มียาอยู่ในช่อง");
player.play(3); // ส่งข้อความว่าไม่มียาอยู่ในช่อง ไปยังไลน์
}
lastSensorCheckTime = currentTime;
}
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi connection lost. Reconnecting...");
reconnectWiFi();
}
}
ต่อไปเป็นส่วนของการตั้งค่าการทำงานด้วย Virtual Pin ของ Blynk โดยมีหลักการคือ ใช้โค้ด
BLYNK_WRITE(V1) { // กดปุ่มในแอป BLYNK เพื่อใช้งานฟังก์ชั่น เมื่อฟังก์ชั่นทำงานเสร็จแล้ว จะกลับสถานะเป็น LOW อัตโนมัติ
int buttonState = param.asInt(); //เปิดใช้งาน กล่องซ้าย
if (buttonState == HIGH) {
Left_Med();
delay(300);
Blynk.virtualWrite(V1, LOW);
} else {
}
}
BLYNK_WRITE(V2) { // เปิดใช้งาน กล่องกลาง
int buttonState = param.asInt();
if (buttonState == HIGH) {
Middle_Med();
delay(300);
Blynk.virtualWrite(V2, LOW);
} else {
}
}
BLYNK_WRITE(V3) { // เปิดใช้งาน กล่องขวา
int buttonState = param.asInt();
if (buttonState == HIGH) {
Right_Med();
delay(300);
Blynk.virtualWrite(V3, LOW);
} else {
}
}
ในส่วนนี้คือ ฟังก์ชั่นการส่งข้อความไปยังไลน์ เมื่อเข้าเงื่อนไขต่างๆ
void Med_taken(){
LINE.notify("ผู้ป่วยทำการหยิบยาแล้ว"); // ส่งข้อความ ทานยาแล้ว ไปยังไลน์
}
void Med_Not_taken(){
LINE.notify("ผู้ป่วยไม่ได้ทำการหยิบยาตามเวลาที่กำหนดไว้"); // ส่งข้อความ ยังไม่ทานยา ไปยังไลน์
}
ส่วนนี้คือฟังก์ชั่นการเชื่อมต่อ WIFI หากการเชื่อมต่อล้มเหลว โดยจะรันคำสั่งเชื่อมต่อใหม่ทุกๆ 5 วินาที นี่คือโค้ดชุดสุดท้ายภายใน ESP32
void reconnectWiFi() {
while (WiFi.status() != WL_CONNECTED ) {
Serial.print("Attempting to reconnect to WiFi ");
Serial.println(")...");
WiFi.begin(ssid, pass);
delay(5000); // Wait for 5 seconds or adjust as needed
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("WiFi reconnected!");
}
}
5.3 โค้ดภายใน ESP32-CAM
ในส่วนของ ESP32-CAM จะใช้เพียงแค่ส่งข้อความและถ่ายรูปไปยังไลน์ ซึ่งจำเป็นต้องใช้ Line Token เดียวกับ ESP32 ที่ได้กล่าวไปข้างต้น ในส่วนนี้จะกล่าวถึงแค่ส่วนที่สำคัญเพียงเท่านั้น สิ่งแรกคือการเพิ่ม libarary ของ Line Notify และกรอกข้อมูล Line Token ชื่อและรหัส WIFI
#include <TridentTD_LineNotify.h>
#include <WiFi.h>
#include "esp_camera.h"
#include "esp_system.h"
#define SSID "---------" //WiFi name
#define PASSWORD "---------------" //PASSWORD
#define LINE_TOKEN "-------------------------------------------------"
ต่อไปจะเป็นการตั้งค่า pin ต่างๆของ esp32-cam และเมื่อกล้องพร้อมใช้งาน จะส่งข้อความไปยังไลน์ แต่หากไม่สามารถใช้งานกล้องได้ จะส่งข้อความไปยังไลน์ว่าไม่สามารถใช้งานกล้องได้ เป็นจำนวน 3 ครั้ง
// Pin definition for CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
const int Led_Flash = 4;
const int Led_run = 13;
void setup() {
Serial.begin(115200);
while (!Serial) { ; }
pinMode(Led_Flash, OUTPUT);
pinMode(Led_run, OUTPUT);
WiFi.begin(SSID, PASSWORD);
Serial.printf("WiFi connecting to %s\n", SSID);
while(WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(400); }
Serial.printf("\nWiFi connected\nIP : ");
Serial.println(WiFi.localIP());
LINE.setToken(LINE_TOKEN);
pinMode(14, INPUT);
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
if(psramFound()){
// FRAMESIZE_ +
//QQVGA/160x120//QQVGA2/128x160//QCIF/176x144//HQVGA/240x176
//QVGA/320x240//CIF/400x296//VGA/640x480//SVGA/800x600//XGA/1024x768
//SXGA/1280x1024//UXGA/1600x1200//QXGA/2048*1536
config.frame_size = FRAMESIZE_VGA;
config.jpeg_quality = 10;
config.fb_count = 1;
config.grab_mode = CAMERA_GRAB_LATEST;
} else {
config.frame_size = FRAMESIZE_QQVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
config.grab_mode = CAMERA_GRAB_LATEST;
}
// Init Camera
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
int i = 0;
for (i=0.;i<3;i++){
LINE.notify("ไม่สามรถใช้งานกล้องได้ กรุณารีบูท");
delay(2000);
}
}
else (ESP_OK){
delay(2000);
LINE.notify("กล้องพร้อมใช้งาน");}
}
ในส่วนของ void loop() จะมีการอ่านค่าของขา 14 ที่ต่ออยู่กับ ESP32 และมีเงื่อนไขหากมีการส่งค่ามาจาก ESP32 จะทำการใช้ฟังก์ชั่นถ่ายภาพแล้วส่งไปยังไลน์ และมัฟังก์ชั่นเชื่อมต่อ WIFI อัตโนมัติกรณี สัญญาณ WIFI ขาด
void loop() {
int status1 = digitalRead(14);
if(status1 == HIGH)
{
Camera_capture();
}
else{}
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi connection lost. Reconnecting...");
connectToWiFi();
}
// Your additional code goes here
delay(1000); // You can adjust the delay based on your needs
}
ส่วนนี้คือฟังก์ชั่นการถ่ายภาพของ ESP32-CAM โดยจะสั่งให้เปิด-ปิด Flash อย่างรวดเร็วเพื่อเพิ่มแสงให้กับภาพ จากนั้นจะบันทึกภาพไปยัง ESP32-CAM แล้วส่งไปยังไลน์ แต่หากไม่สามารถถ่ายภาพได้ ระบบ ESP32-CAM จะรีเซ็ตตัวเองโดยอัตโนมัติ และหลังจากส่งภาพไปยังไลน์ จะล้างข้อมูลของภาพก่อนหน้า และรีเซ็ตตัวเอง เพื่อป้องกันไม่ให้ตัว ESP32-CAM ทำงานขัดข้องในครั้งถัดไป และสุดท้ายคือฟังก์ชั่นการเชื่อมต่อ WIFI อีกครั้ง เหมือนกับตัว ESP32
void Camera_capture() {
camera_fb_t * fb = NULL;
delay(1000);
digitalWrite(Led_Flash, HIGH);
delay(10);
digitalWrite(Led_Flash, LOW);
delay(10);
digitalWrite(Led_Flash, HIGH);
fb = esp_camera_fb_get();
esp_camera_fb_return(fb);
fb = NULL;
fb = esp_camera_fb_get();
delay(100);
Serial.println("Camera captured");
if(!fb) {
Serial.println("Camera capture failed");
esp_restart();
return;
}
digitalWrite(Led_Flash, LOW);
Send_line(fb->buf,fb->len);
esp_camera_fb_return(fb);
}
void Send_line(uint8_t *image_data,size_t image_size){
LINE.notifyPicture("",image_data, image_size);
Serial.println("sending");
delay(8000);
esp_restart();
}
void connectToWiFi() {
Serial.println("Connecting to WiFi...");
WiFi.begin(SSID, PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
}
6. ผลการดำเนินงาน
ผังการทำงานของระบบ
วิธีการใช้งานโปรแกรม
ในการเริ่มต้นการใช้งานระบบแจ้งเตือนการรับประทานยานั้น ต้องตั้ง Automations ภายในแอปพลิเคชั่น Blynk ในโทรศัพท์เสียก่อน จากนั้นในเสียบปลั๊กแล้วรอให้ระบบบูทตัว เมื่อพร้อมใช้จะส่งข้อความมายังไลน์ เมื่อถึงเวลา ไฟ LED จะติด และจะมีเสียงแจ้งเตือนทานยา ดังภาพ
หากยังไม่ได้หยิบยาจะส่งเสียงแจ้งเตือนทุกๆ 25 วินาที (จะแจ้งทั้งหมด 3 ครั้ง) และเมื่อทำการหยิบยาแล้ว จะทำการเล่นเสียงชม ส่งข้อความและถ่ายรูปส่งไปยังไลน์ แต่หากผู้ป่วยไม่ได้ทำการหยิบยาในเวลาที่กำหนด (ในที่นี้กำหนดไว้ 1 นาที เพื่อความรวดเร็วในการนำเสนอ) จะส่งข้อความไปยังไลน์และจบการทำงาน ดังภาพ
หากผู้ป่วยไม่ได้ทำการวางยากลับไปยังช่อง ระบบจะส่งเสียงแจ้งเตือนให้วางยากลับไปยังช่อง และจะส่งข้อความไปยังไลน์ ทุกๆ 15 วินาที
การทดลอง
การทดลองที่ 1 ทดลองการทำงานของระบบ ESP32 โดยการตั้งเวลาผ่าน Automation ใน Blynk App โดยเมื่อถึงเวลาตามที่กำหนดไว้ใน Automation ESP32 จะทำการสั่งให้หลอดไฟ LED ในแต่ละช่วงเวลาตามที่ตั้งไว้ โดยจะแบ่งการทำงานของหลอดไฟ LED เป็นช่วงเช้า ช่วงเที่ยงและช่วงเย็นตามลำดับ จากซ้ายไปขวา และ DFPlayer Mini จะทำการเล่นไฟล์เสียง โดยการทดสอบนี้จะวัดโดยการทำงานของหลอดไฟ LED และ DFPlayer Mini ทำงานได้ตามที่ได้เขียนการทำงานของระบบหรือไม่
ผลการทดลองที่ได้
การบันทึกผลการทดลองการใช้งาน Blynk สั่งให้ ESP32 ทำงานตามเวลา โดยหลังจาก ESP32 ทำงาน จะสั่งให้หลอดไฟ LED สว่างและทำการเล่นเสียงโดยทดลองช่องยาละ 20 ครั้ง เพื่อบันทึกผลที่ออกมาว่าสามารถแจ้งเตือนตรงเวลาหรือไม่ หลังจากนั้นคิดเป็นร้อยละ ของทุกช่องใส่ยา ผลที่ได้คือ ความถูกต้องของเวลาในการสั่งให้ทำงาน คือร้อยละ 100
การทดลองที่ 2 ทดสอบต่อจากการทดลองที่ 1 โดยหลังจากที่ตัวรับรู้อินฟราเรดมีการเปลี่ยนสถานะ ESP32 จะส่งสัญญาณไปยัง ESP32-CAM เพื่อสั่งให้ถ่ายภาพและส่งเข้าไลน์ตามโปรแกรมที่ได้เขียนไว้
การบันทึกผลการทดสอบการถ่ายภาพของ ESP32-CAM และส่งเข้าไลน์โดยทดลองช่องยาละ 20 ครั้ง เพื่อบันทึกผลที่ออกมาว่าสามารถถ่ายภาพและส่งเข้าไลน์ หลังจากนั้นคิดเป็นร้อยละ ของทุกช่องใส่ยา ผลที่ได้คือ ความถูกต้องของระบบในการถ่ายภาพและส่งเข้าไลน์ คือร้อยละ 93.33
การทดลองที่ 3 เป็นการทดสอบระบบแจ้งเตือนการรับประทานยาทั้งระบบว่าเป็นไปตามเงื่อนไขที่ได้กล่าวไปข้างต้นหรือไม่
การบันทึกผลการทดสอบระบบแจ้งเตือนการรับประทานยาโดยทดลองช่องยาละ 20 ครั้ง เพื่อบันทึกผลที่ออกมาว่าระบบมีความถูกต้องและทำงานครบทุกขั้นตอนที่ได้ทำการเขียนหรือไม่ หลังจากนั้นคิดเป็นร้อยละ ของทุกช่องใส่ยา ผลที่ได้คือ ความถูกต้องของระบบในการแจ้งเตือนการรับประทานยา คือร้อยละ 95
7. สรุปผลและข้อเสนอแนะ
7.1 สรุปผล
จากผลการทดสอบของระบบแจ้งเตือนการรับประทานยา สามารถทำงานได้อย่างมีประสิทธิภาพ ซึ่งตรงตามเงื่อนไขที่ได้ทดสอบ ได้แก่
1.1 กำหนดการตั้งเวลาผ่าน Blynk ได้ตามความต้องการและมีระบบเสียงแจ้งเตือนเมื่อถึงเวลารับประทานยา
1.2 ถ่ายภาพโดย ESP32-CAM และส่งเข้าไลน์ ตรงตามเงื่อนไข
1.3 ระบบแจ้งเตือนการรับประทานยา ทำงานได้ตรงตามเงื่อนไข
7.2 ข้อเสนอแนะ
2.1 ESP32-CAM มีโอกาศที่จะทำงานผิดพลาด หากมีการทำงานผิดพลาด ให้กดรีเซ็ต
2.2 สามารถนำไปประยุกต์เป็นระบบแจ้งเตือนการให้อาหารสัตว์ได้
8. อ้างอิง
8.1 โรงพยาบาลเปาโล. ปัญหาผู้สูงอายุกับการใช้ยา ที่คนดูแลไม่ควรมองข้าม. [ออนไลน์] 2564. [สืบค้นวันที่ 18 ตุลาคม 2566]. จาก https://bit.ly/49mNrXg
8.2 Kritsada Jaiyen. Mbits บทที่ 8 ใช้งาน IoT กับ Blynk 2.0. [ออนไลน์] 2565. [สืบค้นวันที่ 18 ตุลาคม 2566]. จาก https://doc.inex.co.th/mbit-with-microblockide-ep8/
8.3 โรบอทสยาม. การใช้งาน IR Infrared Obstacle Avoidance Sensor Module. [ออนไลน์] 2559. [สืบค้นวันที่ 27 มีนาคม 2563]. จาก https://robotsiam.blogspot.com/2016/10/ir-infrared-obstacle-avoidance-sensor.html
8.4 บริษัท เอไอซีเอส จำกัด. สอนใช้งานโมดูลเซ็นเซอร์ตรวจจับวัตถุ IR Infrared กับ Arduino UNO. [ออนไลน์] 2565. [สืบค้นวันที่ 27 มีนาคม 2563]. จาก https://www.ai-corporation.net/2022/02/07/ir-infrared-with-arduino-uno/