Arduinoプログラミング で ものづくり (Seeeduino XIAO版)
小山智史
私達の身近にある電子機器の多くにコンピュータが内蔵されており、このようなコンピュータは「マイコン」と呼ばれます。マイクロコンピュータあるいはマイクロコントローラの略です。マイコンにはメーカーが開発したプログラムが書き込まれています。リモコンの中に入っているマイコンには、押したボタンに応じて決まったパターンで赤外線をオンオフするプログラムが書き込まれています。このプログラムはマイコン製造時に書き込まれ、後から書き換えることはできませんが、フラッシュメモリ(電気的に書き換え可能なプログラムメモリ)を搭載したマイコンを使えば、私達もさまざまな機器を作ることができます。
マイコンを使った機器を設計・製作するには、電子回路、論理回路、コンピュータの仕組み、プログラミングなど広範囲の知識が要求されます。これは大変なことではありますが、一方ではこれらのことを学ぶ格好の教材であるということを意味しています。
このテキストでは、Seeeduino XIAOを使った実習を行いながら、「マイコンを使ったものづくり」について学習します。例題の多くは、ArduinoIDEに用意されている「スケッチの例」をそのまま、あるいは多少アレンジして取り上げています。
☆ 本テキストは実習時に適宜解説や補足を行うことを前提にしています。自学自習には適していないかもしれません。
0. 準備
0.1 ブレッドボードの使い方
ここでは下図左のブレッドボード(EIC-301)を使います。ボードの内部は下図右のように接続されています。
(外観) | (内部の接続) |
| |
ブレッドボードにXIAOを差し込み、GNDと3V3の端子を図のように接続します。USBケーブルをパソコンに接続すると、電源(3.3V)がパソコンから供給されます。XIAOに見やすいラベルを貼っておくことをお勧めします(テプラファイル)。
(回路図) | (実体配線図) |
|
|
初めてブレッドボードを使う場合は、練習としてLEDと抵抗を下図左のように接続して点灯を確認しましょう。電源はパソコンからUSB端子を通じて供給され、電流の流れは下図右のようになります。ブレッドボードの内部の結線も含めて「電流が流れる経路」を意識することが重要です。
(練習) ブレッドボードで下記の回路を組み立て、スイッチを押すとLEDが点灯することを確認しなさい。
0.2 Arduino開発環境の準備
- Arduino 1.8.19(Windows ZIP file)をダウンロードし、マウスの右クリックで[すべて展開]します(時間がかかります)。
- 「c:/arduino」フォルダを作成し、この中に上で展開したArduino-1.8.19フォルダの中のArduino-1.8.19フォルダを移動します。また、c:/arduinoの中にsrcフォルダを、その中にlibrariesフォルダを作成しておきます。
- c:/arduino/Arduino-1.8.19/arduino.exeのショートカットを作り、デスクトップまたはスタートメニューに置きます。
- Arduinoを起動し、[ファイル][環境設定]で、スケッチブックの保存場所を「c:/arduino/src」にします。また、エディタの文字の大きさを「20」程度にすると見やすいです。
- [ツール][ボード]で「Seeeduino XIAO」を選択し、[ツール][シリアルポート]でポートを選択します。
- 液晶モジュールを利用するために、ST7032のライブラリ(ZIPファイル)をダウンロードし、解凍してc:/arduino/src/librariesフォルダに置きます。この中のST7032.cppの24行目を「//#include <avr/pgmspace.h>」のように「//」でコメントアウトします。
1. コマンド操作
* Arduinoは対話的に利用することはできません。
2. デジタル出力(1): LEDの点滅
2.1 LEDを点灯・消灯する
以下の回路を組み立ててください。
(回路図) | (実体配線図) |
|
|
以下のプログラムは、Arduino IDE の [スケッチの例][01.Basics][Blink] の正味の部分を抜粋したものです。「//」以降行末まではコメントですので、入力しなくても構いません。
Blink.ino: ArduinoIDE [スケッチの例][01.Basics][Blink]
int led=9; // 変数ledに9を代入する
void setup(){ // { }内をはじめに1度だけ実行する
pinMode(led, OUTPUT); // ledピンを出力にする
}
void loop(){ // { }内を繰り返し実行する
digitalWrite(led, HIGH); // ledピンにHIGHを出力する
delay(1000); // 1秒(1000ms)待つ
digitalWrite(led, LOW); // ledピンにLOWを出力する
delay(1000); // 1秒(1000ms)待つ
}
|
Arduinoを起動し、下図のようにプログラムを入力し、c:/arduino/srcフォルダにBlinkという名前で保存してください。そのフォルダにBlinkフォルダが作られ、中にBlink.inoというファイルが作られますので、確認してください。そして、ボタンの操作でマイコンに書き込み、LEDが点滅することを確かめてください。プログラムに誤りがあったり、うまく書き込みができない場合は、エラーメッセージが表示されます。
以下、プログラムの内容について簡単に説明します。
- 1行目で整数型の変数「led」に6を代入します。
- 2~4行目の setup(){ } の中は電源投入後に1度だけ実行され、ここでは「pinMode(led,OUTPUT)」でledピン(6ピン)を出力に設定しています。
- 5~10行目のloop(){ } の中は繰り返し実行されます。
- 6行目の「digitalWrite(led,HIGH)」はledピンにHIGH(高い電圧)を出力します。
- 7行目の「delay(1000)」は1秒(1000ms)待ちます。
- 8行目の「digitalWrite(led,LOW)」はledピンにLOW(低い電圧)を出力します。
- 9行目の「delay(1000)」は1秒(1000ms)待ちます。
- 以下、6~9行目が繰り返されます。
下図のようにマイコン内部のスイッチがプログラムで切り替えられ、LEDが点滅すると考えればわかりやすいと思います。スイッチがHIGHになるとLEDに電流が流れ、LOWになると電流が流れません。
この例のように、Arduinoのプログラムは以下のように作ります。なお、Arduinoではプログラムのことをスケッチと呼びますが、このテキストでは一般的な「プログラム」という言葉を使います。
- プログラム全体を通して使う変数を冒頭に定義します(この例では1行目)。変数はデータ(値)を入れる入れ物のことです。中に入れるデータによって入れ物の大きさは異なります。データの型については付録1を参照してください。
- setup(){ }の中に、電源投入後に1度だけ実行したいプログラムを記載します(この例では2~4行目)。
- loop(){ }の中に、繰り返して実行したいプログラムを記載します(この例では5~10行目)。
- 各行の「//」以降はコメントとなります。また、この例にはありませんが、「/* */」で囲まれた箇所は改行も含めてコメントとなります。
- HIGHやLOWやOUTPUTは「組み込み定数」で、あらかじめ値が定義されています(付録1)。
- pinMode()やdigitalWrite()はあらかじめ用意された「組み込み関数」です(付録1)。関数を独自に定義することもできます。
(練習) LEDを1秒毎に交互に点滅させ、D6ピンの電圧をテスターで測りなさい。
(練習) LEDを0.5秒点灯、0.5秒消灯を繰り返すようにプログラムを変更しなさい。また、0.2秒点灯、1.8秒消灯を繰り返すように変更しなさい。
2.2 LEDを高速に点滅させる
LEDの点灯時間と消灯時間を変更してみます。
7行目と9行目を「delay(1000)」→「delay(100)」→「delay(20)」→「delay(10)」→「delay(1)」と変えて、点滅の様子を確認してください。「delay(10)」の時は10ms点灯、10ms消灯を繰り返し、50Hzの点滅になります。「delay(20)」(25Hz)の時は点滅しているのがわかりますが、「delay(10)」(50Hz)では点滅を感じなくなります。人間の目が感じる「点滅のちらつき」はフリッカと呼ばれ、高速の点滅(50~60Hz)ではちらつきを感じなくなります。
通常の蛍光灯は100Hz(50Hzの2倍)で点滅しますが、蛍光管の状態によっては発光にムラが生じ50Hzで点滅し、その場合はちらつきが気になることがあります。インバーター式の蛍光灯はちらつきを抑えるために超高速(数10kHz)で点滅させています。また、ブラウン管テレビが毎秒30フレーム(実際にはインターレースで60Hzのフリッカ)、映画のフィルムが毎秒24フレーム(実際には同じフレームを2度映すことで48Hzのフリッカ)になっているのは、このような目の特性を考慮してのことです。
(練習) LEDの点滅速度を30Hz, 40Hz, 50Hz, 60Hzと変化させ、ちらつきが感じられるかどうかを、ブレッドボードを静止した場合と、左右に振った場合の両方で調べなさい。
(練習) 緑色LEDと1kΩの抵抗をD7ピンに追加接続し、そのLEDを1秒毎に点滅させなさい。また、赤色LEDと緑色LEDが1秒毎に「交互に」点灯するようにしなさい。
3. デジタル入力: スイッチ操作でLEDのオンオフ
3.1 スイッチが押されているかどうか調べる
下図左のようにD2ピンにスイッチを接続し、プログラム中で「pinMode(0, INPUT_PULLUP)」とすると、下図右のようにマイコンに内蔵されたD2ピンの抵抗が有効になります。すると、D2ピンはスイッチをオンにすると低い電圧(0V)になり、スイッチをオフにすると高い電圧(3.3V)になります。この抵抗を「プルアップ抵抗」といいます。「高い電圧に引っ張り上げる抵抗」というような意味です。
スイッチを接続 |
内蔵プルアップ抵抗 |
D2ピンが高い電圧(1)か低い電圧(0)かは「digitalRead(0)」で読み取ります。
(練習) 上図のようにスイッチを接続し、以下のプログラムを実行しなさい。スイッチを押した時と離した時のそれぞれについて、D2ピンの電圧をテスターで計りなさい。
Button.ino: ArduinoIDE [スケッチの例][02.Digital][Button]
void setup(){
pinMode(2, INPUT_PULLUP);
}
void loop(){}
|
|
3.2 スイッチが押されている時にLEDを点灯する
以下のプログラムは、buttonピンがHIGHならば(スイッチが押されていれば)LEDを点灯し、LOWならばLEDを消灯します。
8行目で、「digitalRead(button)」でbuttonピン(D2ピン)がHIGH(高い電圧)かLOW(低い電圧)かを読み取り、HIGHであれば9行目が実行され、そうでなければ11行目が実行されます。
Button.ino: ArduinoIDE [スケッチの例][02.Digital][Button]
int button=2; // 変数buttonに2を代入する
int led=9; // 変数ledに9を代入する
void setup(){
pinMode(button, INPUT_PULLUP); // buttonピンを入力(プルアップ抵抗有効)にする
pinMode(led, OUTPUT); // ledピンを出力にする
}
void loop(){
if(digitalRead(button)==LOW){ // buttonが押されていれば
digitalWrite(led, HIGH); // ledピンにHIGHを出力する
}else{ // そうでなければ
digitalWrite(led, LOW); // ledピンにLOWを出力する
}
}
|
| |
3.3 スイッチ操作でLEDを点灯・消灯する
以下のプログラムは、スイッチを押すと交互にLEDが点灯・消灯します。考え方は、「スイッチが押された時に(つまりさっき押されていなくて今押されたら)LED表示を反転する」というものです。変数buttonStateには「今のスイッチの状態(LOWまたはHIGH)」、変数lastStateには「さっきのスイッチの状態(LOWまたはHIGH)」、変数ledStateには「今のLEDの点灯状態(LOWまたはHIGH)」を保存します。
11行目は、現在のbuttonピンの状態(HIGHまたはLOW)を読み取り、buttonStateに代入します。12行目で、それがlastState(直前のbuttonの状態)と違っていて、かつそれがHIGHなら、ledState(ledの状態)を反転します。13行目でledStateをledピンに出力します。
Debounce.ino: ArduinoIDE [スケッチの例][02.Digital][Debounce] の簡易版
int button=2; // 変数buttonに2を代入する
int led=9; // 変数ledに9を代入する
int ledState=LOW; // 変数ledStateをLOWにする(現在の点灯状態は消灯)
int buttonState; // 現在のbuttonの状態
int lastState; // 直前のbuttonの状態
void setup(){
pinMode(button, INPUT_PULLUP); // buttonピンを入力(プルアップ抵抗有効)にする
pinMode(led, OUTPUT); // ledピンを出力にする
}
void loop(){ // 以下を繰り返す
buttonState=digitalRead(button); // buttonピンを読みbuttonStateに代入
if(buttonState!=lastState && buttonState==LOW) ledState=!ledState;
// それが前の状態と違いかつLOWならledStateを反転
digitalWrite(led, ledState);// ledピンにledStateの値を出力
lastState=buttonState; // lastStateを更新する
delay(10); // チャタリングを考慮し少し待つ
}
|
| |
15行目の「delay(10)」がないと、誤動作することがあります。それは、スイッチを押した時に、ごく短い時間にその接点がオンになったりオフになったりするからです。スイッチを離した時も同様です。スイッチによって異なりますが、10ms位で落ち着きます。この現象は「チャタリング」と呼ばれます。
(練習) 15行目のdelay(10)を極端に長くし、例えば待ち時間が2秒になるようにして、どのような動作になるか調べなさい。
(練習) 緑色LEDを増設し、スイッチを押すと赤色LEDと緑色LEDが交互に点灯するようにしなさい。
3.4 スイッチを4回押すと点灯する
以下のプログラムは、スイッチ操作4回に一度LEDが点灯します。
上の例ではledStateを記憶し、スイッチが押されるとその値を反転させていましたが、ここではCounterにスイッチが押された回数を記憶します。12行目で、buttonState(現在のbuttonの状態)がlastState(直前のbuttonの状態)と違っていて、かつそれがHIGHなら、Counterの値に1を足しこみます。14行目で、「Counter%4==0」がtrueつまりCounterの値を4で割った余りが0であればledにHIGHを出力し、そうでなければLOWを出力します。
StateChangeDetection.ino: ArduinoIDE [スケッチの例][02.Digital][StateChangeDetection] (シリアル通信に関係する行を除き、loop() の最後に「delay(10)」を挿入)
int button=2; // 変数buttonに2を代入する
int led=9; // 変数ledに9を代入する
int counter=0; // 変数counter(スイッチ操作回数)を0にする
int buttonState; // 現在のbuttonの状態
int lastState=0; // 直前のbuttonの状態
void setup(){
pinMode(button, INPUT_PULLUP); // buttonピンを入力(プルアップ抵抗有効)にする
pinMode(led, OUTPUT); // ledピンを出力にする
}
void loop() { // 以下を繰り返す
buttonState=digitalRead(button); // buttonピンを読みbuttonSteteに代入
if(buttonState!=lastState && buttonState==LOW) counter++;
// それが前の状態と違いかつLOWならcounterの値に+1
lastState=buttonState; // lastStateを更新する
if(counter%4==0) digitalWrite(led, HIGH);
// counterの値が4の倍数ならledピンにHIGHを出力し
else digitalWrite(led, LOW);
// そうでなければledピンにLOWを出力
delay(10); // チャタリングを考慮し少し待つ
}
|
| |
(練習) 14行目の「counter%4==0」の箇所を「counter%7==0」や「counter%4!=0」などに変えて、どのような動作になるか調べなさい。
(練習) 2個の押しボタンスイッチを使い、一方のスイッチを押すとLEDが点灯し、もう一方のスイッチを押すと消灯するようにしなさい。
3.5 スイッチが押されるとLEDを3秒点灯する
以下のプログラムは、スイッチが押されるとLEDを3秒点灯します。
3sec.ino:
int button=2; // 変数buttonに2を代入する
int led=9; // 変数ledに9を代入する
void setup(){
pinMode(button, INPUT_PULLUP);// buttonピンを入力(プルアップ抵抗有効)にする
pinMode(led, OUTPUT); // ledピンを出力にする
}
void loop(){
if(digitalRead(button)==LOW){ // buttonピンがLOWならば
digitalWrite(led, HIGH); // ledピンにHIGHを出力する
delay(3000); // 3秒待つ
}else{ // そうでなければ
digitalWrite(led, LOW); // ledピンにLOWを出力する
}
}
|
| |
4. アナログ出力: LEDの明るさを変える
これまでは、LEDが点灯するか消灯するかでした。しかし、点灯時の明るさを変化させたいこともあります。
4.1 LEDの明るさを変える
1.2ではLEDの点灯時間と消灯時間を変えてどのように見えるかを確認しました。ここでは7行目と9行目を「delay(1)」にしてみます。
Blink.ino: ArduinoIDE [スケッチの例][01.Basics][Blink] (7行目と9行目を1000→1に変更)
int led=9; // 変数ledに9を代入する
void setup(){ // { }内をはじめに1度だけ実行する
pinMode(led, OUTPUT); // ledピンを出力にする
}
void loop(){ // { }内を繰り返し実行する
digitalWrite(led, HIGH); // ledピンにHIGHを出力する
delay(1); // 1ms待つ
digitalWrite(led, LOW); // ledピンにLOWを出力する
delay(1); // 1ms待つ
}
|
| |
既に経験したように、点灯と消灯を高速に切り替えると、私達の目は点滅を認識できなくなります。代わりに、常時点灯している場合よりも少しだけ暗く感じます。「平均的に」半分の明るさになるからです。下図左は、この時のledピンの出力の波形を示したものです。8行目のLOWをHIGHにすれば常時点灯となりますから、LEDの明るさを比較してみてください。私達の目は、明るさが半分になっても、「ちょっと暗くなった」ぐらいにしか感じないことがわかります。
次に、7行目はそのままにして9行目を「delay(9)」としてみてください。こうすることにより、1ms点灯、9ms消灯を繰り返すようになり、明るさはより暗く感じられます。この時の波形が上図中央です。
HIGHとLOWの時間が等しい時「デューティ比50%」、常時HIGHの時「デューティ比100%」、HIGHが1msでLOWが9msなら「デューティ比10%」といいます。この技術は「パルス幅変調(PWM)」と呼ばれ、モータのスピードコントロールや、デジタルオーディオアンプなどに利用されています。
さて、Arduinoでは analogWrite( ) という関数が用意されていて、
とすると、ledピンにアナログ値val(0~255)が出力されます。正確に言えば、0/255~255/255のデューティ比で上記のようなPWM信号が出力されます。アナログ出力できるピンは限られているので注意が必要です(付録1)。
pwm.ino:
int led=9; // 変数ledに9を代入する
void setup(){ // { }内をはじめに1度だけ実行する
pinMode(led, OUTPUT); // ledピンを出力にする
analogWrite(led, 10); // ledピンにアナログ値10を出力する
}
void loop(){ } // { }内は空
|
| |
(練習) 3行目の「10」を0~255の間でいくつか変えて、LEDの明るさを観察しなさい。また、その時の波形をオシロスコープで観察して記録しなさい。
(練習) ボタンを押す度に「消灯→暗い→明るい→消灯→...」の動作をするプログラムを作りなさい。
4.2 LEDの明るさを連続的に変える
デューティ比を連続的に変化させれば、下図のように任意の波形を表すことができます。
以下は、デューティ比を0%から100%まで少しずつ変化させるプログラムです。暗い状態から次第に明るくなり、その後次第に暗くなり、これを繰り返します。プログラムの6行目でbrightの値をアナログ出力し、7行目でbrightの値をfadeだけプラスします。
Fade.ino: ArduinoIDE [スケッチの例][01.Basics][Fade]
int led=9; // 変数ledに9を代入する
int bright=5; // 変数brightに5を代入する
int fade=5; // 変数fadeに5を代入する
void setup(){
pinMode(led, OUTPUT); // ledピンを出力にする
}
void loop(){ // 以下を繰り返す
analogWrite(led, bright); // ledピンにbrightの値をアナログ出力する
bright=bright+fade; // brightの値にfadeの値を足し込む
if(bright==0 || bright==255) // brightの値が0または255だったら
fade=-fade; // fadeの符号を逆にする
delay(30); // 30ms待つ
}
|
| |
(練習) スイッチを押すとLEDがだんだん明るくなり、スイッチを離すだんだん暗くなるプログラムを作りなさい。
5. アナログ入力
5.1 アナログ値の入力
analogRead(A1)は、A1ピンの電圧(0~3.3V)に応じて0~1023の値をとります。以下のプログラムはこれを利用し、ボリュームの値に応じてLEDの点滅時間が変化します。
AnalogInput.ino: ArduinoIDE [スケッチの例][03.Analog][AnalogInput]
int led=9; // 変数ledに9を代入する
void setup(){
pinMode(led, OUTPUT); // ledピンを出力にする
}
void loop(){ // 以下を繰り返す
int val=analogRead(A1); // valにA1ピンのアナログ値(0~1023)を代入する
digitalWrite(led, HIGH); // ledピンにHIGHを出力する
delay(val); // valの値だけ待つ
digitalWrite(led, LOW); // ledピンにLOWを出力する
delay(val); // valの値だけ待つ
}
|
| |
5.2 アナログ値に応じてLEDを点灯・消灯
以下のプログラムは、ボリュームのアナログ値(0~1023)が400未満の時にLEDを点灯します。
AutoLight.ino:
int led=9; // 変数ledに9を代入する
void setup(){
pinMode(led, OUTPUT); // ledピンを出力にする
}
void loop(){ // 以下を繰り返す
int val=analogRead(A1); // valにA1ピンのアナログ値(0~1023)を代入する
if(val<400) digitalWrite(led, HIGH); // valが400未満ならledピンをHIGH
else digitalWrite(led, LOW); // そうでなければLOW
}
|
| |
5.3 アナログ値に応じて明るさを変える(調光)
照明の明るさを調整することを「調光」といいます。
以下のプログラムは、ボリュームのアナログ値(0~1023)に応じてLEDの明るさを連続的に変えます。
AnalogLight.ino:
int led=9; // 変数ledに9を代入する
void setup(){
pinMode(led, OUTPUT); // ledピンを出力にする
}
void loop(){ // 以下を繰り返す
int val=analogRead(A1); // valにA1ピンのアナログ値(0~1023)を代入する
analogWrite(led, val/4); // ledにアナログ値(0~255)を出力
delay(30); // 30ms待つ
}
|
| |
6. デジタル出力(2): 数字表示LED
6.1 7セグメントLEDに数字を表示する
数字表示LED(7セグメントLED)に数字を表示してみます。下図の回路を組み立ててください。
4~10ピンはLEDのa~gのセグメントに接続されていて、それぞれ 1(HIGH)を出力した時に点灯、 0(LOW)を出力した時に消灯する回路となっています。下表は、各数字を表示する際に4~10ピンの各々に何を出力すればよいかと、その16進数値を示したものです。
数字 |
10ピン g |
9ピン f |
8ピン e |
7ピン d |
6ピン c |
5ピン b |
4ピン a |
16進 |
0 |
0 |
1 |
1 |
1 |
1 |
1 |
1 |
0x3F |
1 |
0 |
0 |
0 |
0 |
1 |
1 |
0 |
0x06 |
2 |
1 |
0 |
1 |
1 |
0 |
1 |
1 |
0x5B |
3 |
1 |
0 |
0 |
1 |
1 |
1 |
1 |
0x4F |
4 |
1 |
1 |
0 |
0 |
1 |
1 |
0 |
0x66 |
5 |
1 |
1 |
0 |
1 |
1 |
0 |
1 |
0x6D |
6 |
1 |
1 |
1 |
1 |
1 |
0 |
1 |
0x7D |
7 |
0 |
1 |
0 |
0 |
1 |
1 |
1 |
0x27 |
8 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
0x7F |
9 |
1 |
1 |
0 |
0 |
1 |
1 |
1 |
0x67 |
以下のプログラムは、数字表示LEDに「5」を表示するものです。
Count.ino:
void setup(){
for(int i=0; i<7; i++) pinMode(i+4, OUTPUT); // ピン4~10を出力に
digitalWrite(4, 1); // aを点灯
digitalWrite(5, 1); // bを点灯
digitalWrite(6, 1); // cを点灯
digitalWrite(7, 1); // dを点灯
digitalWrite(8, 1); // eを点灯
digitalWrite(9, 1); // fを点灯
digitalWrite(10, 0); // gを消灯
}
void loop(){ }
|
| |
以下は、上の表をもとにchar型(8bit)の配列を定義し、LED[0]~LED[9]で表示したい数に応じた7セグメントパターンを参照できるようにしたものです。
Count.ino:
char LED[]={ // 配列LEDに値を入れる
0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x27, 0x7f, 0x67
};
void led(val){ // LED[val]のビット0~6を順に調べて1なら点灯0なら消灯
int seg=LED[val]; // 7セグメントパターン
for(int i=0; i<7; i++) digitalWrite(i+4, seg&(1< |
| |
(練習) 表示する数字を他の数字に変更してみなさい。
6.2 数字表示LEDにカウントアップ表示する
以下のプログラムは7セグメントLEDに1秒毎に0→9と表示をカウントアップします。ポイントは配列を参照する9行目のLED[i]です。
Countup.ino:
char LED[]={ // 配列LEDに値を入れる
0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x27, 0x7f, 0x67
};
void led(val){ // LED[val]のビット0~6を順に調べて1なら点灯0なら消灯
int seg=LED[val]; // 7セグメントパターン
for(int i=0; i<7; i++) digitalWrite(i+4, seg&(1< |
| |
(練習) 数字表示が9~0にカウントダウンするプログラム Countdown.ino を作りなさい。
(練習) 1秒毎に「H」「E」「L」「L」「O」を表示し、その後3秒間消え、再びこれを繰り返すプログラム Hello.ino を作りなさい。また、「E」「r」「r」「o」「r」の表示にしてみなさい。また、自分の名前を表示できるか工夫してみなさい。
6.3 スイッチを押すと数字表示をカウントアップする
スイッチを押すと数字表示をカウントアップするには次のようにします。
ButtonCountup.ino:
int button=2; // 変数buttonに2を代入する
int count=0; // 変数countに0を代入する
char LED[]={ // 配列LEDに値を入れる
0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x27, 0x7f, 0x67
};
void led(val){ // LED[val]のビット0~6を順に調べて1なら点灯0なら消灯
int seg=LED[val]; // 7セグメントパターン
for(int i=0; i<7; i++) digitalWrite(i+4, seg&(1< |
| |
(練習) スイッチを追加し、一方のスイッチでカウントアップ、もう一方のスイッチでカウントダウンするプログラム Countupdown.ino を作りなさい。
(練習) Countup.ino を、1秒毎ではなく10ms毎に高速でカウントアップするようにしなさい。また、スイッチが押されるとカウントを停止するようにしなさい。
6.4 数字の2桁表示
数字表示LEDに2桁以上の数字を表示するにはどうしたらいいでしょうか。LED毎にa~gをつなげばいいのですが(スタティック点灯)、Arduinoのピンが不足してしまいます。このような場合はダイナミック点灯と呼ばれる方法が用いられます。この方法は、身近にあるさまざまな表示装置に使われています。
2個の数字表示LEDのa~gに表示したい数字のパターンを出力し、D9ピンを「0(低い電圧)」にすると「10の桁」に、D10ピンを0にすると「1の桁」に表示されます。
2digit.ino:
int digit10=2; // 変数digit10に2を代入する
int digit1=3; // 変数digit1に3を代入する
char LED[]={ // 配列LEDに値を入れる
0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x27, 0x7f, 0x67
};
void setup(){
for(int i=2; i<=10; i++) pinMode(i, OUTPUT); // ピン2~10を出力に
}
void led(val){ // LED[val]のビット0~6を順に調べて1なら点灯0なら消灯
int seg=LED[val]; // 7セグメントパターン
for(int i=0; i<7; i++) digitalWrite(i+4, seg&(1< |
| |
ここで、2箇所の「delay(1000)」を「delay(100)」→「delay(20)」→「delay(10)」→「delay(1)」と変えてみてください。1.2の時と同様にフリッカが私たちの目にはわからなくなります。
次に、0~99の数値を表示するにはどうしたらいいでしょうか。今、以下のように表示させたい数「23」を変数Tに入れておくことにします。10の位は「T/10」、1の位は「T%10(Tを10で割った余り)」で求め、出力しています。他は前と同じです。
int T=23; // 表示させたい数
int digit10=2; // 変数digit10に2を代入する
int digit1=3; // 変数digit1に3を代入する
char LED[]={ // 配列LEDに値を入れる
0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x27, 0x7f, 0x67
};
void setup(){
for(int i=2; i<=10; i++) pinMode(i, OUTPUT); // ピン2~10を出力に
}
void loop(){
digitalWrite(digit10, 1); // 10の桁を消灯
digitalWrite(digit1, 1); // 1の桁を消灯
PORTD=LED[T/10]; // 10の位を出力(まだ表示されない)
digitalWrite(digit10, 0); // 10の桁に表示
delay(1); // 1ms待つ
digitalWrite(digit10, 1); // 10の桁を消灯
PORTD=LED[T%10]; // 1の位を出力(まだ表示されない)
digitalWrite(digit1, 0); // 1の桁に表示
delay(1); // 1ms待つ
digitalWrite(digit1, 1); // 1の桁を消灯
}
|
| |
6.5 アナログ値を数字で表示する
以下のプログラムは、ボリュームのアナログ値(0~1023)に応じて、0~9の数字を表示させます。
AnalogDigital.ino
char LED[]={ // 配列LEDに値を入れる
0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x27, 0x7f, 0x67
};
void led(val){ // LED[val]のビット0~6を順に調べて1なら点灯0なら消灯
int seg=LED[val]; // 7セグメントパターン
for(int i=0; i<7; i++) digitalWrite(i+4, seg&(1< |
| |
(練習) ボリュームのアナログ値(0~1023)に応じて、LEDの明るさを変えなさい。
6.6 アナログ値を数字2桁で表示
以下のプログラムは、ボリュームのアナログ値(0~1023)に応じて、0~99の数字2桁を表示させます。
AnalogDigital2.ino
int digit10=2; // 変数digit10に2を代入する
int digit1=3; // 変数digit1に3を代入する
char LED[]={ // 配列LEDに値を入れる
0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x27, 0x7f, 0x67
};
void led(val){ // LED[val]のビット0~6を順に調べて1なら点灯0なら消灯
int seg=LED[val]; // 7セグメントパターン
for(int i=0; i<7; i++) digitalWrite(i+4, seg&(1< |
| |
7. デジタル出力(3): 液晶ディスプレイ
7.1 液晶ディスプレイに文字や数字を表示
7.2 液晶ディスプレイにカウントアップ表示
7.3 アナログ値を液晶ディスプレイに表示
8. センサーの利用
8.1 スイッチをセンサーとして使う
押しボタンスイッチやスライドスイッチは、操作用のスイッチですが、センサーとしても活躍しています。例えば、CD/DVDデッキやコピー機やプリンタなど動きを伴う機器には、所定の位置に来たがどうかを検知するために、マイクロスイッチが必ずといっていいほど使われています。このような使われ方をするスイッチを「リミットスイッチ」と呼ぶこともあります。
13のモーターカーでは車の先端にマイクロスイッチを使いました。ふたつの金属片を接触させるだけでスイッチ(センサー)となるので、オリジナルのスイッチを作ることも難しくはありません。所定の水位になったかどうかを検知するピンポン球を使った浮力スイッチ、傾けると金属ボールが移動する傾斜スイッチなど、用途に応じて工夫するのも楽しいでしょう。
8.2 明るさセンサー(CDS)
5.1のボリュームの代わりに明るさセンサー(CDS)をつないでみましょう。明るくなるほどCDSの抵抗値が低くなり、A1ピンの電圧は高くなります。一定以下に暗くなるとLEDが点灯します。プログラムは5.1と同様です。ただし、7行目の「400」の値は適切な値に変更する必要があります。
AutoLight.ino:
int led=9; // 変数ledに9を代入する
void setup(){
pinMode(led, OUTPUT); // ledピンを出力にする
}
void loop(){ // 以下を繰り返す
int val=analogRead(A1); // valにA1ピンのアナログ値(0~1023)を代入する
if(val<400) digitalWrite(led, HIGH); // valが400未満ならledピンをHIGH
else digitalWrite(led, LOW); // そうでなければLOW
}
|
| |
境界値付近ではledが点灯したり消灯したり不安定になるので、これを避けるためには、以下のように「ヒステリシス特性」を持たせます。8行目はledが消灯(ledStateがOFF)している時に400以下の明るさになったらledを点灯(ledStateをON)し、9行目はledが点灯(ledStateがON)している時に500以上の明るさになったらledを消灯(ledStateをOFF)します。
Hysteresis.ino:
int led=9; // 変数ledに9を代入する
int ledState=LOW; // 変数ledStateにLOWを代入する
void setup(){
pinMode(led, OUTPUT); // ledピンを出力にする
}
void loop(){ // 以下を繰り返す
int val=analogRead(A1); // valにA1ピンのアナログ値(0~1023)を代入する
if(ledState==LOW && val<400) ledState=HIGH;
else if(ledState==HIGH && val>=500) ledState=LOW;
if(ledState==LOW) digitalWrite(led, LOW); // ledStateがLOWならledピンをLOW
else digitalWrite(led, HIGH);// そうでなければHIGH
}
|
| |
(練習) 8行目の400と9行目の500の値を変えて、適切な値をみつけなさい。
8.3 温度センサー(サーミスタ)
CDSの代わりに温度センサー(サーミスタ)をつないでみましょう。温度が高くなるほどサーミスタの抵抗値が低くなり、A1ピンの電圧は高くなります。温度がある値以上になるとLEDが点灯します。プログラムは上と同様です。ただし、7行目の「400」の値は適切な値に変更する必要があります。
AutoLight.ino:
int led=9; // 変数ledに9を代入する
void setup(){
pinMode(led, OUTPUT); // ledピンを出力にする
}
void loop(){ // 以下を繰り返す
int val=analogRead(A1); // valにA1ピンのアナログ値(0~1023)を代入する
if(val<400) digitalWrite(led, HIGH); // valが400未満ならledピンをHIGH
else digitalWrite(led, LOW); // そうでなければLOW
}
|
| |
(練習) ヒステリシス特性を持たせた Hysteresis.ino でその動作を確かめなさい。また、400と500の値を変えて、適切な値をみつけなさい。
8.4 明るさを計測する(数字表示)
以下のプログラムは、明るさを0~9の数字で表示させます。プログラムは5.5と同様です。
AnalogDigital.ino
char LED[]={ // 配列LEDに値を入れる
0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x27, 0x7f, 0x67
};
void setup(){
DDRD=0x7f; // PD0~PD6を出力にする
}
void loop(){
int val=analogRead(A0); // valにA0ピンのアナログ値(0~1023)を代入
val=val/103; // valの値を103で割る(0~9の値になる)
PORTD=LED[val]; // val(0~9)に応じたPD0~PD6をPORTDに出力
}
|
| |
8.5 温度の計測(数字表示)
以下のプログラムは、温度を0~9の数字で表示させます。プログラムは5.5と同様です。ただし、A0ピンの電圧は温度が変化してもあまり大きく変化しません。このような場合は、9行目を以下のように変更し、220~280の値valを0~9に変換します。
val=val/30;
↓
val=map(val, 220,280, 0,9);
|
AnalogDigital.ino
char LED[]={ // 配列LEDに値を入れる
0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x27, 0x7f, 0x67
};
void setup(){
DDRD=0x7f; // PD0~PD6を出力にする
}
void loop(){
int val=analogRead(A0); // valにA0ピンのアナログ値(0~1023)を代入
val=val/30; // 0~9の値になるようにvalの値を30で割る
PORTD=LED[val]; // val(0~9)に応じたPD0~PD6をPORTDに出力
}
|
| |
8.6 音を検知する: 音量センサー
以下のプログラムは、音を検知したらLEDを3秒点灯するものです。
3sec.ino:
int button=2; // 変数buttonに2を代入する
int led=9; // 変数ledに9を代入する
void setup(){
pinMode(button, INPUT_PULLUP);// buttonピンを入力(プルアップ抵抗有効)にする
pinMode(led, OUTPUT); // ledピンを出力にする
}
void loop(){
if(digitalRead(button)==LOW){ // buttonピンがLOWならば
digitalWrite(led, HIGH); // ledピンにHIGHを出力する
delay(3000); // 3秒待つ
}else{ // そうでなければ
digitalWrite(led, LOW); // ledピンにLOWを出力する
}
}
|
| |
(練習) 4.2を参考に、手をたたくとカウントアップするプログラムSoundCountup.inoを作りなさい。
8.7 赤外線反射センサー
赤外線の反射光で数mm程度の距離に反射物を検知するセンサーです。9.7のモーターカーと組み合わせると、ライントレースや衝突回避などが可能になります。以下のプログラムは、数mm程度の距離に反射物を検知したらLEDを点灯するものです。ただし、7行目の「250」の値は適切な値に変更する必要があります。
AutoLight.ino:
int led=9; // 変数ledに6を代入する
void setup(){
pinMode(led, OUTPUT); // ledピンを出力にする
}
void loop(){ // 以下を繰り返す
int val=analogRead(A1); // valにA1ピンのアナログ値(0~1023)を代入する
if(val<250) digitalWrite(led, HIGH); // valが250未満ならledピンをHIGH
else digitalWrite(led, LOW); // そうでなければLOW
}
|
| |
9. ブザー音で演奏
ここではブザー音で電子オルゴールを作ってみましょう。和音など音にこだわる方は「私だけの電子オルゴール」[4]を参考にしてください。
9.1 音を出す
Blink.inoで、LEDと同じ箇所に圧電ブザー(Bzz)を接続し、「1秒ごとにLEDを点滅させるプログラム」Blink.inoを実行してください。するとブザーからカチカチと音が聞こえます。ここで、点灯時間と消灯時間を以下のように変えてみましょう。変更箇所は7行目と9行目です。
Blink.ino: ArduinoIDE [スケッチの例][01.Basics][Blink] (7行目と9行目を1000→1に変更)
int led=9; // 変数ledに9を代入する
void setup(){ // { }内をはじめに1度だけ実行する
pinMode(led, OUTPUT); // ledピンを出力にする
}
void loop(){ // { }内を繰り返し実行する
digitalWrite(led, HIGH); // ledピンにHIGHを出力する
delay(1); // 1ms待つ
digitalWrite(led, LOW); // ledピンにLOWを出力する
delay(1); // 1ms待つ
}
|
| |
この時の波形は下図のように500Hzの矩形波となり、聞こえるのは500Hzの音です(といっても倍音成分が含まれていますが)。
(練習) オシロスコープでトーン信号の波形を観測しなさい。また、WaveGeneなどの信号発生ソフトを用い、パソコンで500Hzの矩形波を出し、音を比べてみなさい。マイコンの内蔵クロックはそれほど正確ではありませんので多少の音程の違いがあるかもしれません。
9.2 音程を変える
Arduinoでは、指定した周波数の音を、指定した時間、出力するtone()という関数が利用できます。以下は時報の音を出すプログラムです。
Tone.ino:
int bzz=9; // 変数bzzに9を代入する
void setup(){
tone(bzz, 784, 200); // bzzピンに784Hzの音を200ms出力する(ポッ)
delay(600); // 600ms待つ
tone(bzz, 784, 200); // bzzピンに784Hzの音を200ms出力する(ポッ)
delay(600); // 600ms待つ
tone(bzz, 784, 200); // bzzピンに784Hzの音を200ms出力する(ポッ)
delay(600); // 600ms待つ
tone(bzz, 1046, 1200); // bzzピンに1046Hzの音を1200ms出力する(ポーン)
delay(1200); // 1200ms待つ
noTone(bzz); // toneを終了
}
void loop(){ }
|
| |
(練習) 440Hzのラの音(A4)に対し1オクターブ高いラ(A5)の音は880Hzで、その間は隣同士(半音)の周波数の比が一定(21/12)となるように音階が作られます。表計算ソフトを用い、A4~A6の各音の周波数を計算しなさい。上のプログラム中の784Hzが「ソ」、1046Hzが「ド」であることが確認できます。
(練習) 上のプログラムを参考に、簡単な曲を演奏させるプログラムを作りなさい。
(練習) 時計のアラーム音、信号機の音、風呂や電気ポットの湯沸かし音など、身近にある電子音を録音し、その音をプログラムで再現しなさい。
9.3 楽譜を演奏する
以下のプログラムは、配列として記述した楽譜情報を参照しながら演奏します。NOTE_C4などの周波数値はpitches.hファイルで定義されています。
toneMelody.ino: ArduinoIDE [スケッチの例][02.Digital][toneMelody] (bzzPin 8→13に変更)
#include "pitches.h" // pitches.hファイルを読み込む
int bzz=9; // 変数bzzに9を代入する
int melody[]={ // 楽譜データ(音階)
NOTE_C4, NOTE_G3,NOTE_G3, NOTE_A3, NOTE_G3,0, NOTE_B3, NOTE_C4
};
int noteDurations[]={ 4,8,8,4,4,4,4,4 }; // 楽譜データ(音符)
void setup(){
for(int thisNote=0; thisNote<8; thisNote++){
int noteDuration = 1000/noteDurations[thisNote];
tone(bzz, melody[thisNote],noteDuration);
int pauseBetweenNotes = noteDuration*1.30;
delay(pauseBetweenNotes);
noTone(bzz); // toneを終了
}
}
void loop(){ }
|
| |
楽譜はさまざまな方法で表現することができます。以下は、少し長い曲を演奏するプログラムです。
playNote.ino:
const PROGMEM int16_t notes[]={ // 楽譜データ
#define C 262
#define D 294
#define E 330
#define F 349
#define G 392
#define A 440
#define B 494
#define c 523
#define d 587
#define e 659
#define f 698
#define g 784
#define R 1 // Rest
R,D,D,B,A,G,D,R, R,D,D,B,A,G,E,R, R,E,E,c,B,A,F,R, R,d,d,d,c,A,B,R,
R,D,D,B,A,G,D,R, R,D,D,B,A,G,E,R, R,E,E,c,B,A,d,d, d,d,e,d,c,A,G,R, d,R,
B,B,B,R, B,B,B,R, B,d,G,A,B,R,R,R, c,c,c,c,c,B,B,B, B,A,A,B,A,R, d,R,
B,B,B,R, B,B,B,R, B,d,G,A,B,R,R,R, c,c,c,c,c,B,B,B, d,d,c,A,G,R, g,R,0
};
int Tempo=300; // 変数Tempoに300を代入する
int bzz=9; // 変数bzzに9を代入する
void setup(){
pinMode(bzz, OUTPUT); // bzzピンを出力にする
}
void loop(){ // 以下を繰り返す
for(int i=0;;i++){ // 楽譜データを順に
int note=pgm_read_word(¬es[i]); // 変数noteにひとつ読み
if(note==0) break; // noteが0なら1曲演奏終了
else if(note!=R) tone(bzz, note, Tempo); // noteがR(休符)でもなければ
// bzzピンにnote[Hz]の周波数の音をTempo[ms]出力する
delay(Tempo*1.1); // Tempoだけ待つ
}
delay(Tempo*8); // しばらくしてから演奏を繰り返す
}
|
| |
(練習) メロディー(notes[]の部分)を変え、あるいは音階を拡張し、好きな曲の電子オルゴールを作りなさい。
9.4 楽器を作る
以下のプログラムは、ボタンを押すと、ボリュームで決まる周波数のブザー音がでます。
Instrument.ino:
int bzz=9; // 変数bzzに9を代入する
int button=2; // 変数buttonに2を代入する
void setup(){
pinMode(bzz,OUTPUT); // bzzピンを出力にする
pinMode(button, INPUT_PULLUP); // buttonピンをプルアップ入力にする
}
void loop(){ // 以下を繰り返す
if(digitalRead(button)==LOW){ // buttonがLOWなら
int val=analogRead(A1);// valにA1ピンのアナログ値(0~1023)を代入する
tone(bzz,val,200); // valの周波数を200ms
delay(200); // 200ms待つ
}
}
|
| |
上のプログラムで一応楽器として使えるのですが、ドレミ...の音を丁度よく出すのは難しいと思います。そこで、0~1023のアナログ値をmap関数を使って0~11の値に変換し、notes[ ]配列の周波数を参照するようにしたのが次のプログラムです。
Instrument.ino:
int bzz=9; // 変数bzzに9を代入する
int button=2; // 変数buttonに2を代入する
// ド レ ミ ファ ソ ラ シ ド レ ミ ファ ソ
int notes[]={262, 294, 330, 349, 392, 440, 494, 523, 587, 659, 698, 784};
void setup(){
pinMode(bzz,OUTPUT); // bzzピンを出力にする
pinMode(button, INPUT_PULLUP); // buttonピンをプルアップ入力にする
}
void loop(){ // 以下を繰り返す
if(digitalRead(button)==LOW){ // buttonがLOWなら
int val=analogRead(A1); // valにA1ピンのアナログ値(0~1023)を代入する
val=map(val, 0,1023, 0,11); // valを0~11の値に変換する
int note=notes[val]; // noteにvalに対応する周波数を代入する
tone(bzz,note,200); // noteを200ms
delay(200); // 200ms待つ
}
}
|
| |
(練習) ボリュームの代わりに明るさセンサーで音程が変わるようにしなさい。また、必要ならば音階を拡張しなさい。
9.5 「合いの手」楽器
音量センサーと楽譜の演奏を組み合わせ、「合いの手」楽器を作ってみます。
MicTone.ino:
#define SOuL 207
#define RAL 220
#define RAuL 233
#define SIL 247
#define DO 262
#define DOu 277
#define RE 294
#define REu 311
#define MI 330
#define FA 349
#define FAu 370
#define SO 392
#define SOu 415
#define RA 440
#define RAu 466
#define SI 494
#define DOH 523
#define DOuH 554
#define REH 587
#define REuH 622
#define MIH 659
#define FAH 698
#define FAuH 740
#define SOH 784
#define R 0 // Rest
#define Tempo 100
#define L1 32*Tempo
#define L2_ 24*Tempo
#define L2 16*Tempo
#define L4_ 12*Tempo
#define L4 8*Tempo
#define L8_ 6*Tempo
#define L8 4*Tempo
#define L16 2*Tempo
#define L32 1*Tempo
int mic=1;
int bzz=9;
int b(int note, int tempo){
if(note>0){
tone(bzz, note, tempo);
delay(tempo*1.1);
return 0;
}else{
unsigned long lastmillis=millis();
int s=0;
while(millis() |
| |
(練習) 違うメロディーやリズムにしてみなさい。
10. 合成音声でおしゃべり
10.1 合成音声でおしゃべり
音声合成LSI(ATP3011F4)を使ってスピーカから合成音声を出します。
日本語音声をしゃべってくれますが、文字列はローマ字で書く必要があります。また、アクセントその他さまざまな約束事があります。
Talk.ino:
void setup(){
Serial.begin(9600); // 9600bpsで通信を開始
while(1){
Serial.write('?'); // '?'を送信する
delay(300); // 300ms待つ
if(Serial.available()>0&&Serial.read()=='>')break;
} // '>'がきたらwhile終了
}
void loop(){ // 以下を繰り返す
Serial.println("ju'nbi/o'-kei");//「準備OK!」
delay(2000); // 2秒待つ
}
|
| |
(練習) 10行目を変えて、違う言葉をしゃべらせてみなさい。
10.2 「九九」の読み上げ
以下の例は、「九九」を合成音声で読み上げるものです[音を聞く]。
10 for A=1 TO 9
20 for B=1 TO 9
30 talk A,"kakeru",B,"wa",A*B
40 next B
50 next A
|
| |
以下は、外部スイッチが押されると「九九」をランダムに読み上げるプログラムです。
10 A=1
20 B=dRead(2)
30 if A=1 if B=0 gosub 100
40 A=B
50 goto 20
100 X=1+rnd(9): Y=1+rnd(9)
110 talk X,"kakeru",Y,"wa",X*Y
120 return
|
| |
(練習) 利用者に適したスイッチを使い、合成音声の読み上げで支援するさまざまなツール(VOCA)を作ることができます。2個のスイッチを接続し、それぞれ「はい」「いいえ」の合成音声が出るようなVOCAを作りなさい。
11. イルミネーション
11.1 プログラムによる点滅とフラッシュ
以下のプログラムは、ひとつ目のLEDを赤で点滅させます。1秒点灯・1秒消灯をくりかえします。右下の図は点滅の時間的な変化をタイムチャートで示したものです。
pixelblink1.ino:
#include
#define PIN 10
#define N 10
Adafruit_NeoPixel pixel=Adafruit_NeoPixel(N, PIN, NEO_GRB+NEO_KHZ800);
void setup(){ pixel.begin(); pixel.show();}
void loop(){
pixel.setPixelColor(0, 50,0,0); // LED 0 を赤に
pixel.show();
delay(1000);
pixel.setPixelColor(0, 0,0,0); // LED 0 を消灯
pixel.show();
delay(1000);
}
|
|
|
pixel.setPixelColor(0,50,0,0);
→ pixel.setPixelColor(0,0,50,0);
とすると緑で点滅します。
次のようにすると10個のLEDが点滅します。
pixelblink10.ino:
#include
#define PIN 10
#define N 10
Adafruit_NeoPixel pixel=Adafruit_NeoPixel(N, PIN, NEO_GRB+NEO_KHZ800);
void setup(){ pixel.begin(); pixel.show();}
void loop(){
for(int i=0;i |
|
|
前半のdelayを
delay 200
と短くすると短時間点灯する「フラッシュ」になります。
(練習) 10個のLEDが1秒毎に「青→黄→赤」を繰り返すプログラムを作りなさい。
11.2 ワイプ
以下のプログラムは、10個のLEDを順に点灯させます。このようなイルミネーション効果は「ワイプ」と呼ばれます。
pixelwipe.ino:
#include
#define PIN 10
#define N 10
Adafruit_NeoPixel pixel=Adafruit_NeoPixel(N, PIN, NEO_GRB+NEO_KHZ800);
void setup(){ pixel.begin(); pixel.show();}
void loop(){
for(int i=0;i |
|
|
pixel.setPixelColor(i,50,0,0);
とすると順に消灯します。
(練習) 「赤のワイプ→緑のワイプ→青のワイプ」を繰り返すプログラムを作りなさい。
11.3 スイープ
以下のプログラムは、10個のLEDを順に点灯・消灯させます。
pixelsweep.ino:
#include
#define PIN 10
#define N 10
Adafruit_NeoPixel pixel=Adafruit_NeoPixel(N, PIN, NEO_GRB+NEO_KHZ800);
void setup(){ pixel.begin(); pixel.show();}
void loop(){
for(int i=0;i |
|
|
(練習) 30行目のdelay を100msにし、また、繰り返しスイープするようにしなさい。
11.4 ウェーブ
以下のプログラムは、2個おきの点灯パターンがスイープし、波(ウェーブ)のように(あるいは追いかけているように)見えます。500行目はサブルーチンになっていて、与えられたR,G,Bの値でウェーブ表示します。10行目は白色、20行目は赤、30行目は緑、40行目は青のそれぞれウェーブで、これを繰り返します。
pixelwave.ino:
#include
#define PIN 10
#define N 10
Adafruit_NeoPixel pixel=Adafruit_NeoPixel(N, PIN, NEO_GRB+NEO_KHZ800);
void setup(){ pixel.begin(); pixel.show();}
void PixelWave(int r, int g, int b){
for(int j=20;j>0;j--){
for(int i=0;i |
|
|
(練習) 520行目を変更し、「1個おき」「3個おき」のウェーブにしてみなさい。
11.5 フェードイン・フェードアウト
以下のプログラムは、ひとつ目のLEDをだんだん明るくし(赤を0→50)、だんだん暗くし(赤を50→0)、これを繰り返します。
pixelfade.ino
#include
#define PIN 10
#define N 10
Adafruit_NeoPixel pixel=Adafruit_NeoPixel(N, PIN, NEO_GRB+NEO_KHZ800);
void setup(){ pixel.begin(); pixel.show();}
void loop(){
for(int r=0;r<=50;r++){
pixel.setPixelColor(0, r,0,0);
pixel.show();
delay(10);
}
for(int r=50;r>=0;r--){
pixel.setPixelColor(0, r,0,0);
pixel.show();
delay(10);
}
}
|
|
|
(練習) 色を変えてみなさい。また速さを変えてみなさい。
(練習) 10個のLEDを同時にフェードイン・フェードアウトさせなさい。
11.6 クロスフェード
ひとつ目のLEDをフェードアウトしながら、ふたつ目のLEDをフェードインすると滑らかに変化させることができます。これを「クロスフェード」といいます。
以下は、クロスフェードをスイープに応用した例で、LEDの点灯位置が滑らかに変化します。
pixelxsweep.ino:
#include
#define PIN 10
#define N 10
Adafruit_NeoPixel pixel=Adafruit_NeoPixel(N, PIN, NEO_GRB+NEO_KHZ800);
void setup(){ pixel.begin(); pixel.show();}
void loop(){
for(int i=0;i |
|
|
11.7 フルカラー表示
クロスフェードを用いると色を滑らかに変化させることができます。以下のプログラムは、ひとつ目のLEDの色が赤→緑に滑らかに変化します。
pixelxrg.ino:
#include
#define PIN 10
#define N 10
Adafruit_NeoPixel pixel=Adafruit_NeoPixel(N, PIN, NEO_GRB+NEO_KHZ800);
void setup(){ pixel.begin(); pixel.show();}
void loop(){
for(int r=50;r>=0;r--){
pixel.setPixelColor(0, r,50-r,0);
pixel.show();
delay(50);
}
}
|
|
|
RGBの値と表示色の関係は下図のようになっています。
以下のプログラムは、すべてのLEDの色をフルカラーで連続的に変化させます(明るさ最大85)。
pixelxrgb.ino:
#include
#define PIN 10
#define N 10
Adafruit_NeoPixel pixel=Adafruit_NeoPixel(N, PIN, NEO_GRB+NEO_KHZ800);
void setup(){ pixel.begin(); pixel.show();}
void loop(){
int R,G,B;
for(int W=0;W<256;W++){
if(W<85){ B=0; G=W; R=85-G; }
else if(W<170){ R=0; B=W-85; G=85-B; }
else{ G=0; R=W-170; B=85-R;}
for(int i=0;i |
|
|
以下のプログラムは、すべてのLEDの色を場所を変えながらフルカラーで連続的に変化させます(明るさ最大85)。500行目はサブルーチンになっていて、0~255の色相Wを与えると、R,G,Bの変数に値(各々0~85)がセットされます。30行目でWに値をセットしてから500行目を呼び出しています。
pixelrgb.ino:
#include
#define PIN 10
#define N 10
Adafruit_NeoPixel pixel=Adafruit_NeoPixel(N, PIN, NEO_GRB+NEO_KHZ800);
void setup(){ pixel.begin(); pixel.show();}
void loop(){
for(int j=0;j<256;j+=8){
for(int i=0;i |
|
|
11.8 イルミネーションの例
以下は、「ドロップ」のイルミネーションの作例です。水滴が滴り落ちるイメージで、最後にキラッと光ります。
pixeldrop.ino:
#include
#define PIN 10
#define N 10
Adafruit_NeoPixel pixel=Adafruit_NeoPixel(N, PIN, NEO_GRB+NEO_KHZ800);
void setup(){
pixel.begin(); pixel.show();
randomSeed(analogRead(0));
}
void loop(){
int u, n=random(6,N+1);
for(int i=0;i=0;b--){ // 明るさbを0までだんだん暗くする
pixel.setPixelColor(n-1, b,b,b);
pixel.show();
delay(3);
}
delay(random(2000)); // 次のドロップまで0~2秒待つ
}
| |
以下は、NeoPixelリング(12個)を使い、ランダムに点灯させるようにした例です。オブジェの作り方はngo-tecさんのページを参考にしました。点灯位置と点灯色と点灯時間間隔をランダムにし、点灯後はだんだん暗くしています。
pixelfire.ino:
#include
#define PIN 10
#define N 12
Adafruit_NeoPixel pixel=Adafruit_NeoPixel(N, PIN, NEO_GRB+NEO_KHZ800);
unsigned int hue[N], u[N], t=1; // hueは色, uは明るさ, t*50ms後にfire
void setup(){
pixel.begin();
for(int i=0;i |
|
(練習) オリジナルのイルミネーションを考え、作ってみなさい。
12. 赤外線リモコン
テレビなどのリモコンは、ボタンを押すと赤外線LEDが「決められたタイミングで点滅」するように作られています。どのように点滅しているかは、リモコン受光器に向けてリモコンのボタンを押し、受光器の信号をオシロスコープで観測するとわかります。
実際に観測してみると、各社テレビのリモコン信号は以下のようになっています。図の着色した部分が信号が「オン」の箇所で、「オン」とは「赤外線LEDを13μs点灯し、13μs消灯し、これを一定時間繰り返す」ことです。また、「オフ」とは「一定時間消灯する」ことです。これを参考に、赤外線LEDを点滅させるプログラムを作ればよいわけです。
電源 ON/OFF | 40 bf 12 ed |
チャンネルUP | 40 bf 1b e4 |
チャンネルDOWN | 40 bf 1f e0 |
ボリュームUP | 40 bf 1a e5 |
ボリュームDOWN | 40 bf 1e e1 |
東芝製テレビのリモコン信号
電源 ON/OFF | 02 20 80 00 3d bd |
チャンネルUP | 02 20 80 00 34 b4 |
チャンネルDOWN | 02 20 80 00 35 b5 |
ボリュームUP | 02 20 80 00 20 a0 |
ボリュームDOWN | 02 20 80 00 21 a1 |
パナソニック製テレビのリモコン信号
電源 ON/OFF | 8f 12 16 d1 |
チャンネルUP | 8f 12 11 a1 |
チャンネルDOWN | 8f 12 12 91 |
ボリュームUP | 8f 12 14 f1 |
ボリュームDOWN | 8f 12 15 e1 |
シャープ製テレビのリモコン信号
電源 ON/OFF | 95 0 |
チャンネルUP | 90 0 |
チャンネルDOWN | 91 0 |
ボリュームUP | 92 0 |
ボリュームDOWN | 93 0 |
SONY製テレビのリモコン信号
以下の回路とプログラムは、Upボタンを押すと各社リモコンの「チャンネルアップ」信号が次々出力され、Downボタンを押すと各社リモコンの「チャンネルダウン」信号が次々出力されます。実際には各社の信号を出す必要はありません。
Remocon.ino:
int led=9, UpButton=2, DownButton=4;
void f38kHz(int n){ // 26μs × n周期
volatile char i;
int j;
for(j=0;j |
|
|
13. 動くものを作る
これまでの例では、マイコンの出力ピンにLEDや圧電ブザーを接続しました。この程度であれば直接接続できます。この章では、モータなど大きな電流が流れる素子を駆動する例を紹介します。大きな電流が流れる回路を制御する場合は、「電流が流れる経路」を意識することが回路の誤動作を防ぐことにつながります。
13.1 モーターカー(この項は作成中です)
DCモータを2個使ったモーターカーです。ボタン操作で前進・後退・左ターン・右ターンします。
Motorcar.ino:
void setup(){
}
void stop(){ // 停止
}
void fwd(){ // 前進
}
void bwd(){ // 後退
}
void right(){ // 右旋回
}
void left(){ // 左旋回
}
void loop(){
}
|
| |
以下は、赤外線反射センサーが「崖」を検知すると、少し後退し、右回転して、再び前進します。
Motorcar.ino:
void setup(){
DDRB=0xff; // PORTBを出力に
delay(1000);
}
void stop(){ // 停止
}
void fwd(){ // 前進
stop();
}
void bwd(){ // 後退
stop();
}
void right(){ // 右旋回
stop();
delay(200);
stop();
}
void left(){ // 左旋回
stop();
delay(200);
stop();
}
void loop(){
fwd(); // 前進し
while(digitalRead(8)==LOW);// 崖なら
bwd(); delay(300); // 少し後退し
right(); // 右旋回
}
|
|
|
13.2 サーボモータを動かす
ラジコン用のサーボモーターは、右図のようなPWM波形で角度(0~180°)を制御するように作られています。
以下の例は、サーボモーターが車のワイパーのように動きます。
| |
Sweep.ino: ArduinoIDE [スケッチの例][Servo][Sweep.ino]
#include // Servo.hを読み込む
Servo myservo;
int pos=0; // 変数posに0を代入する(角度)
void setup(){
myservo.attach(9); // 9ピンをサーボモータに接続
}
void loop(){ // 以下を繰り返す
for(pos=0; pos<180; pos+=1){ // posを0~179まで1度ずつ変える
myservo.write(pos); // サーボモータをposの位置にする
delay(15); // 少し待つ
}
for(pos=180; pos>=1; pos-=1){// posを180~1まで1度ずつ変える
myservo.write(pos); // サーボモータをposの位置にする
delay(15); // 少し待つ
}
}
|
| |
以下のプログラムは、ボリュームの値によってサーボモーターの位置(角度)が変わります。
Knob.ino ArduinoIDE [スケッチの例][Servo][Knob.ino]
#include // Servo.hを読み込む
Servo myservo;
void setup(){
myservo.attach(9); // 9ピンにサーボモータを接続
}
void loop(){ // 以下を繰り返す
int val=analogRead(A1); // valにA1ピンのアナログ値(0~1023)を代入する
val=val*179/1023; // valの値を0~179にする
myservo.write(val); // サーボモータをvalの位置にする
delay(15); // 少し待つ
}
|
| |
(練習) ボリュームの代わりに明るさセンサーを使い、明るさで位置が変わるようにしなさい。
14. 押しボタン信号機
押しボタン信号機の制御プログラムです。
Signal.ino:
int buttonPin=2; // 2ピンに押しボタンを接続
int RPin=9, YPin=8, GPin=7; // 9, 8, 7ピンに車 赤・黄・緑のLEDを接続
int RRPin=6, GGPin=4; // 6, 4ピンに歩行者 赤・緑のLEDを接続
int lastState; // 直前のbuttonの状態
void carsig(int r, int y, int g){ // 車 赤・黄・緑の点灯・消灯
digitalWrite(RPin, r); // RPinピンにrを出力
digitalWrite(YPin, y); // YPinピンにyを出力
digitalWrite(GPin, g); // GPinピンにgを出力
}
void mansig(int r, int g){ // 歩行者 赤・緑の点灯・消灯
digitalWrite(RRPin, r); // RRPinピンにrを出力
digitalWrite(GGPin, g); // GGPinピンにgを出力
}
void setup(){
pinMode(RPin, OUTPUT); pinMode(YPin, OUTPUT); pinMode(GPin, OUTPUT); // 車 赤・黄・緑LEDのピンを出力に
pinMode(RRPin,OUTPUT); pinMode(GGPin,OUTPUT); // 歩行者 赤・緑LEDのピンを出力に
pinMode(buttonPin,INPUT_PULLUP); // 押しボタンスイッチをプルアップ入力に
carsig(LOW,LOW,HIGH); mansig(HIGH,LOW); // 初期状態は 車青・歩行者赤
}
void loop(){
int button=digitalRead(buttonPin); // 押しボタンスイッチの状態を読み取り
if(button!=lastState && button==LOW){ // 押されたら
delay(4000); // 4秒待って
carsig(LOW,HIGH,LOW); delay(4000); // 車黄 4秒
carsig(HIGH,LOW,LOW); mansig(LOW,HIGH); // 車赤・歩行者青 16秒
delay(16000);
for(int i=0; i<8; i++){ // 歩行者青 点滅 8秒
mansig(LOW,HIGH); delay(500);
mansig(LOW,LOW); delay(500);
}
mansig(HIGH,LOW); delay(4000); // 歩行者赤 4秒
carsig(LOW,LOW,HIGH); // 車青
}
lastState=button; // buttonを記憶
}
|
| |
以下は歩行者用の「カッコー」の音を付け加えた例です。
SignalKakko.ino:
int buttonPin=2; // 2ピンに押しボタンを接続
int RPin=9, YPin=8, GPin=7; // 9, 8, 7ピンに車 赤・黄・緑のLEDを接続
int RRPin=6, GGPin=4; // 6, 4ピンに歩行者 赤・緑のLEDを接続
int bzzPin=5; // 5ピンにブザーを接続
int lastState=0; // 直前のbuttonの状態
int Tempo=200, e=659, c=523;
void carsig(int r, int y, int g){ // 車 赤・黄・緑の点灯・消灯
digitalWrite(RPin, r); // RPinピンにrを出力
digitalWrite(YPin, y); // YPinピンにyを出力
digitalWrite(GPin, g); // GPinピンにgを出力
}
void mansig(int r, int g){ // 歩行者 赤・緑の点灯・消灯
digitalWrite(RRPin, r); // RRPinピンにrを出力する
digitalWrite(GGPin, g); // GGPinピンにgを出力する
}
void setup(){
pinMode(RPin, OUTPUT); pinMode(YPin, OUTPUT); pinMode(GPin, OUTPUT); // 車 赤・黄・緑LEDのピンを出力に
pinMode(RRPin,OUTPUT); pinMode(GGPin,OUTPUT); // 歩行者 赤・緑LEDのピンを出力に
pinMode(buttonPin,INPUT_PULLUP); // 押しボタンスイッチをプルアップ入力に
carsig(LOW,LOW,HIGH); mansig(HIGH,LOW); // はじめは 車緑・歩行者赤
}
void loop(){
int button=digitalRead(buttonPin); // 押しボタンスイッチの状態を読み取り
if(button!=lastState && button==LOW){ // 押されたら
delay(4000); // 4秒待って
delay(4000);
carsig(LOW,HIGH,LOW); delay(4000); // 車黄 4秒
carsig(HIGH,LOW,LOW); mansig(LOW,HIGH); // 車赤・歩行者緑
for(int i=0; i<5; i++){ // カッコー16秒
tone(bzzPin, e, Tempo/2); delay(Tempo*2.5);
tone(bzzPin, c, Tempo); delay(Tempo*5.5);
tone(bzzPin, e, Tempo/2); delay(Tempo);
tone(bzzPin, e, Tempo/2); delay(Tempo);
tone(bzzPin, c, Tempo); delay(Tempo*6);
}
for(int i=0; i<8; i++){ // 歩行者 緑点滅 8秒
mansig(LOW,HIGH); delay(500);
mansig(LOW,LOW); delay(500);
}
mansig(HIGH,LOW); delay(4000); // 歩行者 赤4秒
carsig(LOW,LOW,HIGH); // 車青
}
lastState=button; // buttonを記憶
}
|
| |
15. 応用例
15.1 I2Cデバイスを使う
最近は、SDAとSCLの2本の信号線(GndとV+を加えれば4本)で接続するさまざまなI2Cデバイスが入手できます。例えば液晶ディスプレイやモータードライバーや温湿度センサーや加速度センサーなどがあります。
ここでは、液晶ディスプレイに表示する温湿度計を紹介します。この例ではI2Cのプルアップ抵抗はSHT31に内蔵されています。
Thermometer.ino:
#include
#include
#define HDC 0x40
ST7032 lcd;
void setup(){
lcd.begin(16, 2); // 16文字×2行の液晶
lcd.setContrast(45); // 0-63...10(5V), 30(3.3V), 45(3V)
}
void loop(){
Wire.beginTransmission(HDC);
Wire.write(0x00);
Wire.endTransmission();
delay(20);
Wire.requestFrom(HDC, 4);
uint16_t t=Wire.read(); t=(t<<8)|Wire.read(); // 温度を取得
uint16_t h=Wire.read(); h=(h<<8)|Wire.read(); // 湿度を取得
t=(((t>>8)*165)>>8)-40; // 温度の値を変換
h=(((h>>8)*100)>>8); // 湿度の値を変換
lcd.clear(); // 表示クリヤー
lcd.setCursor(0,0); // 位置を指定し
lcd.print(t); lcd.print("\337C"); // 温度を表示
lcd.setCursor(8,0); // 位置を指定し
lcd.print(h); lcd.print("%RH"); // 湿度を表示
delay(1000); // 1秒待つ
}
|
|
|
(参考資料)
[2] Arduino, http://www.arduino.cc
[3] AVRマイコンで学ぶコンピュータの仕組み, https://koyama.verse.jp/elecraft/avr/avrtext.html
[4] 私だけの電子オルゴール, https://koyama.verse.jp/elecraft/mymelo/
(付録1) 入出力モジュール
ブレッドボードで利用できる入出力モジュールの製作例です。
モジュール | 回路図 | 製作例 |
可変抵抗モジュール | | |
明るさセンサーモジュール | | |
10連LEDモジュール | NeoPixel | |
赤外線反射モジュール | | |
音量センサーモジュール | | |
モーターモジュール | | |
(付録2) 使用する主な部品
(付録3) Arduinoのプログラム(スケッチ)の仕様の抜粋
(1) XIAOで使用できるピン
ピン | 変数名 | 備考 |
デジタル入出力 | アナログ出力(PWM) | アナログ入力 |
0 | D0 | DAC | A0 | |
1 | D1 | A1 | |
2 | D2 | A2 | |
3 | D3 | A3 | |
4 | D4 | A4 | I2C SDA |
5 | D5 | A5 | I2C SCL |
6 | D6 | A6 | TX |
7 | D7 | A7 | RX |
8 | D8 | A8 | SCK |
9 | D9 | A9 | MISO |
10 | D10 | A10 | MOSI |
|
設定値または取得値 | 0 / 1 | 0 - 255 | 0 - 1023 | |
(2) データの型
型 | サイズ | 値 |
char int8_t | 1バイト | -128~127 |
unsigned char uint8_t byte | 1バイト | 0~255 |
int int16_t | 2バイト | -32768~32767 |
unsigned int uint16_t | 2バイト | 0~65535 |
long int32_t | 4バイト | -2147483648~2147483647 |
unsigned long uint32_t | 4バイト | 0~4294967295 |
float | 4バイト | 3.4028235E+38~-3.4028235E+38 |
(3) 組み込み定数
定数名 | 内容 |
HIGH, LOW | デジタル出力するまたはデジタル入力されたピンの状態 |
OUTPUT, INPUT, INPUT_PULLUP | pinMode()で設定するデジタルピンの入出力 |
A0~A10 | アナログ入出力pin |
DAC0 | アナログ出力(DAC)pin |
(4) 組み込み関数
関数名 | 内容 |
pinMode(pin, OUTPUT) | pinをOUTPUTにする(OUTPUT, INPUT, INPUT_PULLUP) |
digitalRead(pin) | pinの値を読んでHIGHまたはLOWの値を返す |
digitalWrite(pin, HIGH) | pinにHIGHを出力する(値はHIGHまたはLOW) |
analogRead(A0) | A0ピンのアナログ値を読み、0~1023の値を返す |
analogWrite(pin, value) | pin(7,11,12,13のいずれか)にvalue(0~255)を出力する |
map(val, fromL,fromH, toL,toH) | fromL~fromHの値val(整数)をtoL~toHの値に変換する |
delay(t) | t[ms]待つ |
delayMicroseconds(t) | t[μs]待つ |
tone(pin, freq, t) | pinにfreq[Hz]の矩形波信号をt[ms]出力する |
noTone() | tone()を終了する |
Serial.begin(speed) | speed[bps]でシリアル通信を開始する |
Serial.available() | 受信した文字数を返す |
Serial.read() | 受信した1文字を返す |
Serial.write(c) | cを送信する |
Serial.print(s) | 文字列sを送信する |
Serial.println(s) | 文字列sと改行を送信する |
koyama88@cameo.plala.or.jp