6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

傘置き忘れ防止ストラップ(プロトタイプ)

Last updated at Posted at 2018-08-30

3人でハッカソン(じゃないけど限られた時間で)IoT製品のプロトタイプ作りました

機能

  • 持ち主が傘を置いてから指定した時間経過したらLINEで通知(通知手段は変更可能)
    • 通知する前に傘を持ち主が持ったら通知はしない
  • 時間の指定はロータリースイッチで3段階設定出来る
    • 3段階の具体的時間についてはWeb上で具体的な時間を設定できる
  • 加速度がうまく取れない時(主に電車内を想定)用にタイマーだけを頼りに動作するモード
    スクリーンショット 2018-08-30 19.42.19.png

#利用したもの

  • raspberryPi3
  • 加速度センサ
  • ロータリースイッチ
  • 配線に使うもの(ブレッドボードやケーブル)
  • IFTTT

#配線
ブレッドボードビューはFritzingを使用させていただき、作成しました(http://fritzing.org/home/)
スクリーンショット 2018-08-30 18.08.15.png
ロータリースイッチは使用したものがfritzingで見つからなかったので代用しましたがその部分は以下のような配線です
配線.jpg

#プログラム

index.html
<!doctype html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
    <title>傘の置き忘れ通知</title>
    <script src="../../polyfill/polyfill.js"></script>
    <script src="../../drivers/i2c-grove-accelerometer.js"></script>
    <script src="./main.js"></script>
    <style>
      body {
    	text-align:center;
      }
      #display p{
        background: #F4E8FF;
        font-size: 24px;
        padding: 0.7em;
      }
      span {
      	margin: 0 0.5em;
      	padding: 0.3em;
      	border: 2px solid #3A9F00;
      	background: white;
      }
      hr{
      	border: 2px dotted red;
      }
      #setting #title {
      	border-radius: 10px;
      	padding:0.2em;
      	font-size: 24px;
      	background: #CDE9FF;
      }
	#minute {
		list-style-type: none;
	}
	#train li {
		display: inline-block;
		width:3em;
		background: #800000;
		color: white;
	}
    </style>
  </head>
  <body>
	<div id="display">
		<p>加速度センサーのy座標:<span id="ay">---</span></p>
		<p>通知まで:<span id="count">---</span><!--vBいらない--></p>
	    <!--<div id="ledview"></div> --> 
	    <p><!--現在動いているかid:isMoving-->現在傘が動いているか:<span id="isMoving">---</span></p>
	 	<p><!--何%動いているか-->直近の計測中傘が動いている割合:<span id="movement">---</span>%</p>
	</div>
	<div id="setting">
		<div id="title">設定</div>
		<ul id="minute">
			<li>モード0:<input type="text" value="999999" id="iTime0" /></li>
			<li>モード1:<input type="text" value="60" id="iTime1" /></li>
			<li>モード2:<input type="text" value="120" id="iTime2" /></li>
			<li>モード3:<input type="text" value="180" id="iTime3" /></li>
		</ul>
		<p>↓電車内モード↓</p>
		<ul id="train">
			<li>0 <input type="checkbox" id="mode0" /></li>
			<li>1 <input type="checkbox" id="mode1" /></li>
			<li>2 <input type="checkbox" id="mode2" /></li>
			<li>3 <input type="checkbox" id="mode3" /></li>
		</div>
	</div>
  </body> 
</html>

main.js
'use strict';

window.onload = function(){ 
//onload = function () {
  mainFunction();
}


async function mainFunction() {
  var ay = document.querySelector('#ay');
  var movement = document.querySelector('#movement');
  var isMovingHTML = document.querySelector('#isMoving');
  var count = document.querySelector('#count');

  var intervalTime = -100 //n*60*1000 = n min 
  var valueBefore = 0;
  var countdown = intervalTime; 
  var movePercent = 60;
  
  var alerting = function () {

    let data = "value1=" + (intervalTime / 60) + "&value2=&value3";
    var url = "https://maker.ifttt.com/trigger/LINE/with/key/---";//URL最後の部分だけ隠しました
    var xhr = new XMLHttpRequest();
    xhr.open("POST", url);
    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    xhr.send(data);

  }

  //ロータリースイッチ関連のセットアップ
  var oldRotary = -1;
  var gpioAccess = await navigator.requestGPIOAccess();
  //tgtText.innerHTML = "requestGPIO sucsess";
  var firstInputPort = gpioAccess.ports.get(5);
  var secondInputPort = gpioAccess.ports.get(4);
  var thirdInputPort = gpioAccess.ports.get(6);
  //tgtText.innerHTML = "ports.get sucsess";
  await firstInputPort.export("in");
  await secondInputPort.export("in");
  await thirdInputPort.export("in");

  //加速度センサーのセットアップ
  var i2cAccess = await navigator.requestI2CAccess();
  var port = i2cAccess.ports.get(1);
  var groveaccelerometer = new GROVEACCELEROMETER(port, 0x53);
  await groveaccelerometer.init();

  var term = 0;
  let checkTime = 10;
  var moment = [checkTime];

  //メインループ
  while (true) {
    var values = await groveaccelerometer.read();
    console.log('values(x,y,z):', values.x, values.y, values.z);
    //ax.innerHTML = values.x ? values.x : ax.innerHTML;
    ay.innerHTML = values.y ? values.y : ay.innerHTML;
    //az.innerHTML = values.z ? values.z : az.innerHTML;

    count.innerHTML = countdown;  //for debug

    //スイッチの状態読み取り
    var rotaryValue;
    if (!await firstInputPort.read()) {
      rotaryValue = 1;
    } else if (!await secondInputPort.read()) {
      rotaryValue = 2;
    } else if (!await thirdInputPort.read()) {
      rotaryValue = 3;
    } else {
      rotaryValue = 0;
    }
    

    //スイッチの状態に応じてintervalTimeを変更
    switch (rotaryValue) {
      case 0: intervalTime = document.querySelector('#iTime0').value;
        break;
      case 1: intervalTime = document.querySelector('#iTime1').value;
        break;
      case 2: intervalTime = document.querySelector('#iTime2').value;
        break;
      case 3: intervalTime = document.querySelector('#iTime3').value;
        break;
    }

    //動いたかどうかの判定(30秒間)
    var isMoving;
	  term = (term+1) % checkTime; //ターン進行
    if (Math.abs(values.y - valueBefore) >= 1)//ここの値は要検討
    {
      moment[term] = true;
      isMovingHTML.innerHTML = "動いた";
    }
    else {
      moment[term] = false;
      isMovingHTML.innerHTML = "不動なり。";
    }
    
	  var j;
	  var countJ = 0;
	  for(j=0;j<checkTime;j++){
		  if(moment[j]){
			  countJ++;
		  }
    }


    
    //動いてる率が一定値を上回るか
	  if((countJ / checkTime) * 100 > movePercent){
      isMoving = true;
      movement.innerHTML = "<font color='red'>" + parseInt( (countJ / checkTime) * 100 ) + "</font>";
	  } else{
      isMoving = false;
      movement.innerHTML = parseInt( (countJ / checkTime) * 100 );
	  }

    switch (rotaryValue) {
      case 0:  isMoving = document.querySelector('#mode0').checked ? false : isMoving;
        break;
      case 1:  isMoving = document.querySelector('#mode1').checked ? false : isMoving;
        break;
      case 2:  isMoving = document.querySelector('#mode2').checked ? false : isMoving;
        break;
      case 3:  isMoving = document.querySelector('#mode3').checked ? false : isMoving;
        break;
    }

      if(oldRotary != rotaryValue)
	{
		isMoving = true;
		oldRotary = rotaryValue;
  	}

    if (isMoving)
    {
      //傘もってる!
      countdown = intervalTime;
    }
    else {
      //もってない!
      countdown--;
      if (countdown == 0) {
        alerting();  
      }
    }

    valueBefore = values.y;

    await sleep(1000);
  }
  //}).catch(e => console.error('error', e));
}

//msミリ秒待つ関数
function sleep(ms) {
  return new Promise(function (resolve) {
    setTimeout(resolve, ms);
  });
}

#IFTTT
IFTTTによってLINEに送信されるメッセージは次のようですS__41279498.jpg

#特に時間を要したこと

  • (持ち主以外が同じ傘立てから傘を探すときなど)何かの拍子で傘が動いてしまったときに、アラームがリセットされないようなアルゴリズム
  • 通知手段の模索 (IFTTTにたどり着くまでに時間がかかってしまった)

#追記

  • 時間が短く設定されているなどプロトタイプ仕様になっています

  • 傘忘れ防止を目標にプロトタイプを制作したが、傘以外にも使える汎用性のある製品に出来る可能性もあったのではとご指摘。少し視野が狭くなっていたよう。

  • 課題・未実装部分がいくつかあります。
    未実装1
    Webアプリケーションなりスマホアプリケーションなどで時間の設定を出来るように
    課題1
     電車用に実装したタイマーのみによる通知モードだが、いちいちアプリを開いて時間指定することは現実的ではない(不便)。
     現状、あらかじめ設定した時間でのみの利用となる。通勤ルートにおける時間をあらかじめ設定して利用。
    課題2
     安直にY座標方向の数値で判定してしまったがベクトルで数値をとったほうがより正確に動きをとることが出来る。
     センサの特性をより理解することが必要。

6
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?