コロナがいまだに収束せず、外出する際には人との接触を最小限におさえるよう、気を付けて行動する日々が続いています。
コロナ対策においては重要な避けるべき三密(密接・密集・密閉)というものがあります。
このとき必要になってくるのがソーシャルディスタンス(社会的距離)です。
今回はこのソーシャルディスタンスを定量的にするため、Arduinoでソーシャルディスタンシングバッジのプロトタイプを作りました。
ソーシャルディスタンシングバッジとは、人との距離をセンサで測定し、その距離をディスプレイに表示する、というバッジです。
Contents
ソーシャルディスタンシングバッジ制作で使用したもの
今回使用したものは次の6点です。
Amazonや秋月電子でそろえることができます。
ソーシャルディスタンシングバッジ 制作手順
3段階のステップで制作しました。
- センサの動作確認
- LEDの動作確認
- センサとLEDの連携
センサモジュールの動作確認・使い方
利用したセンサはVL53L0Xという赤外線レーザーを用いた距離センサです。
レーザーが物体で反射して戻ってくるまでの時間をはかることで距離を求めており、Raspberry PiやArduinoなどのマイコンで使用できます。
今回はセンサ単体ではなく、秋月でモジュールを購入しました。
サンプルコードが下のリンクにあります。
https://github.com/pololu/vl53l0x-arduino
LEDの動作確認・表示方法
4桁7セグメントLEDは7画で表された数字を7つのLEDで表し、光るLEDを制御することで数字を表現します。
また、4桁の表現は「ダイナミック点灯」という方法で行われています。
ダイナミック点灯とは、LEDを4桁同時に点灯するのではなく、1桁ずつLEDを順番に点灯していくことです。
1桁ずつ点灯するときの切替時間間隔を小さくすると、4桁同時に点灯しているように見えます。
今回は次の記事を参考にLEDの動作確認をしました。
どのピンがどのLEDに対応しているのかわかりやすく説明してくれています。
http://tsukutta.hatenablog.com/entry/2014/06/30/180629
配線
Arduinoの配線は下の図のようになります。
センサとLEDの連携
一番引っかかったのがセンサ値をLEDに表示するところです。
センサ値を表示する部分で、LEDのダイナミック点灯が滑らかにいかず、消えたり点いたりチカチカしてしまいました。
これを見続けていると非常に目が疲れます。
そして一番左の桁だけ謎に常時点灯という謎の挙動です…。
どうやらloop()中にdelay()を入れてしまうと、delay()のたびにLEDが全消灯してしまうためチカチカしていたみたいです。
それを解決できるのがMsTimer2というものでした。
MsTimer2はタイマー割り込みをするライブラリで、割り込み間隔と割り込み発生時に呼び出す関数を指定するものです。
割り込みとは指定した条件のもとで実行中のプログラムに割り込んで異なるプログラムを一時的に実行するものです。
MsTimer2ではタイマーを開始すると指定した時間ごとに割り込んで関数を呼び出します。
こうするとloop()とは独立で繰り返されるため、全消灯されることがなくなり、チカチカしなくなります。
次のリンクの記事を参考にしました。
https://garretlab.web.fc2.com/arduino/introduction/beginning_with_7seg
Arduinoソースコード
今回使用したArduinoのソースコードは次に示すとおりです。
//*****************************************
//* 簡易距離計プログラム(https://deviceplus.jp/hobby/entry016/)
//*****************************************
#include <Wire.h>
#include <VL53L0X.h>
VL53L0X sensor;
// Uncomment this line to use long range mode. This
// increases the sensitivity of the sensor and extends its
// potential range, but increases the likelihood of getting
// an inaccurate reading because of reflections from objects
// other than the intended target. It works best in dark
// conditions.
#define LONG_RANGE
// Uncomment ONE of these two lines to get
// - higher speed at the cost of lower accuracy OR
// - higher accuracy at the cost of lower speed
//#define HIGH_SPEED
//#define HIGH_ACCURACY
//LED site:(http://happy-arduino.blogspot.jp/2012/08/17_27.html)
//http://tsukutta.hatenablog.com/entry/2014/06/30/180629
const int BUTTON = 11; // プッシュボタンが接続されているピン
const int led_1 = 9; // 7segLED the one's place
const int led_10 = 10; // 7segLED the ten's place
const int led_100 = 11;
const int led_1000 = 12;
int state = 0; // LEDの状態(0ならオフ、1ならオン)
int state_1=0; // the one's digit
int state_10 =0; // the ten's digit
int state_100 =0;
int state_1000 =0;
unsigned long time;
unsigned long init_time;
//*****************************************
//* タイマー(hhttps://garretlab.web.fc2.com/arduino/introduction/beginning_with_7segment_led/)
//*****************************************
#include <MsTimer2.h> // タイマー割り込みを利用する為に必要なヘッダファイル
// LEDをオン・オフする際の出力
#define DIGIT_ON LOW
#define DIGIT_OFF HIGH
#define SEGMENT_ON HIGH
#define SEGMENT_OFF LOW
const int digitPins[] = {9, 10, 11, 12}; // ディジット(コモン)に接続するArduinoのピン
const int segmentPins[] = {13, 3, 5, 7, 8, 2, 4}; // セグメントピンに接続するArduinoのピン
const int numberOfDigitPins = sizeof(digitPins) / sizeof(digitPins[0]); // ディジットの数
const int numberOfSegmentPins = sizeof(segmentPins) / sizeof(segmentPins[0]); // セグメントの数
int numbersToDisplay = 0; // LEDに表示する数字を保持する変数
// 数字と表示させるセグメントの関係
const int digits[] = {
0b00111111, // 0
0b00000110, // 1
0b01011011, // 2
0b01001111, // 3
0b01100110, // 4
0b01101101, // 5
0b01111101, // 6
0b00100111, // 7
0b01111111, // 8
0b01101111, // 9
};
// 数字を表示する
void displayNumber(int n) {
// digits[n]の各ビットを調べて対応するセグメントを点灯・消灯する
for (int i = 0; i < numberOfSegmentPins; i++) {
digitalWrite(segmentPins[i], digits[n] & (1 << i) ? SEGMENT_ON : SEGMENT_OFF);
}
}
// セグメントをすべてオフにする
void clearSegments() {
for (int j = 0; j < numberOfSegmentPins; j++) {
digitalWrite(segmentPins[j], SEGMENT_OFF);
}
}
// 4桁の数字を表示する
void displayNumbers() {
int n = numbersToDisplay; // numberToDisplayの値を書き換えないために変数にコピー
for (int i = 0; i < numberOfDigitPins; i++) { // 右の桁からディジットを選択する
digitalWrite(digitPins[i], DIGIT_ON); // ディジットをオンにする
displayNumber(n % 10); // 10で割った余りを求めて、1の位を求め、表示する
delayMicroseconds(100);
clearSegments(); // セグメントをすべてオフにする
digitalWrite(digitPins[i], DIGIT_OFF); // ディジットをオフにする
n /= 10; // 10で割り、次に表示する数字を、1の位に移す
}
}
// 表示する数字をセットする
void setNumbers(int n) {
numbersToDisplay = n;
}
// setup() は,最初に一度だけ実行される
void setup() {
Serial.begin( 9600 );
Wire.begin();
if (!sensor.init())
{
Serial.println("Failed to detect and initialize sensor!");
while (1) {}
}
#if defined LONG_RANGE
// lower the return signal rate limit (default is 0.25 MCPS)
sensor.setSignalRateLimit(0.1);//0.1
// increase laser pulse periods (defaults are 14 and 10 PCLKs)
sensor.setVcselPulsePeriod(VL53L0X::VcselPeriodPreRange, 18);//18
sensor.setVcselPulsePeriod(VL53L0X::VcselPeriodFinalRange, 14);//14
#endif
#if defined HIGH_SPEED
// reduce timing budget to 20 ms (default is about 33 ms)
sensor.setMeasurementTimingBudget(20);//20
#elif defined HIGH_ACCURACY
// increase timing budget to 200 ms
sensor.setMeasurementTimingBudget(200000);//200000
#endif
/////////
for (int i = 0; i < numberOfDigitPins; i++) {
pinMode(digitPins[i], OUTPUT); // digitPinsを出力モードに設定する
digitalWrite(digitPins[i], DIGIT_OFF);
}
for (int i = 0; i < numberOfSegmentPins; i++) {
pinMode(segmentPins[i], OUTPUT); // segmentPinsを出力モードに設定する
}
MsTimer2::set(1, displayNumbers); // displayNumbers()を1ミリ秒ごとに呼び出す
MsTimer2::start(); // タイマを開始する
}
void loop() {
int sensorData = sensor.readRangeSingleMillimeters();
setNumbers(sensorData);
Serial.println(sensorData);
}
完成したプロトタイプ
ちゃんと7セグLEDにセンサに対応した距離が表示されています!
チカチカもしておらず、なめらかに数値が表示されています。
まとめ・今後の課題
このプロトタイプをウェアラブル化するべく、回路設計をしたりはんだ付けをしていこうと考えています。
また、ただ距離を表示するだけでなく、ログをとって何かしらに活用していきたいと考えてもいます。
まだまだいろいろ発展させていきたいところです!
コメントを残す