LoginSignup
5
1

More than 3 years have passed since last update.

enebular x 導電布で空席を検知する

Posted at

この記事は2020年12月1日に開催された 【オンライン】enebular developer meetup で発表した内容をベースにしています。

はじめに

UXデザイナーの どたてつや です。

普段はUI設計やUX開発などの仕事の傍ら、趣味でプロトタイプなどを作ったりしています。
先日、Eテキスタイルを使ったインプットモジュール「nüno」をnanbwrks さんと作りました。
nünoの最新バージョンはver.2ですが、
今回は余ってるnüno ver.1を使用しての空席通知システムを作ってみました。

nünoについては2018年12月の記事「enebularで布センサーからLINEに通知できるようにしたよ」も参考にしてください!!

できるもの

最終的にできるものの動画はこちらです

わかりづらいですが、一番右下の座布団が黄色になっていますね。(なっています!)
こちらについてご説明します。

全体の構成

構成として空席データ送信はnünoからM5Stack経由でWifiを使用してenebularに接続、
enebularからFirebaseへ検知情報を送っています。

そして空席情報を表示するためのデータ取得はPC(スマホ)からenebularにリクエストを送信、
enbularがFirebaseからデータ尾を取得し、PCへ空席情報を表示する、という流れです。

ScreenShot 2020-12-04 19.22.04.png

ハードウェア

プロトタイプ感まるだしてですが、ハードウェアはこちら。
IMG_0679.JPG

無印良品の座布団にnünoを接続し、M5Stack Core2を使用しています。
M5Stack Core2とnünoの通信はI2Cを使用しています。

Arduino

まずはArduinoから
また、 nünoではMTCH6102という静電タッチセンサをつかっているので、
プログラムと同じ階層にこちらから借りてきた
- MTCH6102.h
- MTCH6102.cpp
を利用させてもらっています。

#include <M5Core2.h>
#include <Arduino.h>
#include <Wire.h>
#include "MTCH6102.h"
#include <WiFi.h>
#include <HTTPClient.h>
#include <Arduino_JSON.h>

#define ADDR 0x25
#define ScreenWidth 320
#define ScreenHeight 240

MTCH6102 mtch = MTCH6102();

const int len = 8;//感知ポイント数
int nuno_mode = 2;
int cnt; //ループ用変数
uint32_t chipId = 0;


const bool ONLINE = true;//オンラインモード
const char* WIFI_SSID ="SSID";
const char* WIFI_PASSWORD = "PASSWORD";
const char* POST_URL = "URL";

WiFiClient client;

void setup() {
  delay(1000);

  // Initialize the M5Stack object
  M5.begin();
  //M5.Power.begin();
  M5.Lcd.fillScreen(TFT_BLACK);
  Serial.begin(115200);
  //mtch6102
  mtch.begin(ADDR);
  delay(100);
  mtch.writeRegister(MTCH6102_NUMBEROFXCHANNELS, 0x08);
  mtch.writeRegister(MTCH6102_NUMBEROFYCHANNELS, 0x03);//最低3点必要なため
  mtch.writeRegister(MTCH6102_MODE, MTCH6102_MODE_FULL);
  mtch.writeRegister(MTCH6102_HORIZONTALSWIPEDISTANCE, 0x04);
  mtch.writeRegister(MTCH6102_MINSWIPEVELOCITY, 0x02);
  mtch.writeRegister(MTCH6102_TAPDISTANCE, 0x02);
  mtch.writeRegister(MTCH6102_SWIPEHOLDBOUNDARY, 0x04);

  mtch.writeRegister(MTCH6102_BASEPOSFILTER, 0x00);
  mtch.writeRegister(MTCH6102_BASENEGAFILTER, 0x00);

  mtch.writeRegister(MTCH6102_CMD, 0x20);
  delay(500);

  //chipID
  for(int i=0; i<17; i=i+8) {
    chipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i;
  }
  Serial.print("chip Id:");
  Serial.println(chipId);

  //WIFI
  if (ONLINE) {
    WiFi.mode(WIFI_STA);
    WiFi.disconnect(true);
    delay(1000);
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    Serial.println("connecting");
    M5.Lcd.print("========== WIFI connecting ==========\n\n");

    while (WiFi.status() != WL_CONNECTED) {
      Serial.print(".");
      delay(500);
      Serial.print(WiFi.status());
      Serial.print(",");
    }
    Serial.println();
    Serial.print("connected: ");
    Serial.println(WiFi.localIP());
    M5.Lcd.print("========== WIFI connected ==========\n\n");
  }
}


const int log_max = 10;
JSONVar move_log;
int move_current = 0;

//動作ログ送信
void SendLog() {
  if (!ONLINE) return;
  if (WiFi.status() != WL_CONNECTED) return;

  HTTPClient http;
  Serial.print("[HTTP] begin...\n");
  http.begin(POST_URL); //HTTP
  http.addHeader("Content-Type", "application/json");
  String jsonString = JSON.stringify(move_log);
  int httpCode = http.POST(jsonString);
  http.end();
}


void loop() {

    M5.update();
    M5.lcd.clear();
    M5.Lcd.setCursor(0, 70);

    byte data;
    int sensVals[len];

    for (int i = 0; i < len; i++) {
      data = mtch.readRegister(MTCH6102_SENSORVALUE_RX0 + i);
      sensVals[i] = data;
      M5.Lcd.fillRect(30 + (i * 35), ScreenHeight - 20, 30, 10, TFT_BLACK);
      M5.Lcd.setCursor(30 + (i * 35), ScreenHeight - 20);
      M5.Lcd.print(data);
    }

    Serial.println(String(chipId));
    for (int j = 0; j < len; j++) {
      move_log["chipId"] = String(chipId);
      move_log["value"][j] = sensVals[j];
      Serial.print(sensVals[j]);
      Serial.print(",");
    }
    Serial.println();
    //ログ投げる


    M5.Lcd.setCursor(0, 70);
    //背景ライン
    for (int i = 0; i < len; i++) {
      M5.Lcd.drawLine((i + 1) * 35, ScreenHeight - 40, (i + 1) * 35, 0, 0x0000cc);
    }
    for (int i = 1; i < 11; i++) {
      M5.Lcd.drawLine(0, i * 20, ScreenWidth, i * 20, 0x0000cc);
    }
    //グラフ線の描画
    for (int i = 0; i < len + 1; i++) {
      float prev = 0;
      float current = 0;
      if (i == 0) {
        prev = 0;
      }else{
        prev = sensVals[i - 1];
      }
      if (i == len) {
        current = 0;
      } else {
        current = sensVals[i];
      }
      M5.Lcd.drawLine(i * 35, 200 - (prev / 255) * 200, ((i + 1) * 35), 200 - (current / 255) * 200, TFT_WHITE);
    }

  SendLog();
  delay(5000);//10秒に1回投げる
}

将来的に複数のデバイスが稼働することを想定しているので

chipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i;

ここでチップIDを取得して、どのデバイスから送信された値かを判別できるようにしています。

Firebase

Firebase側ではRealtimeデータベースを使用しています。
Realtimeデータベースとしてはこちらの記事を参考にさせてもらい、設定しました。

enebular

enebularnのフローはこちらです。
ScreenShot 2020-12-01 17.12.25.png

上が送信用、下が取得ようのフローです。
非常にシンプルですがFirebaseノードが便利ですぐにFirebaseとの連携が実現できました。

1点つまづきポイントとして、データ取得時にCORSのエラーが表示されたので、
http responseノードにCORSのワイルドカードを設定して事なきを得ました。

ScreenShot 2020-12-01 17.16.38.png

javascript

今回はお手軽実装なのでjQueryを使用しています。
nünoのデータは0~255の静電容量値が8点返ってくるので、
その8点のなかで1つでも150以上の値がある場合は着席状態として seated クラスを付与して状態を変更しています。

$(document).ready(function(){

    setInterval(function(){     
        $.ajax({
            url: 'https://nuno-seat2.herokuapp.com/get-data',
            success: function(result) {
                refreshChair(result);
            }
        })
    }, 1000);

    function refreshChair(data){
        $.each(data,function(index, value){
            if(index == "value"){
                console.log(value);
                var m = Math.max.apply(null, value);
                if(m > 150){
                     $('#seat_8481756').addClass('seated');  
                 }else{
                     $('#seat_8481756').removeClass('seated');   
                 }
             }

        });
    }

});

完成!

これで空席検知ができるようになりました。
かんたんなシステムであれば1日くらいで作れるので非常にいいですね!

皆様、よいenebularライフを〜!

5
1
0

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
5
1