實作內容:
自動餵食器 Automatic feeder
倉鼠的飼主遇到以下幾個問題:
1.忘記餵食飼料
2.觀察倉鼠的健康狀況 (透過倉鼠有無按時吃飯)
3.觀察倉鼠的活動量
每隔固定時間進行餵食,同時感測倉鼠有無站在飼料盒前方進食,若無則發出聲音提示
透過滾輪的圈數來了解倉鼠的運動量,允用紅外線感應搭配滾輪上的擋板檢測圈數
運用 LCD 面板顯示倉鼠的資訊
1.進食次數與多久未進食
2.所跑圈數
所有的任務列表
MsgQueue = xQueueCreate(1, sizeof(unsigned int)); // sign for switch display mode
xTaskCreate(InfraredTask, "InfraredTask", 128, NULL, 1, NULL); // detect running lap
xTaskCreate(ButtonTask, "ButtonTask", 128, NULL, 1, NULL); // switch lcd displaymode
xTaskCreate(Display, "Display", 128, NULL, 1, NULL); // show lcd content
xTaskCreate(BuzzerTask, "BuzzerTask", 128, NULL, 1, NULL); // sound beep to remind eating
xTaskCreate(Motor_Task, "Motor_Task", 128, NULL, 1, NULL); // check if have eaten
xTaskCreate(TimerTask, "TimerTask", configMINIMAL_STACK_SIZE, NULL, 1, &xTimerTaskHandle); // Timer
透過藍色按鈕切換顯示模式,用顯示餵食狀況說明並運用 i2c-lcd.h
和 i2c-lcd.c
控制 LCD 面板,lcd_put_cur(0, 0);
可以控制要從 LCD 螢幕中哪個位置開始輸出,(0, 0)
為第一列第一個。
#include "i2c-lcd.h" // for LCD module
if(displaymode == 0){
for (;;) {
lcd_clear(); // clear screen
lcd_put_cur(0, 0); // go to printing place
sprintf(buffer, "eat: %u %lu", eat, timeCounter); // push output string to buffer
lcd_send_string(buffer); // show string
lcd_put_cur(1, 0); // go to printing place
memset(buffer, 0, sizeof(buffer)); // reset buffer
sprintf(buffer, "noneat time: %lu", wait_timeCounter);
lcd_send_string(buffer);
xQueueReceive(MsgQueue, &displaymode, 0);
vTaskDelay(10);
if(displaymode == 1) break;
}
}
在原有的滾輪上加裝擋板,當紅外線感測到檔板時計一圈,並且是當狀態改變的時候增加一圈,避免檔板剛好停在感應處,造成一直增加圈數的問題。
objectDetected
:感測到檔板
RunningLap
:實際的圈數
void InfraredTask(void *pvParameters)
{
for (;;)
{
if (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_4) == GPIO_PIN_RESET) {
if (objectDetected == 0) {
objectDetected = 1;
RunningLap++;
}
}
else
objectDetected = 0;
vTaskDelay(10);
}
}
利用 PWM 控制馬達的運行
if (timeCounter % FEEDING_INTERVAL_SECONDS ==0)
控制餵食頻率,接著初始化馬達HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_4);
,再透過__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_4, 150);
控制馬達的轉速,目的是將馬達轉動 90 度並稍微停頓後轉回,模擬到飼料的情境
void Motor_Task(void *pvParameters){
for (;;){
if (timeCounter % FEEDING_INTERVAL_SECONDS ==0){
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15, GPIO_PIN_RESET);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_4);
uint32_t From_begin_time = HAL_GetTick();
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_4, 150);
while(HAL_GetTick() - From_begin_time < 500 / portTICK_RATE_MS){}
From_begin_time = HAL_GetTick();
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_4, 50);
while(HAL_GetTick() - From_begin_time < 500 / portTICK_RATE_MS){}
From_begin_time = HAL_GetTick();
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_4, 150);
while(HAL_GetTick() - From_begin_time < 500 / portTICK_RATE_MS){}
}
}
}
作為 GPIO 外部中斷 call back 函數,當觸摸壓力感測器,表示有進食因此重設未進食時間並停止蜂鳴器,同時有用黃燈顯示幫助觀察
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) // touch pressure sensor
{
if (GPIO_Pin == GPIO_PIN_5)
{
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12); // yellow light
touch = 1; // set torch to True(1)
wait_timeCounter = 0; // reset wait_timeCounter
}
}
SYS -> Timebase Source : TIM7
驅動馬達的硬體設置
因為需要連接 5 個設備,有些設備的 VCC 不是接建議的 5V ,但是還是可以運作
LCD screen | STM board |
---|---|
GND | GND |
VCC | 5V |
SDA | PB9 |
SCL | PB6 |
馬達 SG-90 | STM board |
---|---|
GND (棕色) | GND |
VCC (紅色) | 5V |
PWM 輸入 (橘色) | PA3 |
紅外線 | STM board |
---|---|
GND | GND |
VCC | VDD |
OUT | PC4 |
蜂鳴器 | STM board |
---|---|
GND | GND |
VCC | 3V |
I/O | PA1 |
壓力感測器 | STM board |
---|---|
GND | GND |
VCC | VDD |
SIG | PC5 |