タッチエンスのショッカクポット(POTU-001-1)を触ってみた。

# はじめに

秋月電子でタッチエンス・ショッカクポット(POTU-001-1) が発売されたとの <A HREF="https://twitter.com/wakwak_akizuki/status/1107443976519352321">Tweet</A> を見て、昔に秋葉原UDX 2F(アキバ・スクエア)の展示スペースで <A HREF="https://www.youtube.com/watch?v=VX1lXLGK6T0">ShokacPot Miniのデモ</A> をやっていたことを思い出し、気になって仕方がなかったので早速入手して触ってみた。
<img src="https://qiita-image-store.s3.amazonaws.com/0/72479/3a9a5331-2f59-b112-2e13-664cd5c72523.png" width="50%">

# 入手経路
今回は秋月電子でシリアル(UART)出力タイプの製品を入手。(※2019年3月21日現在)
Amazon と RT ROBOT SHOP は製品仕様が異なるので入手される場合は注意が必要。

- 秋月電子:　在庫あり
http://akizukidenshi.com/catalog/g/gM-14092/

- 千石電商:　欠品中
https://www.sengoku.co.jp/mod/sgk_cart/detail.php?code=EEHD-5DJD

- Satake Parts Store: 在庫あり
http://www.satakeps.com/items/4637787

- Amazon: 在庫あり
https://www.amazon.co.jp/%E3%82%BF%E3%83%83%E3%83%81%E3%82%A8%E3%83%B3%E3%82%B9-%E3%82%BF%E3%83%83%E3%83%81%E3%82%A8%E3%83%B3%E3%82%B9%E7%A4%BE%E8%A3%BD%E3%82%B7%E3%83%A7%E3%83%83%E3%82%AB%E3%82%AF%E3%83%9D%E3%83%83%E3%83%89_I%E3%82%BF%E3%82%A4%E3%83%97/dp/B0756VX1DZ

- RT ROBOT SHOP: 在庫あり
https://www.rt-shop.jp/index.php?main_page=product_info&cPath=1348_1&products_id=1303

# 仕様
タッチエンスの<A HREF="http://www.touchence.jp/cube/index.html">ホームページ</A>を見るとアナログ出力、I2C出力、SPI出力(開発中)といくつかラインアップがあるが、シリアル(UART)出力のものは記載されていなかった。
秋月電子の商品ページのリンク先から。
<A HREF="http://akizukidenshi.com/download/ds/touchence/POTU-001-1_manual.pdf">取扱説明書</A>

ショッカクポットは三次元方向の変位が検出できる柔軟触覚センサーで、重さを測るセンサーではない。(はじめ勘違いしていた。。)
スポンジの圧縮によりLED-フォトトランジスタ間の光量変化を利用して、スポンジの変位に応じた電圧変化が出力される。電圧変化をスポンジ変形量（変位量）に変換した上で出力する自動較正機能が搭載されているとのこと。

# 接続図
センサー基板はJST-SH/1.0の5ピンコネクタが採用されているため、他のホストデバイスへ接続しやすくするため付属ケーブルの片側を切断し、Groveコネクタへ変換した。(RSTを除いたVDD、GND、Rx、Tx)
今回はホストデバイスとしてNefryBTを使用した。
![スクリーンショット 2019-03-25 10.03.31.png](https://qiita-image-store.s3.amazonaws.com/0/72479/162629f3-fb6d-62ca-f3de-196fcf5e05ab.png)

# シリアル通信
ボーレート[bps]: 57,600
データ[bit]: 8
ストップビット[bit]: 1
パリティ: なし
ホストデバイス(NefryBT)からデータ取得コマンド(0x6D)を送信する(①)と、センサーが測定を開始し、サンプリング時間(平均1.2ミリ秒)経過後にデータが出力される。(②)
10bitのAD変換値の上位2bitと下位8bitに分割されてデータが出力されるとのことだが、実測値で上位2bitに値がセットされることは無かったため、動作確認用のサンプルソースコードでは下位8bitだけ使用することにした。
![スクリーンショット 2019-03-25 10.03.44.png](https://qiita-image-store.s3.amazonaws.com/0/72479/19e8d245-511c-dab6-2db0-d9063e654f23.png)

# ソースコード
ソースコードは<A HREF="https://github.com/kitazaki/touchence/blob/master/NefryBT_TouchPod_02.ino">こちら</A>。
NefryBTに搭載されているESP32は複数のHardwareSerialをサポートしているため、SoftwareSerialは使用しない。
また、NefryBTにはTX,RXポートが存在するが、TX,RXポートにセンサーデバイスを接続したままだとプログラムの書き込みに失敗するので(プログラムを書き込みする時は抜いておき、書き込みが完了した後に接続すれば使用可能だが面倒なので)、D2(IO23)にRX、D3(IO19)にTXを割り当ててHardwareSerialを使用する。
取得したデータはNefryBTのディスプレイへ表示するのと合わせて、Arduino IDEのシリアルプロッタで表示するためシリアルモニターへ出力している。

```c:NefryBT_TouchPod_02.ino
#include <Nefry.h>
#include <NefryDisplay.h>
#include <NefrySetting.h>

void setting(){
  Nefry.disableDisplayStatus();
}

NefrySetting nefrySetting(setting);

HardwareSerial Serial1(1);  // use HardwareSerial, do not use SoftwareSerial

byte cmd[1] = {0x6D};  // データ送信コマンド(0x6d)
unsigned char res[8];  // データ受信バッファ
bool flag = false;

void setup() {
  Nefry.setLed(0,0,0);
  NefryDisplay.clear();
  NefryDisplay.setFont(ArialMT_Plain_16);
  NefryDisplay.drawString(0, 0, "CH1: ");
  NefryDisplay.drawString(0, 16, "CH2: ");
  NefryDisplay.drawString(0, 32, "CH3: ");
  NefryDisplay.drawString(0, 48, "CH4: ");
  NefryDisplay.display();
  Serial.begin(115200);
  Serial1.begin(57600, SERIAL_8N1, 23, 19);  // (speed, type, RX:D2=23, TX:D3=19);
  delay(1000);
  Serial.println("start: ");
}

void loop() {
  Serial1.write(cmd,1);
  while(Serial1.available()>0)
  {
    Serial1.readBytes(res, 8);
    flag=true;
  }
  if(flag)
  {
    flag=false;
    unsigned int P01H = (unsigned int) res[0];  // CH1 AD変換値 上位2bit/10bit (使用しない)
    unsigned int P01L = (unsigned int) res[1];  // CH1 AD変換値 下位8bit/10bit (使用する)
    unsigned int P04H = (unsigned int) res[2];  // CH4 AD変換値 上位2bit/10bit (使用しない)
    unsigned int P04L = (unsigned int) res[3];  // CH4 AD変換値 下位8bit/10bit (使用する)
    unsigned int P02H = (unsigned int) res[4];  // CH2 AD変換値 上位2bit/10bit (使用しない)
    unsigned int P02L = (unsigned int) res[5];  // CH2 AD変換値 下位8bit/10bit (使用する)
    unsigned int P03H = (unsigned int) res[6];  // CH3 AD変換値 上位2bit/10bit (使用しない)
    unsigned int P03L = (unsigned int) res[7];  // CH3 AD変換値 下位8bit/10bit (使用する)

    Serial.print(P01L);
    Serial.print(",");
    Serial.print(P02L);
    Serial.print(",");
    Serial.print(P03L);
    Serial.print(",");
    Serial.print(P04L);
    Serial.println("");

    char buf[10];
    NefryDisplay.clear();
    NefryDisplay.setFont(ArialMT_Plain_16);
    NefryDisplay.drawString(0, 0, "CH1: ");
    sprintf(buf, "%d", P01L);
    NefryDisplay.drawString(50, 0, buf);
    NefryDisplay.drawString(0, 16, "CH2: ");
    sprintf(buf, "%d", P02L);
    NefryDisplay.drawString(50, 16, buf);
    NefryDisplay.drawString(0, 32, "CH3: ");
    sprintf(buf, "%d", P03L);
    NefryDisplay.drawString(50, 32, buf);
    NefryDisplay.drawString(0, 48, "CH4: ");
    sprintf(buf, "%d", P04L);
    NefryDisplay.drawString(50, 48, buf);
    NefryDisplay.display();
  }
  delay(100);
}
```

## NeoPixel WS2812Bと連動
取得したデータの値に連動してネオピク(NeoPixel WS2812B)をLチカしてみる。
取得したデータはそれぞれCH1→R(赤)、CH2→G(緑)、CH3→B(青)に割り当てる。(CH4は使用しない。)
ネオピクの配線(DIN)はNefryBTのD4(GPIO18)に接続する。
![スクリーンショット 2019-03-25 10.16.22.png](https://qiita-image-store.s3.amazonaws.com/0/72479/feae3cd1-befb-aca2-875e-932cf336e9db.png)

ソースコードは<A HREF="https://github.com/kitazaki/touchence/blob/master/NefryBT_TouchPod_03.ino">こちら</A>。

```c:NefryBT_TouchPod_03.ino
#include <Nefry.h>
#include <NefryDisplay.h>
#include <NefrySetting.h>
#include <FastLED.h>

void setting(){
  Nefry.disableWifi();
  Nefry.disableDisplayStatus();
}

NefrySetting nefrySetting(setting);

HardwareSerial Serial1(1);  // use HardwareSerial, do not use SoftwareSerial

// LED Strip
const int numLeds = 256;  // Change if your setup has more or less LED's
#define DATA_PIN 18  //The data pin that the WS2812 strips are connected to. NefryBT D2=GPIO23, D4=GPIO18
CRGB leds[numLeds];

byte cmd[1] = {0x6D};  // データ送信コマンド(0x6d)
unsigned char res[8];  // データ受信バッファ
bool flag = false;

void setup() {
  Nefry.setLed(0,0,0);
  NefryDisplay.clear();
  NefryDisplay.setFont(ArialMT_Plain_16);
  NefryDisplay.drawString(0, 0, "CH1: ");
  NefryDisplay.drawString(0, 16, "CH2: ");
  NefryDisplay.drawString(0, 32, "CH3: ");
  NefryDisplay.drawString(0, 48, "CH4: ");
  NefryDisplay.display();

  Serial.begin(115200);
  
  Serial1.begin(57600, SERIAL_8N1, 23, 19);  // (speed, type, RX:D2=23, TX:D3=19);
  
  FastLED.addLeds<WS2812, DATA_PIN, GRB>(leds, numLeds);
//  FastLED.setBrightness(10);
//  for (int i = 0; i < numLeds; i++) {
//    leds[i] = CRGB(1,1,1);
//  }
//  FastLED.show();

  delay(1000);

  Serial.println("start: ");
}

void loop() {
  Serial1.write(cmd,1);
  while(Serial1.available()>0)
  {
    Serial1.readBytes(res, 8);
    flag=true;
  }
  if(flag)
  {
    flag=false;
    unsigned int P01H = (unsigned int) res[0];  // CH1 AD変換値 上位2bit/10bit (使用しない)
    unsigned int P01L = (unsigned int) res[1];  // CH1 AD変換値 下位8bit/10bit (使用する)
    unsigned int P04H = (unsigned int) res[2];  // CH4 AD変換値 上位2bit/10bit (使用しない)
    unsigned int P04L = (unsigned int) res[3];  // CH4 AD変換値 下位8bit/10bit (使用する)
    unsigned int P02H = (unsigned int) res[4];  // CH2 AD変換値 上位2bit/10bit (使用しない)
    unsigned int P02L = (unsigned int) res[5];  // CH2 AD変換値 下位8bit/10bit (使用する)
    unsigned int P03H = (unsigned int) res[6];  // CH3 AD変換値 上位2bit/10bit (使用しない)
    unsigned int P03L = (unsigned int) res[7];  // CH3 AD変換値 下位8bit/10bit (使用する)

    Serial.print(P01L);
    Serial.print(",");
    Serial.print(P02L);
    Serial.print(",");
    Serial.print(P03L);
    Serial.print(",");
    Serial.print(P04L);
    Serial.println("");

    char buf[10];
    NefryDisplay.clear();
    NefryDisplay.setFont(ArialMT_Plain_16);
    NefryDisplay.drawString(0, 0, "CH1: ");
    sprintf(buf, "%d", P01L);
    NefryDisplay.drawString(50, 0, buf);
    NefryDisplay.drawString(0, 16, "CH2: ");
    sprintf(buf, "%d", P02L);
    NefryDisplay.drawString(50, 16, buf);
    NefryDisplay.drawString(0, 32, "CH3: ");
    sprintf(buf, "%d", P03L);
    NefryDisplay.drawString(50, 32, buf);
    NefryDisplay.drawString(0, 48, "CH4: ");
    sprintf(buf, "%d", P04L);
    NefryDisplay.drawString(50, 48, buf);
    NefryDisplay.display();

    unsigned int r,g,b;
    if (P01L > 10) {r = P01L;} else {r = 1;}
    if (P02L > 10) {g = P02L;} else {g = 1;}
    if (P03L > 10) {b = P03L;} else {b = 1;}
    for (int i = 0; i < numLeds; i++) {
      leds[i] = CRGB(r,g,b);
    }
    FastLED.show();
  
  }
  delay(100);
}
```

## obnizで動作確認
![スクリーンショット 2019-03-25 15.15.27.png](https://qiita-image-store.s3.amazonaws.com/0/72479/9e2f9724-7072-8f58-1933-6c7590db2af0.png)

ソースコードは<A HREF="https://github.com/kitazaki/touchence/blob/master/ShoKacPot.html">こちら</A>。


```html:ShoKacPot.html
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script src="https://obniz.io/js/jquery-3.2.1.min.js"></script>
  <script src="https://unpkg.com/obniz@2.0.1/obniz.js" crossorigin="anonymous"></script>
</head>
<body>

<div id="obniz-debug"></div>
<h1>obniz instant html</h1>
<button id="on">ON</button>
<button id="off">OFF</button>
<div id="print"></div>

<script>
var obniz = new Obniz("OBNIZ_ID_HERE");
obniz.onconnect = async function () {

  obniz.display.clear();

  obniz.io11.output(false); // GND
  obniz.io10.output(true);  // VDD(5V)
  
  var led = obniz.wired("WS2812B", {gnd:2, vcc: 1, din: 0});
  var colors = [];
  var r = [];
  var g = [];
  var b = [];
  var s_flag = [];

  for (var i=0; i<85; i++) {
    colors[i]=([0, 0, 0]);
  }
  led.rgbs(colors);
  
  obniz.switch.onchange = function(state) {
    $('#print').text(state);
    obniz.display.clear();
    obniz.display.print(state);
  }  
  
  $('#on').click(async function () {
    obniz.display.clear();
    obniz.display.print("ON");
    s_flag = 0;
    obniz.uart0.start({tx: 8, rx: 9, baud:57600});
    while(1){
      obniz.uart0.send(0x6D);
      obniz.uart0.onreceive = function(data, text) {
        console.log(data[1] + "," + data[3] + "," + data[5] + "," + data[7]);
        for (var i=0; i<80; i++) {
          if (data[1] > 10) {r = data[1];} else {r = 1;}
          if (data[3] > 10) {g = data[3];} else {g = 1;}
          if (data[5] > 10) {b = data[5];} else {b = 1;}
          colors[i]=([r, g, b]);
        }
      }
      led.rgbs(colors);
      await obniz.wait(100);
      if (s_flag) break;
    }
  });

  $('#off').click(async function () {
    obniz.display.clear();
    obniz.display.print("OFF");
    for (var i=0; i<80; i++) {
      colors[i]=([0, 0, 0]);
    }
    led.rgbs(colors);
    s_flag = 1;
    await obniz.wait(100);
    obniz.uart0.end();
  });
}

</script>
</body>
</html>
```

## 2台のobnizと連動
![スクリーンショット 2019-03-25 15.16.05.png](https://qiita-image-store.s3.amazonaws.com/0/72479/2500520e-d329-e1a2-9259-123a33f596d9.png)

ソースコードは<A HREF="https://github.com/kitazaki/touchence/blob/master/ShoKacPot2.html">こちら</A>。

```html:ShoKacPot2.html
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="https://obniz.io/js/jquery-3.2.1.min.js"></script>
    <script src="https://unpkg.com/obniz@2.0.1/obniz.js" crossorigin="anonymous"></script>
  </head>
  <body>

    <div id="obniz-debug"></div>
    <h1>obniz instant html</h1>
    <button id="on">ON</button>
    <button id="off">OFF</button>
    <div id="print"></div>

    <script>
      var obniz = new Obniz("OBNIZ_ID_HERE");
      var obniz1 = new Obniz("OBNIZ_ID1_HERE");

      obniz1.onconnect = async function () {
        obniz1.display.clear();
        var led1 = obniz1.wired("WS2812B", {gnd:2, vcc: 1, din: 0});
        var colors = [];
        var r = [];
        var g = [];
        var b = [];
        var s_flag = [];
        for (var i=0; i<85; i++) {
          colors[i]=([1, 1, 1]);
        }
        led1.rgbs(colors);
        obniz1.switch.onchange = async function(state) {
          $('#print').text(state);
          //          obniz1.display.clear();
          obniz1.display.print(state);
        }  


        obniz.onconnect = async function () {

          obniz.display.clear();

          obniz.io11.output(false); // GND
          obniz.io10.output(true);  // VDD(5V)

          var led = obniz.wired("WS2812B", {gnd:2, vcc: 1, din: 0});
          var colors = [];
          var r = [];
          var g = [];
          var b = [];
          var s_flag = [];

          for (var i=0; i<85; i++) {
            colors[i]=([0, 0, 0]);
          }
          led.rgbs(colors);

          obniz.switch.onchange = async function(state) {
            $('#print').text(state);
            //          obniz.display.clear();
            obniz.display.print(state);
          }  

          $('#on').click(async function () {
            obniz.display.clear();
            obniz.display.print("ON");
            obniz1.display.clear();
            obniz1.display.print("ON");
            s_flag = 0;
            obniz.uart0.start({tx: 8, rx: 9, baud:57600});
            while(1){
              obniz.uart0.send(0x6D);
              obniz.uart0.onreceive = function(data, text) {
                console.log(data[1] + "," + data[3] + "," + data[5] + "," + data[7]);
                for (var i=0; i<80; i++) {
                  if (data[1] > 10) {r = data[1];} else {r = 1;}
                  if (data[3] > 10) {g = data[3];} else {g = 1;}
                  if (data[5] > 10) {b = data[5];} else {b = 1;}
                  colors[i]=([r, g, b]);
                }
              }
              led.rgbs(colors);
              led1.rgbs(colors);
              await obniz.wait(100);
              if (s_flag) break;
            }
          });

          $('#off').click(async function () {
            obniz.display.clear();
            obniz.display.print("OFF");
            obniz1.display.clear();
            obniz1.display.print("OFF");

            for (var i=0; i<80; i++) {
              colors[i]=([0, 0, 0]);
            }
            led.rgbs(colors);
            led1.rgbs(colors);
            s_flag = 1;
            await obniz.wait(100);
            obniz.uart0.end();
          });
        }
      }
    </script>
  </body>
</html>
```

# 動作確認
ディスプレイ(NefryBTのOLED)表示
<A HREF="https://youtu.be/cm-Eg1iqRDY"><img src="https://qiita-image-store.s3.amazonaws.com/0/72479/bf650ade-5cc3-31b8-f242-9c2c2bb71ff8.png" width="75%"></A>

シリアルプロッタ(Arduino IDE)表示
<img src="https://qiita-image-store.s3.amazonaws.com/0/72479/a772d6f5-8658-1f87-f441-3818fdde23e4.png" width="75%">

NeoPixel(WS2812B)でLチカ
<img src="https://qiita-image-store.s3.amazonaws.com/0/72479/b3c9acf1-f3eb-5a5e-48a8-5fbc3ce74ce4.jpeg" width="75%">

obnizで動作確認
<A HREF="https://youtu.be/GEJEZfuv_So"><img src="https://qiita-image-store.s3.amazonaws.com/0/72479/5548710f-b821-8492-a0b6-ac813c53645c.png" width="75%"></A>

# まとめ
検出点が4箇所あるため、触る場所によって変位量が変わることが確認できました。また、感度(押し込む力に比例して数値が0〜120の間で変化する)や反応速度は良好でした。
良き"ショッカク"ライフを！
<img src="https://qiita-image-store.s3.amazonaws.com/0/72479/a845f4e6-b235-d917-4057-e08bf857e49d.jpeg" width="50%">