113-2學期課程企劃書
專題名稱: 寵物自動餵食器
組員: 411214323林時寬、411214234莫山毅
大綱
利用arduino製作出可以自動餵食寵物的機器,方便。
背景與動機
莫山毅家裡貓太多
材料部分:
arduino有了
伺服馬達(Servo Motor) x 1
超音波感測器 x 1 (偵測飼料高度)有了
WIFI模組(ESP8266或ESP01) x 1 (參考價格: 網路上最便宜約NT$60~100)
飼料儲存桶 x 1 (可回收或現成購買,若購買現成桶子約NT$50~100)
LED指示燈 x 2 有了)
外殼材料 (壓克力板或木板,估計NT$100~200)
其他材料 (杜邦線、麵包板、螺絲等,合計約NT$50~100)
軟體部分:
Arduino IDE
手機App介面 (Blynk)。
功能:
擁有記錄餵食時間、狀態顯示、飼料量檢測等。
專題製作的方法與進行步驟
硬體設計與組裝: 先設計餵食器出料口結構並以伺服馬達控制開合。在飼料桶頂部或內部安裝超音波感測器,用於偵測飼料所剩高度。使用Arduino UNO連接感測器與Wi-Fi模組,並確認電路連接正常。
軟體規劃與開發: 在Arduino IDE中撰寫控制伺服馬達、超音波感測器讀取及Wi-Fi連線程式。與Blynk或自行開發網頁介面串接,實現遠端監控及餵食設定。可包括定時餵食、手動餵食啟動、飼料不足提醒等。
系統整合與測試: 確認整體電路與程式功能運作正常。測試超音波感測器是否能準確偵測飼料量,Wi-Fi連線是否穩定,定時餵食與手動餵食功能是否能如預期執行,並確認飼料不足時能及時通知。
外觀與安全性考量: 以壓克力或木板打造外框,保護內部電路並避免寵物誤觸。在出料口加入防卡飼料機制,以確保餵食流暢。外殼盡量美觀、安全、穩固。
預定進度表
第一至第二週: 搜集智慧餵食器現有產品與技術資料,進行需求分析
第三至第四週: 選購Arduino UNO、感測器、飼料桶等材料並進行硬體規劃
第五至第六週: 開始撰寫程式碼(餵食控制、感測器偵測、Wi-Fi連線)
第七週: 進行初步硬體組裝與測試
第八至第九週: 製作外殼、整合電路、修正程式
第十週: 執行多次測試(飼料偵測、定時餵食、手動餵食)
第十一至第十二週: 優化系統,補強外觀並測試遠端監控功能
第十三至第十四週: 進行成品展示前的最終調整
最後一堂: 期末成果展示與報告
預期結果
莫山毅家裡的貓可以吃得很飽然後變胖。
10/14 利用tinkercad的建模打造出屬於自己的雪人然後利用印表機打出一坨玉米大便
也可以利用搜尋功能找到他人的作品
列印前需要先將印表機預熱,還需要將自己的作品使用其他程式進行片後才可以進行列印
10/21
課程中介紹了基礎的電子材料例如麵包版以及led燈。
也介紹了arduino的構造
Arduino的構造主要包含一塊印刷電路板和一顆可寫入程式的微控制器晶片,板上設置了多組數位與類比輸入輸出腳位,用於讀取感測器訊號或控制外部元件。一般還會具備電源插孔、USB介面、電壓穩壓器以及搭配振盪電路的時脈裝置,USB介面用來與電腦連接進行程式燒錄與供電。部分板型在USB連接線路上還搭配串列轉USB晶片,將Arduino微控制器的UART訊號轉為USB訊號。在微控制器與電源線路之間通常設有穩壓器與保護電路,以確保穩定的工作電壓。除此之外,Arduino板上也常見一些LED燈號指示,如電源指示燈與連接在特定腳位(例如數位腳位13)的LED,方便確認板子的工作狀態。
練習1:
點亮兩個LED燈泡並且讓兩個燈泡輪流閃爍,控制閃爍的頻率在2秒一次。
程式碼:
const int ledPin1 = 8;
const int ledPin2 = 9;
void setup() {
pinMode(ledPin1, OUTPUT);
pinMode(ledPin2, OUTPUT);
}
void loop() {
digitalWrite(ledPin1, HIGH);
digitalWrite(ledPin2, LOW);
delay(2000);
digitalWrite(ledPin1, LOW);
digitalWrite(ledPin2, HIGH);
delay(2000);
}
11/11
這次課程介紹了按鈕這個配件
以及Serial.begin(鮑率):放在void setup()區域,告訴Arduino 要以多少的鮑率來傳送訊號
、digitalRead(腳位):利用digitalRead來讀取Arduino該腳位獲得的訊號、Serial.println(內容):在序列埠監控視窗中 print 出你想要print的東西,若是想要print 完後不換行則是將 Serial.println() 更改為 Serial.print()、 if(條件)如果符合所訂的條件則執行放在if 裡面的程式碼,在條件中會常常用到的符號為 不等於: != 、 等於: ==
練習:
int buttonState = 0;
void setup()
{
pinMode(8, OUTPUT);
pinMode(9, INPUT_PULLUP);
}
void loop()
{
buttonState = digitalRead(9);
if (buttonState == HIGH)
{
digitalWrite(8, LOW);
}
else
{
digitalWrite(8, HIGH);
}
}
11/18
這次課程介紹了函式庫(libary)的功能,Arduino的函式庫通常以C或C++語言撰寫,能將特定功能的程式碼模組化、封裝並以簡潔的介面提供給使用者呼叫。這些函式庫可以是官方或社群開發,常見的例子包括Servo函式庫(用於控制伺服馬達)、Wire函式庫(用於I2C通訊)、SPI函式庫(用於SPI通訊)以及Ethernet、WiFi等網路相關函式庫。使用函式庫時,只需在程式起始處加上#include並指定函式庫名稱,接著就能透過提供的API輕鬆調用各種功能,免去自行撰寫底層驅動或通訊協定的繁瑣過程。在Arduino IDE裡可以透過工具選單中的「管理函式庫」來搜尋、安裝或更新所需函式庫,也能手動將函式庫檔案放入Arduino預設的libraries資料夾以便使用。由於Arduino社群相當活躍,不同領域與應用都有對應的函式庫可供參考,能快速協助使用者完成感測器讀取、馬達控制、顯示器驅動、網路通訊以及更多進階功能。透過這些函式庫,開發者能將重心放在創意與功能設計上,大幅縮短開發時間並提升效率。
練習一:
#include <Wire.h>
#include <ThreeWire.h>
#include <RtcDS1302.h>
ThreeWire myWire(A3, A4, A5);
RtcDS1302<ThreeWire> Rtc(myWire);
void setup ()
{
Serial.begin(9600);
Serial.print("compiled: ");
Serial.print(__DATE__);
Serial.println(__TIME__);
Rtc.Begin();
//__DATE__,__TIME__,是程式碼編譯時的日期和時間
RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
printDateTime(compiled);
Serial.println();
//判斷DS1302是否接好
if (!Rtc.IsDateTimeValid())
{
Serial.println("RTC lost confidence in the DateTime!");
Rtc.SetDateTime(compiled);
}
if (Rtc.GetIsWriteProtected())
{
Serial.println("RTC was write protected, enabling writing now");
Rtc.SetIsWriteProtected(false);
}
if (!Rtc.GetIsRunning())
{
Serial.println("RTC was not actively running, starting now");
Rtc.SetIsRunning(true);
}
RtcDateTime now = Rtc.GetDateTime();
if (now < compiled)
{
Serial.println("RTC is older than compile time! (Updating DateTime)");
Rtc.SetDateTime(compiled);
}
else if (now > compiled)
{
Serial.println("RTC is newer than compile time. (this is expected)");
}
else if (now == compiled)
{
Serial.println("RTC is the same as compile time! (not expected but all is fine)");
}
}
void loop ()
{
RtcDateTime now = Rtc.GetDateTime();
printDateTime(now);
Serial.println();
if (!now.IsValid())
{
Serial.println("RTC lost confidence in the DateTime!");
}
delay(10000);
}
void printDateTime(const RtcDateTime& dt)
{
char datestring[20];
snprintf_P(datestring,
countof(datestring),
PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
dt.Month(),
dt.Day(),
dt.Year(),
dt.Hour(),
dt.Minute(),
dt.Second() );
Serial.print(datestring);
}
練習二:
#include <TM1637Display.h>
#define countof(a) (sizeof(a) / sizeof(a[0]))
#define CLK 11
#define DIO 12
TM1637Display display(CLK, DIO);
ThreeWire myWire(7, 6, 5); //接腳: DAT/IO, SCLK/CLK, RST/CE (DS1302的接線自行更改)
RtcDS1302<ThreeWire> Rtc(myWire);
void setup()
{
Serial.begin(9600);//鮑率57600
display.setBrightness(7);
Serial.print("compiled: ");
Serial.print(__DATE__);
Serial.println(__TIME__);
Rtc.Begin();
RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
printDateTime(compiled);
Serial.println();
if (!Rtc.IsDateTimeValid())
{
// Common Causes:
// 1) 第一次使用但設備還沒跑好
// 2) 電池電量低
Serial.println("RTC lost confidence in the DateTime!");
Rtc.SetDateTime(compiled);
}
if (Rtc.GetIsWriteProtected())
{
Serial.println("RTC was write protected, enabling writing now");
Rtc.SetIsWriteProtected(false);
}
if (!Rtc.GetIsRunning())
{
Serial.println("RTC was not actively running, starting now");
Rtc.SetIsRunning(true);
}
//判斷DS1302上紀綠的時間和編譯時的時間,哪個比較新
//如果編譯時間比較新,就進行設定,把DS1302上的時間改成新的時間
//now:DS1302上紀綠的時間,compiled:編譯時的時間
RtcDateTime now = Rtc.GetDateTime();
if (now < compiled)
{
Serial.println("RTC is older than compile time! (Updating DateTime)");
Rtc.SetDateTime(compiled);
}
else if (now > compiled)
{
Serial.println("RTC is newer than compile time. (this is expected)");
}
else if (now == compiled)
{
Serial.println("RTC is the same as compile time! (not expected but all is fine)");
}
}
void loop()
{
RtcDateTime now = Rtc.GetDateTime();
printDateTime(now);//呼叫副函數
Serial.println();
if (!now.IsValid())
{
// Common Causes:
// 1) the battery on the device is low or even missing and the power line was disconnected
Serial.println("RTC lost confidence in the DateTime!");
}
}
void printDateTime(const RtcDateTime &dt)
{
char datestring[20];
snprintf_P(datestring,
countof(datestring),
PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
dt.Month(),//月
dt.Day(),//日
dt.Year(),//年
dt.Hour(),//小時
dt.Minute(),//分鐘
dt.Second());//秒
Serial.println(dt.Minute());
display.showNumberDecEx(dt.Hour(), 0b11100000, 2, 2); //前兩位數設定為小時
display.showNumberDecEx(dt.Minute(), 0b11100000, true, 2, 2); //後兩位數設定為分鐘
delay(1000);
}
11/25利用光敏電阻配合馬達去感應小恐龍障礙物實現跳躍
程式碼:
#include <Servo.h> //載入函式庫,這是內建的,不用安裝
Servo myservo; // 建立SERVO物件
int r = A0 ; //假設一個字母R代替A0的孔洞
int sensorValue;
void setup() {
Serial.begin(9600);
myservo.attach(9); // 設定要將伺服馬達接到哪一個PIN腳
myservo.write(0); //歸零
// put your setup code here, to run once:
}
void loop() {
sensorValue = analogRead(r);
Serial.println(sensorValue);
if(sensorValue <= 0){ //如果光敏電阻的數值小於或大於某一數值時,則伺服馬達轉動。
myservo.write(30); //旋轉到30度
delay(100); //延遲0.5秒
myservo.write(0); //旋轉到0度
delay(100 ); //延遲0.5秒 可自行更改
}
}
12/02
DHT11溫溼度模組
測濕度:
當環境中的溼度發生變化,感濕元件會吸收或釋放濕氣,讓感測器的電容值發生變化。
通過測量電容值變化,DHT11可以透過先前校準的數值,判斷出當前的溼度值。
同時,熱敏電阻會測量環境的溫度。
內建的控制器會將濕度和溫度值轉換為數位訊號,並將訊號輸出。
微控制器會用通訊腳位與DHT11進行通訊,發出請求並接收感測器返回的數據。
//使用DHT11函式庫
#include <SimpleDHT.h>
//定義腳位自行更改
int pinDHT11 = 18;
//建立DHT11函式庫物件
SimpleDHT11 dht11;
void setup() {
Serial.begin(9600);
}
void loop() {
byte temperature = 0;
byte humidity = 0;
int err = SimpleDHTErrSuccess;
// start working...
Serial.println("=================================");
if ((err = dht11.read(pinDHT11, &temperature, &humidity, NULL)) != SimpleDHTErrSuccess) {
Serial.print("Read DHT11 failed, err="); Serial.println(err);delay(1000);
return;
}
Serial.print("Humidity = ");
Serial.print((int)humidity);
Serial.print("% , ");
Serial.print("Temperature = ");
Serial.print((int)temperature);
Serial.println("C ");
delay(1000); //每1秒顯示一次
}
倒車雷達模擬
透過超聲波感測器、LED和蜂鳴器來模擬倒車雷達
#define m1 294 //頻率1
#define m5 440 //頻率2
#define h5 880 //頻率3
int red = 2;
int yellow = 3;
int green = 4;
int v=5; //蜂鳴器輸出
int trigPin = 12; //定義 觸發輸出腳位 Trig Pin
int echoPin = 11; //定義 回音接收腳位 Echo Pin
float duration, distance; //定義 時間長度duration 與 距離distance 格式為 浮點數
void setup(){
Serial.begin(9600); //設定 序列埠 鮑率
while (!Serial){ ;} //等待序列埠接通
pinMode(red, OUTPUT);
pinMode(yellow, OUTPUT);
pinMode(green, OUTPUT);
pinMode(v, OUTPUT);
pinMode(trigPin, OUTPUT); //設定 觸發輸出腳位(trigPin)的功能為 輸出(OUTPUT)
pinMode(echoPin, INPUT); //設定 回音接收腳位(echoPin)的功能為 輸入(INPUT)
}
void loop(){
//於 觸發輸出腳位 Trig Pin 產生觸發脈衝(應大於 10微秒)
digitalWrite(trigPin, LOW); // 讓觸發腳位trigPin 確實降回低電位,持續 20微秒 (us)
delayMicroseconds(20);
digitalWrite(trigPin, HIGH); // 觸發腳位trigPin 變成高電位,持續 20微秒 (us)
delayMicroseconds(20);
digitalWrite(trigPin, LOW); // 觸發腳位trigPin 再降回低電位,完成脈衝,使HC-SR04產生聲音脈衝出去
duration = pulseIn(echoPin, HIGH); // 於回音接收腳位(Echo Pin) 收到高電位(High) 的時間 (微秒 us)
distance = duration*0.0170; // 將聲音 "來回" 時間(us) 換算成距離 (cm) [ 假設聲波速率為 340 m/s ]
if (distance >= 25 ){
digitalWrite(green, HIGH);
digitalWrite(yellow, LOW); digitalWrite(red, LOW);
tone(v, m1, 200);
delay(250);
}
else if (distance >= 10 and distance <= 25){
digitalWrite(yellow, HIGH);
digitalWrite(green, LOW); digitalWrite(red, LOW);
tone(v, m5, 150);
delay(200);
}
else {
digitalWrite(red, HIGH);
digitalWrite(green, LOW); digitalWrite(yellow, LOW);
tone(v, h5, 100);
delay(150);
}
//Serial.println(distance); // 將 距離distance 傳送到序列埠暫存器
Serial.flush(); // 等待序列埠暫存器中的數據傳送至電腦完畢
delayMicroseconds(duration*1.5); //避免收到更早發出的聲音脈衝 回音
}