เครื่องให้อาหารแมวอัตโนมัติและแจ้งเตือนผ่านLine : (Automatic cat control device and notification via Line:)

ผู้เขียนบทความ :

  1. นายปิยะวัฒน์ สังข์นุ้ย 013 #COE16
  2. นายชนุดร อินมณเทียร 025 #COE16
  3. นายซอฟรอล สาเเล้ะ 027 #COE16
  4. นายณพงษ์ ขวัญสุวรรณ 028 #COE16

คณะวิศวกรรมศาสตร์ สาขาวิศวกรรมคอมพิวเตอร์

วิชา : 04-513-201 การโปรเเกรมคอมพิวเตอร์ขั้นสูง 1/2567

1.ความเป็นมา

ในปัจจุบันที่เทคโนโลยีก้าวหน้ากลายเป็นสิ่งปกติในชีวิตประจำวัน นวัตกรรมใหม่ๆก็เกิดขึ้นและเจริญก้าวหน้ากันมากยิ่งขึ้น   แต่การประยุกต์ใช้เทคโนโลยีในด้านการค้าขายในที่สาธารณะเป็นเรื่องหนึ่งที่ได้รับความสนใจอย่างมาก เนื่องจากในปัจจุบันบางสถานการณ์อาจจะมีนักศึกษา เกิดปัญหาลืมซื้ออุปกรณ์การเขียนก่อนเข้าสอบต่างๆ

ดังนั้น เพื่อการเเก้ปัญหาดังกล่าวผู้จัดทำจึงคิดค้นตู้จำหน่ายอุปกรณ์การเรียนหยอดเหรียญ ส่งเสริมความสะดวกสบายเเละรวดเร็ว โดยลดอุปสรรคในการเข้าสอบช้าเพราะตู้นี้จะวางอยู่ทุกตึกในมหาวิทยาลัย เเละสถานที่สาธารณะต่างๆ

2.วัตถุประสงค์

 2.1 เพื่อศึกษาการสร้างเครื่องให้อาหารแมวอัตโนมัติ
 2.2 เพื่อเปรียบเทียบการให้อาหารแบบเดิมกับเครื่องให้อาหารแมวอัตโนมัต

3.ขอบเขต

    3.1 สามารถให้อาหารแมวโดยอัตโนมัติเมื่อถึงเวลาอาหาร
    3.2 มีการแจ้งเตือนเมื่อแมวมากินหาร           
    3.3 สามารถกำหนดปริมาณอาหารในการให้แต่ละครั้งได้ (กรัม)

4.ประโยชน์ที่คิดว่าจะได้รับ

     4.1 ได้พัฒนทักษะการใช้ไมโครคอนโทรลเลอร์ ESP32 
     4.2 ได้พัฒนาการออกแบบวงจร และการทำแผ่นPCB
     4.3 ได้พัฒนาทักษะการเขียนโค้ดโปรแกรม Arduino ide          
     4.4 เครื่องให้อาหารแมวอัตโนมัติสามารถให้อาหารแมวได้อย่างอัตโนมัติตามที่ตั้งค่าไว้

5.ความรู้ที่เกี่ยวข้อง

อุปกรณ์ที่ใช้ทำเครื่องให้อาหารแมวอัตโนมัติ (Automatic cat feeder)

1.ESP32

MCU – ESP32  เป็น Micro Controller ที่รองรับการเชื่อมต่อ WiFi , Bluetooth – BLE ในตัว ภาษาที่ใช้ในการพัฒนาโปรแกรมคือ ภาษา C หรือ Python  ภาษา Python ต้องทำการอัพเกรดเฟิร์มแวร์ให้รองรับ Python การพัฒนาโปรแกรมขึ้นอยู่กับผู้ที่พัฒนา โปรแกรม IDE ที่ใช้พัฒนาคือ Arduino IDE หรือ Visual Studio สำหรับ Visual Studio จำเป็นต้องติดตั้ง Plugin Espressif IDF หรือ PlatformIO IDE และต้อง Enable (Arduino)

อ้างอิง https://www.etteam.com/prodESP/ESP32-DEV-KIT/ESP32-DEV-KIT.html

2.Micro Servo Motor

Servo คืออุปกรณ์มอเตอร์ ที่สามารถควบคุมการหมุนที่แม่นยำ เซอร์โว SG90 มีขนาดเล็กแรงบิด 1.2-1.4 kg/cmสีน้ำตาลเป็นสายกราวด์ สีแดงเป็นไฟเข้า 4.8-7.2V สีส้มเป็นสัญญาณอินพุต

อ้างอิง http://118.174.134.188/e-learning_new/arduino_project/project1/lesson4/content1/index.php

3.Ultrasonic sensor

     เซ็นเซอร์วัดระยะทาง มีวิธีการทำงานโดยการส่งคลื่นอัลตราโซนิคจำนวนหนึ่งออกไปจากตัวส่ง (Transmitter) เมื่อคลื่นวิ่งไปชนกับวัตถุ คลื่นจะมีการสะท้อนกลับมา แล้ววิ่งกลับไปชนตัวรับ(Receiver) ด้วยการเริ่มนับเวลาที่ส่งคลื่นออกไป จนถึงได้รับคลื่นกลับมานี้เอง ทำให้เราสามารถหาระยะห่างระหว่างวัตถุกับเซ็นเซอร์ได้

อ้างอิง https://www.arduitronics.com/product/720/load-cell-weight-sensor-5-kg-high-quality-yzc-133

5.Step down

   วงจรลดแรงดันแบบ Step-Down หรือเรียกอีกแบบว่า Buck Converter (บัคคอนเวอร์เตอร์) ใช้ลดแรงดันจากแรงดันสูงให้ต่ำลง ใช้หลักการสวิตชิ่ง-ตัวเหนี่ยวนำ(L) จึงทำให้มีความร้อนและความสูญเสียกำลังไฟน้อย ไม่เหมือนกับการลดแรงดันโดยใช้ IC ตระกูล 78xx / 317 ทั่วไปที่ใช้หลักการลดทอนทำให้เกิดความร้อนสูง  วงจรบัคคอนเวอร์เตอร์เมื่อลดแรงดันลงแล้วจะได้กระแส Output เพิ่มขึ้น

อ้างอิง https://www.ab.in.th/product/253/module-dc-to-dc-step-down-converter-lm2596-3a-

6. แจ็คไฟ DC

แจ็คไฟ DC เป็นส่วนประกอบสำคัญสำหรับอุปกรณ์อิเล็กทรอนิกส์สมัยใหม่ส่วนใหญ่ เป็นพอร์ตที่เสียบอะแดปเตอร์ไฟฟ้า และมีหน้าที่จ่ายไฟให้กับอุปกรณ์ มีแจ็คไฟ DC หลากหลายประเภทในท้องตลาด แต่ทั้งหมดมีจุดประสงค์เดียวกัน ซึ่งก็คือการให้วิธีการที่ปลอดภัยและเชื่อถือได้ในการจ่ายไฟไปยังอุปกรณ์อิเล็กทรอนิกส์ประเภทของแจ็คไฟ DC ประเภทของ DC เเจ็ค 1. แจ็คไฟ DC มาตรฐาน 2. แจ็คไฟ DC ขนาดเล็ก 3. แจ็คไฟ DC ของ Surface Mount (SMT)

อ้างอิง https://tweakable-parts.com/de/dc-stecker/611-dc-jack-21mm-schwarz.html

7. Adapter

Adapter Hardware (ฮาร์ดแวร์)

  • เป็นอุปกรณ์ที่ใช้สำหรับเชื่อมต่อหรือแปลงสัญญาณระหว่างสองอุปกรณ์ที่ใช้พอร์ตหรือระบบการเชื่อมต่อที่แตกต่างกัน เช่น อะแดปเตอร์ USB-C to HDMI, อะแดปเตอร์สายไฟ หรืออะแดปเตอร์ที่เปลี่ยนจากไฟบ้าน 220V ไปเป็นไฟฟ้ากระแสตรง (DC) 12V เพื่อใช้ในอุปกรณ์อิเล็กทรอนิกส์บางชนิด

Adapter Software (ซอฟต์แวร์)

  • เป็นการออกแบบหรือการเขียนโปรแกรมที่ช่วยให้สองระบบที่มีอินเทอร์เฟซต่างกันสามารถทำงานร่วมกันได้ ตัวอย่างเช่น “Adapter Pattern” ในการเขียนโปรแกรม ซึ่งเป็นรูปแบบการออกแบบที่ช่วยให้คลาสหรือออบเจ็กต์ที่มีอินเทอร์เฟซต่างกันทำงานร่วมกันได้โดยการสร้าง “adapter” ที่แปลงอินเทอร์เฟซนั้น ๆ

Network Adapter (อะแดปเตอร์เครือข่าย)

  • เป็นฮาร์ดแวร์ที่ช่วยให้คอมพิวเตอร์สามารถเชื่อมต่อกับเครือข่ายได้ เช่น Wi-Fi adapter หรือ Ethernet adapter ซึ่งมีหน้าที่ในการแปลงสัญญาณดิจิทัลระหว่างคอมพิวเตอร์และเครือข่าย

อ้างอิง https://www.musicarms.net/adapter-9v

8.TridentTD_LineNotify

ซึ่งเป็นไลบรารีที่ใช้ในการส่งข้อความทาง LINE Notify และ WiFi.h ซึ่งใช้ในการเชื่อมต่อกับเครือข่าย WiFi ของคุณทำให้ผู้ใช้งานสามารถเขียนโปรแกรมเพื่อสร้างบริการที่ผู้พัฒนาต้องการ ผ่านการส่งข้อความและโต้ตอบกับผู้ใช้ในลักษณะ Chatbot นั่นเอง Line Messaging API มีฟังก์ชันการทำงานมากมาย แต่ที่เราใช้ฟังก์ชันการทำงานคือ การส่งข้อความ สามารถใช้ส่งข้อความแบบข้อความธรรมดา ข้อความรูปภาพ ข้อความวิดีโอ ข้อความเสียง ฯลฯ

ในการสร้างระบบนี้ จำเป็นต้องมี Token ของ Line Messaging API ซึ่งสามารถขอได้จาก Line Developer ข้อความแจ้งเตือนสามารถกำหนดค่าได้ตามต้องการ เช่น ข้อความแจ้งเตือน รูปภาพ หรือวิดีโอ ดังภาพ

ขั้นตอนการใช้ระบบการแจ้งเตือน Line Notify

1.เพิ่ม LINE Notify เป็นเพื่อนโดยการสแกน QR Code นี้ หรือจาก https://notify-bot.line.me/my/

 2. ล็อกอิน LINE เพื่อขอ Token ที่หน้านี้ https://notify-bot.line.me/my/ แล้วกดปุ่ม ออก Token

3. ตั้งค่าการใช้งาน LINE Notifyกำหนดชื่อผู้ส่ง และเลือกกลุ่มที่ต้องการส่งข้อความไปหา

4. ขั้นตอนสุดท้าย รับ Token

ให้กรอกชื่อที่ใช่ส่งข้อมูลเข้า Line แล้วเลือกกลุ่มที่จะส่งข้อมูล กดปุ่ม “ออก Token” จะได้ค่า Token ให้ก็อปค่านี้ไว้ เพื่อนำไปใช้ในโปรแกรมของเรา เพียงเท่านี้ก็พร้อมส่งข้อความจาก NodeMCU ไปหา LINE แล้ว แต่ถ้าลืม สามารถ กลับไปทำตามข้อ 3 ขอรหัส Token ได้ใหม่

5. ดาวน์โหลด Library จากลิ้งค์ข้างล่าง

ดาวโหลด Library

วิธีลง Library ให้ดูตัวอย่างในบทความนี้

void sendLineNotify(String message) {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    http.begin("https://notify-api.line.me/api/notify");
    http.addHeader("Content-Type", "application/x-www-form-urlencoded");
    http.addHeader("Authorization", "Bearer " + LINE_TOKEN);

    String payload = "message=" + message;
    int httpResponseCode = http.POST(payload);

    if (httpResponseCode > 0) {
      String response = http.getString();
      Serial.println("Response: " + response);
    } else {
      Serial.println("Error on sending POST: " + String(httpResponseCode));
    }
    
    http.end();
  } else {
    Serial.println("WiFi not connected");
  }
}

บรรทัดโค๊ดในภาพ เป็นการเรียกใช้ฟังก์ชัน Line setToken เพื่อส่งข้อความแจ้งเตือนไปยังแอพลิเคชั่น Line ฟังก์ชันนี้จะส่งข้อความไปยังไลน์ของผู้ใช้ โดนใช้ Line Token ในบรรทัดถัดไปในโค๊ด จะเรียกใช้ Serial.println กับ LINE.notify ร่วมกัน เพื่อส่งข้อความไปยัง Line ของผู้ใช้งานนั้นเอง

#include <WiFi.h>
#include <ESP32Servo.h>

// กำหนดขา GPIO
const int trigPin = 25;
const int echoPin = 26;
const int servoPin = 4;

// สร้างอ็อบเจ็กต์ Servo
Servo myServo;

// ฟังก์ชันเพื่อวัดระยะทางจากเซ็นเซอร์อัลต้าโซนิค
long readDistance() {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  long duration = pulseIn(echoPin, HIGH);
  long distance = (duration / 2) / 29.1;  // คำนวณระยะทางเป็นเซนติเมตร
  return distance;
}

void setup() {
  Serial.begin(115200);

  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  myServo.attach(servoPin);
  myServo.write(0);  // ตั้งค่าเริ่มต้นของเซอร์โว
}

void loop() {
  long distance = readDistance();
  Serial.print("Distance: ");
  Serial.println(distance);

  if (distance < 10) {  // ถ้าระยะทางน้อยกว่า 10 เซนติเมตร
    myServo.write(90);  // เปิดเซอร์โวที่ 90 องศา
  } else {
    myServo.write(0);   // ปิดเซอร์โวที่ 0 องศา
  }

  delay(500);  // รอ 500ms ก่อนการตรวจจับครั้งถัดไป
}

โค้ดนี้เป็นการใช้งานเซ็นเซอร์อัลตราโซนิค (Ultrasonic Sensor) ร่วมกับมอเตอร์เซอร์โว (Servo Motor) โดยมีการตรวจจับระยะทางด้วยเซ็นเซอร์อัลตราโซนิค และใช้เซอร์โวในการเปิดหรือปิดตามระยะทางที่ตรวจจับได้ ซึ่งสามารถอธิบายการทำงานโดยละเอียดได้ดังนี้

การตั้งค่าของโค้ด:

  1. กำหนดขา GPIO:
    • trigPin = 25: พินสำหรับส่งสัญญาณ (Trigger) ไปยังเซ็นเซอร์อัลตราโซนิค.
    • echoPin = 26: พินสำหรับรับสัญญาณ (Echo) จากเซ็นเซอร์อัลตราโซนิค.
    • servoPin = 4: พินสำหรับเชื่อมต่อกับมอเตอร์เซอร์โว.
  2. Servo Object:
    • มีการสร้างอ็อบเจ็กต์เซอร์โว myServo ซึ่งใช้ในการควบคุมการหมุนของมอเตอร์เซอร์โวในภายหลัง.

ฟังก์ชันการทำงานของโค้ด:

  1. ฟังก์ชัน readDistance():
    • ฟังก์ชันนี้ทำหน้าที่ส่งสัญญาณออกไปจากขา Trig (ขา 25) เพื่อให้เซ็นเซอร์อัลตราโซนิคทำการส่งคลื่นเสียงออกมา.
    • จากนั้น ฟังก์ชัน pulseIn(echoPin, HIGH) จะวัดระยะเวลาที่คลื่นเสียงใช้ในการเดินทางไปและกลับจากวัตถุ โดยใช้ขา Echo (ขา 26) ในการรับสัญญาณกลับ.
    • ค่าระยะเวลานี้ถูกคำนวณเป็นระยะทาง (เซนติเมตร) โดยสูตร distance = (duration / 2) / 29.1 ซึ่งทำให้ได้ค่าระยะทางที่ถูกต้อง.
  2. ฟังก์ชัน setup():
    • กำหนดค่าเริ่มต้นต่าง ๆ เช่น ตั้งค่าพินของ Trig และ Echo ให้เป็นขา OUTPUT และ INPUT ตามลำดับ.
    • ทำการเชื่อมต่อมอเตอร์เซอร์โวเข้ากับพิน servoPin (ขา 4) และกำหนดมุมเริ่มต้นของมอเตอร์เซอร์โวไว้ที่ 0 องศา (เซอร์โวจะอยู่ในตำแหน่งปิด).
  3. ฟังก์ชัน loop():
    • ในลูปหลักของโปรแกรม ฟังก์ชัน readDistance() จะถูกเรียกเพื่ออ่านค่าระยะทางจากเซ็นเซอร์อัลตราโซนิค.
    • ถ้าระยะทางที่วัดได้มีค่าน้อยกว่า 10 เซนติเมตร มอเตอร์เซอร์โวจะหมุนไปที่ 90 องศา ซึ่งอาจหมายถึงการเปิดประตู, เปิดกลไก หรือการทำงานอื่นที่ต้องการให้มอเตอร์หมุนไปที่ 90 องศา.
    • ถ้าระยะทางที่วัดได้มากกว่า 10 เซนติเมตร มอเตอร์เซอร์โวจะหมุนกลับไปที่ 0 องศา (ตำแหน่งปิด).
    • โปรแกรมหน่วงเวลา 500 มิลลิวินาที (delay(500)) ก่อนจะทำการตรวจจับครั้งถัดไป.
#include "HX711.h"
#include <Wire.h> 

#define DOUT  A3
#define CLK   A2

#define DEC_POINT  2
#define STABLE  1

float offset=0;
float calibration_factor = 1;
float real_weight = 1.560;//kg

HX711 scale(DOUT, CLK);

unsigned char state=0;
long  FindZeroFactor();
float get_units_kg();
void  ReadWeight();
void  FindCalibrationFactor();

void setup()
{
  Serial.begin(115200);
  Serial.println();
  Serial.println("Auto Calibrate Program");
  Serial.println("Send 'a' to Find Zero Factor (Please Remove all weight from scale)");
  Serial.println("Send 'b' to Find Calibration Factor (Please insert know the weight on the scales)");
  Serial.println("Send 'c' Show weight on the scales");
}
void loop() 
{
  if(Serial.available())
  {
    char temp = Serial.read();
    if(temp=='a')
      state=1;
    if(temp=='b')
      state=2;   
    if(temp=='c')
      state=3;
  }

  switch(state)
  {
    case 0:
    break;
    case 1:
      FindZeroFactor();
   //   ReadWeight();
      state=0;
    break;
    case 2:
     FindCalibrationFactor();
     state=0;
    break;
    case 3:
      ReadWeight();
      delay(200);
    break;
    case 4:
     
    break;
    
  }
}

long FindZeroFactor()
{
     Serial.println("Find Zero Factor");
     Serial.println("Please wait .....");
     scale.set_scale();
     scale.tare(); 
     long zero_factor = scale.read_average(20); 
     Serial.print("Zero factor: ");
     Serial.println(zero_factor);
     return(zero_factor);
}

void FindCalibrationFactor()
{
  unsigned char flag_stable=0;
  unsigned int decpoint=1;
  for(unsigned char i=0;i<DEC_POINT+1;i++ )
    decpoint = decpoint*10;
  while(1)
  {
      scale.set_scale(calibration_factor); //Adjust to this calibration factor
      Serial.print("Reading: ");
      float read_weight = get_units_kg();
      String data = String(read_weight, DEC_POINT);
      Serial.print(data);
      Serial.print(" kg"); 
      Serial.print(" calibration_factor: ");
      Serial.print(calibration_factor);
      Serial.println();
      long r_weight      = (real_weight*decpoint);
      long int_read_weight = read_weight*decpoint;
      Serial.print(r_weight);
      Serial.print(" , ");
      Serial.println(int_read_weight);
      long x;
      if(r_weight == int_read_weight)
      {
        flag_stable++;
        if(flag_stable>=STABLE)
        {
          Serial.print("Calibration Factor is = ");
          Serial.println(calibration_factor);
          break;
         }
        }
       if(r_weight > int_read_weight)
          {
            x = r_weight - int_read_weight;
            if(x > 100)
              calibration_factor -= 1000; 
            else if(x > 100)
              calibration_factor -= 10;
            else
              calibration_factor -= 1;
            flag_stable=0;
          }
          if(r_weight < int_read_weight)
          {
            x =  int_read_weight-r_weight;
            if(x > 100)
              calibration_factor += 1000; 
            else if(x > 10)
              calibration_factor += 10;
            else
              calibration_factor += 1; 
            flag_stable=0; 
           }  
  }
}

float get_units_kg()
{
  return(scale.get_units()*0.453592);
}
void ReadWeight()
{
  scale.set_scale(calibration_factor); //Adjust to this calibration factor
  Serial.print("Reading: ");
  String data = String(get_units_kg()+offset, DEC_POINT);
  Serial.print(data);
  Serial.println(" kg");
}

ในโค๊ด เราจะเรียกใช้ฟังชันก์ HX711.h เพื่อรับสัญญานหรือรับน้ำหนักจาก Loadcell senser และเราจะกำหนดค่าของLoadcell ด้วยขา pin คือ

LOADCELL_DOUT_PIN 22

LOADCELL_SCK_PIN 23

บรรทัดต่อไปจะเป็นการกำหนดน้ำหนักที่ให้ load cell รับได้ คือ 100 กรัม และบรรทัดสุดท้ายจะเป็นการกำหนดค่า CALIBRATION_FACTOR ของ Load cell คือค่าคงที่ แต่ละ Load cell จะมีค่าไม่เหมือนกัน ค่าในโค้ดคือ calibration_factor =  149781.00 เเละ zero_factor 8314613

#define DOUT 23   // ขา DOUT ของ Load Cell
#define CLK 22    // ขา CLK ของ Load Cell

// ค่าความไวและการคาลิเบรตของ Load Cell
float calibration_factor =  149781.00;
#define zero_factor 8314613 
#define DEC_POINT 2

float offset = 0;
float target_weight = 0.40; // กำหนดเป้าหมายของน้ำหนักอาหารที่ 40 กรัม

// สร้างอ็อบเจ็กต์ Servo และ Load Cell
Servo myServo;
HX711 scale(DOUT, CLK);

bool foodDispensed = false; // ตัวแปรเพื่อตรวจสอบว่าปล่อยอาหารแล้วหรือยัง
bool pauseSystem = false;   // ตัวแปรสำหรับหยุดระบบชั่วคราว
unsigned long pauseStartTime = 0;  // เวลาเริ่มหยุดระบบ
unsigned long pauseDuration = 4 * 60 * 60 * 1000;  // 4 ชั่วโมงในหน่วยมิลลิวินาที

// ฟังก์ชันสำหรับส่งข้อความไปยัง LINE Notify
void sendLineNotify(String message) {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    http.begin("https://notify-api.line.me/api/notify");
    http.addHeader("Content-Type", "application/x-www-form-urlencoded");
    http.addHeader("Authorization", "Bearer " + LINE_TOKEN);

    String payload = "message=" + message;
    int httpResponseCode = http.POST(payload);

    if (httpResponseCode > 0) {
      String response = http.getString();
      Serial.println("Response: " + response);
    } else {
      Serial.println("Error on sending POST: " + String(httpResponseCode));
    }
    
    http.end();
  } else {
    Serial.println("WiFi not connected");
  }
}

ภาพนี้คือโค้ดคือสั่งการทำให้เวลาเครื่องชั่งทำงานครบกำหนดเเล้ว อุปกรณ์จะหยุดทำงาน 4 ชั่วโมง

6. ผลการดำเนินงาน

ภาพรวมโมดูลทั้งหมดและระบบการทำงานของเครื่องให้อาหารแมว

การทำงานของเครื่องอาหารแมว เมื่อกดเปิดสวิตช์ จะสั่งให้ Servo หมุนไปที่ 0 องศา หลังจากนั้น เมื่ออาหารไหลลงมาบน loadcell senser ถึงที่กำหนดใว้ จะสั่งให้ Servo หมุนไปที่ 90 องศา และไม่สามารถทำงานได้จนกว่าอาหารจะลด หรือน้ำหนักบน loadcell จะลดลง เเละจะมี Ultrasonic อีกตัวเพื่อวัดปริมาณอาหาร และแจ้งเตือนผ่าน Line ในขั้นตอนสุดท้าย

โค้ดทั้งหมดของการทำงาน

#include <WiFi.h>
#include <HTTPClient.h>
#include <ESP32Servo.h>
#include "HX711.h"

// ข้อมูล Wi-Fi ของคุณ
const char* ssid = "xxxx";
const char* password = "xxxxxxx";

// LINE Notify Token ที่คุณได้จากการสมัคร
String LINE_TOKEN = "xxxxxxxxxxxxxxxx";

// กำหนดขา GPIO
const int trigPin = xx;
const int echoPin = xx;
const int servoPin = xx;
const int trigPin2 = xx;  // เซ็นเซอร์ตรวจจับอาหาร
const int echoPin2 = xx;
#define DOUT xx   // ขา DOUT ของ Load Cell
#define CLK xx    // ขา CLK ของ Load Cell

// ค่าความไวและการคาลิเบรตของ Load Cell
float calibration_factor =  xxxxx;
#define zero_factor xxxxxx 
#define DEC_POINT 2

float offset = 0;
float target_weight = 0.40; // กำหนดเป้าหมายของน้ำหนักอาหารที่ 40 กรัม

// สร้างอ็อบเจ็กต์ Servo และ Load Cell
Servo myServo;
HX711 scale(DOUT, CLK);

bool foodDispensed = false; // ตัวแปรเพื่อตรวจสอบว่าปล่อยอาหารแล้วหรือยัง
bool pauseSystem = false;   // ตัวแปรสำหรับหยุดระบบชั่วคราว
unsigned long pauseStartTime = 0;  // เวลาเริ่มหยุดระบบ
unsigned long pauseDuration = 4 * 60 * 60 * 1000;  // 4 ชั่วโมงในหน่วยมิลลิวินาที

// ฟังก์ชันสำหรับส่งข้อความไปยัง LINE Notify
void sendLineNotify(String message) {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    http.begin("https://notify-api.line.me/api/notify");
    http.addHeader("Content-Type", "application/x-www-form-urlencoded");
    http.addHeader("Authorization", "Bearer " + LINE_TOKEN);

    String payload = "message=" + message;
    int httpResponseCode = http.POST(payload);

    if (httpResponseCode > 0) {
      String response = http.getString();
      Serial.println("Response: " + response);
    } else {
      Serial.println("Error on sending POST: " + String(httpResponseCode));
    }
    
    http.end();
  } else {
    Serial.println("WiFi not connected");
  }
}

long readDistance() {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  long duration = pulseIn(echoPin, HIGH);
  long distance = (duration / 2) / 29.1;  // คำนวณระยะทางเป็นเซนติเมตร
  return distance;
}

long readFoodDistance() {
  digitalWrite(trigPin2, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin2, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin2, LOW);

  long duration = pulseIn(echoPin2, HIGH);
  long distance = (duration / 2) / 29.1;  // คำนวณระยะทางเป็นเซนติเมตร
  return distance;
}

float get_units_kg() {
  return(scale.get_units() * 0.453592);  // แปลงหน่วยเป็นกิโลกรัม
}

void setup() {
  Serial.begin(115200);
  
  // เชื่อมต่อ Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.println("Connected to WiFi");

  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(trigPin2, OUTPUT);
  pinMode(echoPin2, INPUT);
  myServo.attach(servoPin);
  myServo.write(0);  // ตั้งค่าเริ่มต้นของเซอร์โว

  scale.set_scale(calibration_factor);  
  scale.set_offset(zero_factor);
}

void loop() {
  // ถ้าระบบถูกหยุดอยู่
  if (pauseSystem) {
    // ตรวจสอบว่าครบ 6 ชั่วโมงหรือยัง
    if (millis() - pauseStartTime >= pauseDuration) {
      pauseSystem = false;  // เริ่มระบบใหม่หลังจาก 4 ชั่วโมง
      Serial.println("ระบบเริ่มทำงานใหม่หลังจาก 4 ชั่วโมง");
      sendLineNotify("ระบบกลับมาเริ่มทำงานใหม่หลังจากหยุดเป็นเวลา 4 ชั่วโมง");
    } else {
      return;  // ถ้ายังไม่ครบ 4 ชั่วโมง ให้กลับไปทำงาน loop ใหม่
    }
  }

  long distance = readDistance();
  Serial.print("Distance to cat: ");
  Serial.println(distance);

  // ตรวจจับว่าแมวอยู่ใกล้ (ระยะน้อยกว่า 10 เซนติเมตร)
  if (distance < 10) {
    // ถ้ายังไม่ปล่อยอาหาร
    if (!foodDispensed) {
      myServo.write(90);  // เปิดเซอร์โวที่ 90 องศาเพื่อปล่อยอาหาร
      sendLineNotify("แมวมากินอาหารแล้ว");
      
      delay(2000);  // รอให้อาหารไหลลงมา

      float weight = get_units_kg() + offset;  // อ่านน้ำหนักจาก Load Cell
      Serial.print("Weight: ");
      Serial.println(weight);

      // ถ้าน้ำหนักอาหารถึงเป้าหมาย
      if (weight >= target_weight) {
        Serial.println("น้ำหนักถึงเป้าหมายแล้ว");
        myServo.write(0);  // ปิดเซอร์โว
        sendLineNotify("อาหารแมวเต็ม 40 กรัมแล้ว");
        foodDispensed = true; // ตั้งค่าให้ปล่อยอาหารแล้ว
        
        // เริ่มหยุดระบบ 6 ชั่วโมงหลังจากแมวกินเสร็จ
        pauseSystem = true;
        pauseStartTime = millis();
        Serial.println("ระบบหยุดทำงานเป็นเวลา 4 ชั่วโมง");
        sendLineNotify("ระบบหยุดทำงานเป็นเวลา 4 ชั่วโมง");
      } else {
        Serial.println("น้ำหนักยังไม่ถึงเป้าหมาย");
      }
    }
  } else {
    // รีเซ็ตสถานะเมื่อแมวออกไป
    foodDispensed = false; 
  }

  // ตรวจจับระดับอาหารในเครื่อง
  long foodDistance = readFoodDistance();
  Serial.print("Distance to food: ");
  Serial.println(foodDistance);
  
  // ถ้าไม่มีอาหาร (ระยะเกิน 10 เซนติเมตร)
  if (foodDistance > 10) {
    Serial.println("อาหารหมดแล้ว");
    sendLineNotify("อาหารหมดแล้ว");
  }

  // ตรวจสอบทุกๆ 1.5 วินาที
  delay(1500);  
}


6.การทดลอง

นี้คือภาพการ calibration เพื่อ Load cell มีค่าน้ำหนักเป็น 0 เเละ ต้อง calibration อาหารเพื่อกำหนดปริมาณอาหารที่กำหนดเเละจะทำให้ Servo ปิด เเละอุปกรณ์จะหยุดทำงกา

ภาพดังกล่าวคือภาพการ calibration อาหาร

วิดีโอดังกล่าวคือ วิดีโอการทดลอง เเละ จะเห็นได้ว่ามีการเเจ้งเตือนผ่านไลน์ตามที่กำหนด

ภาพดังกล่าวคือ การเเจ้งเตือนต่างๆผ่านไลน์

7.สรุปผลและข้อเสนอแนะ

1.สรุปผล

จากการทดลองเครื่องให้อาหารแมวอัติโนมัติแจ้งเตือนผ่าน Line ทำงานได้อย่างมีประสิทธิภาพตามเงื่อนไขที่ทดลองใว้ สามารถให้อาหารอย่างต่อเนื่องและเป็นไปตามปริมาณอาหารที่ได้กำหนดใว้

2.ข้อเสนอแนะ

2.1 สามารถนำไปต่อยอดในการเกษตร เพื่อให้อาหารแกสัตว์ที่มีขนาดใหญ่และจำนวนมาก

8.ข้อมูลอ้างอิง

     1 https://youtu.be/hADDxvdPdTI?si=vXt23AWtRsnNjSV3

     2. ProceedingsEECON41Vol2.pdf (psu.ac.th)

     3. วิธีสร้าง เครื่องให้อาหารสุนัข เครื่องให้อาหารแมว ESP8266 Wi-Fi – รับทำโปรเจคและจำหน่าย Arduino NodeMCU ราคาถูกเวอร์ กรุงเทพฯ จัดส่งฟรี : Inspired by LnwShop.com (ec-bot.com)

     4. https://youtu.be/0sKfXwIYvGw?si=UJiI9I0tFcOVP8lR

     5. https://www.arduitronics.com/

     6. https://www.cybertice.com/

     7. http://suwitkiravittaya.eng.chula.ac.th/

     8. https://www.makewebeasy.com/

9. https://www.etteam.com/prodESP/ESP32-DEV-KIT/ESP32-DEV-KIT.html

10. http://118.174.134.188/e-learning_new/arduino_project/project1/lesson4/content1/index.php

11.https://www.arduitronics.com/product/720/load-cell-weight-sensor-5-kg-high-quality-yzc-133

12. https://www.ab.in.th/product/253/module-dc-to-dc-step-down-converter-lm2596-3a-

13. https://tweakable-parts.com/de/dc-stecker/611-dc-jack-21mm-schwarz.html

14.https://www.musicarms.net/adapter-9v

15.https://www.mediafire.com/file/jkigwkd3nouckw5/TridentTD_LineNotify-master.rar/file

16.https://www.cybertice.com/article/94/

    17.ค้นหาข้อมูล https://chatgpt.com/

วีดีโอนำเสนอเครื่องให้อาหารแมวอัตโนมัติและแจ้งเตือนผ่านแอพพลิเคชั้น Line

You may also like...

ใส่ความเห็น

อีเมลของคุณจะไม่แสดงให้คนอื่นเห็น ช่องข้อมูลจำเป็นถูกทำเครื่องหมาย *