TAIWAN ALPHA 円形接触位置+感圧センサ 360度(以下円形センサ)という商品がありまして、圧力センサのうちでもSoftpotに代表される圧力によって位置を測定できるセンサなんですが、データシートを見てもどうやって使ったらいいかとても分かりにくいので、仕組みと使い方をまとめました。
余談
いきなり余談から始めますが、この動画をご覧ください。
今回取り上げた円形センサの宣伝用の動画なんですが、社内でこれを見て「このセンサは触れるだけでいいのか、押し込まないといけないのか」が話題になりました。データシート(PDF)を見ると圧力センサの部分(円の中心)については測定できる圧力の範囲が書かれているのですが、周辺の位置センサの部分は圧力について何も書かれていません。しかし動画を見るとどう見ても指にちょっと力が入ってる感じなのです。
データシートを見よう
データシートは1ページなので必要そうなところを切り抜いて貼っておきます。
上の図から、真ん中の圧力センサは押すと圧力が変わる普通のセンサっぽいことがわかります。そして位置センサは3つの抵抗が輪になっていることが分かります。が、その右上にある「←2」という矢印が謎です。可変抵抗の回路記号(旧JIS)がこんな
なので、この上からの矢印がそれだと思われます。普通の可変抵抗との違いはこの2番ピンは輪になった3つの抵抗全部の上を移動するということです。
つないでみよう
センサとデータシートを眺めていても分からないのでつないでみることにします。マイコンボードは懐かしのArduino Duemilanoveが出てきたのでそれにしました。現行のArduino Unoとだいたい同じです。
抵抗値の変化を測定したいので、Arduinoのアナログ入力端子につなぎます。ちょうど6ピンなのでわかりやすいです。ただセンサのピンは細くてそのままピンソケットに挿し込んでもスカスカなので、センサをピンヘッダにはんだ付けしてからArduinoのピンソケットに挿しました。また、センサのどこにも圧力がかかっていない時に出力がどこにもつながらない状態になってしまうのはよくないので、データシート中のBのピンと2のピンを100kΩでGNDにつなぎました。
スケッチを書こう
久しぶりなので説明しておくと、Arduinoではプログラムのことを「スケッチ」と呼びます。
圧力センサ
まずは簡単そうな圧力センサ部分から。センサの端子AはArduinoのA3に、端子BはA1につながっています。ArduinoのアナログA0〜A5ピンはデジタル14〜19としても使えるので、17番にセンサの端子Aが、15番に端子Bがつながっている事になります。また、前述の通り端子Bは100kΩでプルダウンしてありますから、こんな回路になっています。
17番ピン(図のA)に電源として5Vを供給したいので出力にしてHIGHを出力し、15番ピン(図のB)はアナログ入力A1ピンとして使うためpinModeは宣言しないでおきます。
あとはloop()内でanalogread(1)すれば押した力が読み取れます。強く押すと抵抗値が減って電圧は上がるので値も大きくなります。最大は1023、最小は0です。あまり直線性が高くないようで軽く押したつもりでもすぐ1023になってしまいます。
位置センサ
そして位置センサです。これはちょっと工夫が必要です。一度に3つの抵抗の値を取得はできないので、ピンの設定を変えながら3回読み出します。
1つのピンを電源に、残りの2つのピンをGNDに接続して抵抗値をanalogReadします。
それをピンを変えて3回繰り返せば円周のどこに触れられているかを突き止めることができます。今回は
- 3回とも0だったらどこにも触れられていない
- 3未満の値が読み出せた場合はその回の一つ前の抵抗値を正解とする
という方法を採用しました。
さらに、ここで取得できた値は0〜1023で触れた場所が電源に近いほど大きい数字になるので、Arduinoのmap()を使って値を角度に変換しています。
なお、触れるだけでも値が読み取れたので、力を入れて押し込む必要はなさそうです。
/*
TAIWAN ALPHA Membrane Force and Position Sensor Test Program
https://ssci.to/6253
*/
void outputData(int force, int pos1, int pos2, int pos3) {
Serial.print(force, DEC);
Serial.print(",");
Serial.print(pos1, DEC);
Serial.print(",");
Serial.print(pos2, DEC);
Serial.print(",");
Serial.print(pos3, DEC);
int degree;
if ((pos1 == 0) && (pos2 == 0) && (pos3 == 0)) {
degree = 0;
} else if (pos3 < 3) {
degree = map(pos1, 1023, 0, 0, 119);
} else if (pos1 < 3) {
degree = map(pos2, 1023, 0, 120, 239);
} else if (pos2 < 3) {
degree = map(pos3, 1023, 0, 240, 359);
} else {
degree = 0;
}
Serial.print(",");
Serial.println(degree, DEC);
}
void sensorRead(int* force, int* pos1, int* pos2, int* pos3) {
pinMode(19, INPUT); // 1
pinMode(18, INPUT); // 1'
pinMode(14, INPUT); // 1''
delay(10);
*force = analogRead(1);
delay(10);
pinMode(19, OUTPUT); // 1
pinMode(18, OUTPUT); // 1'
pinMode(14, OUTPUT); // 1''
digitalWrite(19, HIGH);
digitalWrite(18, LOW);
digitalWrite(14, LOW);
delay(10);
*pos1 = analogRead(2);
delay(10);
digitalWrite(19, LOW); // 1
digitalWrite(18, HIGH); // 1'
digitalWrite(14, LOW); // 1''
delay(10);
*pos2 = analogRead(2);
delay(10);
digitalWrite(19, LOW); // 1
digitalWrite(18, LOW); // 1'
digitalWrite(14, HIGH); // 1''
delay(10);
*pos3 = analogRead(2);
delay(10);
}
// the setup function runs once when you press reset or power the board
void setup() {
// initialize digital pin LED_BUILTIN as an output.
Serial.begin(115200);
pinMode(17, OUTPUT); //
digitalWrite(17, HIGH);
}
// the loop function runs over and over again forever
void loop() {
int force;
int pos1, pos2, pos3;
sensorRead(&force, &pos1, &pos2, &pos3);
outputData(force, pos1, pos2, pos3);
delay(100);
}
PCで受信する
得られた角度をPCで活用してみます。今回はモダンにPythonを使うことにしました。Pyxelという2Dゲームエンジンとシリアル通信のためにpyserialを使っています。macOSで動かしたのでシリアルポートの名前が「/dev/tty.usbserial-*」になっていますが、Windowsの場合は「COM*」になると思います。
import readline
import pyxel
import math
import serial
import time
pyxel.init(120, 80)
ser = serial.Serial('/dev/tty.usbserial-A9007U0F',115200,timeout = 1)
time.sleep(3)
while True:
if ser.in_waiting > 7:
raw_data = ser.readline().decode('utf-8')
sensor_data = raw_data.strip().split(',')
print(sensor_data)
pyxel.cls(0)
pyxel.text(1,1,"Press:",3)
pyxel.text(1,7,"Pos 1:",3)
pyxel.text(1,13,"Pos 2:",3)
pyxel.text(1,19,"Pos 3:",3)
pyxel.pset(80,40,3)
pyxel.circb(80,40,int(sensor_data[0])/100,3)
pyxel.circb(80,40,36,3)
force = int(sensor_data[0])
pos1 = int(sensor_data[1])
pos2 = int(sensor_data[2])
pos3 = int(sensor_data[3])
circle_degree = int(sensor_data[4])
posX = -math.sin(math.radians(circle_degree))*36
posY = -math.cos(math.radians(circle_degree))*36
pyxel.text(28,1,str(force),7)
pyxel.text(28,7,str(pos1),7)
pyxel.text(28,13,str(pos2),7)
pyxel.text(27,19,str(pos3),7)
pyxel.circb(int(posX)+80,int(posY)+40,5,7)
pyxel.flip()
まとめ
読み出しに少し工夫が必要ですが、触れただけでも反応しますし、静電センサと違って指以外でも反応してくれるので応用範囲は広そうです。
TAIWAN ALPHA 円形接触位置+感圧センサ 360度