2018年9月27日 星期四

創意組合 : 心動時刻


學習目標 : 利用faya電子積塊組出能偵測心率的裝置
學習時間 : 150min
使用模組 : (1) faya brickNano
                   (2) faya 心跳偵測模組
                   (3) faya 串列8x8點矩陣模組
                   (4) faya 串列七段顯示模組
                   (5) faya 蜂鳴器模組
 工具 : 樂高積木(相容)

====================相關知識====================
這篇文章中所用到的模組使用方式及相關知識整理如下,需進一步了解時可點進去參考:
模組介紹 : faya brickNano
模組介紹 : faya 心跳偵測模組
模組介紹 : faya 串列8x8點矩陣模組
模組介紹 : faya 串列七段顯示模組
模組介紹 : faya 蜂鳴器模組

======================開箱======================
心動時刻(型號NTG-508)是fayalab的第八款創意組合作品,此作品的目標是利用現有的faya模組,配合Arduino程式,組出一個能夠量測心率(BPM - Beats Per Minute, 每分鐘心跳數 )的裝置,當偵測到心跳訊號時,8x8點矩陣會閃爍一次愛心型狀,並在蜂鳴器產生一次嗶聲,在穩定的狀態下,大約偵測6~7次的心跳就會估計出受測者的心率。

盒內包含了製作心跳時刻所需的全部模組及附件:

1. brickNano      2. 8x8點矩陣模組     3. 心跳偵測模組     4. 串列七段顯示模組    5. 蜂鳴器模組
6. 積木柱子            7. 積木蓋子         8. 電源線(B)       9. micro USB傳輸線
10.  跳線組           11. 積木組           12. 積木底板


===================範例實作=====================
目標:
(1) 利用faya電子積塊,量測每分鐘心跳
(2) 量測到心跳訊號時,8x8點矩陣顯示心跳圖案
(3) 量測到心跳訊號時,蜂鳴器產生嗶聲
(4) 七段顯示器顯示每分鐘心跳數 BPM (Beats Per Minute)

組裝:
首先把 [1.faya brickNano] [2.串列8x8點矩陣模組] [3.心跳偵測模組] [4.串列七段顯示模組] [5. 蜂鳴器模組]的四個角落裝上[6.積木柱子]。積木柱子如果塞起來鬆鬆的沒關西,下個步驟透過積木蓋子就可以將柱子扣穩。

積木柱子的組裝過程可參考[功能介紹 : faya電子積塊與LEGO積木的結合]


接著把[2.串列8x8點矩陣模組] [3.心跳偵測模組] [4.串列七段顯示模組] [5. 蜂鳴器模組]的四個角落裝上[7. 積木蓋子],把[1.faya brickNano]的四個角若裝上1x1的積木。

積木蓋子的組裝過程可參考[小技巧 : 積木柱子太鬆時如何處理?]


接下來示範線路的連接和積木的組裝,我們這次要將五個模組以立體結構的方式組在16x16的積木平板上,大家可以按照自己喜愛的方式堆疊積木,但這邊建議先按照底下的範例跟著做一次,熟悉系統運作的方式後,再按照自己的喜好修改造型。如果照著底下的範例,組完後會長這個樣子:


安裝faya brickNano、心跳偵測模組、串列七段顯示模組:

首先利用[11. 積木包] 裡面的4x2,2x2積木,安裝在[12. 積木底板]上,位置如下所示


接著安裝 [1. faya brickNano]、[3.心跳偵測模組 ]、[4.串列七段顯示模組],分別安裝在上圖中的上方藍點、右下紅點和左下白點位置,安裝完後如下圖所示。


接下來安裝電源線,我們利用兩條[8. 電源線(B)]連結模組間的電源座,如下圖所示,將brickNano右邊的電源座連接到串列七段顯示模組,brickNano左邊的電源座連接到心跳偵測模組,連接的詳細原理和說明可參考這篇文章 簡易版


完成後,我們理一下電源線,將其繞到模組下方,保持美觀,然後利用[10.跳線組]完成Arduino和模組之間的訊號連接

心跳感測模組:
     
      Arduino_D2 ===> 心跳偵測模組_Signal


串列七段顯示模組:
     
      Arduino_D7 ===> 串列七段顯示模組_DIN
      Arduino_D6 ===> 串列七段顯示模組_CLK
      Arduino_D5 ===> 串列七段顯示模組_LOAD


同樣的我們把訊號線繞到模組下方保持美觀


安裝串列8x8點矩陣模組:

接著準備往上搭積木以建立立體結構,在這之前,先用3條黃色跳線將數位腳位D12、D11、D10拉出來,未來要連接上方的串列8x8點矩陣顯示模組,另外用一條綠色跳線將數位腳D3拉出來,未來要連接上方蜂鳴器模組。


拉完訊號線後,利用2片2x6平板,安裝在brickNano上方的1x1積木上


接著在2x6平板積木上安裝4顆1x2積木,如下所示


然後在上方覆蓋一片6x8平板積木


完成後,將 [2.串列8x8點矩陣模組]安裝在6x8平板上方,如下所示


接下來連接8x8點矩陣顯示模組和心跳偵測模組間的電源線,如下圖所示


然後利用剛剛引出的黃色跳線,連接Arduinoi和串列8x8點矩陣間的訊號線

串列8x8點矩陣模組:
     
      Arduino_D12 ===> 串列8x8點矩陣模組_DIN
      Arduino_D11 ===> 串列8x8點矩陣模組_CLK
      Arduino_D10 ===> 串列8x8點矩陣模組_LOAD


完成訊號線的連接後,將訊號線整理壓平,才不會擋到稍後積木的安裝


安裝蜂鳴器模組:

首先利用兩片2x6平板,安裝在6x8平板兩側


接著在尾端上方安裝一片2x10平板


然後在2x10平板上方放置兩個2x2積木,位置如下圖所示


 再將一片2x6平板壓在上方


完成以上步驟後,將主體擺一邊,拿出一片2x6平板和兩顆1x4積木,做出以下組合


接著在其上下方兩側各裝上兩顆90度的積木,如下所示


再將[5. 蜂鳴器模組]的積木柱子安裝在側面的四個積木凸點


完成後,將以上立體結構安裝在剛剛作品主體上,位置如下所示


接著連接蜂鳴器模組和串列七段顯示模組間的電源線,如下圖所示

 

然後利用剛剛引出的綠色跳線,連接Arduinoi和蜂鳴器模組間的訊號線,由下圖中我們可以看到此訊號線穿過模組下方的空間往上連接。

蜂鳴器模組:
     
      Arduino_D3 ===> 蜂鳴器模組_BZ


完成主體:

最後我們利用手邊剩下的積木做一些裝飾,這些裝飾不會影響功能,大家可以自由發揮,讓主體看起來不單調,以下是從各個角度檢視我們完成的樣子。

正面視角:


背面視角:


左方視角:


右方視角


側上方視角


範例程式:

底下為本作品的範例程式,程式中會用到LedControl的函式庫,可以點此下載,下載後解壓縮至Arduino下的Libraries資料夾,重新開啟Arduino IDE後會自動載入。

// 2017/9/27
// Faya-Nugget 範例程式 (HeartRateDetector_1.ino)
// 單元:心跳時刻
// 網址:https://fayalab.blogspot.com/2018/09/heart-rate-detector.html
// 目標: (1) 利用faya電子積塊,量測每分鐘心跳
// (2) 量測到心跳訊號時8x8點矩陣顯示心跳圖案
// (3) 量測到心跳訊號時蜂鳴器產生嗶聲
// (4) 七字節顯示器顯示每分鐘心跳數 BPM (Beats Per Minute)
// 接線: Arduino ==> faya模組
// D12 ==> DIN (點矩陣顯示模組)
// D11 ==> CLK (點矩陣顯示模組)
// D10 ==> LOAD (點矩陣顯示模組)
// D7 ==> DIN (7段顯示器模組)
// D6 ==> CLK (7段顯示器模組)
// D5 ==> LOAD (7段顯示器模組)
// D3 ==> BZ (蜂鳴器模組)
// D2 ==> Signal (心跳偵測模組)
#include <LedControl.h> //引入LED Control 函式庫
//定義Arduino與串列七字節模組的連接腳位與參數
LedControl faya7seg=LedControl(7,6,5,1); // DIN, CLK, LOAD, 串列數量 to 串列七字節
//定義Arduino與串列8x8點矩陣模組的連接腳位與參數
LedControl faya8x8=LedControl(12,11,10,1); // DIN, CLK, LOAD, 串列數量 to 串列點矩陣
#define irPulseIn 2 // 連接心跳感應模組輸入腳位
#define bzOut 3 // 連接蜂鳴器模組輸出腳位
boolean heartFull[8][8] = //定義形狀 - 實心愛心
{
{0,0,0,0,0,0,0,0},
{0,1,1,0,0,1,1,0},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{0,1,1,1,1,1,1,0},
{0,0,1,1,1,1,0,0},
{0,0,0,1,1,0,0,0}
};
boolean heartEmpty[8][8] = //定義形狀 - 空心愛心
{
{0,0,0,0,0,0,0,0},
{0,1,1,0,0,1,1,0},
{1,0,0,1,1,0,0,1},
{1,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,1},
{0,1,0,0,0,0,1,0},
{0,0,1,0,0,1,0,0},
{0,0,0,1,1,0,0,0}
};
int input, lastInput; // 輸入狀態, 前一筆輸入狀態
int pulseTime, lastTime; // 心跳時間點, 前一筆心跳時間點
int heartPeriod, heartRate; // 兩心跳間時間 (mS), 心率 (BPM)
int avgHeartRate = 72; // 平均心率
const int maxCount = 7; // 儲存心率資料筆數
int H_array[maxCount] = {72,72,72,72,72,72,72}; // 矩陣用來儲存心率
int count = 1; // 用來記錄儲存次數
void setup()
{
Serial.begin(9600); // 設定串列埠傳輸速率
pinMode(irPulseIn, INPUT); // 紅外線偵測連接到輸入腳位 (D2)
pinMode(bzOut, OUTPUT); // 輸出腳位 (D3) 連接到蜂鳴器
// 初始化七字節顯示器
faya7seg.shutdown(0,false); // 第0個元件不要進入省電模式
faya7seg.setIntensity(0,8); // 第0個的元件亮度8 (0~15)
faya7seg.clearDisplay(0); // 清除第0個元件
show4digits(0); // 輸出數字0000
// 初始化8x8點矩陣顯示器
faya8x8.shutdown(0,false); // 第0個元件不要進入省電模式
faya8x8.setIntensity(0,8); // 第0個的元件亮度8 (0~15)
faya8x8.clearDisplay(0); // 清除第0個元件
matrixEmpty(); // 輸出空心愛心圖形
}
void loop()
{
heartRateGet(); // 取得單次心率
rollingAverage(); // 計算平均心率
show4digits(avgHeartRate); // 顯示平均心率
}
void heartRateGet() //取得單次心率
{
input = digitalRead(irPulseIn); //讀取輸入狀態
if ((input != lastInput)&&(input == HIGH)) //上緣觸發
{
pulseTime = millis(); // 紀錄偵測到心跳時的起始時間
heartPeriod = pulseTime - lastTime; // 計算兩心跳間的時間間隔
lastTime = pulseTime; // 更新最後一次偵測到心跳的時間
heartRate = 60000/heartPeriod; // 心率 (每分鐘心跳數 BPM) = 60 / 心跳間隔時間(ms) = 60000 / 心跳間隔時間 (s)
if(heartRate > 150 || heartRate < 40) // 太高或太低的心率視為無效
{
heartRate = avgHeartRate; // 用平均心率取代
}
else
{
showSerialMonitor(); //顯示心率於串列埠傳輸視窗
tone(bzOut,800,100); // 蜂鳴器短beep
matrixFull(); // 8x8點矩陣秀出實心愛心
delay(100); // 延遲100ms
matrixEmpty(); // 8x8點矩陣秀出空心愛心
}
}
lastInput = input; // 更新input值
}
void rollingAverage()
{
if(H_array[count-1] != heartRate && abs(avgHeartRate-heartRate)<20) // 當偵測到心率改變,且改變不能太大
{
if(count == maxCount) //記錄到最後一筆資料時
{
count=0; //歸回第一筆重新記錄
}
else
{
H_array[count] = heartRate; //紀錄心率資料
count = count+1; //指向下一筆資料儲存區
}
}
avgHeartRate = (H_array[0]+H_array[1]+H_array[2]+H_array[3]+H_array[4]+H_array[5]+H_array[6])/maxCount; // 取最近七次有效量測的平均值
}
void showSerialMonitor() //顯示心率資料於串列埠監控視窗
{
Serial.print(heartRate); // 單次心率
Serial.println("BPM (One Time)");
Serial.print(H_array[0]); // 儲存於第一筆的心率資料
Serial.print("\t");
Serial.print(H_array[1]); // 儲存於第二筆的心率資料
Serial.print("\t");
Serial.print(H_array[2]); // 儲存於第三筆的心率資料
Serial.print("\t");
Serial.print(H_array[3]); // 儲存於第四筆的心率資料
Serial.print("\t");
Serial.print(H_array[4]); // 儲存於第五筆的心率資料
Serial.print("\t");
Serial.print(H_array[5]); // 儲存於第六筆的心率資料
Serial.print("\t");
Serial.println(H_array[6]); // 儲存於第七筆的心率資料
Serial.print(avgHeartRate); // 平均心率
Serial.println("BPM (Average)");
Serial.println();
}
void show4digits(int number) //顯示數字於串列七字節顯示模組
{
int num1,num2,num3,num4;
num1=number/1000; //千位數
num2=(number%1000)/100; //百位數
num3=(number%100)/10; //十位數
num4=number%10; //個位數
faya7seg.setDigit(0,3,num1,false); //點亮千位數
faya7seg.setDigit(0,2,num2,false); //點亮百位數
faya7seg.setDigit(0,1,num3,false); //點亮十位數
faya7seg.setDigit(0,0,num4,false); //點亮個位數
}
void matrixFull() //8x8點矩陣顯示實心愛心
{
for(int i=0; i<=7; i++)
{
for (int j=7; j>=0; j--)
{
faya8x8.setLed(0,i,j,heartFull[i][j]);
}
}
}
void matrixEmpty() //8x8點矩陣顯示空心愛心
{
for(int i=0; i<=7; i++)
{
for (int j=7; j>=0; j--)
{
faya8x8.setLed(0,i,j,heartEmpty[i][j]);
}
}
}

備註:
- L81~L83: 程式主要由此三行組成,必須重複執行,因此存於Loop()迴圈。
- L81: heartRateGet() - 用來獲取並計算單次心率,並且顯示心跳圖案於8x8點矩陣模組與顯示聲音於蜂鳴器模。
- L82 : rollingAverage() - 利用滾動式平均法取得較穩定的平均心率值
- L83 : show4digits() - 將平均心率值秀在七段顯示器。

L86~109: heartRateGet()副程式:

- 心跳感應模組的原理可參考之前的文章 [模組介紹 : faya 心跳偵測模組]
-L89:  if ((input != lastInput)&&(input == HIGH)); 偵測數位腳位上源觸發時的語法,第一個條件判斷邏輯準位改變時,第二個條件判斷狀態為HIGH時。每次的上緣信號代表一次的心跳。
-L91~94 : 每次偵到上源訊號時,記錄當下時間,算出兩次上源觸發的時間差後,取倒數算出頻率,並且轉換單位變成每分鐘的心跳數。


-L95~98 : 量測時心跳訊號時,有時會因為周邊雜訊或者身體的移動,造成不合哩的心率數值,我們利用此段程式過濾不合理的心率,並用穩定的平均心率值取代。
-L99~106 : 當量測到合理的心跳數值時,緊接著於串列埠監控視窗輸出量測統計表、一個短嗶聲、顯示實心與空心的愛心圖案,讓量測者可以察覺已完成一次有效量測。

 L111~126: rollingAverage()副程式:

- L58: 設立了一個可存七筆資料的矩陣,儲存每次量測到的心率,七筆資料用來計算滾動式平均值
- L113: 通過兩個條件才將資料存放在矩陣中,第一個條件是量到的心率有變化時,第二個條件是變化量不能太大,例如原本的心率是72 BPM,下一次的心率上升到110 BPM,這是不合理的心率值,需要濾掉。

- L115~118 : 資料存到最大筆數時,回到第一筆重新儲存
- L120~123 : 儲存有效資料至矩陣中,並指定下一筆資料存放區
- L125 : 每次更新一筆心率資料後,立刻算出最近七筆心率的平均值

 L128~148: showSerialMonitor()副程式:
- 第一行秀出單次的心律
- 第二行秀出最近七次的有效心率
- 第三行秀出算出的滾動式平均心率


 L150~162: show4digits(int)副程式:
- L22、L67~70、L150~162 : 於串列七段顯示模組顯示指定數字,相關用語法解釋可參照之前的文章 : [模組介紹 : faya 串列七段顯示模組]

 L128~148: matrixFull() / matrixEmpty()副程式:
- L24、L29~51、L74~86、L164~184: 在串列8x8點矩陣模組顯示指定圖案,相關語法解釋可參照之前的文章 : [模組介紹 :faya 串列8x8點矩陣模組]

實心愛心圖的點矩陣配置:

空心愛心圖的點矩陣配置:
 

結果演示:


影片中可看出從穩定量測開始,大約經過7次量測後,心率值達到穩定狀態,量測結果和Garmin的心率錶幾乎一致,介於62~64BPM之間。

量測技巧:
  • 請以坐姿量測,左右手食指皆可量測,量測時,手軸靠在桌上,手指輕壓感測器表面,身體和手指不要移動並保持穩定的壓力,可獲得穩定的測結果,大約經過6~7次的量測,會得到準確的心率值。
  • 如量測出來的數值怪怪的,可用示波器觀察波型,或者直接旋轉心跳偵測模組的可變電阻改變gain值再試試看,通常可變電阻指向一點鐘方向可以獲得滿不錯的效果。
  • 由於每個人的皮膚厚度不同,量測不到訊號時,可以試著將量測點挪到不同的指腹位置,直到獲得穩定量測結果為止。
討論:

以下是我們開發此創意組合時的結論與經驗,大家可以參考參考
  • 我們利用心跳時信號產生的上源觸發,計算兩次心跳間的間隔,再算出每分鐘頻率,如果只取一次的量測值,由於手的移動或其他干擾,每次計算出的輸出數值會有很大的變化,因此我們加入了滾動式平均法,當量測到有效的心率,就和前6筆有效資料總合起來並取平均值,如此一來的輸出資料會比較平滑。
  • 我們透過程式濾掉了不合理的心率值,增加了輸出值的準確性。

歡迎大家在底下留言或到我們的粉絲團留言喔!
====================================
fayalab 粉絲團
FB本篇留言版



沒有留言:

張貼留言