Edited at

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


はじめに

秋月電子でタッチエンス・ショッカクポット(POTU-001-1) が発売されたとの Tweet を見て、昔に秋葉原UDX 2F(アキバ・スクエア)の展示スペースで ShokacPot Miniのデモ をやっていたことを思い出し、気になって仕方がなかったので早速入手して触ってみた。


入手経路

今回は秋月電子でシリアル(UART)出力タイプの製品を入手。(※2019年3月21日現在)

Amazon と RT ROBOT SHOP は製品仕様が異なるので入手される場合は注意が必要。


仕様

タッチエンスのホームページを見るとアナログ出力、I2C出力、SPI出力(開発中)といくつかラインアップがあるが、シリアル(UART)出力のものは記載されていなかった。

秋月電子の商品ページのリンク先から。

取扱説明書

ショッカクポットは三次元方向の変位が検出できる柔軟触覚センサーで、重さを測るセンサーではない。(はじめ勘違いしていた。。)

スポンジの圧縮によりLED-フォトトランジスタ間の光量変化を利用して、スポンジの変位に応じた電圧変化が出力される。電圧変化をスポンジ変形量(変位量)に変換した上で出力する自動較正機能が搭載されているとのこと。


接続図

センサー基板はJST-SH/1.0の5ピンコネクタが採用されているため、他のホストデバイスへ接続しやすくするため付属ケーブルの片側を切断し、Groveコネクタへ変換した。(RSTを除いたVDD、GND、Rx、Tx)

今回はホストデバイスとしてNefryBTを使用した。

スクリーンショット 2019-03-25 10.03.31.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


ソースコード

ソースコードはこちら

NefryBTに搭載されているESP32は複数のHardwareSerialをサポートしているため、SoftwareSerialは使用しない。

また、NefryBTにはTX,RXポートが存在するが、TX,RXポートにセンサーデバイスを接続したままだとプログラムの書き込みに失敗するので(プログラムを書き込みする時は抜いておき、書き込みが完了した後に接続すれば使用可能だが面倒なので)、D2(IO23)にRX、D3(IO19)にTXを割り当ててHardwareSerialを使用する。

取得したデータはNefryBTのディスプレイへ表示するのと合わせて、Arduino IDEのシリアルプロッタで表示するためシリアルモニターへ出力している。


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

ソースコードはこちら


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

ソースコードはこちら


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

ソースコードはこちら


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)表示

シリアルプロッタ(Arduino IDE)表示

NeoPixel(WS2812B)でLチカ

obnizで動作確認


まとめ

検出点が4箇所あるため、触る場所によって変位量が変わることが確認できました。また、感度(押し込む力に比例して数値が0〜120の間で変化する)や反応速度は良好でした。

良き"ショッカク"ライフを!