---
# System prepended metadata

title: 高二下自主學習計畫---遠端寵物飼料餵食器

---

# 高二下自主學習計畫---遠端寵物飼料餵食器
## 目錄
[TOC]
## 動機
在高一探究與實作的課程中，我們有研究可以藍芽控制的寵物飼料餵食器，不過由於藍芽接收範圍有限之原因，因此不能夠完全實現能夠在美國遠端台灣的餵食器的想法。因此這次的自主學習，我們決定要實現此想法，並且改善裝置的設計。
## 預計功能
* 能夠在國外也能用手機定時餵食家中寵物
## 實踐想法
**這次計畫預計的想法是:前端網頁送出選擇時間後經由後端去計算多久之後執行，最後透過Arduino的HTTPCLIENT程式庫去讀取最後計算出來所需的時間讓馬達在時間到後運作。**

為了達成遠端的功能，勢必不能只依靠無線WI-FI和藍芽，因此我想到可以上傳project到IoT雲平台上，因此我使用了Heroku這個支持多種程式語言的雲平台當作這次架設網頁的Server端。這也是我第一次接觸到要設計後端程式運作，由於之前有學過一點點python，所以這次是使用Flask去架設後端程式網頁。
## 程式架構

**以下先附上這次的程式碼:**

**HTML網頁程式碼如下:**
```htmlembedded=
<!DOCTYPE html>
<head>
  <h1>遠端控制飼育機器</h1>

  <!--for python flask-->
  <script type="text/javascript" src="{{ url_for('static' , filename='js/jquery.js')}}"></script>
  <script type="text/javascript" src="{{ url_for('static' , filename='js/jquery-ui.js')}}"></script>
  <script type="text/javascript" src="{{ url_for('static' , filename='js/jquery-ui-sliderAccess.js')}}"></script>
  <script type="text/javascript" src="{{ url_for('static' , filename='js/jquery-ui-timepicker-addon.js')}}"></script>
</head>

<body>
<!--Form-->
<div id='modes'>
  <form id='myform'  method="POST" action='' autocomplete="off">
    <div id='FeedTime'>
      <label for='select-FeedTime'>FeedTime</label>
      <input type='text' name='select-FeedTime' id='select-FeedTime'> 
    </div>
    <div id='WaterTime'>
      <label for='select-WaterTime'>WaterTime</label>
      <input type='text' name='select-WaterTime' id='select-WaterTime'>
    </div>
    </div>
  <input type="submit" id="submit" value="Send">
  </form>  
</div>
<!--for HTML-->
<script type="text/javascript" src="/static/js/jquery.js"></script>
  <script type="text/javascript" src="/static/js/jquery-ui.js"></script>
  <script type="text/javascript" src="/static/js/jquery-ui-sliderAccess.js"></script>
  <script type="text/javascript" src="/static/js/jquery-ui-timepicker-addon.js"></script>
  <link rel="stylesheet" href="/static/css/jquery-ui.min.css">
  <link rel="stylesheet" href="/static/css/jquery-ui-timepicker-addon.css">
<!--datetimepickerLaunch-->
<script>
  jQuery('#select-FeedTime').datetimepicker({dateFormat:'yy.mm.dd', timeFormat:'HH:mm', defaultDate:''});
</script>
<script>
  jQuery('#select-WaterTime').datetimepicker({dateFormat:'yy.mm.dd', timeFormat:'HH:mm', defaultDate:''});
</script>
</body>
```
**PYTHON程式碼如下:**
```python=
from flask import Flask,request,url_for,render_template,redirect
from datetime import datetime,timezone,timedelta
import time
app = Flask(__name__)
@app.route("/timetest")
def Time_cmp():
    ty = timezone(timedelta(hours =+8))
    datetimeNow = datetime.now(ty).strftime('%Y.%m.%d %H:%M')
    return datetimeNow

@app.route("/")
def home():
    global work
    work="FALSE"
    return "Hello Heroku."

@app.route("/htmltest", methods=["POST","GET"])
def htmltest():
    if request.method == "POST":
        WaterTime = request.form['select-WaterTime']
        FeedTime =request.form['select-FeedTime']
        print("Feed",FeedTime,"Water",WaterTime)
        if FeedTime != '':
            global A 
            A = Feed(FeedTime)
            work = "TRUE"
            return redirect(url_for("FEEDING"))                                       
        else:
            global B
            B = WATER(WaterTime)
            work = "TRUE"
            return redirect(url_for("WATERING"))                                               
    else:
        return render_template("index.html") 
@app.route("/FEEDING")
def FEEDING():
    return A 

@app.route("/WATERING")
def FEEDING():
    return B

@app.route("/WORK")
def WORK():
    reset()
    return work
    
def reset():
    time.sleep(1)
    work= "FALSE"
    return work

def Feed(selectFeedTime):
    ty = timezone(timedelta(hours =+8))
    datetimeNow = datetime.now(ty).strftime('%Y.%m.%d %H:%M:%S')
    datetimeNow = datetime.now().strptime(datetimeNow , '%Y.%m.%d %H:%M:%S')
    selectFeedTime = datetime.strptime(selectFeedTime , '%Y.%m.%d %H:%M')

    timeGapFeed = selectFeedTime - datetimeNow
    timeGapFeedSecond =  str(timeGapFeed.total_seconds())
    return timeGapFeedSecond

def Feed(selectWaterTime):
    ty = timezone(timedelta(hours =+8))
    datetimeNow = datetime.now(ty).strftime('%Y.%m.%d %H:%M:%S')
    datetimeNow = datetime.now().strptime(datetimeNow , '%Y.%m.%d %H:%M:%S')
    selectFeedTime = datetime.strptime(selectFeedTime , '%Y.%m.%d %H:%M')

    timeGapWater = selectWaterTime - datetimeNow
    timeGapWaterSecond =  str(timeGapWater.total_seconds())
    return timeGapWaterSecond

if __name__ == "__main__":
    app.run(debug=True)
```
**Arduino程式碼如下:**
```Arduino=
#include <WiFi.h>
#include <HTTPClient.h>
#include <Servo.h>
char severName[] = "feed-your-pet-anywhere-omg.herokuapp.com";
const char* ssid = "******";
const char* pass = "******";
const char* headerKeys[] = {"Referer","Cookie"};
Servo myservo;
int pos=0;
#define USE_SERIAL Serial

void setup() {
  myservo.attach(9);
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid,pass);
  
  Serial.println("");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);         
    Serial.print(".");
  }
  Serial.println("Wi-Fi ready...");
}

void loop(){
  if((WiFi.status()== WL_CONNECTED)){
    httpclient();
  }
}

void httpclient(){
  HTTPClient feedcheck;
  feedcheck.begin("http://feed-your-pet-anywhere-omg.herokuapp.com/FEEDING");

  int httpCode = feedcheck.GET();
  USE_SERIAL.print(httpCode);
  USE_SERIAL.print("\n");
  if(httpCode == HTTP_CODE_OK) {
    String payload1 = feedcheck.getString();
    int FEED=payload1.toInt();
 for(int t=FEED;t<0;t=t-1){ 
  if(t==0){
    Serial.print("ENDFEED");
    for(pos = 0; pos < 180; pos += 1)   
   {                                 
      myservo.write(pos);             
      delay(15);                      
    }
  for(pos = 180; pos>=1; pos-=1)     
    {                               
      myservo.write(pos);             
      delay(15);                      
    }
  }
 }
    Serial.print("Sever response playload:");
    Serial.print(payload1);
    Serial.print("\n");
  }
    else {
    USE_SERIAL.print("Error Response Code:" );
    USE_SERIAL.print(httpCode);
  }
  feedcheck.end();
}
```
## 運行截圖
![](https://i.imgur.com/tmj8Ifl.png)

![](https://i.imgur.com/pv38lPD.png)

![](https://i.imgur.com/bQl8a3Z.png)

## 遭遇困難 & 解決辦法
### HTML內部的Input time 無法選擇日期
HTML中的`input type="time"`只能選擇幾點幾分而不能選擇哪一天，為了能更精確選擇日期，查詢網路過後，發現可以使用額外的Javascript套件Datetimepicker，就能夠選擇日期了。

### Arduino判斷運作條件的方法
一開始的時候計畫要讓Arduino偵測網址，在python上面計時，時間到即跳轉頁面，Arduino偵測到後即運作。結果因為頁面是預先寫好的Arduino在偵測時就會直接偵測到計時完成的頁面而不停運作。

為了能實踐時間到才運作的想法，因此改成讓Arduino偵測要過多久才運作，並且將計時移動到Arduino端來進行，如此一來就能正常運行。
## 未來可改進之處
### 能夠餵飼料和倒水同時進行
當初在寫Arudino端偵測網頁時，一直找不到原因可以讓Arduino同時偵測飼料和倒水的頁面成功，導致一次只能選擇兩者之一進行，只能夠分開選兩次時間，如果能一次選擇兩個的時間能讓使用效果更好。
### 網頁介面
之後可以學習CSS的寫法，讓網頁介面整個看起來更美觀一些。
## 心得
這次自主學習整個程式都是由我來負責，其他人則負責製作裝置，因此我也感覺自己身負重任，畢竟缺少了程式整個都不能運作。不過很可惜的是當時因為疫情爆發而被迫線上教學，當時程式並還沒debug完，後來回學校後發現裝置被誤丟掉了，也因此沒有搭配過裝置運作。

不過這次的自主學習對我來說還是很好的一次學習經驗，這也是我第一次接觸到網頁的程式設計，我覺得非常有趣而javacript對html的擴˙充性也讓我再次讚嘆程式真的是無極限，甚麼都能做的互相搭配做出來。

## 參考資料
[發布網站到Heroku - iT 邦幫忙](https://ithelp.ithome.com.tw/articles/10196129)
[HttpClient - Arduino Reference](https://www.arduino.cc/reference/en/libraries/httpclient/)
[Arduino库教程-Bridge-HTTP Client - 创客智造](https://www.ncnynl.com/archives/201608/539.html)
[Python Flask 入門指南: 輕量級網頁框架教學](https://devs.tw/post/448)
[W3School-HTML Element Reference](https://www.w3schools.com/tags/default.asp)