このページは作成途中です。
Arduinoプログラミング で ものづくり (ESP8266版)
小山智史
私達の身近にある電子機器の多くにコンピュータが内蔵されており、このようなコンピュータは「マイコン」と呼ばれます。マイクロコンピュータあるいはマイクロコントローラの略です。マイコンにはメーカーが開発したプログラムが書き込まれています。リモコンの中に入っているマイコンには、押したボタンに応じて決まったパターンで赤外線をオンオフするプログラムが書き込まれています。このプログラムはマイコン製造時に書き込まれ、後から書き換えることはできませんが、フラッシュメモリ(電気的に書き換え可能なプログラムメモリ)を搭載したマイコンを使えば、私達もさまざまな機器を作ることができます。
マイコンを使った機器を設計・製作するには、電子回路、論理回路、コンピュータの仕組み、プログラミングなど広範囲の知識が要求されます。これは大変なことではありますが、一方ではこれらのことを学ぶ格好の教材であるということを意味しています。
このテキストでは、ESP8266を使った実習を行いながら、「マイコンを使ったものづくり」について学習します。例題の多くは、ArduinoIDEに用意されている「スケッチの例」をそのまま、あるいは多少アレンジして取り上げています。
☆ コンピュータの仕組みを学びたい方は「AVRマイコンで学ぶコンピュータの仕組み」[3]を参考にしてください。
☆ ここでは複雑な電子回路は登場していませんが、電気回路の最低限の知識は前提としています。必要な場合はこちらの資料[5]を参考にしてください。
☆ 本テキストは実習時に適宜解説や補足を行うことを前提にしています。自学自習には適していないかもしれません。
0. 準備
0.1 ブレッドボードの使い方
ここでは下図左のブレッドボード(EIC-301)を使います。ボードの内部は下図右のように接続されています。
(外観) | (内部の接続) |
| |
ブレッドボードにESP8266を差し込み、GNDと3V3の端子を図のように接続します。USBケーブルをパソコンに接続すると、電源(3.3V)がパソコンから供給されます。ESP8266に見やすいラベルを貼っておくことをお勧めします(テプラファイル)。
(回路図) | (実体配線図) |
|
|
初めてブレッドボードを使う場合は、練習としてLEDと抵抗を下図左のように接続して点灯を確認しましょう。電源はパソコンからUSB端子を通じて供給され、電流の流れは下図右のようになります。ブレッドボードの内部の結線も含めて「電流が流れる経路」を意識することが重要です。
(練習) ブレッドボードで下記の回路を組み立て、スイッチを押すとLEDが点灯することを確認しなさい。
0.1 Arduino開発環境の準備
- Arduino 1.6.12(Windows ZIP file)をダウンロードし、マウスの右クリックで[すべて展開]します(時間がかかります)。
- 「c:/arduino」フォルダを作成し、この中に上で展開したArduino-1.6.12フォルダの中のArduino-1.6.12フォルダを移動します。また、c:/arduinoの中にsrcフォルダを、その中にlibrariesフォルダを作成しておきます。
- c:/arduino/Arduino-1.6.12/arduino.exeのショートカットを作り、デスクトップまたはスタートメニューに置きます。
- Arduinoを起動し、[ファイル][環境設定]で、スケッチブックの保存場所を「c:/arduino/src」にします。また、エディタの文字の大きさを「20」程度にすると見やすいです。
- [ファイル][環境設定]で、「追加のボードマネージャのURL」に「http://arduino.esp8266.com/stable/package_esp8266com_index.json」と入力し、OKを押します。[ツール][ボード][ボードマネージャ]で「esp8266」を探し、インストールします。
- [スケッチ][ライブラリをインクルード][ライブラリの管理]で、一覧の中から「ESP8266」「esp8266_mdns」「ESP8266HTTP Client」「ESP8266 WiFi」「Servo(esp8266)」をインストールします。
- 液晶モジュールを利用するために、ST7032のライブラリ(ZIPファイル)をダウンロードし、解凍してc:/arduino/src/librariesフォルダに置きます。この中のST7032.cppの24行目を「//#include <avr/pgmspace.h>」のように「//」でコメントアウトします。
- WebSocket機能を利用するために、arduinoWebSocketsライブラリ(ZIPファイル)をダウンロードし、解凍してc:/arduino/src/librariesフォルダに置きます。
- [ツール][ボード]で「Generic ESP8266 Module」を選択し、[ツール][Flash Mode]を「QIO」、[ツール][Flash Size]を「4M(3M SPIFFS)」、[ツール][Reset Method]を「nodemcu」にします。また、[ツール][シリアルポート]でポートを選択します。
1. コマンド操作
* Arduinoは対話的に利用することはできません。
2. デジタル出力(1): LEDの点滅
2.1 LEDを点灯・消灯する
以下の回路を組み立ててください。
(回路図) | (実体配線図) |
|
|
以下のプログラムは、Arduino IDE の [スケッチの例][01.Basics][Blink] の正味の部分を抜粋したものです。「//」以降行末まではコメントですので、入力しなくても構いません。
Blink.ino: ArduinoIDE [スケッチの例][01.Basics][Blink]
int led=13; // 変数ledに13を代入する
void setup(){ // { }内をはじめに1度だけ実行する
pinMode(led, OUTPUT); // ledピン(13ピン)を出力にする
}
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が点滅することを確かめてください。プログラムに誤りがあったり、うまく書き込みができない場合は、エラーメッセージが表示されます。
ArduinoIDEを利用する場合の表示画面
|
以下、プログラムの内容について簡単に説明します。
- 1行目で整数型の変数「led」に13を代入します。
- 2~4行目の setup(){ } の中は電源投入後に1度だけ実行され、ここでは「pinMode(led,OUTPUT)」でledピン(13ピン)を出力に設定しています。
- 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秒毎に交互に点滅させ、D13ピンの電圧をテスターで測りなさい。
(練習) 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Ωの抵抗をD12ピンに追加接続し、そのLEDを1秒毎に点滅させなさい。また、赤色LEDと緑色LEDが1秒毎に交互に点灯するようにしなさい。
3. デジタル入力: スイッチ操作できるものを作る(LEDのオンオフ)
3.1 スイッチが押されているかどうか調べる
下図左のようにD14ピンにスイッチを接続し、プログラム中で「pinMode(14, INPUT_PULLUP)」とすると、下図右のようにマイコンに内蔵されたD14ピンの抵抗が有効になります。すると、D14ピンはスイッチをオンにすると低い電圧(0V)になり、スイッチをオフにすると高い電圧(3.3V)になります。この抵抗を「プルアップ抵抗」といいます。「高い電圧に引っ張り上げる抵抗」というような意味です。
スイッチを接続 |
内蔵プルアップ抵抗 |
D14ピンが高い電圧(1)か低い電圧(0)かは「digitalRead(14)」で読み取ります。
(練習) 上図のようにスイッチを接続し、以下のプログラムを実行しなさい。スイッチを押した時と離した時のそれぞれについて、D14ピンの電圧をテスターで計りなさい。
Button.ino: ArduinoIDE [スケッチの例][02.Digital][Button]
void setup(){
pinMode(0, INPUT_PULLUP);
}
void loop(){}
|
|
3.2 スイッチが押されている時にLEDを点灯する
スイッチと抵抗を追加し、下図の回路にします。
以下のプログラムは、buttonピンがHIGHならば(スイッチが押されていれば)LEDを点灯し、LOWならばLEDを消灯します。
8行目で、「digitalRead(button)」でbuttonピン(14ピン)がHIGH(高い電圧)かLOW(低い電圧)かを読み取り、HIGHであれば9行目が実行され、そうでなければ11行目が実行されます。
Button.ino: ArduinoIDE [スケッチの例][02.Digital][Button]
int button=14; // 変数buttonに14を代入する
int led=13; // 変数ledに13を代入する
void setup(){
pinMode(button, INPUT); // buttonピンを入力にする(この行はなくても良い)
pinMode(led, OUTPUT); // ledピン(13ピン)を出力にする
}
void loop(){
if(digitalRead(button)==HIGH){//buttonピンがHIGHならば
digitalWrite(led, HIGH); // ledピンにHIGHを出力する
}else{ // そうでなければ
digitalWrite(led, LOW); // ledピンにLOWを出力する
}
}
|
| |
3.3 スイッチ操作で交互に点灯・消灯する
以下のプログラムは、スイッチを押すと交互に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=14; // 変数buttonに14を代入する
int led=13; // 変数ledに13を代入する
int ledState=HIGH; // 変数ledStateをHIGHにする(現在の点灯状態)
int buttonState; // 現在のbuttonの状態
int lastState; // 直前のbuttonの状態
void setup(){
pinMode(button, INPUT); // buttonピンを入力にする
pinMode(led, OUTPUT); // ledピンを出力にする
}
void loop(){ // 以下を繰り返す
buttonState=digitalRead(button); // buttonピンを読みbuttonStateに代入
if(buttonState!=lastState && buttonState==HIGH) ledState=!ledState;
// それが前の状態と違いかつHIGHなら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=14; // 変数buttonに14を代入する
int led=13; // 変数ledに13を代入する
int Counter=0; // 変数Counter(スイッチ操作回数)を0にする
int buttonState; // 現在のbuttonの状態
int lastState=0; // 直前のbuttonの状態
void setup(){
pinMode(button, INPUT); // buttonピンを入力にする
pinMode(led, OUTPUT); // ledピンを出力にする
}
void loop() { // 以下を繰り返す
buttonState=digitalRead(button); // buttonピンを読みbuttonSteteに代入
if(buttonState!=lastState && buttonState==HIGH) Counter++;
// それが前の状態と違いかつHIGHなら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=14; // 変数buttonに14を代入する
int led=13; // 変数ledに13を代入する
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=13; // 変数ledに13を代入する
void setup(){ // { }内をはじめに1度だけ実行する
pinMode(led, OUTPUT); // ledピン(13ピン)を出力にする
}
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=13; // 変数ledに13を代入する
void setup(){ // { }内をはじめに1度だけ実行する
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=13; // 変数ledに13を代入する
int bright=5; // 変数brightに5を代入する
int fade=5; // 変数fadeに5を代入する
void setup(){ }
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(A0)は、A0ピンの電圧(0~3V)に応じて0~1023の値をとります。以下のプログラムはこれを利用し、ボリュームの値に応じてLEDの点滅時間が変化します。
AnalogInput.ino: ArduinoIDE [スケッチの例][03.Analog][AnalogInput]
int led=13; // 変数ledに13を代入する
void setup(){
pinMode(led, OUTPUT); // ledピン(13ピン)を出力にする
}
void loop(){ // 以下を繰り返す
int val=analogRead(A0); // valにA0ピンのアナログ値(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=13; // 変数ledに13を代入する
void setup(){
pinMode(led, OUTPUT); // ledピン(13ピン)を出力にする
}
void loop(){ // 以下を繰り返す
int val=analogRead(A0); // valにA0ピンのアナログ値(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=13; // 変数ledに13を代入する
void setup(){
pinMode(led, OUTPUT); // ledピン(13ピン)を出力にする
}
void loop(){ // 以下を繰り返す
int val=analogRead(A0); // valにA0ピンのアナログ値(0~1023)を代入する
analogWrite(led, val/4); // ledにアナログ値(0~255)を出力
}
|
| |
6. デジタル出力(2): 数字表示LED
7. デジタル出力(3): 液晶ディスプレイ
7.1 液晶ディスプレイに文字や数字を表示
7.2 液晶ディスプレイにカウントアップ表示
7.3 アナログ値を液晶ディスプレイに表示
8. センサーの利用
8.1 スイッチをセンサーとして使う
押しボタンスイッチやスライドスイッチは、操作用のスイッチですが、センサーとしても活躍しています。例えば、CD/DVDデッキやコピー機やプリンタなど動きを伴う機器には、所定の位置に来たがどうかを検知するために、マイクロスイッチが必ずといっていいほど使われています。このような使われ方をするスイッチを「リミットスイッチ」と呼ぶこともあります。
9.7のモーターカーでは車の先端にマイクロスイッチを使いました。ふたつの金属片を接触させるだけでスイッチ(センサー)となるので、オリジナルのスイッチを作ることも難しくはありません。所定の水位になったかどうかを検知するピンポン球を使った浮力スイッチ、傾けると金属ボールが移動する傾斜スイッチなど、用途に応じて工夫するのも楽しいでしょう。
8.2 明るさセンサー(CDS)を使う
5.1のボリュームの代わりに明るさセンサー(CDS)をつないでみましょう。明るくなるほどCDSの抵抗値が低くなり、A0ピンの電圧は高くなります。一定以下に暗くなるとLEDが点灯します。プログラムは5.1と同様です。ただし、7行目の「400」の値は適切な値に変更する必要があります。
AutoLight.ino:
int led=13; // 変数ledに13を代入する
void setup(){
pinMode(led, OUTPUT); // ledピン(13ピン)を出力にする
}
void loop(){ // 以下を繰り返す
int val=analogRead(A0); // valにA0ピンのアナログ値(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=13; // 変数ledに13を代入する
int ledState=LOW; // 変数ledStateにLOWを代入する
void setup(){
pinMode(led, OUTPUT); // ledピン(13ピン)を出力にする
}
void loop(){ // 以下を繰り返す
int val=analogRead(A0); // valにA0ピンのアナログ値(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の代わりに温度センサー(サーミスタ)をつないでみましょう。温度が高くなるほどサーミスタの抵抗値が低くなり、A0ピンの電圧は高くなります。温度がある値以上になるとLEDが点灯します。プログラムは上と同様です。ただし、7行目の「400」の値は適切な値に変更する必要があります。
AutoLight.ino:
int led=13; // 変数ledに13を代入する
void setup(){
pinMode(led, OUTPUT); // ledピン(13ピン)を出力にする
}
void loop(){ // 以下を繰り返す
int val=analogRead(A0); // valにA0ピンのアナログ値(0~1023)を代入する
if(val<400) digitalWrite(led, HIGH); // valが400未満ならledピンをHIGH
else digitalWrite(led, LOW); // そうでなければLOW
}
|
| |
(練習) ヒステリシス特性を持たせた Hysteresis.ino でその動作を確かめなさい。また、400と500の値を変えて、適切な値をみつけなさい。
8.4 音を検知する: 音量センサー
以下のプログラムは、音を検知したらLEDを3秒点灯するものです。
3sec.ino:
int button=14; // 変数buttonに14を代入する
int led=13; // 変数ledに13を代入する
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を出力する
}
}
|
| |
8.5 赤外線反射センサーを使う
赤外線の反射光で数mm程度の距離に反射物を検知するセンサーです。9.7のモーターカーと組み合わせると、ライントレースや衝突回避などが可能になります。以下のプログラムは、数mm程度の距離に反射物を検知したらLEDを点灯するものです。ただし、7行目の「250」の値は適切な値に変更する必要があります。
AutoLight.ino:
int led=13; // 変数ledに13を代入する
void setup(){
pinMode(led, OUTPUT); // ledピン(13ピン)を出力にする
}
void loop(){ // 以下を繰り返す
int val=analogRead(A0); // valにA0ピンのアナログ値(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=13; // 変数ledに13を代入する
void setup(){ // { }内をはじめに1度だけ実行する
pinMode(led, OUTPUT); // ledピン(13ピン)を出力にする
}
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=13; // 変数bzzに13を代入する
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=13; // 変数bzzに13を代入する
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=13; // 変数bzzに13を代入する
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=13; // 変数bzzに13を代入する
int button=14; // 変数buttonに14を代入する
void setup(){
pinMode(bzz,OUTPUT); // bzzピンを出力にする
pinMode(button, INPUT_PULLUP); // buttonピンをプルアップ入力にする
}
void loop(){ // 以下を繰り返す
if(digitalRead(button)==LOW){ // buttonがLOWなら
int val=analogRead(A0);// valにA0ピンのアナログ値(0~1023)を代入する
tone(bzz,val,200); // valの周波数を200ms
delay(200); // 200ms待つ
}
}
|
| |
上のプログラムで一応楽器として使えるのですが、ドレミ...の音を丁度よく出すのは難しいと思います。そこで、0~1023のアナログ値をmap関数を使って0~11の値に変換し、notes[ ]配列の周波数を参照するようにしたのが次のプログラムです。
Instrument.ino:
int bzz=13; // 変数bzzに13を代入する
int button=14; // 変数buttonに14を代入する
// ド レ ミ ファ ソ ラ シ ド レ ミ ファ ソ
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(A0); // valにA0ピンのアナログ値(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=2;
int bzz=13;
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(1)
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. イルミネーション
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=13, UpButton=1, DownButton=5;
void f38kHz(int n){ // 26μs × n周期
volatile char i;
int j;
for(j=0;j |
| |
13. 動くものを作る
これまでの例では、マイコンの出力ピンにLEDや圧電ブザーを接続しました。AVRマイコンは最大20mAの電流を流すことができるので、この範囲であれば直接接続できます。この章では、モータなど大きな電流が流れる素子を駆動する例を紹介します。大きな電流が流れる回路を制御する場合は、「電流が流れる経路」を意識することが回路の誤動作を防ぐことにつながります。
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(16); // 16ピンをサーボモータに接続する
}
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(16); // 16ピンにサーボモータを接続する
}
void loop(){ // 以下を繰り返す
int val=analogRead(A0); // valにA0ピンのアナログ値(0~1023)を代入する
val=val*179/1023; // valの値を0~179にする
myservo.write(val); // サーボモータをvalの位置にする
delay(15); // 少し待つ
}
|
| |
(練習) ボリュームの代わりに明るさセンサーを使い、明るさで位置が変わるようにしなさい。
14. 押しボタン信号機
押しボタン信号機の制御プログラムです。
Signal.ino:
int buttonPin=14;
int RPin=16, YPin=15, GPin=14, RRPin=10, GGPin=9, buttonMemo;
void carsig(int r, int y, int g){
digitalWrite(RPin,r); digitalWrite(YPin,y); digitalWrite(GPin,g);
}
void mansig(int r,int g){digitalWrite(RRPin,r); digitalWrite(GGPin,g);}
void setup(){
pinMode(RPin, OUTPUT); pinMode(YPin, OUTPUT); pinMode(GPin, OUTPUT);
pinMode(RRPin,OUTPUT); pinMode(GGPin,OUTPUT);
pinMode(buttonPin,INPUT_PULLUP);
carsig(LOW,LOW,HIGH); mansig(HIGH,LOW); // 車青・歩赤
}
void loop(){
int button=digitalRead(buttonPin);
if(button!=buttonMemo && button==LOW){
delay(4000);
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); // 車青
}
buttonMemo=button;
}
|
| |
以下は歩行者用の「カッコー」の音を付け加えた例です。
SignalKakko.ino:
int bzz=11; // 変数bzzに11を代入する
int button=14; // 変数buttonに14を代入する
int RPin=16; // 変数RPinに16を代入する
int YPin=15; // 変数YPinに15を代入する
int GPin=14; // 変数GPinに14を代入する
int RRPin=10; // 変数RRPinに10を代入する
int GGPin=9; // 変数GGPinに9を代入する
int lastState=0; // 直前の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(bzz, OUTPUT);
pinMode(RPin, OUTPUT);
pinMode(YPin, OUTPUT);
pinMode(GPin, OUTPUT);
pinMode(RRPin,OUTPUT);
pinMode(GGPin,OUTPUT);
pinMode(button,INPUT_PULLUP);
carsig(LOW, LOW, HIGH);
mansig(HIGH,LOW); // 車青・歩赤
}
void loop(){
int i, Tempo=200, button=digitalRead(buttonPin), e=659, c=523;
if(button!=buttonMemo && button==LOW){
delay(4000);
carsig(LOW,HIGH,LOW); delay(4000); // 車黄4秒
carsig(HIGH,LOW,LOW); mansig(LOW,HIGH); // 車赤・歩青
for(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(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); // 車青
}
buttonMemo=button;
}
|
| |
15. WiFiの利用
15.1 NTP時計
NTPサーバに同期し、正確な時計を作ります(詳しくはこちら)。
ntpClock.ino:
#include
#include
#include
#include
#include
#define PIN 13
#define N 92
Adafruit_NeoPixel led=Adafruit_NeoPixel(N, PIN, NEO_GRB + NEO_KHZ800);
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "ntp.nict.jp", 32400, 86400000); // UTC+9H, every 24H
static const char ssid[]="...", password[]="...";
uint8_t lastmm=99;
uint8_t R=0, G=20, B=0;
char LED[]={ // 配列LEDに値を入れる
0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x27, 0x7f, 0x67
};
void numled(uint8_t d, uint8_t n){ // d: 0-3, n: 0-9
if(d==0) for(uint8_t i=0;i<8;i++)if(n==1) led.setPixelColor(i,R,G,B); // b,c
else led.setPixelColor(i,0,0,0);
else{
uint8_t i=8+(d-1)*28;
for(uint8_t mask=0x01;mask<0x80;mask<<=1) for(uint8_t j=0;j<4;j++)
if(LED[n]&mask) led.setPixelColor(i++,R,G,B);
else led.setPixelColor(i++,0,0,0);
}
led.show();
}
void setup(){
Serial.begin(115200);
WiFi.begin(ssid, password);
delay(10);
while(WiFi.status()!=WL_CONNECTED){ delay(500); Serial.print("."); }
Serial.print("\nConnected to "); Serial.println(ssid);
Serial.print("IP address: "); Serial.println(WiFi.localIP());
led.begin();
led.show();
}
void loop(){
if(timeClient.update()) setTime(timeClient.getEpochTime());
Serial.println((String)year()+"/"+(month()<10?"0":"")+month()+"/"+(day()<10?"0":"")+day()+" "
+(hour()<10?"0":"")+hour()+":"+(minute()<10?"0":"")+minute()+":"+(second()<10?"0":"")+second());
uint16_t hh=hour(), mm=minute();
if(hh>=12) hh-=12;
if(lastmm!=mm){
numled(0,hh/10); numled(1,hh%10); numled(2,mm/10); numled(3,mm%10);
lastmm=mm;
}
delay(1000);
}
|
|
15.2 Webページにアクセスする
15.3 Webサーバになる
- 既存のWiFiアクセスポイントに接続(ESP8266はSTATION)
- mDNSで「esp8266.local」でESP8266にアクセス可能。ただし、WindowsではBonjourをインストールする必要あり。
- ESP8266がWebサーバ(http://esp8266.local)となり、PCやスマホのブラウザからアクセス可能
- Webページはindex.hに分離。
WebServer.ino:
#include
#include
#include
#include "index.h"
static const char ssid[]="...", password[]="...";
MDNSResponder mdns;
ESP8266WebServer server=ESP8266WebServer(80);
static void writeLED(int onoff){ LEDState=onoff; digitalWrite(LEDpin, onoff);}
void handleRoot(){ server.send(200, "text/html", INDEX_HTML);}
void setup(){
Serial.begin(115200); delay(10);
WiFi.begin(ssid, password);
Serial.println(""); while(WiFi.status()!=WL_CONNECTED){ delay(500); Serial.print("."); }
Serial.println(""); Serial.print("Connected to "); Serial.println(ssid);
IPAddress ip=WiFi.localIP();
Serial.print("IP address: "); Serial.println(ip);
if(mdns.begin("esp8266", ip)){
Serial.println("MDNS responder started");
mdns.addService("http", "tcp", 80);
}else Serial.println("MDNS.begin failed");
Serial.println("Connect to http://esp8266.local or http://"+WiFi.localIP());
server.on("/", handleRoot);
server.begin();
}
void loop(){
server.handleClient();
}
|
index.h:
static const char PROGMEM INDEX_HTML[] = R"rawliteral(
WiFi制御
2016.6.13
WiFi制御
●
koyama@hirosaki-u.ac.jp
)rawliteral";
|
| |
15.4 WebSocketを使った双方向通信
- ESP8266がWebSocketサーバとなり、スイッチとWebページ上のボタン、ボリュームとWebページ上のスライダ、LEDとWebページ上の●表示、LEDの明るさとWebページ上のバーグラフがそれぞれ同期してコントロールできる。
- 複数のPC/スマホで同期表示。
WebSocketIO.ino:
#include
#include
#include
#include
#include
#include "index.h"
int LEDpin=13, SWpin=14, Aval=0, LEDState, SWState;
static const char ssid[]="...", password[]="...";
MDNSResponder mdns;
ESP8266WebServer server=ESP8266WebServer(80);
WebSocketsServer ws =WebSocketsServer(81);
static void writeLED(int onoff){ LEDState=onoff; digitalWrite(LEDpin, onoff);}
void wsEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length){
Serial.printf("webSocketEvent(%d, %d, ...)\r\n", num, type);
switch(type){
case WStype_DISCONNECTED: Serial.printf("[%u] Disconnected!\r\n", num); break;
case WStype_CONNECTED:{
IPAddress ip=ws.remoteIP(num);
Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\r\n", num, ip[0],ip[1],ip[2],ip[3], payload);
if(LEDState) ws.sendTXT(num, "ledon", 5); // Send the current LED status
else ws.sendTXT(num, "ledoff", 6);
}
break;
case WStype_TEXT:
Serial.printf("[%u] get Text: %s\r\n", num, payload);
if(strcmp("ledon", (const char *)payload)==0) writeLED(1);
else if(strcmp("ledoff", (const char *)payload)==0) writeLED(0);
else analogWrite(LEDpin, atoi((const char *)payload)*10);
ws.broadcastTXT(payload, length); // send data to all connected clients
break;
case WStype_BIN:
Serial.printf("[%u] get binary length: %u\r\n", num, length);
hexdump(payload, length);
ws.sendBIN(num, payload, length); // echo data back to browser
break;
default: Serial.printf("Invalid WStype [%d]\r\n", type); break;
}
}
void handleRoot(){ server.send(200, "text/html", INDEX_HTML);}
void handleNotFound(){
String message="File Not Found\n\n";
message+="URI: "+server.uri();
message+="\nMethod: "+(server.method()==HTTP_GET)?"GET":"POST";
message+="\nArguments: "; message+=server.args()+"\n";
for(uint8_t i=0; i100) val=100;
if(val!=Aval && val%5==0){
char s[5];
itoa(val, s, 10);
Serial.print("VR: "); Serial.println(s);
analogWrite(LEDpin, val*10);
ws.broadcastTXT(s, strlen(s));
Aval=val;
}
}
|
index.h:
static const char PROGMEM INDEX_HTML[] = R"rawliteral(
WiFi制御
2016.6.13
WiFi制御
●
koyama@hirosaki-u.ac.jp
)rawliteral";
|
| |
16. 応用例
16.1 I2Cデバイスを使う
最近は、SDAとSCLの2本の信号線(GndとV+を加えれば4本)で接続するさまざまなI2Cデバイスが入手できます。例えば液晶ディスプレイやモータードライバーや温湿度センサーや加速度センサーなどがあります。
ここでは、液晶ディスプレイに表示する温湿度計を紹介します。この例ではI2Cのプルアップ抵抗はHDC1000に内蔵されています。
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) 入出力モジュール
ブレッドボードで利用できる入出力モジュールの製作例です。
モジュール | 回路図 | 製作例 |
可変抵抗モジュール | | |
明るさセンサーモジュール | | |
赤外線反射モジュール | | |
音量センサーモジュール | | |
|
モーターモジュール | | |
(付録2) 使用する主な部品
(付録3) Arduinoのプログラム(スケッチ)の仕様の抜粋(ESP8266版)
(1) 使用できるピン
種類 | ピン番号 |
デジタル出力ピン | 0, 2, 4, 5, 12, 13, 14, 15, 16 |
デジタル入力ピン |
アナログ出力ピン |
アナログ入力ピン | A0 |
(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 | アナログpin |
DDRB, PORTB, PB0~PB7 | ATmega328P固有のポートおよびビット指定 |
DDRD, PORTD, PD0~PD7 | ATmega328P固有のポートおよびビット指定 |
(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