マイクロビットと「サーボモーター」をつかった作品のネタをいろいろ考えていたのですが、「潜水艦のソナー画面」や「空港の管制レーダー画面」のようなものをつくったら面白いのではないか?と思いつきました。
サーボモーターに「超音波センサ」を取り付け、サーボモーターの「角度」情報と、超音波センサから取得した「障害物までの距離」情報を組み合わせて、PC画面上に表示するものです。
サーボモーターの首振り角度を変えるだけでなく、その角度情報をPC画面での表示時にも活用することになり、サーボモーターをつかった作品として適しているのでは?と思います。
さっそくつくってみることにしました。
まずはハードウェアです。
マイクロビットの小さい端子から信号を取り出すために、今回は「KEYESTUDIOモータードライバー拡張ボード for BBC micro:bit(KS4033)」を使います。
単三電池4本用の電池ボックスを取り付けておきます。
サーボモーターは秋月電子でも販売されている「FS90」を、超音波センサは定番の「HC-SR04」を使います。
木片に超音波センサをねじ止めし、その木片の底面にサーボホーンを取り付けます。
サーボモーターの各端子は以下のようにつなぎます。
| サーボモーター | 拡張ボード |
|---|---|
| VCC | 5V |
| GND | G |
| SIG | P8 |
超音波センサの各端子は以下のようにつなぎます。
| 超音波センサ | 拡張ボード |
|---|---|
| VCC | 5V |
| GND | G |
| Trig | P2 |
| Echo | P1 |
サーボモーターにサーボホーンを差し込みます。
あたま(超音波センサ側)が重くなってサーボモーターが倒れてしまうので、固定するために両面テープで電池ボックスに貼り付けておきました。

マイクロビット用プログラムはこちらです。

Aボタンを押すたびに、変数「動作中」が「真」と「偽」で切り替わります。
「動作中」が「真」のとき、サーボが指定された「角度」に動き、超音波センサが障害物との「距離」を測定します。
なお、超音波センサで距離が正常に測定できなかったとき、測定値は「0」になってしまうので、そのときは「距離」を「500」に変更しています。
サーボの「角度」と、超音波センサで測定した「距離」を、「シリアル通信」でPCに送信します。
これらの処理を50ミリ秒間隔で繰り返します。その際、角度は「0度」〜「180度」の範囲で「1度」ずつ変動します。
マイクロビットから送られてきたデータを、PC画面上で表示するために「Processing」を使います(Processingのインストール方法などは こちら)。
Processing用プログラムはこちらです。
import processing.serial.*;
Serial myPort;
float angle = 90;
float distance = 20;
boolean hasData = false;
int lastReceiveTime = 0;
final int originX = 480;
final int originY = 540;
final int radarRadius = 500;
final int distanceScale = 10;
void serialEvent(Serial myPort) {
String myString = myPort.readStringUntil('\n');
myString = trim(myString);
String[] values = split(myString, ',');
if (values.length >= 2) {
angle = float(values[0]);
distance = float(values[1]);
hasData = true;
lastReceiveTime = millis();
}
}
void setup() {
size(960, 540);
smooth();
background(0);
printArray(Serial.list());
myPort = new Serial(this, Serial.list()[2], 115200);
myPort.bufferUntil('\n');
}
void draw() {
noStroke();
fill(0, 0, 0, 5);
rect(0, 0, 960, 540);
stroke(0, 255, 0);
strokeWeight(1);
noFill();
for(int i=0; i<=radarRadius*2; i+=radarRadius*0.4) {
arc(originX, originY, i, i, PI, TWO_PI);
}
for(int i=0; i<=180; i+=30) {
line(originX, originY, originX-radarRadius*cos(radians(i)), originY-radarRadius*sin(radians(i)));
}
if(millis() - lastReceiveTime > 500) {
hasData = false;
}
if(hasData) {
stroke(0, 255, 0, 10);
strokeWeight(10);
float x = cos(radians(angle));
float y = sin(radians(angle));
line(originX, originY, originX+radarRadius*x, originY-radarRadius*y);
if(distance <= radarRadius/distanceScale) {
noStroke();
fill(255,0,0);
ellipse(originX+distance*distanceScale*x, originY-distance*distanceScale*y, 10, 10);
}
}
}
画面の背景はこちらです。

「draw()」が実行される度に「fill(0, 0, 0, 5);」と「rect(0, 0, 960, 540);」で画面全体を半透明で塗りつぶしており、これにより画面表示内容がだんだん消えていくようにしています。そのため、「draw()」が実行されるたびにこの背景を再描画しています。
マイクロビットからデータを受信する度に、受信した「角度」情報をもとにして緑色の太いラインを描画しています。
また、受信した「距離」情報が「50センチ」以内だった時、その「角度」と「距離」の場所に赤色の丸を描画しています。
動かしたときの表示内容はこちらです。
なかなかいい感じに表示されています。
知り合いの小学生に見せたところ、かなり面白がってくれました。
「これをロボカーに取り付けて、受信したデータに応じて進行方向を制御するようにしたらもっと面白いのでは?」などと、新たなアイデアも出してくれました。
それも良さそうですね。
なお、私がマイクロビットの使い方を習得するのにあたっては、以下の書籍を参考にさせていただきました。
初心者向けから、比較的高度なものまで、さまざまな情報が記載されているだけでなく、子供向けの作例も多数掲載されていますので、「プログラミング教育」のための題材さがしなどにもおすすめです。


