把遇到錯誤的、學習到的前端筆記在這邊

總網頁瀏覽量

Copyright © Tzeng Ying-chi. 技術提供:Blogger.

2019/10/07

[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 留言:

張貼留言