[2019鐵人賽] - 22.筆記IoT初練習看- 偵測老闆的一個Move!薪水小偷必備 - Johnny Five 之 Motion 動作感測器-
昨天主要講解PIR的工作原理和測試、實驗一下,
今天要來作一點小應用 ヽ(・×・´)ゞ
下音樂 again ~
登登楞登~登登~ (多拉A夢的音效)
「老 闆 移 動 偵 測 器」~ ( σ՞ਊ ՞)σ
謎之音:這有什麼好做的....不就人體偵測嗎...ಠ_ಠ
本魯:當然要加一點花樣騙騙人啊...欸不對...應該是發揮才能才對 ...
故事是這樣開始的…
因為我的 PIR Sensor 是從 Webduino 買豪華套件包來的,
發現我的 PIR Sensor 不能調觸發模式,就不能做更好玩的應用了....嗚嗚 QQ
( 以後會改善的Orz )
所以既然不能調觸發模式,那就想別的方法吧~
又是靈光一閃過!(✪ω✪)
科技始於人性,
像我們開發者整天坐在辦公室,需求一直改一直改,不如等到完全定案之後再開發
等待的時間又要假裝有在上班,實際上我就不說了...XD
所以研發了這個東西!
我想的功能是:
當薪水小偷可以不用很緊張,老闆快走過來時即時開啟 Stack overflow 或是 MDN 假裝在查Doc!
發生事件情境設定:
先設定好「看起來認真上班的網頁」 → 設定好之後開始當薪水小偷EX:上班看YT、巴哈姆特等....
當老闆快要走到我的位子的時候 → 人體感測器感測到老闆 → `motionstart`事件 → 透過 Socket 給前端資料 → 紀錄資料並開啟預設「看起來認真上班的網頁」。
恩~ 計画通り!
接下來跟著我一起浪費才能吧~ヽ(・×・´)ゞ
謎之音:又來了....(ㆆᴗㆆ)
補充說明 - Events
首先補充一下昨天的沒有仔細說到的Events部分觸發 Johnny-Five Motion 物件後,會 retrun 資料物件
```json
{
// 時間戳
timestamp: 1570416994150,
// 偵測動作狀態,若有動作則返回布林值 `true` , 反則返回布林值 `false`
detectedMotion: true,
// 是否校準,已校準則返回布林值 `true` , 反則返回布林值 `false`
isCalibrated: true
}
```
處理精度為毫秒的時間戳!
這邊要注意的 Johnny-Five 返回的 timestamp 是13位數的時間戳,> 13位數的時間戳精度為「毫秒」,
> 10位數的時間戳精度則是「秒」;
但 JavaScript 處理的是10位的時間戳,要怎麼辦呢?
本魯宅:因為現在用不到毫秒等級那麼細的時間戳,只好切掉了!(≖_≖)✧
使用 Javascript 的字串處理 substr 函式 ,截取從 0~10 的字元就好
```
// 返回一個從指定位置開始的指定長度的子字串
String.substr(指定開始位置,截取長度)
```
> MDN - String.prototype.substr()
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr
但這邊又要注意了~
因為 Johnny-Five 回傳的 timestamp 型別是 `Number` 而不是 `String` ,
直接用 substr() 函式擷取字元的話,會因為型別不對而報錯!
範例說明:
```
motion.on('motionstart', function(data) {
timestamp = data.timestamp;
console.log(typeof timestamp); // Number
});
```
轉型
所以我們要把 Johnny-Five 吐出的時間戳轉為字串型別(String),在用 substr() 截取時間戳字串!
這樣就可以取出時間戳來轉換成人讀的時間了~(ง๑ •̀_•́)ง
下個步驟!拆解各個事件返回值
剛剛提到物件中, Johnny-Five 還會返回 `isCalibrated` 和 `detectedMotion` 物件,這兩個物件的值都是布林值 true / false 。
我們先了解物件的意義:
isCalibrated
`isCalibrated` 和 `motion.on('calibrated', function() { });` 一樣都只會在一開始做一次,當第一次發生目標物移動時則偵測訊號成功,`isCalibrated` 返回就會 true 值;
之後觸發的 motion 動作,不管是 start 還是 end 的都會看到 `isCalibrated` 值都是 true。
detectedMotion
當目標物有移動動作,PIR Sensor 偵測到時便會回傳 `detectedMotion` 為ture,就字面上的意思來說就是「偵測到動作了!」;
當目標物停止移動動作,會回傳 `detectedMotion` 為false,意思來說就是「剛剛的偵測到的動作停止了!」。
不同的事件返回物件值
但每個觸發不同的事件,回傳的也會不一樣,> 'calibrated' 校準事件
> function data 不會回傳物件值,事件 'calibrated' 就表示只是一個狀態!
```
motion.on('calibrated', function(data) {
console.log(data);
console.log('calibrated');
});
```
> 'motionstart' 偵測到動作事件
>
```
motion.on('motionstart', function(data) {
console.log(data);
console.log('motionstart');
});
```
此事件會 function data 返回
- 偵測到動作的時間戳
- detectedMotion 返回 true
- isCalibrated 返回 true
```
{
timestamp: 1570420021897,
detectedMotion: true,
isCalibrated: true
}
```
> 'motionend' 沒有偵測到動作了
>
```
motion.on('motionend', function(data) {
console.log(data);
console.log('motionstart');
});
```
此事件會 function data 返回
- 動作停止的時間戳
- detectedMotion 返回 false
- isCalibrated 返回 true
```
{
timestamp: 1570420021897,
detectedMotion: false,
isCalibrated: true
}
```
完整的一個循環~
講好多....終於要來實作啦~
程式碼很簡單,我們透過JavaScript window.open() 事件來實現這個應用(ง๑ •̀_•́)ง
後端部分:
```
var io = require('socket.io');
var express = require('express');
var five = require('johnny-five');
var board = new five.Board();
var app = express();
app.use(express.static('www'));
var server = app.listen(3000, function() {
console.log('connected!');
});
var sio = io(server);
board.on('ready', function() {
var motion = new five.Motion({
pin: '7',
freq: 250,
});
sio.on('connection', function(socket) {
motion.on('calibrated', function() {
//PIR Sensor Ready
console.log('準備好啦!');
});
motion.on('motionstart', function(data) {
// 偵測到有生物在動,觸發事件
console.log('偵測到老闆!');
socket.emit('startData', {
// socket 傳送資料給前端
isAction: data,
});
});
});
});
```
前端部分
HTML
網頁就隨便裝飾一下,目的只是要開啟Socket連線而已
我有做老闆出現的話會append出老闆出現的時間,也就是偵測到動作的時間戳。
```
<body>
<div class="container">
<h2 class="p-5">老 闆 移 動 偵 測</h2>
</div>
<nav class="navbar navbar-light bg-light fixed-bottom">
IoT沒那麼難!新手用JavaScript入門做自己的玩具 系列文 Tzeng,Ying-Chi
</nav>
<script src="/socket.io/socket.io.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="index.js"></script>
</body>
```
JavaScript
```
var socket = io.connect();
socket.on('startData', function(data) {
//接收到偵測資料
motionData = data.isAction;
// 時間戳處理
timestamp = parseInt((motionData.timestamp + '').substr(0, 10));
// 取得是否偵測到動作
isMotion = motionData.detectedMotion;
// 轉換成人看的時間格式
humanCanReadTime = getTime(timestamp);
if (isMotion === true) {
// 當老闆來時,在新分頁打開裝認真的網頁
window.open(
'https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array',
'_blank',
);
// 印出時間點
$('.container').append(
'<div class="alert alert-danger boss-alert" role="alert">老闆出現於 <span class="time">' +
humanCanReadTime +
'</span> !</div>',
);
}
// 時間戳格式轉換
function getTime(timestamp) {
var time = new Date(timestamp * 1000);
var h = time.getHours();
var min = time.getMinutes();
var s = time.getSeconds();
h = checkTime(h);
min = checkTime(min);
s = checkTime(s);
timeStr = h + ':' + min + ' ' + s + '秒';
return timeStr;
}
function checkTime(i) {
if (i < 10) {
i = '0' + i;
} // add zero in front of numbers < 10
return i;
}
});
```
> Demo
0 留言:
張貼留言