ソロでたのしむ

キャンプ、電子工作、DIY、乗り物などの趣味を紹介

電動でスライド格納するテーブルの自作。ジムニーの助手席をステッピングモーターで豪華にします

電動スライド開閉式テーブルの自作。JB23ジムニーの助手席を快適で豪華に

JB23ジムニーの助手席への固定式テーブル設置に失敗して、しばらく時間が経ってしまいました。

助手席の人から「カップホルダーがない!」との悲痛な声が多くなってきたので、そろそろカップホルダー付きのテーブルを、初代よりも進化させて自作します。

初代の固定式テーブル

助手席からの評判も良かったのですが、短期間しか使わなかったテーブルです。

f:id:solocamptouring:20180616192634j:plain

初代のコンセプトは、

  • ペットボトルホルダー付き
  • 助手席の乗降の邪魔にならない

の2点でした。

固定位置を上下方向で2カ所設定して剛性を出そうとしたので、勢いで2階建てになっています。そのために上側のテーブル面が、エアバックの展開に影響しそうな位置になってしまい、すぐに外しました。

その後は、良いアイデアが思いつくまでペットボトルホルダーはあきらめて、ソーラーパワー腕時計の充電スタンドをつけた板を、テーブルの代わりに置いていました。

f:id:solocamptouring:20180616193309j:plain

今回の自作テーブルのコンセプト

今回は、先回の時に欠点となっていたエアバックへの影響は無くします。

また、蓋で密閉できるペットボトルは対象とせず、コンビニで手軽に買えるカップコーヒーが置ける事を目指します。

  • カップホルダー付き
  • 助手席の乗降の邪魔にならない
  • エアバックの展開を阻害しない

この3点をコンセプトとして、今回のテーブルを自作します。

テーブルの構造を決める

まず、カップホルダーはテーブル面上に市販のものを、最後に固定することにします。後から取り替えもできるので、テーブル自体が完成するまでは、あまりこだわらないことにします。

テーブル面は格納式にして、乗降性とエアバック展開の条件をクリアします。

さらに、高級感をだすために、電動スライド開閉式に挑戦します。

スライドテーブルの奥行き

JB23ジムニーのグローブボックス上にある凹形状の奥行きは、約10cmです。

グローブボックスの扉面から少し出ていても乗降性に影響しないので、テーブルの台座となる板の奥行きは15cmとします。

台座となる奥行き15cmの板を、MDFから切り出しました。

f:id:solocamptouring:20180616193357j:plain

テーブル面はアクリル板にして、その下に配置する機構部やArduinoなどのLED光が見えるようにします。家で何かないか探したところ、もう使わなくなっていたサンバイザーがでてきました。

f:id:solocamptouring:20180616193421j:plain

このサンバイザー、横幅もちょうどいいので、外形は加工せずにそのまま使います。

テーブルのスライドレール

テーブルを滑らかに動かすためのレールを、安価な引き出し用のスライドレールから探してみましたが、汎用品は20cm以上のものばかりでした。

スライド長さ10cm以下では、アルミの押し出し材を使ったものを唯一みつけたので、早速2個注文しました。

小さなスライド構造DIYに最適な約8cm長のスライドレール
取付用の小さなネジを別に入手しないといけませんが、レール自体は滑らかに動き、強度も充分にありそうです。

電動格納部の自作

ステップモーター28BYJ-48を使う

今回の作品のキモとなる電動開閉機構は、ステップモーター(ステッピングモーター)をArduino で制御するものを自作します。

ステップモーター28BYJ-48は、5Vと12V駆動のものがあります。今回は重力の影響を受けない水平方向の動きで、回転速度も遅くていいので、5V駆動のものを使ってみます。

ステップモーター28BYJ-48をArduino で制御して、電動開閉テーブルを自作

スライド機構

台座の両側に、電動開閉機構部と当たらない高さまで壁をつけて、スライドレールを取り付けます。

テーブルを取り付ける側は、L字断面のプラスチック押し出し材をつけておきます。

f:id:solocamptouring:20180624060332j:plain

台座となる板は、モーターとギヤ用のかさ上げ板を追加して、黒く塗装しておきました。

電動開閉機構

まずは、ステップモーターの出力をギヤで減速します。ギヤは、壊れたプリンターから外したものを、加工して使います

ステップモーター28BYJ-48に合うギヤを、廃品プリンターからリサイクル

不必要に長い軸や邪魔な出っ張りの部分を切り落として、ヤスリで表面を整えました。

減速したドリブン側のギヤにアームを付けて、開閉方向の動きに変えます。

ステップモーターで開閉する機構。電動スライド開閉式テーブルの自作

 

ArduinoとステップモータードライバULN2003を使う回路

Fritzingで回路図を作りました。

Arduinoと28BYJ-48を使う回路図。車用 電動スライド開閉式テーブルの自作回路図の右半分は、ステップモーター28BYJ-48と、そのULN2003ドライバーモジュールなので、安く購入できる完成品を使っています。

テーブルを電動開閉するアームの位置情報を得るために、全開と全閉時に押される位置にポジションスイッチ(リミットスイッチ)を使います。それぞれスイッチがオンの時はHIGH(5V)、オフの時はプルダウン抵抗でLOW(約0V)の信号をArduinoに送ります。

操作用のモメンタリースイッチも、別に1個使います。

LEDは、モーターを動かす間だけ光るようにスケッチ(プログラム)を書きます。

ブレッドボード上で動作確認してから、汎用基板で回路を作り台座に搭載しました。

Arduinoと28BYJ-48を使う。電動スライド開閉式テーブルの自作


ポジションスイッチ動画

制御方法

Arduinoで、テーブルの開閉をコントロールします。

開閉の状態切り替え命令は、モメンタリースイッチ使って、順番に

  1. 全閉。
  2. 開く方向に動かす。途中でスイッチが押されることなく全開したらに移行。
  3. (開作動中にスイッチが押されたら)止める。
  4. 閉じる方向に動かす。全閉したらに移行。
  5. 全開。
  6. 閉じる方向に動かす。途中でスイッチが押されることなく全閉したらに移行。
  7. (閉作動中にスイッチが押されたら)止める。
  8. 開く方向に動かす。全開したらに移行。

の状態に切り替えます。途中状態が多くなっていますが、 何か挟まった時等の非常時に、ボタンを押せば止められて、さらにボタンを押せば反転して動くようにするために、8パターンの状態を推移するようにしました。

電源投入時は、全閉/全開検出スイッチの状態によって、

  • 全閉状態なら
  • 全開状態なら
  • 途中状態なら 

から始めます。途中で止まっている状態で電源を入れた場合は、テーブル上に飲み物等があるかもしれないので、次に操作スイッチを押したら開く側の動きにします。

スケッチ(プログラム)は最後に紹介します。

完成!(動画あり)

スケッチをArduinoに書き込んだら、車に取り付けて動かしてみます。

定電圧で低回転速度をつくれるステップモーターだからできる、静かで力強い動きです。


Arduino project #4 「車の助手席用の電動格納テーブルをつくる」Making auto-open/close table for passenger seat of car

出力軸の形状が特殊なのでギヤの入手が難しいですが、それをクリアできれば28BYJ-48は、静かにゆっくり動かしたい物を自作する時に重宝するモーターです。

スケッチ(プログラム)の紹介

int operationledPin = 3;    //  operation indicator led output
int motorclosePin = 8;    // full close position sw input
int motoropenPin = 9;     // full open position sw input
int operationswPin = 10;    // operation sw input
int powerOnled = 12;    // power on LED

int motorPin1 = 4;    // Blue   - 28BYJ48 pin 1
int motorPin2 = 5;    // Pink   - 28BYJ48 pin 2
int motorPin3 = 6;    // Yellow - 28BYJ48 pin 3
int motorPin4 = 7;    // Orange - 28BYJ48 pin 4
                      // Red    - 28BYJ48 pin 5 (VCC)

int motorSpeed;  
int count = 0;          // count of steps made
int speedcontr = 0;
int countstop = 320; // counts to stop to avoid heat
int conditionNumber = 0; // 0:close  1:move to open  2:stop to close  3:move to close  4:open  5:move to close  6:stop to open  7:move to open
int lookup[8] = {B01000, B01100, B00100, B00110, B00010, B00011, B00001, B01001};

void setup() { 
  pinMode(operationledPin,OUTPUT) ; 
  pinMode(motorclosePin,INPUT) ; 
  pinMode(motoropenPin,INPUT) ; 
  pinMode(operationswPin,INPUT) ;
  //declare the motor pins as outputs
  pinMode(motorPin1, OUTPUT);
  pinMode(motorPin2, OUTPUT);
  pinMode(motorPin3, OUTPUT);
  pinMode(motorPin4, OUTPUT);
  Serial.begin(9600);

  if (digitalRead(motoropenPin) == HIGH){
  conditionNumber = 4; // table is open
  } else if (digitalRead(motorclosePin) == HIGH){
  conditionNumber = 0; // table is close
  } else {
  conditionNumber = 6; // table is middle next open    
  }
 digitalWrite(powerOnled , HIGH); 
}

void loop() {

if (digitalRead(operationswPin) == HIGH) { 
    if(conditionNumber<7){   
    conditionNumber++;
    }else {
    conditionNumber = 0;   
    }
    delay(50);
    while(digitalRead(operationswPin)==HIGH){}
}

if(conditionNumber == 0){
  if (digitalRead(motorclosePin) == HIGH){
  digitalWrite(operationledPin, LOW);    
  digitalWrite(motorPin1,LOW);
  digitalWrite(motorPin2, LOW);
  digitalWrite(motorPin3, LOW);
  digitalWrite(motorPin4, LOW);
  count = 0;  
  } else {
  digitalWrite(operationledPin, HIGH);    
  speedcontr = (3*count);
  motorSpeed = (2000+speedcontr);
  anticlockwise();
  count++;  
  }

}else if(conditionNumber == 1){
  if (digitalRead(motoropenPin) == HIGH){
  digitalWrite(operationledPin, LOW);    
  digitalWrite(motorPin1,LOW);
  digitalWrite(motorPin2, LOW);
  digitalWrite(motorPin3, LOW);
  digitalWrite(motorPin4, LOW);
  count = 0;
  conditionNumber = 4;
  } else {
  digitalWrite(operationledPin, HIGH);    
  speedcontr = (3*count);
  motorSpeed = (2000+speedcontr);
  clockwise();
  count++;  
  }

}else if(conditionNumber == 2){
  digitalWrite(operationledPin, LOW);    
  digitalWrite(motorPin1,LOW);
  digitalWrite(motorPin2, LOW);
  digitalWrite(motorPin3, LOW);
  digitalWrite(motorPin4, LOW);
  count = 0;

}else if(conditionNumber == 3){
  if (digitalRead(motorclosePin) == HIGH){
  digitalWrite(operationledPin, LOW);    
  digitalWrite(motorPin1,LOW);
  digitalWrite(motorPin2, LOW);
  digitalWrite(motorPin3, LOW);
  digitalWrite(motorPin4, LOW);
  count = 0;
  conditionNumber = 0;
  } else {
  digitalWrite(operationledPin, HIGH);    
  speedcontr = (3*count);
  motorSpeed = (2000+speedcontr);
  anticlockwise();
  count++;  
  }

} else if(conditionNumber == 4){
  if (digitalRead(motoropenPin) == HIGH){
  digitalWrite(operationledPin, LOW);    
  digitalWrite(motorPin1,LOW);
  digitalWrite(motorPin2, LOW);
  digitalWrite(motorPin3, LOW);
  digitalWrite(motorPin4, LOW);
  count = 0;  
  } else {
  digitalWrite(operationledPin, HIGH);    
  speedcontr = (3*count);
  motorSpeed = (2000+speedcontr);
  clockwise();
  count++;  
  }

}else if(conditionNumber == 5){
  if (digitalRead(motorclosePin) == HIGH){
  digitalWrite(operationledPin, LOW);    
  digitalWrite(motorPin1,LOW);
  digitalWrite(motorPin2, LOW);
  digitalWrite(motorPin3, LOW);
  digitalWrite(motorPin4, LOW);
  count = 0;
  conditionNumber = 0;
  } else {
  digitalWrite(operationledPin, HIGH);    
  speedcontr = (3*count);
  motorSpeed = (2000+speedcontr);
  anticlockwise() ;
  count++;  
  }

}else if(conditionNumber == 6){
  digitalWrite(operationledPin, LOW);    
  digitalWrite(motorPin1,LOW);
  digitalWrite(motorPin2, LOW);
  digitalWrite(motorPin3, LOW);
  digitalWrite(motorPin4, LOW);
  count = 0;

}else if(conditionNumber == 7){
  if (digitalRead(motoropenPin) == HIGH){
  digitalWrite(operationledPin, LOW);    
  digitalWrite(motorPin1,LOW);
  digitalWrite(motorPin2, LOW);
  digitalWrite(motorPin3, LOW);
  digitalWrite(motorPin4, LOW);
  count = 0;
  conditionNumber = 4;
  } else {
  digitalWrite(operationledPin, HIGH);    
  speedcontr = (3*count);
  motorSpeed = (2000+speedcontr);
  clockwise();
  count++;  
  }
}
}

void anticlockwise() 
{
  for(int i = 0; i < 8; i++)
  {
    setOutput(i);
    delayMicroseconds(motorSpeed);
  }
}

void clockwise()  
{
  for(int i = 7; i >= 0; i--)
  {
    setOutput(i);
    delayMicroseconds(motorSpeed);
  }
}

void setOutput(int out)
{
  digitalWrite(motorPin1, bitRead(lookup[out], 0));
  digitalWrite(motorPin2, bitRead(lookup[out], 1));
  digitalWrite(motorPin3, bitRead(lookup[out], 2));
  digitalWrite(motorPin4, bitRead(lookup[out], 3));
}

トイラジコンを自動走行ロボットにしたい。超音波センサーとArduinoで改造

自動運転の車が実際に走る社会が近づいてきました。5千円ほどの材料を使っての簡単な工作レベルで、どこまで自動走行ができるのかを試してみます。

トイラジコンを自動走行ロボットにする。超音波センサー5個とArduinoで工作

自動運転(走行)で楽しくなる

自動運転と聞くと、スタート地点からゴール地点まで無人で走る車を思い浮かべます。そんな社会になったら移動時間の使い方の幅が多いに広がります。

もっと細かい分野では、自動ブレーキ、自動速度(全車との間隔)制御、自動駐車など、既に実用化されているものがあります。

さらに屋内では、お掃除ロボットが走りまわっている家も多くなっています。

楽になることはいいのですが、趣味の世界では楽しくないといけない!と思うので、今回自作する自動走行ロボットは、少しコミカルな動きを取り入れて、見ていて面白いものを目指します。

自動走行の動作

どのような動かし方をするかを決めます。

今回使うセンサーや認識ツールは、画像処理のような高価な技術は使わずに、一個あたり数百円で入手できる超音波センサーのみとします。

そのため、動作もシンプルで破綻(動けなくなる)しにくいものにします。

  • 障害物との距離が遠い方に操舵する
  • 前方障害物との距離に応じてスピードを変える
  • 行き詰まったら、少し後退して回転する

自動走行ロボットは、市販のトイラジコンを改造して作りますが、元のコントローラも有効利用します。

  • 2個のシーソースイッチを全後進の右旋回と左旋回ボタンとして、割り込み操作可能にする

ベースマシンの選定

ニッコー マックストラックス NIKKO MAXX TRAXX

改造ベースとするトイラジコンを選びます。

屋外で自動走行させる予定ですが、障害物の乗り越え性能はそこそこで良しとします。

少ないセンサーで、障害物の間に挟まったりして破綻しない為には、超信地旋回が有利です。そのため、タイヤで駆動し操舵するものではなく、モーターが2個になってしまいますが無限軌道タイプにします。

早速調べてみたところ数千円で入手できる無限軌道のトイラジコンをみつけました。

速度は可変せず、左右それぞれのモーターに対して前進-停止-後進の3パターンしかありません。

f:id:solocamptouring:20180602092718j:plain

常にフルパワーでは制御しにくいので、自動走行時は障害物との距離に応じて、Arduinoを使ってPWMで出力(速度)可変できるようにする計画です。

(補足)PWMについての簡単な説明

DCモーターの出力(回転数)は、モーター端子間の電圧を変えることでコントロールできます。

PWM(Pulse width modulation、パルス幅変調)は、電圧一定で、周期も一定のパルス波形にしたものです。これにより、電源電圧は一定でも、パルスの幅を変えれば電圧の平均値が変わるため、結果として電圧を変えた場合と同じ効果があります。

トイラジコンの分解調査

手元に届いて簡単に作動確認後、すぐに分解します。

電子回路基板

ニッコー マックストラックス(NIKKO MAXX TRAXX)の電子回路

トイラジコンに搭載されている基板についてネットで調べてみると、回路図が見つかりました。基板をラジコン用汎用モジュールとして取り扱っているようです。

写真中央のチップが、左のアンテナ(短いハーネス)から受け取った信号を処理する頭脳部です。

写真下の方にある、モーターからの配線が繋がっている2個のチップが、Hブリッジ回路が内蔵されたモータードライバICのようです。

この2個のチップにArduinoからパルスを入力すれば、PWM制御で速度コントロールができると思われます。

モーター

ニッコー マックストラックス(NIKKO MAXX TRAXX)の中身

直径約25mmほどのモーターが2個使われています。

左右駆動モジュール化されており、モーター単体レベルまで分解してはいないので、メーカーや型式は不明です。

制御しやすいステッピングモーターで同じサイズのものがあれば載せ替えたかったのですが、簡単に載せ替えられそうなものは無く、またギヤボックスへの取り付けやギヤの入手に苦労しそうな為、このままDCモーターを使ってPWMで速度制御してみます。

Arduino と超音波センサー5個の配線

超音波センサーモジュールについては、別の記事でスケッチ(プログラム)の使い方や得られる距離データを確認しました。

自動走行ロボットには、前向きに3個と後ろ向きに2個の、合計5個のセンサーを配置して、それぞれの正面にある障害物との距離を測ります。

複数の超音波センサーで距離を測る場合、通常は音の干渉による誤測定を防止するために1個づつ順番に音を出して測定していきます。

今回は、5個のセンサーは全て違う方向を向いているので、5個同時に音を出してみます。

Arduino を使う回路図

トイラジコンを自動走行ロボットにするArduinoスケッチ

回路図はFritzingで作成しています。

回路図右下のICが、トイラジコンのモータードライバー2個をまとめて現したものです。

このICに、 Arduino からパルスを入力する事で、モーターの出力を可変制御します。

  • D5 : 右側モーター後進用PWMパルス出力
  • D6 : 右側モーター直進用PWMパルス出力
  • D9 : 左側モーター直進用PWMパルス出力
  • D11: 左側モーター後進用PWMパルス出力

超音波センサーは、上の3個でロボットの前側、左の2個で後ろ側の距離を測定します。

  • D2 : 超音波センサー5個共通のトリガー出力
  • D7 : 前右側の超音波センサーのエコー入力
  • D8 : 前中央の超音波センサーのエコー入力
  • D10: 前左側の超音波センサーのエコー入力
  • D3 : 後ろ右側の超音波センサーのエコー入力
  • D4 : 後ろ左側の超音波センサーのエコー入力

トイラジコンに付属のリモコンからの全後進x2チャンネルの信号を、ラジコン頭脳チップから入力します。

  • A0(D14): リモコンの右側後進命令信号の入力
  • A1(D15): リモコンの右側前進命令信号の入力
  • A2(D16): リモコンの左側前進命令信号の入力
  • A3(D17): リモコンの左側後進命令信号の入力

自動走行ロボットの雰囲気を出すために、LEDを光らせます。

1個は超音波測定時に点灯して、状態をモニターできるようにします。

さらに1個を、前方の近距離に障害物があることを検知している場合に点灯します

  • A4(D18): 超音波測定中を表示するLEDへの出力
  • A5(D19): 障害物検知を表示するLEDへの出力

(補足)デジタルピンが足りない場合にアナログピンを使う方法

上の回路図説明で、リモコン操作による前後進のデジタル信号をアナログピンに入力しています。またLEDを光らせるデジタル信号のHIGH(5V)をアナログピンから出力しています。

私がよく使うArduino unoやnanoは入出力ピンの数が限られています。そこで、アナログピンをデジタルピンとして扱えるという便利な機能を使います。

アナログピン0~5には、14から19までのデジタルピン番号がついているので、たとえばアナログピン0をデジタルピン14の入力に使うことを宣言して、その状態(HighかLow)を読みます。

   pinMode(14, INPUT);
   digitalRead(14);

INPUTをOUTPUTに変えて、デジタル出力として使うこともできます。

   pinMode(18, OUTPUT);
   digitalWright(18, HIGH);

スケッチ(プログラム)全文は、最後に紹介いたします。

ロボット作りの作業開始

センサー部をつくる

超音波センサーを5個を使います。

f:id:solocamptouring:20180602172552j:plain

今回の作品はArduinoのピンをほぼ使い切っているだけあって、はんだ付け作業がかなり多いです。

最初に、合計5個のセンサーを、前向き用3個、後ろ向き用2個に分けて、それぞれ汎用基板にはんだ付けして配線します。

できたものがこちらです。

トイラジコンを自動走行ロボットにするための超音波センサー5個

それぞれの角度に固定されると、一段とセンサーらしさが増した気がします。

トイラジコンには、樹脂のホルダーやステーを使って装着します。前側は、センサー5個にオマケでついてきたホルダーを使いました。

トイラジコンの前側に超音波センサー3個を装着。自動走行ロボット化計画

後ろ側のセンサーは、余っていたL字押し出し材を使って、トイラジコンに固定します。使いまわしているので穴だらけですが、性能には影響しないので、このまま使います。

トイラジコンの後ろ側に超音波センサー2個を装着。自動走行ロボット化計画

トイラジコンの基盤に割り込み

ArduinoからのPWM用パルスの入力信号と、リモコン操作信号をArduinoに送る出力信号は、トイラジコンの基盤回路に割り込みます。

まずは、基盤のモーター制御用の信号線を削って切断します。

f:id:solocamptouring:20180603190820j:plain

写真の右側が信号を出すIC、左側がモータードライバーです。

IC側は、Arduinoに送る信号線をはんだ付けできるように、少しプリント導線を露出させておきます。

モータードライバー側は、丸く導線露出した部分がはんだ付けにちょうどいいので、使わせていただきす。

f:id:solocamptouring:20180603191412j:plain

写真下部の工場出荷時のはんだ付けが少し雑ですが、上部の自分で作業した部分も同レベルなので、そのままにしました。

追加したはんだ付け部は、あとでテープを使い絶縁保護しました。

Arduinoを搭載

配線本数が多いので、とにかく黙々とはんだ付けを続けます。

完成したらArduinoを搭載した基板はボックスに入れたいので、とりあえずトイラジコンにプロジェクトボックスの蓋を貼り付けて、さらにその上にArduinoを両面テープで貼り付けました。

Arduinoをトイラジコンに装着して自動走行ロボット完成

むき出し感がありますが、完成です!

自動走行 By アルドゥイーノ

早速、動かしてみます。家の中だとモーター音が響いて家族に驚かれるので、外で試験走行しました。

PWMでスピードを落としていますが、それでも速すぎて距離検知が遅れぎみです。

検知が追い付いつかないので暴れん坊のような動きになってしまい、自動走行というよりもランダム走行に見えます。

youtu.be

後退時に後ろの障害物に当たりにいってしまうのも、改善点です。

初回のスケッチ(プログラム)にしては動いている方だと前向きにとらえて、今後も自動走行に見えるレベルまで、プログラムや定数を改善していきます。

低速モーター化(後日談)

その後、少し間が開いてしまいましたが、低速走行を可能にするために、ステップモーターに組み替えました!

いい感じの速度とトルクになっています。

 

www.solocamptouring.com

 

トイラジコンを超音波センサー5個とArduinoで工作自動走行ロボットにしてみます

スケッチ(プログラム)の紹介

現時点での暴れん坊バージョンのスケッチを紹介いたします。

// 全てのセンサーを1発のトリガーで動かす
int trigPin = 2;
// 前3個のセンサーのエコー
int echoFr = 7; // front right
int echoFc = 8; // front center
int echoFl = 10; // front left
// 後ろ2個のセンサーのエコー
int echoRr = 3; // rear right
int echoRl = 4; // rear left

// PWMパルス出力
int pwmRf = 6; // right 前進
int pwmRr = 5; // right 後進
int pwmLf = 9; // left 前進
int pwmLr = 11; // left 後進

// リモコンの割り込み入力
int remoteRf = 15; // right 前進
int remoteRr = 14; // right 後進
int remoteLf = 16; // left 前進
int remoteLr = 17; // left 後進

// LED出力
int measurLed = 18; // 測定中に光るLED
int detectLed = 19; // 障害物検知中に光るLED

// モーター出力を定義
int maxSpeed = 100; // 最大出力を255以下で定義
int midSpeed = 90; // 中間出力を255以下で定義
int lowSpeed = 80; // 最小」出力を255以下で定義

// その他
boolean inReverse =false; // 後退中は真
int distSteer = 50; // 方向を変える横方向距離
int distStop = 50; // 危険と判断して止める距離
int distClear = 60; // 危険が無くなったと判断する距離
int maxStraight = 300; // 全速を許可する時の前方安全範囲
int nMeasure = 1; // 平均値をとるための計測回数

void setup() {

pinMode(trigPin, OUTPUT); 
pinMode(pwmRf, OUTPUT); 
pinMode(pwmRr, OUTPUT); 
pinMode(pwmLf, OUTPUT); 
pinMode(pwmLr, OUTPUT); 

pinMode(echoFr, INPUT);
pinMode(echoFc, INPUT);
pinMode(echoFl, INPUT);
pinMode(echoRr, INPUT);
pinMode(echoRl, INPUT);
pinMode(remoteRf, INPUT);
pinMode(remoteRr, INPUT);
pinMode(remoteLf, INPUT);
pinMode(remoteLr, INPUT);

Serial.begin(9600);

digitalWrite(measurLed, LOW); 
digitalWrite(detectLed, LOW); 
delay(2000);
}

void loop() {

if(digitalRead(remoteRf) == digitalRead(remoteRr) && digitalRead(remoteLf) == digitalRead(remoteLr)) 
{
  //リモコンの割り込み操作がなければ距離測定を開始

digitalWrite(measurLed, LOW); 

 if(inReverse == false){
 //前進
 int distanceFr;
 int distanceFc;
 int distanceFl;
 
 long durationFr;
 long durationFc;
 long durationFl;
 
 int distAFr = 0;
 int distAFc = 0;
 int distAFl = 0;

 int distFr ;
 int distFc ;
 int distFl ;

 int i ;
    for (i=0 ; i < nMeasure ; i++) {
    digitalWrite(trigPin, LOW); // 初期化
    delayMicroseconds(2);
    digitalWrite(trigPin, HIGH); // 10mSのトリガーをモジュールに出す
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);

    durationFr = pulseIn(echoFr, HIGH);// echoピンから帰ってくる数値mSを読む
 
    digitalWrite(trigPin, LOW); // 初期化
    delayMicroseconds(2);
    digitalWrite(trigPin, HIGH); // 10mSのトリガーをモジュールに出す
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);

    durationFc = pulseIn(echoFc, HIGH);
 
    digitalWrite(trigPin, LOW); // 初期化
    delayMicroseconds(2);
    digitalWrite(trigPin, HIGH); // 10mSのトリガーをモジュールに出す
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);

    durationFl = pulseIn(echoFl, HIGH);
    
    digitalWrite(measurLed, HIGH);
         
// 距離計算
    if(durationFr == 0){
    distanceFr = 250;
    } else {
    distanceFr= durationFr*0.034/2;
    }
    if(durationFc == 0){
    distanceFc = 250;
    } else {  
    distanceFc= durationFc*0.034/2;
    }
    if(durationFl == 0){
    distanceFl = 250;
    } else {    
    distanceFl= durationFl*0.034/2;
    }
 
    distAFr  = distAFr + distanceFr ;
    distAFc  = distAFc + distanceFc ;
    distAFl  = distAFl + distanceFl ;
    } 
    
  distFr = distAFr/nMeasure  ;   // 平均値をアウトプット
  distFc = distAFc/nMeasure  ; 
  distFl = distAFl/nMeasure  ;

 Serial.print(distFr);
 Serial.print("   ");
 Serial.print(distFc);
 Serial.print("   ");
 Serial.print(distFl);
 Serial.println("DRIVE");
 
digitalWrite(measurLed, LOW); 

  if( distFc < distStop){
  inReverse = true ; //前方に進めなければ後進
  } else {
     digitalWrite(detectLed, LOW); 
    if(distFr > distSteer && distFl > distSteer){ //左右が開けていて
      if(distFc > maxStraight){ //前方も開けていたら全開
        analogWrite(pwmRf,maxSpeed);
        analogWrite(pwmRr,0); 
        analogWrite(pwmLf,maxSpeed);
        analogWrite(pwmLr,0); 
      } else { //前方は開けていなければ中速
        analogWrite(pwmRf,midSpeed);
        analogWrite(pwmRr,0); 
        analogWrite(pwmLf,midSpeed);
        analogWrite(pwmLr,0); 
      }
    }else if(distFc > distClear){   //左右が開けていないが、前に充分スペース有
      if(distFr > distFl){
      analogWrite(pwmRf,lowSpeed);
      analogWrite(pwmRr,0); 
      analogWrite(pwmLf,midSpeed);
      analogWrite(pwmLr,0); 
      }else if(distFr < distFl){
      analogWrite(pwmRf,midSpeed);
      analogWrite(pwmRr,0); 
      analogWrite(pwmLf,lowSpeed);
      analogWrite(pwmLr,0); 
      }else{
      analogWrite(pwmRf,midSpeed);
      analogWrite(pwmRr,0); 
      analogWrite(pwmLf,midSpeed);
      analogWrite(pwmLr,0);
      }
   }else{    //左右が開けておらず、前に充分なスペース無し 
      digitalWrite(detectLed, HIGH); 
      if(distFr > distFl){
      analogWrite(pwmRf,0);
      analogWrite(pwmRr,0); 
      analogWrite(pwmLf,lowSpeed);
      analogWrite(pwmLr,0); 
      }else if(distFr < distFl){
      analogWrite(pwmRf,0);
      analogWrite(pwmRr,lowSpeed); 
      analogWrite(pwmLf,0);
      analogWrite(pwmLr,0); 
      }else{
      analogWrite(pwmRf,lowSpeed);
      analogWrite(pwmRr,0); 
      analogWrite(pwmLf,lowSpeed);
      analogWrite(pwmLr,0);
      }
   }
  }
 } else {
 //後退

    digitalWrite(measurLed, HIGH);   
 int distanceRr;
 int distanceFc;
 int distanceRl;
 
 long durationRr;
 long durationFc;
 long durationRl;
 
 int distARr = 0;
 int distAFc = 0;
 int distARl = 0;

 int distRr ;
 int distFc ;
 int distRl ; 

 int i ;

    for (i=0 ; i < nMeasure ; i++) {
 
    digitalWrite(trigPin, LOW); // 初期化
    delayMicroseconds(2);
    digitalWrite(trigPin, HIGH); // 10mSのトリガーをモジュールに出す
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);

    durationRr = pulseIn(echoRr, HIGH);// echoピンから帰ってくる数値mSを読む
 
    digitalWrite(trigPin, LOW); // 初期化
    delayMicroseconds(2);
    digitalWrite(trigPin, HIGH); // 10mSのトリガーをモジュールに出す
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);

    durationFc = pulseIn(echoFc, HIGH);
 
    digitalWrite(trigPin, LOW); // 初期化
    delayMicroseconds(2);
    digitalWrite(trigPin, HIGH); // 10mSのトリガーをモジュールに出す
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);

    durationRl = pulseIn(echoRl, HIGH);

// 距離計算

    if(durationFc == 0){
    distanceFc = 250;
    } else {  
    distanceFc= durationFc*0.034/2;
    }
    if(durationRr == 0){
    distanceRr = 250;
    } else {   
    distanceRr= durationRr*0.034/2;
    }
    if(durationRl == 0){
    distanceRl = 250;
    } else {      
    distanceRl= durationRl*0.034/2;
    }
    
    distAFc  = distAFc + distanceFc ;
    distARr  = distARr + distanceRr ; 
    distARl  = distARl + distanceRl ;
    } 
    
  distFc = distAFc/nMeasure  ;  
  distRr = distARr/nMeasure  ; 
  distRl = distARl/nMeasure  ;

 Serial.print(distFc);
 Serial.print("   ");
 Serial.print(distRr);
 Serial.print("   ");
 Serial.println(distRl);

 Serial.println("REVERSE");

digitalWrite(measurLed, LOW); 
   if(distFc < distClear){
    if(distRr > distSteer && distRl > distSteer){
      digitalWrite(detectLed, HIGH); 
      analogWrite(pwmRf,0);
      analogWrite(pwmRr,midSpeed); 
      analogWrite(pwmLf,0);
      analogWrite(pwmLr,midSpeed); 
    }else if(distRr > distRl){
      digitalWrite(detectLed, HIGH); 
      analogWrite(pwmRf,0);
      analogWrite(pwmRr,lowSpeed); 
      analogWrite(pwmLf,0);
      analogWrite(pwmLr,midSpeed); 
    }else if(distRr < distRl){
      digitalWrite(detectLed, HIGH); 
      analogWrite(pwmRf,0);
      analogWrite(pwmRr,midSpeed); 
      analogWrite(pwmLf,0);
      analogWrite(pwmLr,lowSpeed); 
    }else{
      digitalWrite(detectLed, HIGH); 
      analogWrite(pwmRf,0);
      analogWrite(pwmRr,midSpeed); 
      analogWrite(pwmLf,0);
      analogWrite(pwmLr,midSpeed);
    }
  } else {
  digitalWrite(detectLed, LOW); 
  if(distRr > distRl){
     analogWrite(pwmRf,midSpeed);
     analogWrite(pwmRr,0); 
     analogWrite(pwmLf,0);
     analogWrite(pwmLr,midSpeed);
     delay(100);
       inReverse = false;
  }else{
     analogWrite(pwmRf,0);
     analogWrite(pwmRr,midSpeed); 
     analogWrite(pwmLf,midSpeed);
     analogWrite(pwmLr,0); 
     delay(100);
       inReverse = false;
   }
  }
 }
} else {
//リモコン操作の割り込みがある場合
  if(digitalRead(remoteRf) == HIGH){
    analogWrite(pwmRf,lowSpeed);
    analogWrite(pwmRr,0);
  } else if(digitalRead(remoteRr) == HIGH){
    analogWrite(pwmRf,0);
    analogWrite(pwmRr,lowSpeed);
  } else {
    analogWrite(pwmRf,0);
    analogWrite(pwmRr,0);
  }
  if(digitalRead(remoteLf) == HIGH){
    analogWrite(pwmLf,lowSpeed);
    analogWrite(pwmLr,0);
  } else if(digitalRead(remoteLr) == HIGH){
    analogWrite(pwmLf,0);
    analogWrite(pwmLr,lowSpeed);
  } else {
    analogWrite(pwmLf,0);
    analogWrite(pwmLr,0);
  }
 }
}

赤外線センサーと超音波センサ-で距離をはかってみた。Arduinoで性能比較

arduinoで距離をはかる。赤外線モジュールGP2Y0E02Aと超音波モジュールHC-SR04

初心者でも簡単に電子工作が楽しめるarduino(アルドゥイーノ)を使って実用的な作品作りをしています。

たまには遊び心のある作品もいいじゃない!と思い、近いうちに簡単な自動走行ロボットを作ってみたいと思います。

自動走行を実現するためには、距離センサーが必要です。

まずは、安価で一般的な赤外線モジュールと超音波モジュールを、arduinoを使って実際に距離測定しながら比較して、ロボットへの搭載位置や個数を決める時に活用します。

Arduinoで距離をはかる。赤外線モジュールGP2Y0E02Aと超音波モジュールHC-SR04

赤外線モジュールGP2Y0E02A(左)と超音波モジュールHC-SR04(右)

赤外線センサー GP2Y0E02A

シャープの赤外線式の測距モジュールとしては、GP2Y0A21YKがArduino作品でよく使われます。

今回はもっと小型のものを試してみたいと思い、3.3V電源で使用する測距モジュールモジュールGP2Y0E02Aを選択しました。

18.9 × 8.0 × 5.2mmとたいへんコンパクトなモジュールですが、赤外線LEDとCMOSイメージセンサーを備えています。

4~50cmの距離を高精度に素早く測定できますが、赤外線がしっかり反射する材質と面積と位置(角度)の相手物でないと測定できません。つまり、

  • 測定が早くて正確
  • 測定する相手物は、真正面
  • 相手物にはセンサーと平行の赤外線反射面が必要
  • 反射面の面積が、ある程度必要

なので、例えば決められたルートや迷路を自動走行するロボットなどには使えそうです。

ArduinoとGP2Y0E02Aの配線図

Arduinoと赤外線モジュールGP2Y0E02Aの配線図
Arduinoと赤外線モジュールGP2Y0E02Aの配線

回路図はFritzingで作成しています。

赤外線式距離モジュールGP2Y0E02Aには、4本の端子があります。

  1. VDD(3.3V電源)
  2. GND
  3. Vout(出力)
  4. GPIO1

1番と2番はArduinoの3.3VとGNDに接続します。

3番のVoutは、電圧で距離情報を返してくるので、Arduinoのアナログピン(回路図ではA3ピン)に接続します。

4番のGPIO1は、電圧がVDD付近であれば計測、GND付近であればスタンバイ(計測しない)モードとなります。今回はお試しなので簡易的に1番のVDDに直接つなぎます。

GP2Y0E02Aを使うスケッチ(プログラム)

void setup() {
 Serial.begin(9600);
}

void loop() {
delay (100);
     long distAns ;
     distAns = IDMread(3)  ;           // アナログピン3番のセンサーの値を読込む
     long distAns1 = map(distAns,0,1023,0,500) ; // 電圧値に変換する(0から5V)
     long distAns2 = map(distAns1,55,220,50,4) ;  // 電圧値から距離に変換する(0.55から2.2Vを50から4cmに)
     int distResult = distAns2; 
     Serial.println(distResult);  
}

// 赤外線測距モジュールから読み込む処理
int IDMread(int PinNo) {
     long distA ;
     int i ;
     distA = 0 ;
     for (i=0 ; i < 100; i++) {
          distA  = distA + analogRead(PinNo) ;   // アナログピンから読取る
     } 
     return distA/100 ;                        // 100回の平均値を返す
}

測定した距離を、センチメートルでシリアルモニターに表示するスケッチです。

測定時間が短いので、100回に一回、その平均値を表示します。それでも早すぎるので表示するまで0.1秒のディレイ(休憩時間)を入れています。

シリアルモニターには、このように表示されます。

f:id:solocamptouring:20180526150338p:plain

平らな板ではなく、手帳のざらざらした革表紙を計測してみたため、少しバラツキが大きい結果が表示されました。

GP2Y0E02A まとめ

室内で、センサーと平行な関係になることが分かっている平らな壁との距離を、早く正確に測ることができます。4~50cmの測定範囲が用途にあえば、小型で使いやすいモジュールです。

実用では、衣類などに対するバラツキ幅を乱数として有効活用して、1/fゆらぎを発生するUSB扇風機を作ってみました。

www.solocamptouring.com

 

超音波モジュール HC-SR04

Arduinoで距離を測る場合に、よく使われるモジュールです。

45×20×15mmと少し大柄ですが、超音波発信部と受信部の作りがロボットの目のようで愛嬌がある形です。

データシートでは15°の範囲で2~400cmの距離を測れることになっていますが、相手物がしっかり音を反射して返すかどうかで、計測精度や検知可否が変わってきます。

他にも試して気付いたことを、纏めてみます。

  • 近くの測定は、そこそこ早くて正確。遠くの測定は少し時間がかかる
  • 測定できる相手物は、近くは真正面から左右7.5°の範囲だが、遠くになると正面だけ
  • 相手物にセンサーと平行の超音波反射面がある方が、測定精度が良い
  • 検知物が90°のカドだと45°方向からは計測できない

超音波は思いのほか拡散して薄まるのか、遠くまでしっかり計測できるのは正面方向だけのようです。

とはいえ、1m以内の近距離のものは赤外線センサーよりも安定して計測できるので、こちらの方が使いやすそうです。

ArduinoとHC-SR04の配線図

Arduinoと超音波モジュールHC-SR04の回路図
Arduinoと超音波モジュールHC-SR04の回路

超音波モジュールHC-SR04には、4本の端子があります。

  1. VCC(5V電源)
  2. Trig(トリガー)
  3. Echo(エコー)
  4. GND

1番と4番はArduinoの5VとGNDに接続します。

2番のTrigは、Arduinoから10mSのパルスを入れることで、超音波モジュールに8x40KHzの超音波を発するように指示します。今回はD7に接続します。

3番のEchoは、超音波が返ってくるまでの間ONになります。この時間をArduinoで読むことで、距離を計測できます。今回はD8に接続します。

HC-SR04を使うスケッチ(プログラム)

int trigPin = 7; // trigピンはARDUINOのD7に接続
int echoPin = 8; // echoピンはARDUINOのD8に接続
// defines variables
long duration;
long distance;
long distA ;
long distResult;
int i ;

void setup() {
pinMode(trigPin, OUTPUT); 
pinMode(echoPin, INPUT);
Serial.begin(9600);
}
void loop() {

distA = 0 ;
    for (i=0 ; i < 10; i++) {
 
    digitalWrite(trigPin, LOW); // 初期化
    delayMicroseconds(2);

    digitalWrite(trigPin, HIGH); // 10mSのトリガーをモジュールに出す
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);

    duration = pulseIn(echoPin, HIGH);// echoピンから帰ってくる数値mSを読む
    distance= duration*0.034/2;// 距離計算
    distA  = distA + distance ; 
    } 
distResult = distA/10 ;   // 10回の平均値をアウトプット

Serial.println(distResult);
}

赤外線モジュール同様に、測定した距離をセンチメートルでシリアルモニターに表示します。

超音波モジュールは遠くまで測定するのに時間がかかるので、100回ではなく10回に一回、その平均値を表示します。

シリアルモニターの表示です。

f:id:solocamptouring:20180526184839p:plain

HC-SR04 まとめ

シリアルモニターの表示でもわかるとおり、近距離の測定結果が赤外線モジュールよりも安定しています。

少し角度がついている面でも、実際よりは遠い距離だと認識しますが計測結果は得られます。 

この超音波モジュールを数個使って自動走行ロボットを制作中です。まだ粗削りの動きなのでレベルアップに挑戦し続けており、製作記事は順次更新いたします。

www.solocamptouring.com

 

LEDサウンドレベルメーターの自作。RGB LEDをナイトライダー風に光らせる

LEDサウンドレベルメーターの自作。WS2812Bをナイトライダー風に光らせる

ナイトライダーのK.I.T.T.をご存知ですか?

私と同年代の方々は、頭の中でテーマソングの演奏が始まってしまったのではないでしょうか。

おさらいしておくと、ナイトライダーは80年代のアメリカのドラマで、主人公マイケル.ナイトと彼に仕える人工知能を搭載した車の話です。

K.I.T.T.は人工知能の名前で「キット」と呼ばれていました。調べてみてわかったのですが、Knight Industries Two Thousand(ナイト2000)の頭文字をとったものだったそうです。

今回再現するのは、キットが喋る時に連動するボイスインジケータ の光り方だけで、ディテールにはこだわりません。

ドラマでは「了解です、マイケル」などたくさん喋っていましたが、私は運転していて話しかけられても照れるので、キットの声は再現しません。

かわりに、車内の音の大きさに反応するようにして、音楽や人の声にあわせて光るようにします。

 

市販品があった!

 今回Arduinoで自作しようと決意する前に、市販品で同じようなものがないか探してみたところ、過去に販売されていました。

シガーソケットに差し込むタイプのUSB電源(2個)ですが、いくつかのパターンで喋るようです。

しかし、もう生産されていないようで、日本でもアメリカでもオークションサイトで高値で取引きされています。

簡単に入手できれば、ボックスだけ使って光り方はArduinoで制御したかったのですが、残念です。

Arduinoと接続するモジュール

 ボイスインジケータ を再現するにあたり、WS2812Bを8x8個並べたカラーLEDモジュールと、サウンドセンサーモジュールを使います。

LEDはバーグラフタイプのモジュールの方が実物に近いのですが、ちょうどいい大きさのものが見つからなかったので、使いやすいWS2812Bで進めます。

 サウンドセンサーで検知した音の大きさに応じて、キットのように上下方向にグラフを立てて、カラーLEDモジュールに表示します。

 表示イメージとパターン

 8x8マトリックスLEDのうち6x8個を使って、ボイスインジケータを再現します。

 実物同様の赤色だけで光るパターンの他に、他の色で光ったり、余っている2x8個を違う光り方で装飾したりするパターンにも切り替えられるようにします。

パターン① KITTを再現します(赤色)。

ナイト2000のボイスインジケータをArduinoとWS2812Bで再現

パターン② KITT風で、色を徐々に変化させます。

ナイト2000のボイスインジケータをArduinoとWS2812Bで再現

パターン③ パターン①に加え、3本のインジケーターの間の余っているスペースに光を流します。今回は上下ですが左右に流れる光こそナイトライダーのイメージそのものですね。

パターン④ パターン②に加え、3本のインジケーターの間の余っているスペースに光を流します。

f:id:solocamptouring:20180408122511j:plain
f:id:solocamptouring:20180408122530j:plain

パターン⑤ KARRも再現します(本当はアンバーですが、私の趣味で緑色)。

パターン⑥ KARR風で、色を徐々に変化させます。

KARRのインジケータをArduinoとWS2812Bで再現
パターン⑤(左)と、パターン⑥(右)

パターン⑦ パターン⑤に加え、3本のインジケーターの間の余っているスペースに光を流します。

f:id:solocamptouring:20180408123033j:plain
f:id:solocamptouring:20180408123048j:plain

 

回路図

ナイト2000のボイスインジケータをArduinoとWS2812Bで再現

Fritzingで回路図を作成しました。

マイクモジュールは、MAX4466を搭載したものを使っています。

可変抵抗(ボリューム)を使い、表示7パターンを切り替えられるようにします。

スケッチ(プログラム)は、記事の最後に掲載しておきます。

完成しました!

 まずブレッドボード上で回路を組んで、ipodタッチで出した音をマイクで拾いながら、マイク感度とLED残像調整をしてスケッチを完成させました。

ナイト2000のボイスインジケータをArduinoとWS2812Bで作成

 

完成後の作動状況は動画で確認できます。

7種類の光り方だけ見たい場合は、2:37くらいから確認してみてください。


Arduino project #3 「サウンドレベルメーターをWS2812Bでつくる」Sound level meter with WS2812B

 

 

今回作ったものは、車のオーバーヘッドコンソールに組み込んで光らせますが、その場所には既に、以前の記事で紹介した8x8LEDが搭載されていて、レインボーカラーなどで光っています。

www.solocamptouring.com

 今回のナイトライダー化は、既に搭載されているArduinoにマイクモジュールを追加して、スケッチ(プログラム)を書き換えるだけなので、車での作業はあっという間に終了しました。

f:id:solocamptouring:20180415185857j:plain

スケッチを紹介します

簡単なスケッチ(プログラム)で思い通りの光らせ方ができるGitHubのFastLEDライブラリを使います。

LED光の残像効果は、スケッチ内のfadeToBlackBy()の部分で調整しています。

#include "FastLED.h"
#define DATA_PIN    4
#define LED_TYPE    WS2812
#define COLOR_ORDER GRB
#define HEIGHT 8
#define WIDTH 8
#define NUM_LEDS HEIGHT*WIDTH
CRGB leds[NUM_LEDS];
#define BRIGHTNESS  3

const int sampleWindow = 20; // Sample window width in mS 
unsigned int sample;
int volts;
int micPin = 2;
int selectPin = 5;
int val = 0;
int numLedsToLight = 0;
int numLedsSide = 0;

void setup() {
  delay(3000); //delay for recovery
  FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(BRIGHTNESS); 
}

// List of patterns to cycle through.  Each is defined as a separate function below.
typedef void (*SimplePatternList[])();
SimplePatternList gPatterns = { kittOriginal,  kittColor, kittOriginalWithSinelon,  kittColorWithSinelon,  karrOriginal,  karrColor,   karrOriginalWithSinelon };

uint8_t gCurrentPatternNumber = 0; // Index number of which pattern is current
uint8_t gHue = 96; // rotating "base color" used by many of the patterns
  
void loop()
{
   unsigned long startMillis= millis();  // Start of sample window
   unsigned int peakToPeak = 0;   // peak-to-peak level
   unsigned int signalMax = 0;
   unsigned int signalMin = 1024;
 
   // collect data for a while
   while (millis() - startMillis < sampleWindow)
   {
      sample = analogRead(micPin);
      if (sample < 1024)
      {
         if (sample > signalMax)
         {
            signalMax = sample;  // save the max levels
         }
         else if (sample < signalMin)
         {
            signalMin = sample;  // save the min levels
         }
      }
   }
   peakToPeak = signalMax - signalMin;  // max - min = peak-peak amplitude
   volts = (peakToPeak*5/130); // adjusting to the output of the mic 


 val = analogRead(selectPin); //check the volume to choose the pattern
      if (val < 150) {
    gCurrentPatternNumber = 0;
       } else if (val < 300){
    gCurrentPatternNumber = 1;
       } else if (val < 450){
    gCurrentPatternNumber = 2;
       } else if (val < 600){
    gCurrentPatternNumber = 3;
       } else if (val < 750){
    gCurrentPatternNumber = 4;
       } else if (val < 900){
    gCurrentPatternNumber = 5;
       } else {
    gCurrentPatternNumber = 6;      
       }

 
  gPatterns[gCurrentPatternNumber](); // Call the current pattern function once, updating the 'leds' array
  FastLED.show();  // send the 'leds' array out to the actual LED strip

  EVERY_N_MILLISECONDS( 300 ) { gHue++; } // slowly cycle the "base color" through the rainbow
}

void sinelon()
{
  fadeToBlackBy( leds, NUM_LEDS, 8);
  int pos = beatsin16(13,16,23);
  leds[pos] += CHSV( gHue, 255, 192);
}
void sinelonTwo()
{
  fadeToBlackBy( leds, NUM_LEDS, 8);
  int pos = beatsin16(13,40,47);
  leds[pos] += CHSV( gHue, 255, 192);
}

void onecolorsinelon()
{
  fadeToBlackBy( leds, NUM_LEDS, 8);
  int pos = beatsin16(13,16,23);
  leds[pos] += CRGB::MidnightBlue;
}

void onecolorsinelonTwo()
{
  fadeToBlackBy( leds, NUM_LEDS, 8);
  int pos = beatsin16(13,40,47);
  leds[pos] += CRGB::MidnightBlue;
}

void kittOriginal() 
{
       if(volts < 4){
        numLedsToLight = volts; 
          if(volts < 1){
          numLedsSide = 0; 
          }else{
          numLedsSide = numLedsToLight-1; 
          }
       }else{
        numLedsToLight = 4;
        numLedsSide = 3;      
       }
        fadeToBlackBy( leds, NUM_LEDS, 20);
        for(int led = 4-numLedsSide; led < 4; led++) { 
            leds[led] += CRGB::Red; 
        }
        for(int led = 4; led < 4+numLedsSide; led++) { 
            leds[led] += CRGB::Red; 
        }
        for(int led = 12-numLedsSide; led < 12; led++) { 
            leds[led] += CRGB::Red; 
        }
        for(int led = 12; led < 12+numLedsSide; led++) { 
            leds[led] += CRGB::Red; 
        }
        for(int led = 28-numLedsToLight; led < 28; led++) { 
            leds[led] += CRGB::Red; 
        }
        for(int led = 28; led < 28+numLedsToLight; led++) { 
            leds[led] += CRGB::Red; 
        }
        for(int led = 36-numLedsToLight; led < 36; led++) { 
            leds[led] += CRGB::Red; 
        }
        for(int led = 36; led < 36+numLedsToLight; led++) { 
            leds[led] += CRGB::Red; 
        }
        for(int led = 52-numLedsSide; led < 52; led++) { 
            leds[led] += CRGB::Red; 
        }
        for(int led = 52; led < 52+numLedsSide; led++) { 
            leds[led] += CRGB::Red; 
        }
        for(int led = 60-numLedsSide; led < 60; led++) { 
            leds[led] += CRGB::Red; 
        }
        for(int led = 60; led < 60+numLedsSide; led++) { 
            leds[led] += CRGB::Red; 
        }
}

void kittColor() 
{
       if(volts < 4){
        numLedsToLight = volts; 
          if(volts < 1){
          numLedsSide = 0; 
          }else{
          numLedsSide = numLedsToLight-1; 
          }
       }else{
        numLedsToLight = 4;
        numLedsSide = 3;      
       }
        fadeToBlackBy( leds, NUM_LEDS, 20);
        for(int led = 4-numLedsSide; led < 4; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 4; led < 4+numLedsSide; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 12-numLedsSide; led < 12; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 12; led < 12+numLedsSide; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 28-numLedsToLight; led < 28; led++) { 
            leds[led] += CHSV( gHue, 255, 192); 
        }
        for(int led = 28; led < 28+numLedsToLight; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 36-numLedsToLight; led < 36; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 36; led < 36+numLedsToLight; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 52-numLedsSide; led < 52; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 52; led < 52+numLedsSide; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 60-numLedsSide; led < 60; led++) { 
            leds[led] += CHSV( gHue, 255, 192); 
        }
        for(int led = 60; led < 60+numLedsSide; led++) { 
            leds[led] += CHSV( gHue, 255, 192); 
        }
}

void kittOriginalWithSinelon() 
{
  sinelon() ;
  sinelonTwo() ;
  kittOriginal() ;
}

void kittColorWithSinelon() 
{
  onecolorsinelon() ;
  onecolorsinelonTwo() ;
  kittColor()  ;
}
void karrOriginal() 
{
       if(volts < 4){
        numLedsToLight = volts; 
        numLedsSide = volts;  
        }else{
        numLedsToLight = 4;
        numLedsSide = 4;      
        }
        fadeToBlackBy( leds, NUM_LEDS, 20);
        for(int led = 8-numLedsSide; led < 8; led++) { 
            leds[led] += CRGB::Green; 
        }
        for(int led = 0; led < 0+numLedsSide; led++) { 
            leds[led] += CRGB::Green; 
        }
        for(int led = 16-numLedsSide; led < 16; led++) { 
            leds[led] += CRGB::Green; 
        }
        for(int led = 8; led < 8+numLedsSide; led++) { 
            leds[led] += CRGB::Green; 
        }
        for(int led = 28-numLedsToLight; led < 28; led++) { 
            leds[led] += CRGB::Green; 
        }
        for(int led = 28; led < 28+numLedsToLight; led++) { 
            leds[led] += CRGB::Green; 
        }
        for(int led = 36-numLedsToLight; led < 36; led++) { 
            leds[led] += CRGB::Green; 
        }
        for(int led = 36; led < 36+numLedsToLight; led++) { 
            leds[led] += CRGB::Green; 
        }
        for(int led = 56-numLedsSide; led < 56; led++) { 
            leds[led] += CRGB::Green; 
        }
        for(int led = 48; led < 48+numLedsSide; led++) { 
            leds[led] += CRGB::Green; 
        }
        for(int led = 64-numLedsSide; led < 64; led++) { 
            leds[led] += CRGB::Green; 
        }
        for(int led = 56; led < 56+numLedsSide; led++) { 
            leds[led] += CRGB::Green; 
        }
}
void karrColor() 
{
       if(volts < 4){
        numLedsToLight = volts; 
        numLedsSide = volts;  
        }else{
        numLedsToLight = 4;
        numLedsSide = 4;      
        }
        fadeToBlackBy( leds, NUM_LEDS, 20);
        for(int led = 8-numLedsSide; led < 8; led++) { 
            leds[led] += CHSV( gHue, 255, 192); 
        }
        for(int led = 0; led < 0+numLedsSide; led++) { 
            leds[led] += CHSV( gHue, 255, 192); 
        }
        for(int led = 16-numLedsSide; led < 16; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 8; led < 8+numLedsSide; led++) { 
            leds[led] += CHSV( gHue, 255, 192); 
        }
        for(int led = 28-numLedsToLight; led < 28; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 28; led < 28+numLedsToLight; led++) { 
            leds[led] += CHSV( gHue, 255, 192); 
        }
        for(int led = 36-numLedsToLight; led < 36; led++) { 
            leds[led] += CHSV( gHue, 255, 192); 
        }
        for(int led = 36; led < 36+numLedsToLight; led++) { 
            leds[led] += CHSV( gHue, 255, 192); 
        }
        for(int led = 56-numLedsSide; led < 56; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 48; led < 48+numLedsSide; led++) { 
            leds[led] += CHSV( gHue, 255, 192); 
        }
        for(int led = 64-numLedsSide; led < 64; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
        for(int led = 56; led < 56+numLedsSide; led++) { 
            leds[led] += CHSV( gHue, 255, 192);
        }
}
void karrOriginalWithSinelon() 
{
  sinelon() ;
  sinelonTwo() ;
  karrOriginal() ;
}

 

 

超簡単な電気・電子回路を使えば、もっとArduinoが使える。「日常で使えるArduino作品」別冊

以前の記事で、車やバイクとキャンプにおけるArduino実用例をいくつか紹介しました。

www.solocamptouring.com

 

そこで紹介した作品でも実践していますが、Arduinoを実用するにあたり電子部品を使ってArduinoに適したデジタル信号をつくることがあります。

今回は、実際に電子部品を使った例について紹介いたします。

記事内の回路図はFritzingで作りました。


デジタルについて

まず簡単にデジタル回路について紹介します。

デジタル信号の基本構成は、

  •  1  か  0

です。

同じ状態に対して、他にも使い方(呼び方)があります。

  •  1   か   0
  • ON   か OFF
  • High か Low
  • 5V   か  0V

5Vで動かす電子回路の場合、最後の「5Vか0V」を回路内で作れば、その電圧を電子回路のデジタル信号として使えます。
5Vを使うArduinoでは、デジタルピンにかかっている電圧が「5Vか0V」を読み取って、「HIGHかLOW」と読み替えて制御用の情報にします。

HIGHとLOWの境目は、Arduinoの種類によっても違いますが、ここでは中央値の2.5V近辺としておきます。

スイッチ操作をデジタル信号に変える

スイッチのON/OFF状態を正しくArduinoに認識させるために抵抗を使います。

まず下の抵抗を使わない回路で考えてみます。

f:id:solocamptouring:20180304194503j:plain

スイッチS1がつながると、ArduinoのD7ピンには5Vがかかります。その時ArduinoはD7ピンが「HIGH」だと認識します。

スイッチS1がつながっていない時のD7ピンが問題です。「5Vかかっていないのだから0V」だと勘違いしそうですが、D7ピンはどこにもつながっていないので、「電圧がわからない状態」が正解です。

スイッチS1がつながっていない時にArduinoからD7のデジタル状態を読もうとしても、場合によってHIGHだったりLOWだったりして安定しません。

プルダウン抵抗とプルアップ抵抗

どこにもつながっていないArduinoのデジタルピンは、HIGHでもLOWでもないことを紹介しました。

スイッチの状態を正しくArduinoに教えるためには、積極的に5Vと0Vを作ることが必要です。

下の回路で考えてみます。

f:id:solocamptouring:20180304195045j:plain

スイッチS1がつながっていると、ArduinoのD7ピンには5Vがかかります。この時、抵抗R1には5Vがかかっていますが、10KΩなど高い抵抗値を設定しておけば、ほとんど電流は流れません。

スイッチS1がつながっていない時、ArduinoのD7ピンは抵抗R1を中継してグランドに接続されています。電子回路では電流はほとんど流れないので抵抗R1での電圧変化はなく、D7ピンは約0Vの状態に保たれます。

この抵抗R1をプルダウン抵抗と呼びます。

 

次に、プルダウン抵抗の回路からスイッチと抵抗のつなぎ方を変えてみます。

f:id:solocamptouring:20180304195246j:plain

スイッチS1がつながっている時、ArduinoのD7ピンはグランドに接続されてるので0Vです。

スイッチS1がつながっていない時、ArduinoのD7ピンは抵抗R1を中継して5Vに接続されており、プルダウン回路と同様に抵抗には電流がほとんど流れないので、D7ピンは約5Vになります。

この抵抗R1をプルアップ抵抗と呼びます。

Arduino内部のプルアップ抵抗を使う

プルダウン抵抗、プルアップ抵抗について紹介しましたが、実はArduinoの内部にプルアップ抵抗が準備されており、少しスケッチを変えるだけで簡単に使うことができます。
たとえば、これまで紹介してきた回路でデジタルピン7の状態(HIGH,LOW)を調べた結果を”sw1Status”に代入する場合、スケッチ(抜粋)は

void setup() {
pinMode(7, INPUT);
}
void loop(){
int sw1Status = digitalRead(7);
}

となります。

これが、Arduino内部のプルアップ抵抗を使う場合は、

void setup() {
pinMode(7, INPUT_PULLUP);
}
void loop(){
int sw1Status = digitalRead(7);
}

として、「7番ピンはインプットで使うけど、ついでに内部のプルアップ抵抗も使います」と宣言するだけです。

ただし、そのピンに何も接続していない状態では、常に5Vがかかっている事になるので、隣りのピンなどとショートしないように注意しないといけません。

12V電圧をデジタル信号に変える

一般的に、車やバイクのブレーキ灯、ウインカー、リバース灯から並列で得られる電圧は約12~14Vです。

Arduinoで使える「HIGHかLOW」という信号にするために、下の回路で変換します。

12Vをデジタル信号(5V)に変える_回路図

 

スイッチがつながっている時、ArduinoのD7ピンの電圧は約4.5V(5V×10÷11)になっています。

スイッチがつながっていない時、ArduinoのD7ピンはプルダウン抵抗R1を中継してグランドに接続されてるので、D7ピンは約0Vになります。


可変抵抗をセレクタとして使う

最後に、Arduinoのアナログピンで、可変抵抗(ボリューム)をセレクタとして使う方法を紹介します。

可変抵抗をセレクタとして使う

可変抵抗に5Vを印加して、可変抵抗で変えた電圧を、Arduinoのアナログピン(下の回路図ではA5)に接続します。

可変抵抗をセレクタとして使う

電圧とArduinoの読み取り値は、下記の条件で比例しています。

  • 電圧; 0〜5V
  • Arduinoの読み取り値; 0〜1023(整数)

セレクターとして使う場合は、1023を選択肢数で割って、それぞれの範囲での条件式を作ります。

下のスケッチは、以前に8x8カラーLEDボードを6種類で光らせた時に紹介した記事からの抜粋です。

void loop()
{
val = analogRead(analogPin);

if (val < 150) {
gCurrentPatternNumber = 0;
} else if (val < 300){
gCurrentPatternNumber = 1;
} else if (val < 450){
gCurrentPatternNumber = 2;
} else if (val < 600){
gCurrentPatternNumber = 3;
} else if (val < 750){
gCurrentPatternNumber = 4;
} else if (val < 900){
gCurrentPatternNumber = 5;
} else {
gCurrentPatternNumber = 6; 
}
}

測定した電圧に応じて、パターン番号を0~6まで変えています。

パターン「0」では何も光らせていないので、実際は6種類の光らせ方をコントロールしています。

ボリュームは、変化を確認しながらワンモーションで狙いのパターンに変更できるので、車のような手元に視線を集中できない環境化ではプッシュ式の切り替えスイッチよりも操作性がいいです。

www.solocamptouring.com

以上が、Arduinoを使った作品で、私がよく使う回路です。

DCモーターなど電力消費が多いものを動かす場合は、トランジスタやリレーをを使うこともありますが、小電流の作品であれば今回紹介した回路の応用で、ほとんど事足ります。

日常で使えるArduino 作品集。使い方とできることを回路やスケッチ付きで紹介します

Arduino の実用的な使用例と作品紹介〜車とバイク

Arduinoで遊び始めてから、その面白さにはまってしまい、次々に作品をつくってきました。

なるべく普段使えるものを作るようにしてきたので、ほとんどのものは今なお現役で動かしています。

その作品たちを簡単に紹介したいと思いますが、思いのほか作品が多くて、少し長めの記事になってしまいました。

既につくりたいものが決まっている場合は、下の目次をクリックして、その場所に直行していただくと、目的の情報を素早く発見できると思います。

回路図やスケッチ(プログラム)は、制作記事リンクのクリック先で確認していただけます。

日常で使えるArduino 作品集。使い方とできることを回路やスケッチ付きで紹介します

 

はじめに

Arduinoとサンプルスケッチ(プログラム)を使い、ブレッドボード上でLEDやモーターなどを作動させるだけでも楽しいのですが、ある程度扱えるようになってくると実用的なものに応用したい!との想いが強くなってきます。

ここでは、まずはサンプルスケッチでの基本的な動かし方、そして趣味の世界が多いですが、実用している作品例を紹介して、「Arduino を日常生活に活かしたい!」と考えている方に、少しでも参考にして頂ければいいなと思います。

冒頭の繰り返しになりますが、回路図やスケッチ(プログラム)は、それぞれの製作記事内で紹介しています。

参考にしていただけそうなものがあれば、記事へのリンクから、制作記事に直接移動できます。

なお、記事内の回路図作成にはFritzingを使いました。

ArduinoでLEDを光らせる(Lチカ)

 サンプルスケッチでLEDを光らせてみる

Arduino IDEのサンプルスケッチ(ファイル→スケッチ例→01.Basics→Blink)を使いますが、どうせならArduinoのオンボードLEDではなく外付けのLEDを光らせてみます。

LEDの配線

Arduino Lチカ 回路のつなぎかた

上の絵はArduino UNOで、右側の番号がふられているピンがデジタルピンです。光らせるLEDはどのデジタルピンにつないでもいいのですが、今回は7番を使う場合で進めます。

LEDをArduinoに直結すると、LEDに5V電圧が直接かかってしまうので、抵抗を直列でつないでおきます。

5V電圧において一般的な20mAのLEDであれば抵抗は150オーム、不安であれば少し大きめの抵抗値のものでも光ります。

LEDのサンプルスケッチに手を加える 

LEDを7番のデジタルピンにつないでいるので、サンプルスケッチの中に3か所登場する「LED_BUILTIN」を「7」に書き換えて使います。

無事に点滅したら、次は点滅周期を変えてarduinoにスケッチを送りなおして光らせてみるなどのプチ改造を繰り返すと、スケッチを作ってから実際に動かすという一連の操作に慣れてきます。

LEDの実用的な使用例

多数の3色LEDをコントロールすることが簡単に実現できて便利なWS2812Bモジュールを使って、いくつかイルミネーション作品をつくりました。

その1;好きな色で光らせてみる

64個のカラーLEDを使って、いろいろな光り方を選択できるようにして車に装着しました。

実用的とは言えませんが、装着した車内の雰囲気は盛り上げてくれます。

www.solocamptouring.com 

その2;ナイトライダー

ナイトライダーのボイスインジケータのように光るサウンドインジケータも作ってみました。

ドラマでは車内のステアリングホイール前に設置されていましたが、私は天井のコンソールに付けて楽しんでいます。

www.solocamptouring.com

その3;キャンプのテント周りのイルミネーション

キャンプでテントまわりの安全確保をしたいと思い、好みの光り方で、常夜灯を作ってみました。

省電力なのでモバイルバッテリーで一晩中使えて、遠くから自分のテントを発見しやすくなる効果もあり、たいへん便利です。

www.solocamptouring.com

その4;お部屋でキャンプファイヤー気分

家でもキャンプファイヤーの雰囲気が出せたら楽しいに違いない!と思い、フロアーランプにファイヤー フレーム パターンを仕込んでみました。

自分でもびっくりするくらい本物感がでていて、毎晩見入っています。

www.solocamptouring.com

その5;夜釣りで使う集魚灯

色や光り方を自由に変えられる点を活かして、魚の方から寄ってきてくれそうな集魚灯を、自作してみたくなりました。

これで爆釣できるか?!

www.solocamptouring.com

Arduinoでステップモーターを動かす 

 ステップモーターは、安価で入手しやすい28BYJ-48とドライバー基板のセットを使います。

安価で入手しやすい28BYJ-48とドライバー基板のセットを使いArduinoで動かす

ステップモーターの使い方

28BYJ-48はユニポーラのステップモーターのため、Arduino IDEのバイポーラモーター用のサンプルスケッチを使う場合は、工夫が必要になります。

そんな苦労をするくらいなら、サンプルスケッチを使わずに動かしてみます。

ステップモーターをシンプルに説明すると、内部の電磁石に順番に電気を流すことで、永久磁石がついている出力軸が回転します。

順番に電気を流す命令を送るのが、ドライバーのIN1~4に接続する4本の信号線です。

この4本の信号線を横に並べて,HIGH(5V)を1、LOW(0V)を0として、

1,0,0,0

1,1,0,0

0,1,0,0

0,1,1,0

0,0,1,0

0,0,1,1

0,0,0,1

1,0,0,1

 の8パターンの信号を順番に、そしてループして出し続ければ、ステップモーターの出力軸が回ります。

4本の信号線をArduinoのデジタルピン4~7につないでモーターを回すだけのスケッチを紹介します。

void setup()

{

  pinMode(4, OUTPUT);  

  pinMode(5, OUTPUT);  

  pinMode(6, OUTPUT);  

  pinMode(7, OUTPUT);  



  int speedL = 10;

}



void loop()

{

  digitalWrite(4, 1);    

  digitalWrite(5, 0);

  digitalWrite(6, 0);

  digitalWrite(7, 0);

  delay(speedL); 



  digitalWrite(4, 1);    

  digitalWrite(5, 1);

  digitalWrite(6, 0);

  digitalWrite(7, 0);

  delay(speedL); 



  digitalWrite(4, 0);    

  digitalWrite(5, 1);

  digitalWrite(6, 0);

  digitalWrite(7, 0);

  delay(speedL); 



  digitalWrite(4, 0);    

  digitalWrite(5, 1);

  digitalWrite(6, 1);

  digitalWrite(7, 0);

  delay(speedL); 



  digitalWrite(4, 0);    

  digitalWrite(5, 0);

  digitalWrite(6, 1);

  digitalWrite(7, 0);

  delay(speedL); 



  digitalWrite(4, 0);    

  digitalWrite(5, 0);

  digitalWrite(6, 1);

  digitalWrite(7, 1);

  delay(speedL); 



  digitalWrite(4, 0);    

  digitalWrite(5, 0);

  digitalWrite(6, 0);

  digitalWrite(7, 1);

  delay(speedL); 



  digitalWrite(4, 1);    

  digitalWrite(5, 0);

  digitalWrite(6, 0);

  digitalWrite(7, 1);

  delay(speedL); 

}

ステップモーターを動かすスケッチは様々な書き方(種類)があります。この次に紹介する実用作品の制作記事では、少し違った書き方になっています。

ここでは配線について詳しく紹介しませんが、一点おすすめしておきたい事があります。

モータードライバーの電源は、Arduinoの5V出力から取るのではなく、外部電源を使うようにしておくと、Arduinoの許容電流を超えて動きがおかしくなったり、故障したりする可能性を減らせます。

ステップモーターの実用的な使用例

その1;車の電動開閉バックモニター

12V用のステップモーター28BYJ-48を使って、リバースギアに連動する電動開閉バックモニターを作成して、車に装着しました。

簡単に速度制御ができる上に静かなので、滑らかに動く作品を目指す場合は、ステップモーターがおすすめです。

www.solocamptouring.com

 

youtu.be

その2;車の電動開閉テーブル

車用としては他にも、普段は格納しておきたい助手席用のテーブルを、電動開閉式で自作してみました。

自重の影響を受けない水平方向の動きなので、5V用のステップモーター28BYJ-48を使っています。

これもステップモーターのおかげで静かに動くので、それだけで高級感がある動きになります。

www.solocamptouring.com

その3;キャンプ用の電動ロティサリー(回転焼き器)

シンプルに回すだけですが、自在に回転数を変えられるステップモーターの利点を生かして、ロティサリーを作ってみました。

ソロキャンプで食べきれる具材の大きさは限られているので、持って行きやすいコンパクトなロティサリーを、モバイルバッテリーで動かしています。

食材が自動でクルクル回りながら焼かれているのを見ているだけで、自作した達成感に満たされます。

www.solocamptouring.com

その4;釣り竿のガイド取り付け用フィニッシングモーター

釣り竿DIY用のフィニッシングモーターは、エポキシ系の塗料が硬化するまでの間に垂れてしまわない為の、

  • 長い時間
  • ゆっくりと

釣り竿を回し続けるためのものです。

この条件、ステップモーターにピッタリ!!!と思いつき、フィニッシングモーターも自作してみました。

www.solocamptouring.com

 

Arduinoでサーボモーターを動かす

f:id:solocamptouring:20181007050128j:plain

ラジコンでよく使われる、DCサーボモーターを動かしてみます。

写真の2種類のサーボモーターは、どちらも3本の電線がついていて、その配線配列は同じです。

  • オレンジ色 ; PWM信号
  • 赤色    ; プラス電源
  • 茶色    ; マイナス電源(GND)

サーボモーターは電力を使うので、Arduinoで使う電源の電流量が少ない場合は、サーボモーターは別電源にしてマイナス電源をArduinoのGNDと繋ぎます。

PWM(Pulse width modulation、パルス幅変調)信号は、Arduinoのスケッチ(プログラム)で作ることもできますが、わざわざ複雑なスケッチを作るよりも、Arduino IDEに準備されているライブラリやスケッチを使う方が楽です。

サンプルスケッチでサーボモーターを動かす

Arduino IDEの、

ファイル → スケッチ例 → Servo → Knob

を選択します。

サーボモーター用のライブラリが使われているので、スケッチの最初の部分に、

#include <Servo.h>

が記載されています。

サンプルスケッチの配線は、

  • ポテンショメーター(ボリューム)は、A0
  • サーボモーターへのPWM信号は、D9

に接続するものになっています。

短いスケッチですが、ボリュームから得られる0~5Vの電圧に応じて、サーボモーターの出力軸角度が変わります。

後ほど紹介する実用的な使用例の作品も、ピン番号は違いますが、配線方法と動かし方は同じです。

ライブラリでサーボモーターを動かす

速度も制御したい場合は、GitHubのVarSpeedServoライブラリが便利です。

Arduino IDEに準備されている<Servo.h>ライブラリは、

  myservo.write(角度);  

で、フルスピードで目標角度まで動くものですが、<VarSpeedServo.h>ライブラリは、

  myservo1.write(角度, 速度, true);

で、動く速度を変えつつ、"true"で目標角度になるまで次のコマンドに移行しないように制限をかけることもできます。

サーボモーターの実用的な使用例:半自動飯盒炊飯器

キャンプ用のガスバーナー(コンロ)の火力をサーボモーターで制御して、自動炊飯器を作りました。

記事内で配線図やスケッチも紹介しているので、サーボモーターの具体的な配線や制御方法の参考にしてみてください。

www.solocamptouring.com

 

ArduinoでLCDに表示する

サンプルスケッチでLCDに表示する(hello world)

液晶ディスプレイ(Liquid Crystal Display、LCD)を初めて試してみる場合、「hello world」が有名です。

 Arduino IDEの、たいへん短いサンプルスケッチ(ファイル→スケッチ例→LiquidCrystal→HelloWorld)で動かすだけですが、LCDとの配線に少し手間がかかります。

f:id:solocamptouring:20171218192148j:plain

ブレッドボード上の配線(サンプルスケッチのピン選択とは異なります)

 

LCDの実用的な使用例

表示したい文字数に応じて、これまでに2種類の大きさのLCDを使ってみました。それぞれの使用例を、制作記事とともに紹介します。

f:id:solocamptouring:20171218161817j:plain

上が16x2文字、下が20x4文字のLCD

16x2文字LCDの実用例

GPSモジュールから、位置・高度・速度情報をArduinoに取り込んで、16x2文字のLCDに表示する作品を、車とバイクに装着しました。

f:id:solocamptouring:20180211222201j:plain

GPSモジュール

GPSモジュールは、緯度経度だけ扱うのであれば比較的に簡単ですが、高度などの計算に時間がかかる情報を取り出す場合は「その情報があるならば読み込む」ような命令文をスケッチに入れる工夫が必要です。

まずは16x2文字LCDと組み合わせた試作品で、試行錯誤しながらスケッチを修正して、GPSモジュールの使い方について勉強しました。

<車に装着した例>

www.solocamptouring.com

 

<バイクに装着した例>

車に取り付けた作品がたいへん便利に使えることがわかったので、コンパクトで防水性を考慮した作品も作って、オフロードバイクに取り付けました。

www.solocamptouring.com

 

20x4文字LCDの実用例:オーバーヘッド マルチモニター

 現在も車に装着しているLCDです。

一個のArduinoで、複数のセンサーからの情報を処理してみることを試してみたくなり、情報表示可能量を増やすために、20x4文字LCDを使いました。

現状では、GPSモジュール、加速度センサーモジュール、温湿度センサーから、8種類の情報を表示しています。

www.solocamptouring.com


Arduino project #1「GPSマルチモニター 」Multi-information monitor with GPS module

 

Arduinoで傾きや加速度を測る

f:id:solocamptouring:20180818065644j:plain

加速度センサーは、加速度や軸加速度、傾きを計測できるセンサーで、自律してバランスをとるロボットやドローンを自作する場合などに使われています。

実用的な使用例は、先述の「20x4文字LCDの使用例」の記事内に記載されているので、ここでは少し趣向を変えて、お試しで使ってみた記事を紹介しておきます。

www.solocamptouring.com

加速度センサーの実用的な使用例:クライノメーター(傾斜計)

安く入手できるセンサーなので、ドローンのような高度な作品でなくても実用してみたいと思い、簡易クライノメーター(傾斜計)のセンサーとして使って、先述の20x4文字LCDに車の傾きを表示しています。

簡易クライノメーター(傾斜計)を自作して、車の傾きを表示

Arduinoで距離を測る

Arduinoで距離を測る

測距センサーは、「距離を測る」というシンプルなものですが、近い将来にもっと様々な物が自動化されていくのに欠かせないセンサーです。

価格に応じて、測定時間、範囲、距離や精度が良くなりますが、趣味のArduino作品、おもちゃ、お掃除ロボットでは、数百円程度までのものを使うのが、一般的です。

手軽に入手できるものの中でもよく使われている、超音波センサーモジュール赤外線センサーモジュールについて、別の記事で性能を比較しながらまとめてみました。

www.solocamptouring.com

 

測距センサーの実用的な使用例

1/fゆらぎ風のUSB扇風機

測距センサーの弱点でもある、衣服などの柔らかい対象物の測定ではバラツキ幅が大きくなる特徴を逆に活かして、1/fゆらぎ風を起こすUSB扇風機を作ってみました。

仕事中に使っていますが、高原のような風で、眠気に襲われることもある!ほどの気持ち良さです。

www.solocamptouring.com

 

トイラジコンを自動走行させてみる

扱いやすい超音波モジュールとArduinoをトイラジコンと接続して、自動走行に挑戦しています。実用的ではありませんが、動きを見ていると癒されるようなものを目指しています。

まだ粗削りな動きしかできていないので、随時更新していきたいと思います。

www.solocamptouring.com

 

(おまけ) 車でArduinoを使う際の電源について

 最後に、車でArduino を使う場合の電源についての紹介です。

車のアクセサー電源(普通はおおよそ12~14V)でも、ArduinoUNOやNanoは動きますが、このような高い電圧をかけ続けるとArduinoが熱を持ちます。

できることなら5~6V付近(種類によっては3.3V)で使用したいので、電圧安定化もかねて12Vから6Vへの電圧コンバーターを使い、上流の+側(12V側)にはヒューズを入れて、Arduinoやモジュール類の電源として使っています。

自作した電子回路を車で使うための安定化電源

車でArduino作品を動かすための電源として、5年以上使っていますが、駆動系と表示系の作品ともに、トラブルなく使えています。

GPS計測で正確なスピードメーターをバイク用で自作~ArduinoとGPSモジュール

バイクに付ける正確なデジタルスピードメーターを自作

最近の多くのバイクは、デジタルスピードメーターが当たり前のようについていますが、私が持っているバイクにはアナログのスピードメーターしか付いていません。

最初のバイクのメーターに追いつきたい!と思い、正確なデジタルスピードメーターを自作してみました。

 

正確な速度をGPSで計測する

車もバイクも、タイヤの回転数を速度に換算して、メーターに表示しています。このため、タイヤの種類や状態によって誤差が生じます。

今回デジタルスピードメーターを自作するにあたり、どうせなら正確なものを作りたいと思いました。

ちょうど同時期に、車用でGPSを使ったデジタルディスプレイを作っていたので、アイデアを転用することにします。

バイク用GPSスピードメーターをArduinoとGPSモジュールで作成

スピードメーターにGPSを使うメリット

GPSを使って位置情報を得るために、GPSモジュールを使います。上の写真の茶色の基板と長方形のアンテナがセットで数千円で入手できます。

衛星受信状態やモジュールの種類にもよりますが、だいたい1秒に1回は各種の最新情報を取り出すことができます。取り出せる情報は、

  • 緯度経度
  • 速度
  • 時刻
  • 進行方向
  • 高度

などで、全てリアルタイムかつ正確な情報です。

速度だけでなく、進行方向などバイクでも役に立つ情報を入手でき、また時刻も補正不要となれば、これを使わない手はありせん。

スピードメーターにGPSを使うデメリット

デメリットもあるので、あげておきます。

車検があるバイクの場合、GPSメーターだけでは車検を受けられません。車検場のローラー上では車両の動きがないので、デジタルメーターの速度は「0Km/h」のままです。

またトンネル内や地下駐車場などのGPSを補足できない場所では、GPSメーターは作動しません。

このため、ノーマルメーターの置き換えではなく、あくまで補助的な役割の追加メーターとして使います。

デジタルメーターの自作 

スピードメーターをバイク用で自作~ArduinoとGPSモジュール

緯度経度を数字で表示しても、走行中には活用できません。

そこで、

  • 速度
  • 時刻
  • 進行方向
  • 高度

を表示します。

Arduino初心者(の私)でもできた作り方の詳細は、別の記事で紹介しています。回路やスケッチ(プログラム)は、車用に作ったものと全く同じです。

www.solocamptouring.com

車とバイク用で違う点としては、電源を5.5VのUSB電源にしておいて、バイクから電源がとれない場合でもモバイルバッテリーを使用できるようにしています。

なるべくコンパクトなものにしたいため、回路とモニターやアンテナがギリギリ収まるような大きさのプラスチック 製のプロジェクトボックスに納めました。

f:id:solocamptouring:20180527174834j:plain

オフロードバイクに取り付けた状態です。車用のものと違い、防水対策も手厚く施しています。

f:id:solocamptouring:20180204120923j:plain

使ってみた感想

背面照明付きのLCDディスプレイを使っていますが、直射日光が当たると見づらいため、バイザーを付けるなどの対策をしようと考えています。

一年以上にわたりオフロードバイクにつけてダートも走っていますが、振動による故障もなく、しっかり働いています。

オーバーヘッドコンソール付けGPSディスプレイ完成(Arduinoでコクピット⑦)

オーバーヘッドコンソール付けGPSディスプレイ完成

Arduinoを使って自作したGPS時計や方位計のディスプレイですが、車のオーバーヘッドコンソールにお試しで装着(シリーズ記事⑥)してから一週間たちました。

前回の記事はこちらです。

www.solocamptouring.com

 問題なく作動しており、おおむね満足していますが、改善したい点がでてきました。

少変更を入れつつ、最終的に完成させます。

 

最終改善ポイント

「S」が「5」に見える対策

GPSモジュールが位置計算に使用している人工衛星の数をLCDの中央下に表示しています。

数字の後ろに衛星(サテライト)を意味する「Sats」を表示していますが、パッと見たときに「S」が数字の「5」に見えてしまいます。

f:id:solocamptouring:20180203095841j:plain

写真は、衛星7機を使って位置計算していることを表示している画面ですが、75っぽく見えてしまいます。

そこで、「S」を小文字に変更して「sats」と表示することで、見やすくします。

 

停止していると方位角度におかしな数値が出ることがある

GPSで得られる進行方向データを方位として代用しています。

しかし進行方向はGPSモジュールの位置(=車)を動かさないと計算できないため、車が停止している状態だと、稀におかしな数値を表示します。

進行方向を計算できていない時は方位角度を表示せず、「ACT. REQUIRED」と表示するようにスケッチ(プログラム)を変更します。

f:id:solocamptouring:20180203125501j:plain

最新のスケッチ全文は、最後に載せておきます。

 

オーバーヘッドコンソールへの取り付け

車に一週間装着してみて、ビビリ音もなく作動も安定しているので、透明パネルのままで手を加えていなかったディスプレイ取り付け面を仕上げます。

LCD表示部でない部位は、個人的に好きな黒カーボン柄のカッティングシートでブラックアウトします。

車のオーバーヘッドコンソールに取付けて、動作確認しました。 

自作オーバーヘッドコンソールの完成(Arduinoで表示する20x4LCDディスプレイ付き)


Arduino project #1「GPSマルチモニター 」Multi-information monitor with GPS module

最新のスケッチ(プログラム)

GPSモジュールは、tinygpsplusライブラリ、

温湿度センサーは、GitHub - adafruit/ DHT11用ライブラリを使っています。

#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <LiquidCrystal.h>
#include<Wire.h>
#include <DHT.h>
#define DHTPIN 13
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);
LiquidCrystal lcd( 12, 11, 8, 7, 6, 5 );
byte upword[8] = {
  B00100,
  B01110,
  B11111,
  B00000,
  B00100,
  B01110,
  B11111,
};
byte downword[8] = {
  B11111,
  B01110,
  B00100,
  B00000,
  B11111,
  B01110,
  B00100,
};
byte righthand[8] = {
  B01000,
  B01100,
  B01110,
  B01111,
  B01110,
  B01100,
  B01000,
};
byte lefthand[8] = {
  B00010,
  B00110,
  B01110,
  B11110,
  B01110,
  B00110,
  B00010,
};
byte centerpoint[8] = {
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
};
TinyGPSPlus gps;
SoftwareSerial gpsSerial(9, 10); // RX, TX (TX not used)
const int serialTime = 200; // enough time to read all NMEA sentences once
const int MPU_addr=0x68;  // I2C address of the MPU-6050
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;

void setup()
{
   Wire.begin();
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  Serial.begin(9600);
  lcd.createChar(1, upword);
  lcd.createChar(2, downword);
  lcd.createChar(3, righthand);
  lcd.createChar(4, lefthand);
  lcd.createChar(5, centerpoint);
  lcd.begin(20,4);
  lcd.clear();  
  gpsSerial.begin(9600);
  lcd.setCursor(0,0);
  lcd.print("  CAPTURING         ");
  delay(1000);
  lcd.setCursor(0,1);
  lcd.print("  SATELLITES        ");
  lcd.setCursor(0,2);
  lcd.print("        Powered by  ");
  lcd.setCursor(0,3);
  lcd.print("           Arduino  ");
  delay(3000);
  lcd.clear(); 
  dht.begin();
  
} // end setup


void loop()
{
int start = millis(); // reference for starting timestamp
int now = millis();
do {
while (gpsSerial.available() > 0) {
gps.encode(gpsSerial.read());
}
now = millis();
} while ((now - start) < serialTime); // breaks out of read gps serial

  if (gps.altitude.isUpdated())
  {
    int h;
    h = (gps.time.hour()) + 9;
    if( 24 <= h ) {
    h = h - 24;
    }
       if (h < 10) {
       lcd.setCursor(0,0);
       lcd.print(" ");
       lcd.setCursor(1,0);
       lcd.print(h);
       } else  {
       lcd.setCursor(0,0);
       lcd.print(h);
       }

    lcd.setCursor(2,0);
    lcd.print(":");

    int m;
    m = gps.time.minute();
       if (m < 10) {
       lcd.setCursor(3,0);
       lcd.print("0");
       lcd.setCursor(4,0);
       lcd.print(m);
       } else {
       lcd.setCursor(3,0);
       lcd.print(m);
       }
       
   int sec;
    sec = gps.time.second();
       if (sec < 10) {
       lcd.setCursor(0,1);
       lcd.print("     ");
       } else  if (sec < 20){
       lcd.setCursor(0,1);
       lcd.write(5);
       } else  if (sec < 30){
       lcd.setCursor(1,1);
       lcd.write(5);
       } else  if (sec < 40){
       lcd.setCursor(2,1);
       lcd.write(5);      
       } else  if (sec < 50){
       lcd.setCursor(3,1);
       lcd.write(5);
       } else {
       lcd.setCursor(4,1);
       lcd.write(5);         
       }

  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B);  //starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr,14,true);  // request a total of 14 registers
  AcX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)     
  AcY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ=Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  Tmp=Wire.read()<<8|Wire.read();  // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
  GyX=Wire.read()<<8|Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  GyY=Wire.read()<<8|Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  GyZ=Wire.read()<<8|Wire.read();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)

    lcd.setCursor(16,2);  
    lcd.write(5);

     if (AcX < -5000) {
       lcd.setCursor(16,1);
       lcd.write(1);
       } else {
       lcd.setCursor(16,1);
       lcd.print(" ");
       }

     if (AcX > 5000) {
       lcd.setCursor(16,3);
       lcd.write(2);
       } else {
       lcd.setCursor(16,3);
       lcd.print(" ");
       }

     if (AcY < -5000) {
       lcd.setCursor(14,2);
       lcd.write(4);
       lcd.write(4);
       } else if (AcY < -2500) {
       lcd.setCursor(14,2);
       lcd.print(" ");
       lcd.write(4);
       } else  {
       lcd.setCursor(14,2);
       lcd.print("  ");
       }

     if (AcY > 5000) {
       lcd.setCursor(17,2);
       lcd.write(3);
       lcd.write(3);
       } else if (AcY > 2500) {
       lcd.setCursor(17,2);
       lcd.write (3);
       lcd.print(" ");
       } else  {
       lcd.setCursor(17,2);
       lcd.print("  ");
       }
  }
  
 if (gps.altitude.isUpdated())
  {
    lcd.setCursor(6,1);  
    lcd.print("Alt");
     int koudo;
     koudo = (int)(gps.altitude.meters()); 
     if (koudo == 0) {
     lcd.setCursor(9,1);
     lcd.print("----"); 
     } else if (koudo < 10) {
     lcd.setCursor(9,1);
     lcd.print("   "); 
     lcd.setCursor(12,1);
     lcd.print(koudo); 
     } else if (koudo < 100) {
     lcd.setCursor(9,1);
     lcd.print("  "); 
     lcd.setCursor(11,1);
     lcd.print(koudo); 
     } else if (koudo < 1000) {
     lcd.setCursor(9,1);
     lcd.print(" "); 
     lcd.setCursor(10,1);
     lcd.print(koudo); 
     } else {
     lcd.setCursor(9,1);
     lcd.print(koudo); 
     }
    lcd.setCursor(13,1);
    lcd.print("m");
  }

  if (gps.speed.isUpdated())
  {
        int satV;
        satV = (int)(gps.satellites.value());
        if (satV < 10){
        lcd.setCursor(8,3);
        lcd.print(" ");
        lcd.setCursor(9,3);
        lcd.print(satV);
         } else {
        lcd.setCursor(8,3);
        lcd.print(satV);
         }
        lcd.setCursor(10,3);
        lcd.print("sats");            
    
    int s;
    s = (int)(gps.speed.kmph());
       if (s == 0) {
       lcd.setCursor(0,3);
       lcd.print("---");
       lcd.print("Km/h");
       } else if (s < 10) {
       lcd.setCursor(0,3);
       lcd.print("  ");
       lcd.setCursor(2,3);
       lcd.print(s);
       lcd.print("Km/h");
       } else if (s < 100) {
       lcd.setCursor(0,3);
       lcd.print(" ");
       lcd.setCursor(1,3);
       lcd.print(s);
       lcd.print("Km/h");
       } else {
       lcd.setCursor(0,3);
       lcd.print(s);
       lcd.print("Km/h");
       }
  }

   if (gps.course.isUpdated())
  {
   int headDir = (gps.course.deg());
   
    if ((gps.course.deg()) <= 0) {
     lcd.setCursor(6,0);
     lcd.print("ACT. REQUIRED ");     
    }
    else if ((gps.course.deg()) > 360) {
     lcd.setCursor(6,0);
     lcd.print("ACT. REQUIRED "); 
    } 
    else if ((gps.course.deg()) < 11.3) {
     lcd.setCursor(13,0);
     lcd.print("W--N--E"); 
      if ((gps.course.deg()) < 10) {
      lcd.setCursor(6,0);
      lcd.print("Dir"); 
      lcd.print("  "); 
      lcd.print(headDir);
      lcd.print(" "); 
      }else {
      lcd.setCursor(6,0);
      lcd.print("Dir"); 
      lcd.print(" "); 
      lcd.print(headDir); 
      lcd.print(" "); 
      }    
     }
    else if ((gps.course.deg()) < 33.8) {
     lcd.setCursor(13,0);
     lcd.print("--N");
     lcd.setCursor(16,0);
     lcd.write(1); 
     lcd.setCursor(17,0);
     lcd.print("--E"); 
      lcd.setCursor(6,0);
      lcd.print("Dir"); 
      lcd.print(" "); 
      lcd.print(headDir); 
      lcd.print(" "); 
     }
   else if ((gps.course.deg()) < 56.3) {
     lcd.setCursor(13,0);
     lcd.print("-N-"); 
     lcd.setCursor(16,0);
     lcd.write(1); 
     lcd.setCursor(17,0);   
     lcd.print("-E-"); 
      lcd.setCursor(6,0);
      lcd.print("Dir"); 
      lcd.print(" "); 
      lcd.print(headDir);
      lcd.print(" "); 
     }
   else if ((gps.course.deg()) < 78.8) {
     lcd.setCursor(13,0);
     lcd.print("N--"); 
     lcd.setCursor(16,0);
     lcd.write(1); 
     lcd.setCursor(17,0);   
     lcd.print("E--"); 
      lcd.setCursor(6,0);
      lcd.print("Dir"); 
      lcd.print(" "); 
      lcd.print(headDir);
      lcd.print(" "); 
     }
   else if ((gps.course.deg()) < 101.3) {
     lcd.setCursor(13,0);
     lcd.print("N--E--S"); 
      if ((gps.course.deg()) < 100) {
      lcd.setCursor(6,0);
      lcd.print("Dir"); 
      lcd.print(" "); 
      lcd.print(headDir);
      lcd.print(" "); 
      }else {
      lcd.setCursor(6,0);
      lcd.print("Dir"); 
      lcd.print(headDir); 
      lcd.print(" "); 
      }    
     }
  else if ((gps.course.deg()) < 123.8) {
     lcd.setCursor(13,0);
     lcd.print("--E"); 
     lcd.setCursor(16,0);
     lcd.write(1); 
     lcd.setCursor(17,0);   
     lcd.print("--S"); 
      lcd.setCursor(6,0);
      lcd.print("Dir"); 
      lcd.print(headDir);
      lcd.print(" "); 
     }
 else if ((gps.course.deg()) < 146.3) {
     lcd.setCursor(13,0);
     lcd.print("-E-");  
     lcd.setCursor(16,0);
     lcd.write(1); 
     lcd.setCursor(17,0);   
     lcd.print("-S-"); 
      lcd.setCursor(6,0);
      lcd.print("Dir"); 
      lcd.print(headDir);  
      lcd.print(" "); 
     }
 else if ((gps.course.deg()) < 168.8) {
     lcd.setCursor(13,0);
     lcd.print("E--");  
     lcd.setCursor(16,0);
     lcd.write(1); 
     lcd.setCursor(17,0);   
     lcd.print("S--"); 
      lcd.setCursor(6,0);
      lcd.print("Dir"); 
      lcd.print(headDir);  
      lcd.print(" "); 
     }
 else if ((gps.course.deg()) < 191.3) {
     lcd.setCursor(13,0);
     lcd.print("E--S--W"); 
      lcd.setCursor(6,0);
      lcd.print("Dir"); 
      lcd.print(headDir);  
      lcd.print(" "); 
     }
 else if ((gps.course.deg()) < 213.8) {
     lcd.setCursor(13,0);
     lcd.print("--S"); 
     lcd.setCursor(16,0);
     lcd.write(1); 
     lcd.setCursor(17,0);   
     lcd.print("--W"); 
      lcd.setCursor(6,0);
      lcd.print("Dir"); 
      lcd.print(headDir);  
      lcd.print(" "); 
     }
else if ((gps.course.deg()) < 236.3) {
     lcd.setCursor(13,0);
     lcd.print("-S-"); 
     lcd.setCursor(16,0);
     lcd.write(1); 
     lcd.setCursor(17,0);   
     lcd.print("-W-"); 
      lcd.setCursor(6,0);
      lcd.print("Dir"); 
      lcd.print(headDir);  
      lcd.print(" "); 
     }
else if ((gps.course.deg()) < 258.8) {
     lcd.setCursor(13,0);
     lcd.print("S--"); 
     lcd.setCursor(16,0);
     lcd.write(1); 
     lcd.setCursor(17,0); 
     lcd.print("W--"); 
      lcd.setCursor(6,0);
      lcd.print("Dir"); 
      lcd.print(headDir);  
      lcd.print(" "); 
     }
else if ((gps.course.deg()) < 281.3) {
     lcd.setCursor(13,0);
     lcd.print("S--W--N"); 
      lcd.setCursor(6,0);
      lcd.print("Dir"); 
      lcd.print(headDir);  
      lcd.print(" "); 
      }
else if ((gps.course.deg()) < 303.8) {
     lcd.setCursor(13,0);
     lcd.print("--W"); 
     lcd.setCursor(16,0);
     lcd.write(1); 
     lcd.setCursor(17,0); 
     lcd.print("--N"); 
      lcd.setCursor(6,0);
      lcd.print("Dir"); 
      lcd.print(headDir);  
      lcd.print(" "); 
     }
else if ((gps.course.deg()) < 326.3) {
     lcd.setCursor(13,0);
     lcd.print("-W-"); 
     lcd.setCursor(16,0);
     lcd.write(1); 
     lcd.setCursor(17,0); 
     lcd.print("-N-"); 
      lcd.setCursor(6,0);
      lcd.print("Dir"); 
      lcd.print(headDir);  
      lcd.print(" "); 
     }
else if ((gps.course.deg()) < 348.8) {
     lcd.setCursor(13,0);
     lcd.print("W--"); 
     lcd.setCursor(16,0);
     lcd.write(1); 
     lcd.setCursor(17,0); 
     lcd.print("N--"); 
      lcd.setCursor(6,0);
      lcd.print("Dir"); 
      lcd.print(headDir);  
      lcd.print(" "); 
     }
else {
     lcd.setCursor(13,0);
     lcd.print("W--N--E");
      lcd.setCursor(6,0);
      lcd.print("Dir"); 
      lcd.print(headDir);  
      lcd.print(" "); 
     } 
    
  }

 int hmdt = dht.readHumidity();
 int tmpr = dht.readTemperature();

  if (isnan(hmdt) || isnan(tmpr) ){
     lcd.setCursor(0,3);
     lcd.print("UPDATING           ");
  }else{
   lcd.setCursor(0,2);
   lcd.print("Tmp");
   if (tmpr < 10) {
   lcd.setCursor(3,2);
   lcd.print(" ");
   lcd.setCursor(4,2);
   lcd.print(tmpr);
   } else {
   lcd.setCursor(3,2);
   lcd.print(tmpr);
   }
   lcd.setCursor(5,2);
   lcd.print("c ");
   lcd.setCursor(7,2);
   lcd.print("Hmd");
   lcd.setCursor(10,2);
   lcd.print(hmdt);
   lcd.setCursor(12,2);
   lcd.print("%");
  }

}

LEDでレインボーカラー。シリアルLEDのWS2812BとArduinoで簡単に実現しましょう!

カラフルな照明って、見ているだけで楽しいですよね~

いろいろなものを、自分好みに光らせてみたい!と思いつつも、どうやって実現させようか?と考えてみると、けっこう大変そうだと思って、躊躇してしまいがちです。

でも、様々な基本プログラムが準備(公開)されているArduinoを使えば簡単にできる!ということがわかりました。

まずは手始めとして、64個のRGB-LEDをいくつかのパターンで光らせます。

f:id:solocamptouring:20180201054058j:plain

光り方のパターンは、ボリューム(可変抵抗)で変えられるようにします。

見た目だけなので利便性の効果はありませんが、車のオーバーヘッドコンソールに埋め込んで使っています。

ランダムな光り方に1980年代の映画に出てくるコンピュータ的な雰囲気が漂い、気分は007かナイトライダーです。

後日、ナイトライダーのボイスインジケータ風バージョンも作ってみたので、記事内に動画リンクをはっておきます。

 

原点回帰してLEDで遊ぶ

私は、マイコンを扱った経験は無いなかで、初めてArduinoを手にしてすぐに「Lチカ」をやってみました。儀式的にLEDを点滅させただけですが、スケッチ(プログラム)の数値を変更し、点滅間隔やパターンを変えてみたりして、Arduinoの基礎の基礎がなんとなく理解できた気がしました。

 それからしばらくの間はモーターを動かしたりGPSモジュールで遊んでいましたが、原点回帰でLEDを綺麗に光らせてみたいと考えました。

クリスマスの綺麗なイルミネーションのような、見ているだけで幸せになる点滅光やカラフルな光を目指します。

f:id:solocamptouring:20180916190042j:plain

 Arduinoを使ってLEDを光らせる

 Arduinoで遊びはじめる前から、ランダムな、もしくは有機的な光らせ方に興味があり、ホタル発光する回路とか、100均のロウソクLEDをランダムパターン発生器の代わりにした回路を作って遊んでいました。

せっかく Arduinoを使うのであれば、もっと多くのLEDを使って、いろんな光り方を試してみたいと思います。

LEDは WS2812Bを選択

 多数のLEDをモジュール化したものは、大きく分けると、一列に配置したテープ状のものと、マトリックスに配置されたボード状のものがあります。

 

今回は光らせて遊ぶだけでなく実際に何かに使いたいので、テープ状のものよりも使いやすそうなボード状のものを使ってみます。

次にLEDの選択ですが、これも実使用を考慮すると配線数を極力減らしたいと思います。調べてみるとWS2812Bモジュールというニーズに合致したLEDがある事がわかりました。

WS2812Bはマイコンチップを搭載したシリアルLEDで、直列に繋いだ多数のLEDを、数に限界はありますが信号線一本で制御できます。

マイコンチップごとにアドレシングできるので、制御信号としては個々のLEDの光り方情報が数珠繋ぎになっているデータを送ります。

一個目のLEDが自分の光り方情報だけ抜き取って、次のLEDに渡す。次のLEDがまた同じ動作をする。ということを繰り返していき、各LEDが個別に違った発光をするものです。

このWS2812Bを64個(8x8個)ならべたボードが市販されているので、入手しました。

f:id:solocamptouring:20180408114052j:plain

Arduinoとの配線

配線は、少なくていいので簡単です。

5~6V電源でArduinoとWS2812Bを駆動して、ボリュームにも電圧をかけたら、

  • WS2812BのデータピンとArduinoのデジタルピン(D4)
  • ボリュームで変えた電圧とArduinoのアナログピン(A5)

を配線して終了です。

カッコ内のピン番号は最後に紹介するスケッチ(プログラム)の場合です。

私のWS2812Bモジュールは、DINがデータのアウトプット(次のモジュールにデータをわたす)ピンで、DOUTがデータの入力ピンになっています。その場合の回路図をFritzingで作成しました。

f:id:solocamptouring:20180408112231j:plain

光らせ方パターン

多くの光り方パターンが定義されていて、簡単なスケッチ(プログラム)で思い通りの光らせ方ができるGitHubのFastLEDライブラリを使うことにします。

あまり多くのパターンを使うと、ボリュームでパターン変更する時にセンシティブになってしまうので、光らせ方は以下の7パターンだけにしておきます。

  1. onlycolorGlitter,;ストロボっぽくランダム発光
  2. sinelon, ;尾を引きながら横に流れる光
  3. onecolorconfetti, ;残像を残しながらランダム発光(一色)
  4. confetti,;残像を残しながらランダム発光(変色)
  5. sinelonWithGlitter ;上記組み合わせ
  6. rainbowWithGlitter; 64個全面で虹色発光(変色)しながら、白色Glitterを入れる
  7. allwhite,;全面で白色発光

7つの アルファベット名称はスケッチで定義したものです。

ArduinoでWS2812B(RGB-LED)を光らせる
ArduinoでWS2812B(RGB-LED)を光らせる
sinelonとallwhite

車のオーバーヘッドコンソールに装着 

せっかく作ったので、車の中で光らせます。

天井の自作コンソールに埋め込みました。

WS2812Bを64(8x8)個ならべたボードをオーバーヘッドコンソールに装着

今回のレインボーカラーからは進化させたサウンドレベルメーター風の光らせ方ですが、同じ車のコンソールに取り付けた作動状態は、動画で確認できます。

今回つかったsinelon(尾を引きながら横に流れる光)も、一部に取り込んでいます。


Arduino project #3 「サウンドレベルメーターをWS2812Bでつくる」Sound level meter with WS2812B

 Arduinoのスケッチ(プログラム)

GitHubのFastLEDで公開されているサンプルスケッチに手を加えて作成しました。

光り方に飽きてしまった場合に簡単に変えられるように、今回は使っていない光らせ方も定義だけは最後の部分にのせています。

#include "FastLED.h"

FASTLED_USING_NAMESPACE
#if FASTLED_VERSION < 3001000
#error "Requires FastLED 3.1 or later; check github for latest code."
#endif

#define DATA_PIN    4
#define LED_TYPE    WS2812
#define COLOR_ORDER GRB
#define HEIGHT 8
#define WIDTH 8
#define NUM_LEDS HEIGHT*WIDTH
CRGB leds[NUM_LEDS];
#define BRIGHTNESS          3
#define FRAMES_PER_SECOND  60
int analogPin = 5;
int val = 0;
void setup() { delay(3000); // 3 second delay for recovery // tell FastLED about the LED strip configuration FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip); //FastLED.addLeds<LED_TYPE,DATA_PIN,CLK_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip); // set master brightness control FastLED.setBrightness(BRIGHTNESS); } // List of patterns to cycle through. Each is defined as a separate function below. typedef void (*SimplePatternList[])(); SimplePatternList gPatterns = { onlycolorGlitter, sinelon, onecolorconfetti, confetti, sinelonWithGlitter, rainbowWithGlitter, allwhite }; uint8_t gCurrentPatternNumber = 0; // Index number of which pattern is current uint8_t gHue = 96; // rotating "base color" used by many of the patterns void loop() { val = analogRead(analogPin); if (val < 150) { gCurrentPatternNumber = 0; } else if (val < 300){ gCurrentPatternNumber = 1; } else if (val < 450){ gCurrentPatternNumber = 2; } else if (val < 600){ gCurrentPatternNumber = 3; } else if (val < 750){ gCurrentPatternNumber = 4; } else if (val < 900){ gCurrentPatternNumber = 5; } else { gCurrentPatternNumber = 6; } // Call the current pattern function once, updating the 'leds' array gPatterns[gCurrentPatternNumber](); // send the 'leds' array out to the actual LED strip FastLED.show(); // insert a delay to keep the framerate modest FastLED.delay(1000/FRAMES_PER_SECOND); // do some periodic updates EVERY_N_MILLISECONDS( 800 ) { gHue++; } // slowly cycle the "base color" through the rainbow } void rainbow() { // FastLED's built-in rainbow generator fill_rainbow( leds, NUM_LEDS, gHue, 1); } void rainbowWithGlitter() { // built-in FastLED rainbow, plus some random sparkly glitter rainbow(); addGlitter(40); } void addGlitter( fract8 chanceOfGlitter) { if( random8() < chanceOfGlitter) { leds[ random16(NUM_LEDS) ] += CRGB::White; } } void addcolorGlitter( fract8 chanceOfGlitter) { if( random8() < chanceOfGlitter) { leds[ random16(NUM_LEDS) ] += CHSV( gHue, 255, 192); } } void sinelon() { // a colored dot sweeping back and forth, with fading trails fadeToBlackBy( leds, NUM_LEDS, 30); int pos = beatsin16(13,24,32); leds[pos] += CHSV( gHue, 255, 192); } void sinelonWithGlitter() { // built-in sinelon, plus some random sparkly glitter sinelon(); addGlitter(5); } void onlycolorGlitter() { // some random sparkly glitter FastLED.clear(); addcolorGlitter(5); } void confetti() { // random colored speckles that blink in and fade smoothly fadeToBlackBy( leds, NUM_LEDS, 20); int pos = random16(600); leds[pos] += CHSV( gHue, 255, 192); } void onecolorconfetti() { // random colored speckles that blink in and fade smoothly fadeToBlackBy( leds, NUM_LEDS, 20); int pos = random16(600); leds[pos] += CRGB::MidnightBlue; } void allwhite() { // all led white color fill_solid(leds, NUM_LEDS, CRGB::White); }

 

自作オーバーヘッドコンソールにLCDモニターをに取付ける(Arduinoでコクピット⑥)

Arduinoで作るジムニーのコクピットディスプレー。スケルトンモデルが完成

 今回のシリーズ記事⑥では、20x4文字の大きなLCDが収まるオーバーヘッドコンソールを組み立てます。

 

前回の記事⑤のおさらい

前回は、オーバーヘッドコンソールの箱を作成しました。

www.solocamptouring.com

今回つくる回路

Arduino(アルドゥイーノ)nano1個で、20x4文字のLCDモニターに8種類の情報を表示します。

  • 時刻
  • 高度
  • 速度
  • 進行方向
  • 捕捉している衛星数
  • 温度
  • 湿度
  • 加速度

 

使用するセンサーモジュールは

  • GPSモジュール
  • 温湿度センサーモジュール
  • 加速度センサーモジュール

の3種類です。

 

Fritzingで作成した回路図。

Arduinoでつくるコクピット情報表示LCDモニターの回路図

 

ブレッドボードで試作した時の様子。

Arduinoで作るコクピットモニター ブレッドボードで試作

回路図を見てもブレッドボードでの試作時の写真を見ても、今回は配線が多くてはんだ付けがたいへんそうですが、気持ちを奮い立たせてやりきってしまいます。

作業を開始します!

フリップダウンモニターの移植

まず、今回は何も変更しないフリップダウンモニターを、古いコンソールから新しいものに移植します。

このモニターもArduinoを使っており、リバース連動オート化されています。

断線しないように注意しながら、Arduinoとステップモータードライバーをはずして、モニター機構部ごと新しいコンソールに移植します。

Arduinoで作動するオートフリップダウンモニターを新しいオーバーヘッドコンソールに移動

新しいコンソールの回路スペースのうち、写真だと右側の部分を空けておきます。今回作るコクピット情報表示用のArduinoを、この部分に取り付けます。 

回路の作成

先延ばししていたはんだ付け作業を開始します。配線数が多いですが、一本づつ丁寧にはんだ付けしていきます。今回は、この作業にいちばん時間がかかりました。

LCDモニターから作り始めます。

JB23ジムニー オーバーヘッドコンソールにモニターをつける
JB23ジムニー オーバーヘッドコンソールにモニターをつける

汎用基盤には、写真だと裏側になってしまっていますが、コントラスト調整用の可変抵抗を付けておきました。

 

Arduinoと、温湿度センサー加速度センサーGPSモジュールも、はんだ付けで結線します。

JB23ジムニー オーバーヘッドコンソールにArduinoで作った戦闘機風モニターをつける

はんだ付けがたいへんでしたが、完成後の「ごちゃごちゃ感」が好きです。

映画でよくある「どの色のケーブルを切るか、一か八か....」のような手作り回路感がたまりません。

Arduinoの車載状態

ダッシュボードの写りこみが激しいですが、車への取付け状態です

スケルトンモデルの完成

下の写真は、車への装着状態からは上下逆転した状態で床に置いて撮影しています。

LCDモニターを取り付けたPET板が透明のままなので中の木が丸見えですが、試験運用が終わったらモニター部以外はブラックアウトします。

Arduinoで作るジムニーのコクピットディスプレー。スケルトンモデルが完成

底面となるアクリルパネルは透明のままにして、ArduinoなどのオペレーションLEDの光を見られるようにします。

f:id:solocamptouring:20180604210259j:plain

 これで、オーバーヘッドコンソールの作成作業は全て終了です。

⑦(最終回)に向けて

車に取り付けて、作動状態を少し様子を見た後、問題なければモニター面のブラックアウトなどをして、最終的に完成とします。

車両へ取り付けた後の状態は、動画でも確認できます。


Arduino project #1「GPSマルチモニター 」Multi-information monitor with GPS module

 

オーバーヘッドコンソールを木工で自作(Arduinoでコクピット⑤)

今回の記事⑤では、20x4文字の大きなLCDが収まるように、オーバーヘッドコンソールを作りなおします。

Arduinoとセンサーで20x4LCDに情報表示
20x4LCDをオーバーヘッドコンソールに埋め込む

 

前回④までのおさらい

Arduino(アルドゥイーノ)nano1個で、20x4文字のLCDモニターに、時刻や速度、加速度、温湿度など8種類の情報を表示して、作動することを確認しました。

www.solocamptouring.com

 

使用するセンサーモジュールは、GPSモジュール温湿度センサーモジュール加速度センサーモジュールの3種類です。

今回やること

はんだ付け作業は後回しにして、まずは取付先となるオーバーヘッドコンソールを作成します。

これまで使っていた16x2文字のLCDが、20x4文字で大型になるために、コンソールのモニター埋め込み部を下方向に拡大します。

同時に、今までコンソール下にアイフォンのイヤホンケースに入れてぶら下げていたステップモーター駆動用の回路も、見栄えを良くする予定です。

現状のコンソール。フリップダウンモニターと回路ケースが丸見え。

作業を開始します!

現状のコンソールを車から取り外す

まず、細いスクリュー2本でとまっているLCDディスプレイ部をはずします。

コンソール裏に詰まっている配線から、目的のコネクターを見つけ出して外します。

オーバーヘッドコンソール裏の配線たち(Arduinoを使ったモニター)

コネクターだらけで収拾がつかなくなりつつあるので、今回のコンソール拡大時に配線も整理します。 

木材の切り出し

今のコンソールを参考に側面の板の形を決めます。

下方向に拡大することを考慮しつつ、今のコンソールをなぞってMDF板に書き写しました。

JB23ジムニ 自作オーバーヘッドコンソール作成

コンソールの側面となるパネルはMDF板を2枚重ねて、ジグゾーで切り出しました。

他にも底面や補強とする板もジグゾーで作ります。昔は糸ノコで必死に切っていたのに比べて、たいへん便利です。

JB23ジムニ 自作オーバーヘッドコンソール作成

組み立て 

上の写真では既に穴をあけておいたのですが、木工用ボンドを塗布しながら、板どうしをスクリューで締め付けて組み立てます。

底面となるパネル(切り欠きがある正方形に近い板)だけは、表皮を巻いてから締め付けたいので、穴をあけていません。

前回作ったときに余っていた表皮を、新しいものにもボンドG17で貼りつけて、コンソールの大枠ができあがりました。

JB23ジムニー 自作オーバーヘッドコンソール作成中
JB23ジムニー 自作オーバーヘッドコンソール作成中

表皮がクセ付きしやすいものを使っているため、接着直後はシワだらけですが、そのうち良い感じになじむ筈です。

プラスチックの外形も切っておきます。下面はアクリル、モニター面は穴があけやすいPETの透明樹脂の板を使います。下面のアクリル板は、アルミのアングル材でとめることにしました。

f:id:solocamptouring:20180129000058j:plain

これで、コンソールの作成は終了です。

次回⑥の予定

Arduinoとセンサーモジュール類やLCDを汎用基盤に実装したら、今回作成したオ-バーヘッドコンソールに埋め込みます。

ようやく最終形が見えてきました!

加速度センサーGY-521(MPU-6050)を使う。(Arduinoでコクピット③,④)

 Arduino で加速度、位置、時計、温度をLCDに表示。ウェルカム画面

今回は、加速度センサーモジュールGY-521(MPU-6050)を使って、

③センサーの感度確認と閾値決め。

④20x4文字LCD画面に全ての情報を表示。

の2パートに分けて進めていきます。

 

③センサーの感度確認と閾値決め

前回の②で決めたこと

前回の記事では、20x4文字LCDディスプレイに表示する各情報の位置を決めました。

www.solocamptouring.com

 

2004LCDにArduinoでたくさんの情報を表示する。採用案(色あり)

今回の内容

3方向加速度&3軸角加速度センサーのGY-521(MPU-6050)モジュールを使って、傾斜計としてLCDに表示するための準備(閾値決め)をじます。

GY-521(MPU-6050)モジュール

小さなモジュールですが、3方向&3軸まわり加速度に加えて、温度情報も出力しているようです。

私は傾斜計として使いたいので、回転方向のジャイロ情報は使いません。

Arduino Playground - MPU-6050に、モジュールの説明(英語)と、”Short example sketch” という完結で使いやすいスケッチが紹介されているので、使わせていただきます。

Arduino nanoとGY-521(MPU-6050)の接続

I2C接続を使えるので簡単です。

Arduinoの種類によって異なりますがが、nanoの場合のモジュールとの接続は、

A4SDA  

A5SCL  

D2INT   

に繋ぐほかは、電源とグランドだけです。

ブレッドボード上で、接続してみました。

Arduino nanoとGY-521(MPU-6050)の接続

加速度の出力をシリアルモニターで確認

本番では、X方法を前後方向、Y方向を横方向とする予定なので、写真の状態が車が前向きでの取り付け状態になるようにします。

シリアルモニターで出力値を確認してみました。

G sensor serial monitor output

とりあえず、無事に出力されています。

モジュールをいろんな方向に傾けてみたところ、車用で使用する閾値としては、AcX、AcYともに±5,000あたりが、加減速とコーナリング時に、頻繁でもなく稀でもないぐらいで良さそうな気がします。

暫定の閾値を決定

前後方向は±5,000、左右方向は±2,500と±5,000の二段階を閾値とします。これらの閾値を超えたら▲表示をするスケッチは、次回の記事で紹介いたします。

次は、他のセンサーモジュールもつないで、全ての情報をLCDモニターに出力します。

④20x4文字LCD画面に全ての情報を表示

下表の情報をLCDに表示させます。速度と使用衛星数の配置を、前前回の記事から少し変更しました。

20x4文字LCDに時間、高度、速度、進行方向、温度、傾斜を表示する

回路図

最後に追加したGY-521(MPU-6050)モジュールが、I2C接続のため必然的にArduino nanoのA4,A5,D2ピンを使うことになり、各モジュールとの接続ピン割振りを見直しました。

ArduinoとGY-521(MPU-6050)を使うスケッチ(プログラム)~ArduinoとLCDでコクピット情報を表示

回路図はFritzingで作成しました。
左上付近にあるGY521が今回追加するセンサーです。回路図では違う部品が載っていますが接続は同じです。

ブレッドボードで作動確認

スケッチは最後に紹介します。

スケッチをArduinoに書き込んで、ブレッドボード上で配線して表示させてみました。

ウエルカム画面

Arduino で加速度、位置、時計、温度をLCDに表示。ウェルカム画面

電源オン後、走り出すまでの画面

Arduino で加速度、位置、時計、温度をLCDに表示。車両停止時画面

動きがないと進行方向が計算できないので、LCD画面の右上に「ACT. REQUIRED」と表示します。

平坦路で、前後および横Gが発生していない時の画面

Arduino で加速度、位置、時計、温度をLCDに表示。Gがない時の画面

LCD画面の右下の■(車のイメージ)のまわりには、どの向きにも▲マークがありません。

坂路もしくはGがかかっている状態

Arduino で加速度、位置、時計、温度をLCDに表示。Gがかかっている時の画面

写真手前の加速度センサーを傾けたので、LCD画面の右下の■のまわりに、▲マークが表示されます。

スケッチ

③の加速度センサーのサンプルスケッチと、前回記事②のGPS&温湿度センサーのスケッチを足し合わせてから、LCDの表示場所取りを見直したり条件式をいくつか追加したりして書いてみました。

スケッチは、完成した時の記事内で紹介しています。

次回⑤以降の記事

Arduinoとセンサーモジュール類、LCDを汎用基盤に実装したら、車のオ-バーヘッドコンソール部に埋め込みます。

完成後に撮った動画で、車に装着して走った時のディスプレイを確認できます。


Arduino project #1「GPSマルチモニター 」Multi-information monitor with GPS module

いろんなセンサーでコクピット情報を20x4LCDモニターに表示(Arduinoでコクピット②)

今回の記事②では、いろいろなセンサーから得た情報を、20x4LCDモニターに表示する場所を決めます。

前回①のおさらい

これまでに、Arduino(アルドゥイーノ)nano1個で、20x4文字のLCDモニターに表示できている情報は、

・時刻
・高度
・速度
・進行方向
・捕捉している衛星数
・温度
・湿度

の7項目です。

Arduinoとセンサーモジュールでバージュンアップ途中の車内モニター

使用しているモジュールは

・GPSモジュール
・温湿度センサーモジュール

の2つです。

ArduinoのデジタルI/OピンもアナログINピンもまだ余っているので、加速度センサーモジュールも今後追加する予定です。

www.solocamptouring.com

今回②の目標

今後、加速度センサーモジュールをArduinoにつないで、傾斜計(クライノメーター)風の表示ができるようにしますが、その前にLCD内の表示位置決めをしておきたいと思います。

今回は20x4文字LCDの画面レイアウトについて考えます。

LCDの画面レイアウト

現状の車内モニター

ArduinoとGPSモジュールで作った、現状の車用GPSモニター

車両停止時のLCD表示。移動中は左下の使用GPS数の位置に速度を表示。

16x2文字のモニターでは、ちょうどいいくらいの情報量と場所取りになっています。
図表化すると、このようになります↓

1602LCD 衛星情報表示レイアウト 現状

各情報を色分けして、わかりやすくした図表↓

1602LCD 衛星情報表示レイアウト 現状(色分け)

温度、湿度、傾斜計を追加した画面レイアウト案

案1

2004LCDにArduinoでたくさんの情報を表示する。案1
まず、場所をとる傾斜計表示(●、▲、▼、<、>で表示する部分)を中央に置いて、そのまわりに他の情報を置いてみました。

まあまあ上手く配置できているのですが、傾斜計の中心点(●)と、進行方向の中心点(この図表の場合は”N”)の、各中心が別の位置にあるのが気になります。


そこで、傾斜計と進行方向(磁針)を縦にならべてみる事にしました。

案2

2004LCDにArduinoでたくさんの情報を表示する。案2
傾斜計と進行方向(磁針)を縦にならべました。
速度を示す”Spd”は場所がなくなり、”S”のみとなりました。
進行方向(角度)を示す”Dir”も同様の理由で、”HD”(ヘッディング)にしました。
黒く塗りつぶされている部分は、時刻の秒針を10秒刻み(この図表は50から60秒の間の状態)で示しています。

この案だと傾斜計の周りが窮屈そうに見えるので、次の案では傾斜計と進行方向(磁針)を右隅に寄せてみます。

案3

2004LCDにArduinoでたくさんの情報を表示する。案3
傾斜計と進行方向(磁針)を右隅に寄せました。
残りのスペースに、他の情報を入れていきます。
進行方向(角度)を示す”Dir”は、復活できました。

あと少し、傾斜計まわりをスッキリさせたいので、
次の案では、速度を示す”S”とセミコロンを無くしてみます。

最終案

2004LCDにArduinoでたくさんの情報を表示する。採用案(色あり)
窮屈感もなくなり、見やすくなりました。

実際の見え方に近くするために、色分けをなくすと、こうなります↓

2004LCDにArduinoでたくさんの情報を表示する。最終案

いい感じですね!

20x4LCDモニターの画面レイアウトも決まったので、次回は加速度センサーモジュールをArduinoとつないで、動作確認してみます。

動画でも画面レイアウトを確認できます!


Arduino project #1「GPSマルチモニター 」Multi-information monitor with GPS module

 

温湿度センサーDHT11で温度と湿度をディスプレイに表示する(Arduinoでコクピット①)

車に装着している、ArduinoとGPSモジュールを使用したマルチインフォメーションモニターに、全7回に分けて表示する情報量を最大化するバージョンアップを紹介していきます。

バージョンアップ前のGPSモニター制作記事はこちらです。

solocamptouring.hatenablog.com

 

現在(バージョンアップ前)の車両取付状態は、このような感じです。

JB23ジムニーに装着した、自作オーバーヘッドディスプレイのGPSモニター

モニターの選択

現在使っているGPSモニターには、

  • 時刻
  • 高度
  • 速度
  • 進行方向
  • 捕捉している衛星数

の5項目の情報を表示しています。

16x2文字のLCDモニターを使っていますが、5項目を表示するのが精いっぱいで、さらに追加で情報を表示するためには、モニターを大型化しないといけません。


簡単に入手できるLCDのうち、次に表示文字数の多いものは20x4文字なので、これに変更する前提でセンサー等を追加していきます。

LCDモニター 1602と2004 比較

追加表示する情報

今後、追加表示していきたいのは、

  • 温度
  • 傾斜計(クライノメーター)

の2項目です。

まず今回は、比較的に簡単な温度表示の追加について試してみます。

Arduinoで温度を知る

温湿度センサーモジュールDHT11を使って、温度を計測します。

安くて精度はまあまあというものですが、今回は小数点以下の精度を必要としないので十分使えます。

Arduinoで、GPSモジュールと温湿度モジュールを使いLCDディスプレイに表示する

左上のクリップで挟んでいるのがDHT11

回路図

DHT11で温度と湿度をディスプレイに表示するArduinoスケッチ

Fritzingで回路図作成しました。


バージョンアップ前の回路に対して、左上の温湿度センサーが追加になっています。センサーの電源電圧は5Vです。

温湿度センサー用のスケッチ

温湿度センサー用のライブラリーは、GitHubのadafruit/DHTセンサライブラリからダウンロードして使いました。
前回記事で紹介したGPSモニターのスケッチに、次に紹介する行を挿入します。

まずは、DHT11用ライブラリを使う事を宣言します。

#include <DHT.h>
#define DHTPIN 13
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);

 セットアップ部で、DHT11と通信開始します。

void setup()
{
  dht.begin();
}

 ループ部で、DHT11からの読み込みとLCDディスプレイへの書き出しを命令します。

void loop()
{
 int h = dht.readHumidity();
 int t = dht.readTemperature();
  if (isnan(h) || isnan(t) ){
     lcd.setCursor(0,3);
     lcd.print("UPDATING           ");
  }else{
   lcd.setCursor(0,3);
   lcd.print("Humid:");
   lcd.setCursor(6,3);
   lcd.print(h);
   lcd.setCursor(8,3);
   lcd.print("%");
   lcd.setCursor(10,3);
   lcd.print("Temp:");
   lcd.setCursor(15,3);
   lcd.print(t);
   lcd.setCursor(17,3);
   lcd.print("c");
  }
}
 

作動確認

20x4文字のLCDを使い、問題なく作動しました。

Arduinoで、GPS情報、温度、湿度をLCDモニターに表示する
センサーを手で温めると、すぐに温度が上がってくるので、変動レスポンスも良さそうです。

今回はDHT11とGPSモジュールを同時に使えるか確認することが主目的なので、各情報の表示位置は暫定です。次回は、傾斜計など全ての表示を含めたレイアウト検討をします。

スケッチは、完成記事(⑦)内で紹介しています。

www.solocamptouring.com

 

完成状態の動画もつくりました。


Arduino project #1「GPSマルチモニター 」Multi-information monitor with GPS module

 

ステップモーター28BYJ-48で、リバース連動オートフリップダウン式モニターを自作

JB23ジムニー フリップダウンバックモニター 自作 1
JB23ジムニー フリップダウンバックモニター 自作 2

リバース連動で開閉する自作バックモニターですが、サーボモーターを使った1号機が思いのほか騒がしい為、ステップモーター(ステッピングモーター)に変更します。この2号機が、現在の最新状態です。

 

使用するステップモーター;28BYJ-48

JB23ジムニー フリップダウンモニター自作 ステップモーターとサーボモーター

右が、ステップモーター(5V用)とドライバー

ステップモーターは、低速ほどトルクがでます。

今回は低速作動で、さらに外部ギヤでも減速して使用するので、安価な28BYJ-48でも動くと考えて選択しました。

この28BYJ-48ですが、作動電圧が5Vと12Vの2種類あります。

はじめは5Vのものを使用してみましたがトルクに余裕がなかったために、12Vのものを採用しました。

 外部減速用のギヤ(ドライブギヤとドリブンギヤ)は、28BYJ-48の出力軸径に合うものを市販品から見つけられず、やむなく壊れたプリンターから取り出したギヤを加工して使っています。


オートフリップダウンモニターのギヤ部

 
回路図

 

ステップモーター28BYJ-48回路図回路図は、Fritzingで作成しています。

右半分の回路は、ステップモーターとドライバーで、セットで安く入手できるので、自作してはいません。

 左半分のArduino本体以外が、自作した回路です。

それでは、回路図の詳細について補足説明させていただきます!

Reverse Signal

車のギアをリバースに入れた時の信号で、具体的には後退灯の12Vを分岐してきています。

そのままだと12Vなので、抵抗3個とダイオード1個で5Vのデジタル信号にして、ArduinoのD10につないでいます。

Full Close SW

ステップモーターには位置情報がないのですが、モニターの現位置がわからないと、制御できません。

そのため、モニターを全閉した時に押される位置にプッシュスイッチをつけて、全閉するたびに位置情報をリセットするようにしています。

スイッチの状態を5Vのデジタル信号にするために、抵抗1個を使用して、ArduinoのD8につないでいます。

LED5

故障時のトラブルシュートが簡単になるように、モーターへの回転指令が出ているかどうかの確認用でつけておきました。

Arduinoを使った位置と速度管理

モニターが開く(降りる)方向は、モーターのステップ数だけで位置管理しています。

そのため、開いている途中でモニターの動きを強制的に止めると、中途半端に開いた状態でも全開だと誤認識して止まります。

全開検出スイッチを追加すれば回避できるのですが、そもそもモニターの自重が開き方向に働いているので、開く途中でモーターが脱調する確率は、0%に近いと考えられます。

開き方向はステップ数だけで位置管理して止める事にしました。

 

モニターが閉じる方向は、徐々にゆっくり閉まるようにしています。

動きに高級感を出しつつ、多少の外乱があったとしても徐々にトルクが強くなる効果でいつかは閉まることを狙っています。 

 

モニターの動きがわかる動画をアップしました!

 


Arduino project #2 「リバースギヤ連動 オートフリップダウン バックモニター」Auto flip down rear view monitor

 スケッチ

最後に、ご参考でスケッチを紹介しておきます。

int reversePin = 10;    // reverse signal input
int operationPin = 9;     // operation indicator led output
int motororiginPin = 8;    // motor initial position sw input
int motorPin1 = 4;    // Blue   - 28BYJ48 pin 1
int motorPin2 = 5;    // Pink   - 28BYJ48 pin 2
int motorPin3 = 6;    // Yellow - 28BYJ48 pin 3
int motorPin4 = 7;    // Orange - 28BYJ48 pin 4
int motorSpeed;  
int count = 0;          // count of steps made
int speedcontr = 0;
int countsperrev = 320; // number of steps for full open
int lookup[8] = {B01000, B01100, B00100, B00110, B00010, B00011, B00001, B01001};
unsigned long timeZero = millis();
boolean fullOpenning =false;
boolean fullClosing =false;
void setup() {
  pinMode(reversePin,INPUT) ;  
  pinMode(operationPin,OUTPUT) ; 
  pinMode(motororiginPin,INPUT) ;
  pinMode(motorPin1, OUTPUT);
  pinMode(motorPin2, OUTPUT);
  pinMode(motorPin3, OUTPUT);
  pinMode(motorPin4, OUTPUT);
  Serial.begin(9600);
}
void loop() {
  if (digitalRead(reversePin) == HIGH) { 
       timeZero = millis();
       fullClosing =false;    
         if(count < countsperrev ){
         digitalWrite(operationPin, HIGH);
         speedcontr = (1*count);
         motorSpeed = 900;
         clockwise();
         count++;
         } else {
         digitalWrite(operationPin, LOW);    
         digitalWrite(motorPin1,LOW);
         digitalWrite(motorPin2, LOW);
         digitalWrite(motorPin3, LOW);
         digitalWrite(motorPin4, LOW); 
         fullOpenning =true;
        }    
   } else {
     if (digitalRead(motororiginPin) == LOW) {
          if (fullOpenning == true){ 
               if (millis() - timeZero > 1000) {
               digitalWrite(operationPin,HIGH);
                 if (count > 100){
                 motorSpeed = 1000;
                 anticlockwise();
                 count--;
                 } else {
                 speedcontr = (30*count);
                 motorSpeed = (4000-speedcontr);
                 anticlockwise();
                 count--;  
                 }
               } else {
               digitalWrite(operationPin,HIGH);
               }
           } else if (fullClosing == true){
           digitalWrite(operationPin, LOW);    
           digitalWrite(motorPin1,LOW);
           digitalWrite(motorPin2, LOW);
           digitalWrite(motorPin3, LOW);
           digitalWrite(motorPin4, LOW); 
           } else {           
               if (millis() - timeZero > 1000) {
               digitalWrite(operationPin,HIGH);
                 if (count > 100){
                 motorSpeed = 1000;
                 anticlockwise();
                 count--;
                 } else {
                 speedcontr = (5*count);
                 motorSpeed = (1500-speedcontr);
                 anticlockwise();
                 count--;  
                 }
               } else {
               digitalWrite(operationPin,HIGH);
               }
           }           
      } else {
       digitalWrite(operationPin, LOW);    
       digitalWrite(motorPin1,LOW);
       digitalWrite(motorPin2, LOW);
       digitalWrite(motorPin3, LOW);
       digitalWrite(motorPin4, LOW); 
        count = 0;
        fullOpenning =false;
        fullClosing = true;
      }
    }   
 }
void anticlockwise() 
{
  for(int i = 0; i < 8; i++)
  {
    setOutput(i);
    delayMicroseconds(motorSpeed);
  }
}
void clockwise() 
{
  for(int i = 7; i >= 0; i--)
  {
    setOutput(i);
    delayMicroseconds(motorSpeed);
  }
}
void setOutput(int out)
{
  digitalWrite(motorPin1, bitRead(lookup[out], 0));
  digitalWrite(motorPin2, bitRead(lookup[out], 1));
  digitalWrite(motorPin3, bitRead(lookup[out], 2));
  digitalWrite(motorPin4, bitRead(lookup[out], 3));
}