ATTinyで離着席検出装置を自作してみた

この記事は、NIFTY Advent Calendar 2017の21日目の記事です。

# まえおき
ニフティのIoT部門が買収で別会社（FJCT）になってしまう前に某イベントハウスで座席の利用状況を管理する案件を担当することになりましたが、どちらかというと技術研究が目的だったため、プロトタイプを作っただけで世に出ることもなく埋もれてしまいました。
１年前の話で恐縮ですが、供養のために記事にしたいと思います。

#概要
某イベントハウスで空き座席への誘導が大変なので楽にしたいという話があり、100席以上ある座席の利用状況を確認できる仕組みを作ることになりました。
座っているかどうかの判定は色々な方法が考えられますが、椅子に接触センサーを取り付けて座っているかどうか判定することにし、センサー部分とデータを送信する子機を製作することにしました。（当時、ケツ圧測定器って言えると面白いよねとか言ってましたが、結局判定に圧力は使いませんでした）

![image.png](https://qiita-image-store.s3.amazonaws.com/0/218020/4e43512f-c99f-cd98-8fc4-136935b811a3.png)

100席以上で動作させないとならないため、要件としては
- 安価であること
- 電池交換などのメンテナンスが簡単なこと
- wifiなどの通信に干渉しないこと
などが挙げられました。
EnOcean社のサブギガヘルツ帯を使用する環境発電型スイッチを椅子に仕込む方法が図書館などでの実績もあってよさそうだったのですが、当時は価格が高めで入手しづらく、既存の椅子への組み込みも難しそうということで断念しました。
紆余曲折の後、接触センサー＋ATTiny13Aに乗せたArduino＋技適OKのinterplan社の[IM315TX](http://www.interplan.co.jp/solution/wireless/im315txrx.php)の構成になりました。
親機はPoC界隈でおなじみのRaspberry Piです。

# 接触センサーの製作
既製品で安くて良い物が見つけられず、結局自作することにしました。最初、慶應義塾大学 杉浦氏の[FuwaFuwa](http://yutasugiura.com/projects/fuwafuwa)やタッチエンス社の[ショッカクポット](http://www.touchence.jp/cube/index.html)ようにフォトリフレクターを使用した圧力検知を試作したのですが、主に自分の実装スキルのせいで薄い座布団ではうまく判定ができずに挫折しました。

結局、[Fabric Potentiometer](https://cdn-shop.adafruit.com/datasheets/HandcraftingSensors.pdf)のように導電性シートを使用したもっと簡単な接触スイッチにしたのですが、導電性シートのvelostatは国内での入手先が見つからず、海外からの輸入になってしまうため代用品として[静電気防止テープ](https://www.amazon.co.jp/dp/B002A5XK32)を使用しました。雑な作りですが、問題なく動作します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/218020/99d9bb18-9ff5-961f-909e-6a8db78a0c19.png)

#開発環境準備
Arduino IDEのインストールやATtiny13AのようなICへの書き込みなどの話は公式サイトに素晴らしいドキュメントがありますので割愛します。
ATTiny用のArduino Coreはいくつかあるのですが、このときは[MicroCore](https://github.com/MCUdude/MicroCore)を使用しました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/218020/46f9276b-d264-8ac2-1a09-0f34d207a062.png)

1.　Arduino IDEを起動し、「ファイル」＞「環境設定」の「追加のボードマネージャのURL」に「`https://mcudude.github.io/MicroCore/package_MCUdude_MicroCore_index.json`」を追加
2.　「ツール」＞「ボード」＞「ボードマネージャ」を選択し、一覧からMicroCoreをインストール
3.　正常にインストールされれば「ツール」＞「ボード」で表示されるボードの一覧にATTiny13Aが追加される

セットアップが終わったら、お馴染みの動作テストLチカをやりましょう。
普通はpinMode(),digitalWrite()を使うところですが、ATTiny13Aはプログラムを10kb以下にしないと入らないので、できるだけレジスタのビットフラグを直接操作するやり方にしてメモリを節約します。

```led.ino
#include <avr/io.h>
#include <util/delay.h>
 
int main(void)
{
    DDRB |= 0x04;   //PB2(7番ピン)をOUTPUTに設定
 
    while(1){
        PORTB |= 0x04;      //PB2(7番ピン)をHIGHに設定
        _delay_ms(1000);    //1秒間sleep
        PORTB &= ~0x04;     //PB2(7番ピン)をLOWに設定
        _delay_ms(1000);    //1秒間sleep
    }
 
    return 0;
}
```
7番ピンにLEDをつないで点滅すればOKです。あと、ATTiny13Aから[IM315TX](http://www.interplan.co.jp/solution/wireless/im315txrx.php)にデータを送るためにシリアル通信ライブラリが必要ですが、Arduinoオフィシャルのものは容量的に厳しいのでコンパクトな[BasicSerial3](http://nerdralph.blogspot.jp/2014/01/avr-half-duplex-software-uart.html)をインストールします。

1. [BasicSerial3](http://nerdralph.blogspot.jp/2014/01/avr-half-duplex-software-uart.html)をダウンロード
2. 「スケッチ」＞「ライブラリのインクルード」＞「.ZIP形式のライブラリをインストール」でダウンロードしてきたBasicSerial3.zipを指定する

インストールできたら貧弱なATTiny13Aに合わせてシリアル通信のボーレート（baudrate）を変更と入出力ピンを別のものに変更するためにBasicSerial3.h / BasicSerial3.Sを書き換えておきます。

◎シリアル通信のボーレート（baudrate）を115200から9600に変更

``` BasicSerial3.h
//#define BAUD_RATE 115200
#define BAUD_RATE 9600
```
◎シリアル通信のpinを変更する(送信:PB3 受信:PB4)

``` BasicSerial3.S
//#define UART_Tx 5
//#define UART_Rx 5
#define UART_Tx 3
#define UART_Rx 4
```

#子機の実装
センサーは7番ピン、[IM315TX](http://www.interplan.co.jp/solution/wireless/im315txrx.php)の入出力は2番、3番ピンに繋ぐ想定です。
電力消費をできるだけ抑えたかったので、Attinyのwatchdog timer機能を使用して8秒間隔で動作するようにし、動作していないときはsleepして節電するようにしました。

座っているかの判定は、センサーからの取得した値が前後で変化しているかで判定するようにしていて、閾値THRESHOLDは実測値を元に決め打ちで入れています。
座ったときは「TXDT 0001」、立ったときは「TXDT 0002」をシリアル通信で[IM315TX](http://www.interplan.co.jp/solution/wireless/im315txrx.php)に送りますが、[IM315TX](http://www.interplan.co.jp/solution/wireless/im315txrx.php)はTXDTのようなテキストコマンド的なものが用意されていて、とても使いやすいモジュールです。

``` seat_checker.ino
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <BasicSerial3.h>

#define ANALOGPIN 1
#define ARDUINOPIN 2
#define THRESHOLD 400

#define WDT_reset() __asm__ __volatile__ ("wdr")
#define WDT_8S 0x21 // 8秒間隔(B100001)

// WDTタイマー開始
void WDT_start(uint8_t t) {
  cli();                             // 全割り込み禁止
  WDT_reset();                       // ウォッチドッグタイマーリセット
  WDTCR |= _BV(WDCE)|_BV(WDTIE);     // WDCE:ウォッチドッグ変更許可、WDTIE:動作種別=割り込み
  WDTCR |= t;                        // 割り込み間隔設定  
  sei();                             // 全割り込み許可
}

// WDTタイマー停止
void WDT_stop() {
  cli();                             // 全割り込み停止
  WDT_reset();                       // ウォッチドッグタイマリセット
  MCUSR &= ~_BV(WDRF);               // ウォッチドッグリセットフラグ解除
  WDTCR |= _BV(WDCE)|_BV(WDE);       // WDCEとEDEに1をセット
  WDTCR =  0;                        // ウォッチドッグ禁止
  sei();                             // 全割り込み許可
}

// シリアル通信
void serialWrite(const char str[]){
  while (*str) {
    TxByte (*str++);
  }
}

volatile unsigned int beforeData;

// ウォッチドックタイマー割り込み処理
ISR(WDT_vect) {
  unsigned int data = 0;
  int diff = 0;

  digitalWrite(ARDUINOPIN, HIGH);
  data = analogRead(ANALOGPIN);
  digitalWrite(ARDUINOPIN, LOW);
  
  if(data > 0) {
    diff = data - beforeData;
    beforeData = data;
    
    if(diff > THRESHOLD) {
      serialWrite("TXDT 0002\r\n");
    } else if(diff < THRESHOLD * -1) {
      serialWrite("TXDT 0001\r\n");
    }
    beforeData = data;
  }
}

void setup() {
  WDT_stop();

  beforeData = 0;
  
  ACSR |= _BV(ACD);
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // 一番消費電力の少ない動作停止
  WDT_start(WDT_8S);
}

void loop() {
  sleep_mode();
}

```

予算がなかったので100均で買ってきた座布団を使ったり、ジップロックコンテナをケースにしたりしましたが、こんな感じでオフィスに設置して実験したりしました。電池は3Vのコイン型電池を使っています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/218020/2ba9bab2-6537-ba82-b0cf-9630cacf901a.png)

最終的にはbotを入れて離着席を確認できるようにしてみたりしましたが、量産化には至らずお蔵入りになりました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/218020/f8b6aaf8-fa52-2e6c-b5ec-905d08fe2373.png)

# 最後に
お蔵入りにはなりましたが、個人的には色々と気づきのある一人プロジェクトでした。機会を与えてくれた上司、FJCT社に行ってしまった同僚の皆様ありがとうございました。