更新

ブレッドボードを使ったtinyBasic流 ものづくり(Arduino NANO版)
コンピュータの仕組み: ATtiny2313版
Arduino流: ATtiny4313版ATmega328P版Arduino NANO版ESP8266版
tinyBasic流: Arduino NANO版ESP8266版

小山智史(弘前大学教育学部)


0. 準備
1. tinyBasicのコマンド操作
2. デジタル出力(1): LEDの点滅
3. デジタル入力: スイッチ操作でLEDのオンオフ
4. アナログ出力: LEDの明るさを変える
5. デジタル出力(2): 数字の表示
6. アナログ入力: アナログ値のデジタル表示
7. デジタル出力(3): 液晶ディスプレイに表示
8. センサーの利用
9. 音の出るものを作る
10. イルミネーション
11. 応用例: 押しボタン信号機を作る
12. 動くものを作る
13. ビデオ表示
(付録1) tinyBasicのArduinoへの書き込み
(付録2) 入出力に利用できるピン
(付録3) tinyBasic リファレンス
(付録4) 入出力モジュールの例
(付録5) 使用する主な部品


 私達の身近にある電子機器の多くにコンピュータが内蔵されており、このようなコンピュータは「マイコン」と呼ばれます。マイクロコンピュータあるいはマイクロコントローラの略です。マイコンにはメーカーが開発したプログラムが書き込まれています。リモコンの中に入っているマイコンには、押したボタンに応じて決まったパターンで赤外線をオンオフするプログラムが書き込まれています。このプログラムはマイコン製造時に書き込まれ、後から書き換えることはできませんが、フラッシュメモリー(電気的に書き換え可能なプログラムメモリー)を搭載したマイコンを使えば、私達もさまざまな機器を作ることができます。
 マイコンを使った機器を設計・製作するには、電子回路、論理回路、コンピュータの仕組み、プログラミングなど広範囲の知識が要求されます。これは大変なことではありますが、一方ではこれらのことを学ぶ格好の教材であるということを意味しています。
 このテキストでは、Arduino版tinyBasicを使った実習を行いながら、「マイコンを使ったものづくり」について学習します。

☆ ここでは複雑な電子回路は登場していませんが、電気回路の最低限の知識は前提としています。必要な場合はこちらの資料[4]を参考にしてください。
☆ 本テキストは実習時に適宜解説や補足を行うことを前提にしています。自学自習には適していないかもしれません。

0. 準備

0.1 ブレッドボードの使い方

 ここでは下図(a)のブレッドボード(EIC-701)を使います。ボードの内部は下図(b)のように接続されています。


(a) ブレッドボード(EIC-701)の外観

(b) ブレッドボードの内部の接続

 Arduino Nanoをブレッドボードに下図(b)のように装着します。USBケーブルをパソコンに接続すると、電源(5V)がパソコンから供給されます。(a)が回路図、(b)が実体配線図です。


(a) 回路図(電源はUSBから供給される)

(b) 実体配線図

 ここで、初めてブレッドボードを使う場合は、練習としてLEDと抵抗を下図のように接続してみます。電流の流れは(c)のようになります。ブレッドボードの内部の結線も含めて「電流が流れる経路」を意識することが重要です。


(a) 回路図

(b) 実体配線図

(c) 電流の流れ

0.2 Arduino NanoにtinyBasicを準備

 付録1を参考に、Arduino Nano にtinyBasicのプログラム(1)を書き込みます。

 tinyBasicのプログラムはArduinoのマイコンで動作し、パソコンからの指示を受けて、ハードウェアを制御したり、命令の並び(プログラム)としてRAMに一時保存したり、そのプログラムを1行ずつ順に解釈しながら実行(RUN)したりします(下図左)。

 また、RAMのプログラムはマイコン上のEEPROM(不揮発メモリー)に保存(SAVE)することができます。電源を切るとRAM内のプログラムは消えてしまいますが、EEPROMに保存したプログラムをRAMに読み込むことができ(LOAD)、また電源投入時には自動的に読み込まれてプログラムが自動実行されます。

 プログラムが完成したら、電池を直接接続したり、100均などで入手できるUSB電源アダプタを使うとよいでしょう。ただし、電池とUSBを同時に接続しないよう注意してください。

[パソコンと接続してプログラム作成]
[完成後は自立動作]

Arduino Nanoで製作(電源はUSBから供給)
→「SAVE」→
電池で利用

 このテキストはArduino Nanoとブレッドボードを使ったものづくりの練習を中心に扱っています。テレビ画面に表示する応用例は13章に紹介しました。

0.3 通信ソフトTeraTermの準備

 パソコンに通信ソフトTeraTermをインストールします。[設定][シリアルポート]で「ポート」をArduino NANOを接続して現れるポートにし、「ボー・レート」は「9600」にします。また、[設定][フォント]で「サイズ」を「20」ぐらいにすると見やすいかもしれません。設定が終わったら[設定][保存]で保存します。


1. tinyBasicのコマンド操作

1.1 コマンドの実行

 パソコンから通信ソフトTeraTermを用いてArduino Nanoに接続すると、下の画面が表示されます。


パソコンの通信ソフト(TeraTerm)の表示画面
 はじめに、コマンドで直接指示を与える方法を試してみましょう。
print 100
これは、「100を表示しなさい」という「命令」を表していて、この1行が「命令文」になっています。「文」といっても、疑問文や感嘆文があるわけではありません。コンピュータ(tinyBasic)に対して指示を与える「命令文」があるだけです。
 では、次のようにするとどうでしょうか。
print 100*200/50+2 print 30000+2000 print 30000+8000
それぞれ計算結果が表示されますが、最後の命令文は計算結果が扱える数「-32767~32767」を越えてしまったため、正しく表示されません。

 文字列を表示することもできます(日本語も表示できます)。また、「,(カンマ)」で区切ると、文字列や数値(計算結果)を続けて表示することができます。

print "ABC" print "弘前大学" print "100+200" print "100+200=",100+200

(練習)大文字と小文字は区別されないので、キー操作は「print」でも「PRINT」でも構いません。また、「print」の代わりに「?」でも構いません。確かめなさい。

1.2 変数

 「変数」は値をしまっておく入れ物です。変数にはA~Zの名前がついていて(変数名)、26個あります。大文字小文字は区別されません。変数には-32767~32767の整数を格納できます。

X=1 Y=2
と操作すると、「X」「Y」という変数(入れ物)にそれぞれ「1」「2」という値が代入されます。XとYに値を入れたので、以下の操作で変数同士の計算結果が表示されます。
print X+Y


2. デジタル出力(1): LEDの点滅

2.1 LEDを点灯・消灯する

 以下の回路を組み立ててください。


(a) 回路図

(b) 実体配線図

 LEDはD11ピンに接続します。「dWrite」というコマンドでD11ピンに高い電圧を出力したり低い電圧を出力したりすることができます。

dWrite 11, 1 または dWrite 11,HIGH

と操作すると、D11ピンに高い電圧(5V)が出力され、接続したLEDが点灯します。
dWrite 11, 0 または dWrite 11,LOW

と操作すると、D11ピンに低い電圧(0V)が出力され、LEDは消灯します。

上記の「dWrite 11, 1(またはHIGH)」は「D11ピンに1(高い電圧)を出力しなさい」という意味です。

 LEDが点灯する時の電流の流れは下図のようになります。

(練習)LEDを他のピンにつなぎ替えて、点灯・消灯してみなさい。

2.2 プログラムでLEDを点滅させる

 はじめに、「NEW」の操作でプログラムを消去します。

new

 プログラムのはじめの一歩は「Lチカ」です。以下の「プログラム」を入力してください。プログラムは「命令の並び」で、並びの順に実行されます。各行先頭の10~50は「行番号」で、コンピュータの中では、行番号の小さい行から順に並んでいます。先に大きな行番号の行を入力して後から小さな行番号の行を入力しても結果は同じになります。また、同じ行番号で入力しなおせば、置き換えられます。行番号だけを入力するとその行は削除されます。

10 dWrite 11, 1 20 delay 1000 30 dWrite 11, 0 40 delay 1000 50 goto 10

 以下、プログラムの内容について簡単に説明します。10~50の各行は「命令文」になっていて、それぞれ次のような意味です。これにより、行10~50が繰り返されます。

 「LIST」と操作すると、入力したプログラムが表示されます。

list

 「RUN」と操作すると、最も小さい行番号から順にプログラムが実行され、LEDが点滅を繰り返します。

run

 プログラムの実行を中断するには「ESCキー」を押します。

 後から途中にプログラムを挿入したくなることもありますから、行番号は10刻みぐらいにしておきます。

 1行に「:」で区切って複数の命令文を書くことができます。以下のプログラムはこの方法で書き直したものです。動作は同じです。

10 dWrite 11, 1: delay 1000 20 dWrite 11, 0: delay 1000 30 goto 10

 このように入力したプログラムは、RAMに一時保存されていて、電源を切ると消えてしまいます。「SAVE」の操作でEEPROM(不揮発メモリー)に保存することができます。

save

 保存したプログラムは、「LOAD」の操作で読み込まれます。

load

保存できるプログラムはひとつで、名前をつけたりすることはできません。

(練習) (1)上のプログラムを入力し(LISTで確認)、(2)SAVEで保存し、(3)NEWで消去し(LISTで確認)、(4)LOADしなさい(LISTで確認)。また、一旦電源を切り、その後電源を入れてプログラムが自動的にロードされて実行されることを確認しなさい。

(練習) LEDを0.5秒点灯、0.5秒消灯を繰り返すようにプログラムを変更しなさい。また、0.2秒点灯、1.8秒消灯を繰り返すように変更しなさい。

2.3 LEDを高速に点滅させる

 LEDの点灯時間と消灯時間を変更してみます。

 「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 スイッチが押されているかどうか調べる

 下図のようにD12ピンにスイッチと抵抗を接続すると、D12ピンはスイッチをオンにすると高い電圧(5V)になり、スイッチをオフにすると低い電圧(0V)になります。次のように、dRead(12)でD12ピンが高い電圧(1)か低い電圧(0)かを読み取り、PRINTで表示することができます。

print dRead(12)
回路図

3.2 スイッチが押されている時にLEDを点灯する

 以下のプログラムは、D12ピンの状態を読み取って(0または1)変数Aに代入し、この値をLEDに出力し、これを繰り返します。

10 A=dRead(12) 20 dWrite 11,A 30 goto 10
回路図

 以下は「IF文」を使ったプログラムで、動作は上と同じです。行10の「IF文」でdRead(12)が1かどうかを調べ、1であればLEDを点灯した後、行10に行きます。そうでなければ行20で、LEDを消灯します。tinyBasicでは「ELSE」を使えないので、このようにします。

10 if dRead(12)=1 dWrite 11,1: goto 10 20 dWrite 11,0 30 goto 10

3.3 スイッチ操作で交互に点灯・消灯する

 以下のプログラムは、スイッチを押すと交互にLEDが点灯・消灯します。考え方は、「スイッチが押された瞬間に(つまりさっき押されていなくて今押されたら)LED表示を反転する」というものです。変数Bには「今のスイッチの状態(0または1)」、変数Aには「さっきのスイッチの状態(0または1)」、変数Lには「今のLEDの点灯状態(1が点灯、0が消灯)」を保存します。

 行20で、現在のD12ピンの状態(1または0)を読み取り、Bに入れます。行30で、スイッチがさっきまで押されていなくて今押されているならLの値を反転(0なら1、1なら0)します(行30)。その後、行40で今のスイッチの状態を記憶し、行50でD11ピン(LED)にLの値を出力します。行60で、少し待ってから行20に行きます。

10 A=0: L=0 20 B=dRead(12) 30 if A=0 if B=1 L=!L 40 A=B 50 dWrite 11, L 60 delay 10 70 goto 20
回路図

 「delay 10」がないと、誤動作することがあります。それは、スイッチを押した時に、ごく短い時間にその接点がオンになったりオフになったりするからです。スイッチを離した時も同様です。スイッチによって異なりますが、10ms程で落ち着きます。この現象は「チャタリング」と呼ばれます。

(練習) 「delay 10」を極端に長くし、例えば待ち時間が2秒になるようにして、どのような動作になるか調べなさい。
(練習) 緑色LEDを増設し、スイッチを押すと赤色LEDと緑色LEDが交互に点灯するようにしなさい。

3.4 スイッチを4回押すと点灯する

 以下のプログラムは、スイッチ操作4回に一度LEDが点灯します。

 上の例ではLEDの表示状態を記憶し、スイッチが押されるとその値を反転させていましたが、ここではCにスイッチが押された回数を記憶します。スイッチがさっきまで押されていなくて今押されているならCの値に1を加えます(行40)。その後、今のスイッチの状態を記憶し(行50)、「C%4=0」つまりCの値を4で割った余りが0であればLEDを点灯し(行60)、そうでなければLEDを消灯します(行70)。最後に、少し待ってから行20に行きます。

10 A=0: L=0: C=0 20 B=dRead(12) 40 if A=0 if B=1 C=C+1 50 A=B 60 if C%4=0 dWrite 11,1: goto 80 70 dWrite 11,0 80 delay 10 90 goto 20
回路図

(練習) 行60の「C%4=0」の箇所を「C%7=0」や「C%4!=0」などに変えて、どのような動作になるか調べなさい。

3.5 プルアップ抵抗

 話を3.2に戻しましょう。以下に回路図とプログラムを再掲します。

10 if dRead(12)=1 dWrite 11,1: goto 10 20 dWrite 11,0 30 goto 10
回路図

 このスイッチ入力の箇所をピックアップした図が下図(1)です。D12の入力信号はスイッチがオンの時に1(高い電圧)、オフの時に0(低い電圧)となります。

 ここで、抵抗とスイッチを入れ替えると(2)のようになります。こうすると、マイコンの入力信号はスイッチがオンの時にLOW、オフの時に1となります。このように接続した時の抵抗を「プルアップ抵抗」といいます。「高い電圧に引っ張り上げるための抵抗」という意味です。プログラムは変えていませんから、スイッチを押せば消灯、離せば点灯します。押した時に点灯させるためには以下の箇所を書き換えます。

こうすると、スイッチを押せば点灯し、離せば消灯します。

 実は、プルアップ抵抗はマイコンの中に既に用意されていて、外部に抵抗をつなぐ必要はありません。図の(3)はこの様子を示したものです。


(1)前出の入力回路

(2)スイッチと抵抗を逆に接続

(3)マイコン内蔵の抵抗を利用

 抵抗をひとつ省略できただけですが、回路は下図のようにシンプルになります。

10 if dRead(12)=0 dWrite 11,1: goto 10 20 dWrite 11,0 30 goto 10
回路図

(練習) 上記のスイッチ接続に合わせ、3.3の「スイッチ操作で交互に点灯・消灯する」を修正しなさい。

(練習) 上記のスイッチ接続に合わせ、3.4の「スイッチを4回押すと点灯する」を修正しなさい。

(練習) 2個の押しボタンスイッチを使い、一方のスイッチを押すとLEDが点灯し、もう一方のスイッチを押すと消灯するようにしなさい。

3.6 スイッチが押されるとLEDを3秒点灯する

 以下のプログラムは、スイッチが押されるとLEDを3秒点灯します。

10 if dRead(12)=0 dWrite 11,1: delay 3000: goto 10 20 dWrite 11,0 30 goto 10
回路図

4. アナログ出力: LEDの明るさを変える

4.1 LEDの明るさを変える

 これまでは、LEDが点灯するか消灯するかでした。しかし、点灯時の明るさを変化させたいこともあります。 aWrite という命令を使い

とすると、指定したピンにアナログ値 0~255 を出力できます。

 LEDの接続はそのままで、以下の操作をしてみましょう。

aWrite 11, 10 aWrite 11, 255

LEDの明るさが変わるのが確認できます。

(練習)LEDの明るさを0~255の範囲で変えてみなさい。また、その時の波形をオシロスコープで観察して記録しなさい。

4.2 プログラムでLEDの明るさを変える

 2.3ではLEDの点灯時間と消灯時間を変えてどのように見えるかを確認しました。ここでは行20と行40を「delay 1」にしてみます(1ms)。

10 dWrite 11,1 20 delay 1 30 dWrite 11,0 40 delay 1 50 goto 10

 既に経験したように、点灯と消灯を高速に切り替えると、私達の目は点滅を認識できなくなります。代わりに、常時点灯している場合よりも少しだけ暗く感じます。「平均的に」半分の明るさになるからです。下図左は、この時のLEDの出力の波形を示したものです。行30の0を1にすれば常時点灯となりますから、LEDの明るさを比較してみてください。私達の目は、明るさが半分になっても、「ちょっと暗くなった」ぐらいにしか感じません。

PWM波形

 次に、行20はそのままにして行40を「delay 9」としてみてください。こうすることにより、1ms点灯、9ms消灯を繰り返すようになり、明るさはより暗く感じられます。この時の波形が上図中央です。

 HIGHとLOWの時間が等しい時「デューティ比50%」、常時HIGHの時「デューティ比100%」、HIGHが1msでLOWが9msなら「デューティ比10%」といいます。この技術は「パルス幅変調(PWM)」と呼ばれ、モータのスピードコントロールや、デジタルオーディオアンプなどに利用されています。

 4.1で使ったaWriteというコマンドを使うと、0/255~255/255のデューティ比で上記のようなPWM信号が出力される仕組みになっています。

(練習) スイッチを押す度に「消灯→暗い→明るい→消灯→...」の動作をするプログラムを作りなさい。

4.3 LEDの明るさを連続的に変える

 デューティ比を連続的に変化させれば、下図のように任意の波形を表すことができます。

PWM波形

 以下は、デューティ比を0%から100%まで少しずつ変化させるプログラムです。暗い状態から次第に明るくなり、その後次第に暗くなり、これを繰り返します。Bが明るさの値、Fが明るさの変化量です。行20でBの値をアナログ出力し、行30でBの値をFだけプラスします。

10 B=5: F=5 20 aWrite 11, B 30 B=B+F 40 if B=0 F=5 50 if B=255 F=-5 60 delay 30 70 goto 20

(練習) スイッチを押すとLEDがだんだん明るくなり、スイッチを離すだんだん暗くなるプログラムを作りなさい。


5. デジタル出力(2): 数字の表示

5.1 7セグメントLEDに数字を表示する

 数字表示LED(7セグメントLED C-551SR)に数字を表示してみます。下図の回路を組み立ててください。

 7セグメントLEDをD2~D8に接続した場合、「NUMLED」で指定した数(0~9)を表示させることができます。

numled 5
回路図

(練習) 表示する数字を他の数字に変更してみなさい。

5.2 数字表示をカウントアップする

 以下のプログラムは1秒毎に0→9と表示をカウントアップします。

20 for I=0 to 9 30 numled I 40 delay 1000 50 next I 60 goto 20
回路図

 以下は、スイッチを押すと数字表示がカウントアップするプログラムです。

10 C=0 20 if C<9 if dRead(12)=0 C=C+1: delay 300 30 numled C 40 goto 20
回路図

(練習) 数字表示が9~0にカウントダウンするプログラムを作りなさい。
(練習) 1秒毎ではなく50ms毎に高速でカウントアップするようにしなさい。また、スイッチが押されるとカウントを停止するようにしなさい。


6. アナログ入力: アナログ値のデジタル表示

6.1 アナログ値の入力

 以下のようにA0ピンにボリュームを接続し、ボリュームを中ほどの位置にして以下の操作をしてみてください。ボリュームの位置に応じて0~1023の値が表示されます。aReadは指定されたピンのアナログ値(0~1023)を返す「関数」です。aRead(0)は、A0ピンの電圧(0~5V)に応じて0~1023の値が返ってきます。ボリュームの位置を変えて、表示される値を確認してください。

print aRead(0)

 以下のプログラムは、ボリュームの値に応じてLEDの点滅時間が変化します。

10 dWrite 11,1 20 delay aRead(0) 30 dWrite 11,0 40 delay aRead(0) 50 goto 10
回路図

6.2 アナログ値に応じてLEDを点灯・消灯する

 以下のプログラムは、ボリュームのアナログ値(0~1023)が600未満の時にLEDを点灯します。

10 if aRead(0)<600 dWrite 11,1: goto 10 20 dWrite 11,0 30 goto 10
回路図

6.3 アナログ値に応じて明るさを変える(調光)

 照明の明るさを調整することを「調光」といいます。

 以下のプログラムは、ボリュームのアナログ値(0~1023)に応じてLEDの明るさを連続的に変えます。

10 A=aRead(0)/4 20 aWrite 11, A 30 goto 10
回路図

6.5 アナログ値を数字で表示する

 以下のプログラムは、ボリュームのアナログ値(0~1023)に応じて、0~9の数字を表示させます。

10 A=aRead(0)/103 20 numled A 30 goto 10
回路図

7 デジタル出力(3): 液晶ディスプレイに表示

 I2C接続の液晶ディスプレイ(2行16文字)を以下のように接続します。

 CLS、CURSOR、PRINTの3つのコマンドは、通信ソフト(teraTerm等)と液晶の両方の表示に適用されます。

cls print 100

8. センサーの利用

8.1 スイッチをセンサーとして使う

 押しボタンスイッチやスライドスイッチは、操作用のスイッチですが、センサーとしても活躍しています。例えば、CD/DVDデッキやコピー機やプリンタなど動きを伴う機器には、所定の位置に来たがどうかを検知するために、マイクロスイッチが必ずといっていいほど使われています。このような使われ方をするスイッチを「リミットスイッチ」と呼ぶこともあります。

 ふたつの金属片を接触させるだけでスイッチ(センサー)となるので、オリジナルのスイッチを作ることも難しくはありません。所定の水位になったかどうかを検知するピンポン球を使った浮力スイッチ、傾けると金属ボールが移動する傾斜スイッチなど、用途に応じて工夫するのも楽しいでしょう。

8.2 明るさセンサー(CDS)を使う

 6.2のボリュームの代わりに明るさセンサー(CDS)をつないでみましょう。明るくなるほどCDSの抵抗値が低くなり、A0ピンの電圧は高くなります。一定以下に暗くなるとLEDが点灯します。プログラムは6.2と同様です。ただし、行10の「600」の値は適切な値に変更する必要があります。

10 if aRead(0)<600 dWrite 11,1: goto 10 20 dWrite 11,0 30 goto 10
回路図

 街路灯などでは、境界値付近でledが点灯したり消灯したり不安定になるのを避けるために、下図のような「ヒステリシス特性」を持たせます。

行30はledが消灯(Lが0)している時に600未満の明るさになったらledを点灯(Lを1)します。行50はledが点灯(Lが1)している時に700以上の明るさになったらledを消灯(Lを0)します。実際の点灯消灯は行60,70で行っています。

10 L=0 20 if L=0 if aRead(0)<600 L=1 30 if L=1 if aRead(0)>=700 L=0 40 dWrite 11,L 50 goto 20
回路図

(練習) 行20の600と行30の700の値を変えて、適切な値をみつけなさい。

8.3 音を検知する: 音量センサ

 以下のプログラムは、音を検知したら(A0のレベルが300を超えたら)、LEDを3秒点灯します。

10 if aRead(0)>300 dWrite 11,1: delay 3000: goto 10 20 dWrite 11,0 30 goto 10
回路図

(練習) 5.2のスイッチの代わりに音センサを使い、手をたたくとカウントアップするようにしなさい。

(練習) LEDをエアコンに置き換えて考えてみましょう。街路灯の場合と同様、境界値付近で点灯したり消灯したり不安定になるのを避けるために、下図のような「ヒステリシス特性」を持たせます。

ヒステリシス特性を持たせたプログラムでその動作を確かめなさい。

(練習) 6.4を参考に、温度が高ければ赤、温度が低ければ青というようにLEDの色が変わるようにしてみなさい。

8.4 明るさを計測する(数字表示LEDに表示)

 以下のプログラムは、明るさを数字表示LEDに0~9の数字で表示させます。プログラムは6.5と同様です。

10 A=aRead(0)/103 20 numled A 30 goto 10
回路図

8.5 温度を計測する(数字表示LEDに表示)

 今度は温度計を作ります。センサにはI2C接続の温湿度センサー(HDC1000)を使います。tRead()で温度を整数値で返してくれます。5.3のダイナミック点灯の方法を使って2桁表示します。

10 dWrite 9,1 20 dWrite 10,1 25 T=tRead() 30 numled T/10 40 dWrite 9,0 50 delay 10 60 dWrite 9,1 70 numled T%10 80 dWrite 10,0 90 delay 10 100 dWrite 10,1 110 goto 20
回路図

(練習) 上記の「delay 10」の箇所(10ms)を遅くすると(100ms)表示がどうなるか観察しなさい。また、速くすると(1ms)表示がどうなるか観察しなさい。

8.6 温湿度計を作る(液晶ディスプレイに表示)

 液晶ディスプレイとI2C接続の温湿度センサー(HDC1000)を以下のように接続します。温度を取得するtRead()と湿度を取得するhRead()のコマンドを使い、温湿度計を作ります。なお、パソコンと接続している場合は、通信ソフト(teraTerm等)にも液晶ディスプレイと同じ表示が現れます。

10 cls 20 cursor 0,0 30 print tRead(),chr(&df),"C" 40 cursor 8,0 50 print hRead(),"%RH" 60 delay 1000 70 goto 10

 プログラムを保存(SAVE)しておくと、パソコンと接続しなくても、電源を入れるとプログラムが起動します。ケースに入れれば温湿度計の完成です。


9. 音の出るものを作る

 ここではブザー音で電子オルゴールを作ってみましょう。和音など音にこだわる方は「私だけの電子オルゴール」[8]を参考にしてください。

9.1 音を出す

 LEDと同じ箇所に圧電ブザー(Bzz)を接続し、「1秒ごとにLEDを点滅させるプログラム」を実行してください。するとブザーからカチカチと音が聞こえます。ここで、点灯時間と消灯時間を以下のように1msにしてみましょう。

10 dWrite 11,1 20 delay 1 30 dWrite 11,0 40 delay 1 50 goto 10

 この時の波形は下図のように500Hzの矩形波となり、聞こえるのは500Hzの音です(といっても倍音成分が含まれていますが)。

トーン波形

(練習) オシロスコープでトーン信号の波形を観察しなさい。また、WaveGeneなどの信号発生ソフトを用い、パソコンで500Hzの矩形波を出し、音を比べてみなさい。

9.2 音程を変える

 指定したピンに、指定した周波数の音を、指定した時間出力する tone という命令を使います。以下は時報の音を出すプログラムです。ソが784Hz、ドが1046Hzです。

10 tone 11,784,200 20 delay 400 30 tone 11,784,200 40 delay 400 50 tone 11,784,200 60 delay 400 70 tone 11,1046,1200

(練習) 440Hzのラの音(A4)に対し1オクターブ高いラ(A5)の音は880Hzで、その間は隣同士(半音)の周波数の比が一定(21/12)となるように音階が作られます。表計算ソフトを用い、A4~A6の各音の周波数を計算しなさい。上のプログラム中の784Hzが「ソ」、1046Hzが「ド」であることが確認できます。
(練習) 上のプログラムを参考に、簡単な曲を演奏させるプログラムを作りなさい。

9.3 楽譜を演奏する

 以下は、文字列で記述した楽譜情報を参照しながら演奏するプログラムです。楽譜情報がプログラムと分離されているので、長い曲も表しやすくなっています。

10 P=top(0): @P,"RDDBAGDR RDDBAGER REEcBAFR RdddcABR RDDBAGDR RDDBAGER REEcBAdd ddedcAGR" 20 C=peek(P) 25 if C=13 stop 30 if C=67 F=262: goto 200 40 if C=68 F=294: goto 200 50 if C=69 F=330: goto 200 60 if C=70 F=349: goto 200 70 if C=71 F=392: goto 200 80 if C=65 F=440: goto 200 90 if C=66 F=494: goto 200 100 if C=99 F=523: goto 200 110 if C=100 F=587: goto 200 120 if C=101 F=659: goto 200 130 if C=102 F=698: goto 200 140 if C=103 F=784: goto 200 150 if C=82 delay 300: goto 210 160 P=P+1 170 goto 20 200 tone 11,F,300 210 P=P+1 220 goto 20

(練習) 行10の楽譜を変え、必要な場合は30~140行を拡張し、好きな曲の電子オルゴールを作りなさい。

9.4 楽器を作る

 以下のプログラムは、ボタンを押すと、ボリュームで決まる周波数のブザー音がでます。

10 if dRead(12)=1 goto 10 20 A=aRead(0) 30 tone 11,A,200 40 goto 10

 上のプログラムで一応楽器として使えるのですが、ドレミ...の音を丁度よく出すのは難しいと思います。そこで、ドレミ...の音しか出ないようにしたのが次のプログラムです。

10 if dRead(12)=1 goto 10 20 A=aRead(0) 30 if A>784 tone 11,784,200: goto 10 40 if A>698 tone 11,698,200: goto 10 50 if A>659 tone 11,659,200: goto 10 60 if A>587 tone 11,587,200: goto 10 70 if A>523 tone 11,523,200: goto 10 80 if A>494 tone 11,494,200: goto 10 90 if A>440 tone 11,440,200: goto 10 100 if A>392 tone 11,392,200: goto 10 110 if A>349 tone 11,349,200: goto 10 120 if A>330 tone 11,330,200: goto 10 130 if A>294 tone 11,294,200: goto 10 140 tone 11,262,200 150 goto 10

(練習) ボリュームの代わりに明るさセンサで音程が変わるようにしなさい。また、必要ならば音階を拡張しなさい。

9.5 しゃべるものを作る

 I2C接続した音声合成LSI(ATP3011F4)を使ってスピーカから合成音声を出します。

 まず、コマンド操作で動かしてみてください。

talk "aiueo" talk 123 talk 4,"kakeru",5,"wa",4*5,"desu" talk "ju'nbi/o'-kei"

 日本語音声をしゃべってくれますが、文字列はローマ字で書く必要があります。また、上の最後の操作例のように、アクセントその他さまざまな約束事があります。

 以下の例は、「九九」を合成音声で読み上げるものです[音を聞く]。

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. イルミネーション

10.1 NeoPixelの利用

 NeoPixelはフルカラーLEDがシリアルに連結されていて(ここでは10連LEDを使用)、1本の信号線で任意のLEDの発光をコントロールできるのが特徴です。tinyBasicでは、A3(D17)ピンに接続した10連NeoPixelをpixelコマンドで制御することができます。パラメータで、順にLEDの番号(0~9)、Redの明るさ(0~255)、Greenの明るさ(0~255)、Blueの明るさ(0~255)を指定します。

 まず、コマンド操作で試してみてください。

pixel 0,50,0,0 pixel 0,0,0,0 pixel 0,50,0,0 pixel 1,0,50,0 pixel 2,0,0,50 pixel 3,50,50,0 pixel 4,0,50,50 pixel 5,50,50,50

10.2 プログラムによる点滅とフラッシュ

 以下のプログラムは、ひとつ目のLEDを赤で点滅させます。1秒点灯・1秒消灯をくりかえします。下図右は点滅の時間的な変化をタイムチャートで示したものです。

10 pixel 0,50,0,0 20 delay 1000 30 pixel 0,0,0,0 40 delay 1000 50 goto 10

10行目を

10 pixel 0,0,50,0 とすると緑で点滅します。

 次のようにすると10個のLEDが点滅します。

10 for i=0 to 9 20 pixel i,0,50,0 30 next i 40 delay 1000 50 for i=0 to 9 60 pixel i,0,0,0 70 next i 80 delay 1000 90 goto 10

20行目を

20 pixel i,0,100,0 40行目を 40 delay 200 とすると明るく短時間点灯する「フラッシュ」になります。

(練習) 10個のLEDが1秒毎に「青→黄→赤」を繰り返すプログラムを作りなさい。

10.3 ワイプ

 以下のプログラムは、10個のLEDを順に点灯させます。このようなイルミネーション効果は「ワイプ」と呼ばれます。

10 for i=0 to 9 20 pixel i,50,0,0 30 delay 1000 40 next i

20行目を

20 pixel i,0,0,0 とすると順に消灯します。

(練習) 「赤のワイプ→緑のワイプ→青のワイプ」を繰り返すプログラムを作りなさい。

10.4 スイープ

 以下のプログラムは、10個のLEDを順に点灯・消灯させます。

10 for i=0 to 9 20 pixel i,50,0,0 30 delay 1000 40 pixel i,0,0,0 50 next i

10.5 ウェーブ

 以下のプログラムは、2個おきの点灯パターンがスイープし、波(ウェーブ)のように(あるいは追いかけているように)見えます。500行目はサブルーチンになっていて、与えられたR,G,Bの値でウェーブ表示します。10行目は白色、20行目は赤、30行目は緑、40行目は青のそれぞれウェーブで、これを繰り返します。

10 R=50:G=50:B=50:gosub 500 20 R=50:G=0: B=0: gosub 500 30 R=0: G=50:B=0: gosub 500 40 R=0: G=0: B=50:gosub 500 50 goto 10 500 for j=20 to 0 step -1 520 for i=0 to 9 530 if (i+j)%3=0 pixel i,R,G,B:goto 550 540 pixel i,0,0,0 550 next i 560 delay 80 570 next j 580 return

10.6 フェードイン・フェードアウト

 以下のプログラムは、ひとつ目のLEDをだんだん明るくし(赤を0→50)、だんだん暗くし(赤を50→0)、これを繰り返します。

10 pixel 0,50,0,0 20 for r=0 to 50 30 pixel 0,r,0,0 40 delay 10 50 next r 60 for r=50 to 0 step -1 70 pixel 0,r,0,0 80 delay 10 90 next r 100 goto 10

(練習) 色を変えてみなさい。また速さを変えてみなさい。

(練習) 10個のLEDを同時にフェードイン・フェードアウトさせなさい。

10.7 クロスフェード

 ひとつ目のLEDをフェードアウトしながら、ふたつ目のLEDをフェードインすると滑らかに変化させることができます。これを「クロスフェード」といいます。

10 pixel 0,50,0,0 20 for r=0 to 50 30 pixel 0,r,0,0 40 delay 10 50 next r 60 for r=50 to 0 step -1 70 pixel 0,r,0,0 80 delay 10 90 next r 100 goto 10

 以下は、クロスフェードをスイープに応用した例で、LEDの点灯位置が滑らかに変化します。

10 for i=0 to 9 20 for r=0 to 50 30 pixel i,50-r,0,0 40 pixel (i+1)%10,r,0,0 50 delay 10 60 next r 70 next i 80 goto 10

10.8 フルカラー表示

 クロスフェードを用いると色を滑らかに変化させることができます。以下のプログラムは、ひとつ目のLEDの色が赤→緑に滑らかに変化します。

10 for r=50 to 0 step -1 20 pixel 0,r,50-r,0 30 delay 10 40 next r

 RGBの値と表示色の関係は下図のようになっています。


 以下のプログラムは、すべてのLEDの色をフルカラーで連続的に変化させます(明るさ最大85)。

10 for W=0 to 255 20 if W<85 B=0:G=W:R=85-G:goto 50 30 if W<170 R=0:B=W-85:G=85-B:goto 50 40 G=0:R=W-170:B=85-R 50 for i=0 to 9 60 pixel i,R,G,B 70 next i 80 delay 10 90 next W 100 goto 10

 以下のプログラムは、すべてのLEDの色を場所を変えながらフルカラーで連続的に変化させます(明るさ最大85)。500行目はサブルーチンになっていて、0~255の色相Wを与えると、R,G,Bの変数に値(各々0~85)がセットされます。30行目でWに値をセットしてから500行目を呼び出しています。

10 for j=0 to 256 step 8 20 for i=0 to 9 30 W=(i*256/10+j)%256:gosub 500 40 pixel i,R,G,B 50 next i 60 delay 10 70 next j 80 goto 10 500 if W<85 B=0:G=W:R=85-G:return 510 if W<170 R=0:B=W-85:G=85-B:return 520 G=0:R=W-170:B=85-R:return

10.9 イルミネーションの例

 以下は、「ドロップ」のイルミネーションの作例です。水滴が滴り落ちるイメージで、最後にキラッと光ります。

10 N=6+rnd(5) 20 for i=0 to N-2 30 u=(i+1)*10 40 for b=0 to u 50 pixel i, u-b,u-b,u-b 60 pixel (i+1)%10,b,b,b 70 delay 3 80 next b 90 delay (10-N)*2 100 next i 110 for b=u to 255 120 pixel N-1,b,b,b 130 delay 3 140 next b 150 for b=255 to 0 step -1 160 pixel N-1,b,b,b 170 delay 3 180 next b 190 delay rnd(2000) 200 goto 10
N:LEDの長さ(ランダムに6~10)...どこまで長くドロップさせるか iをNまで繰り返す  uは最大の明るさ  明るさbを0~u   iのLEDをu-bの明るさで表示(だんだん暗く)   i+1のLEDをbの明るさで表示(だんだん明るく)   3ms待つ  速さ調整 明るさbを最大までだんだん明るくする(キラリ)  速さ調整 明るさbを0までだんだん暗くする  速さ調整 次のドロップまで0~2秒待つ

(練習) オリジナルのイルミネーションを考え、作ってみなさい。


11. 応用例: 押しボタン信号機を作る

 押しボタン信号機の制御プログラムです。まず行10で「車用信号を青」、行20で「歩行者用信号を赤」にします。行30と行40で歩行者のボタンが押されるまで待ち、ボタンが押されると、4秒後に行60で「車用信号を黄色」にし、4秒後に行80で「車用信号を赤」、行90で「歩行者用信号を青」にします。その後16秒待ち、行110~160で歩行者用信号を点滅させます。行170で「歩行者用信号を赤」にして4秒後に行190行で「車用信号を青」にして、以上を繰り返します。

10 dWrite 2,0: dWrite 3,0: dWrite 4,1 20 dWrite 5,1: dWrite 6,0 30 B=dRead(12) 40 if B=1 goto 30 50 delay 4000 60 dWrite 2,0:dWrite 3,1:dWrite 4,0 70 delay 4000 80 dWrite 2,1:dWrite 3,0:dWrite 4,0 90 dWrite 5,0: dWrite 6,1 100 delay 16000 110 for I=1 to 8 120 dWrite 5,0:dWrite 6,1 130 delay 500 140 dWrite 5,0:dWrite 6,0 150 delay 500 160 next I 170 dWrite 5,1: dWrite 6,0 180 delay 4000 190 dWrite 2,0: dWrite 3,0: dWrite 4,1 200 A=B 210 goto 30

 以下は歩行者用の「カッコー」の音を付け加えた例です。

10 e=659: c=523 20 dWrite 2,0: dWrite 3,0: dWrite 4,1 30 dWrite 5,1: dWrite 6,0 40 B=dRead(12) 50 if B=1 goto 40 60 delay 4000 70 dWrite 2,0: dWrite 3,1: dWrite 4,0 80 delay 4000 90 dWrite 2,1: dWrite 3,0: dWrite 4,0 100 dWrite 5,0: dWrite 6,1 110 for I=1 to 5 120 tone 11,e,100: delay 400 130 tone 11,c,200: delay 900 140 tone 11,e,100: delay 100 150 tone 11,e,100: delay 100 160 tone 11,c,200: delay 1000 170 next I 180 for I=1 to 8 190 dWrite 5,0:dWrite 6,1: delay 500 200 dWrite 5,0:dWrite 6,0: delay 500 210 next I 220 dWrite 5,1: dWrite 6,0 230 delay 4000 240 dWrite 2,0: dWrite 3,0: dWrite 4,1 250 A=B 260 goto 40

 「交通安全教室用信号機」はこれを発展させたものです。


12. 動くものを作る

 これまでの例では、マイコンの出力ピンにLEDや圧電ブザーを接続しました。AVRマイコンは最大20mAの電流を流すことができるので、この範囲であれば直接接続できます。この章では、モータなど大きな電流が流れる素子を駆動する例を紹介します。大きな電流が流れる回路を制御する場合は、「電流が流れる経路」を意識することが回路の誤動作を防ぐことにつながります。

11.1 サーボモーターを動かす

 以下のようにサーボモーターを接続し、コマンド操作で動かしてみてください。

servo 10,0 servo 10,90 servo 10,180

11.2 プログラムでサーボモーターを動かす

 以下の例は、サーボモーターが車のワイパーのように動きます。

10 P=0: D=1 20 servo 10,P 30 P=P+D 40 if P=180 D=-1 50 if P=0 D=1 60 delay 15 70 goto 20

 以下のように「for文」を使っても結果は同じです。

10 for P=0 to 179 20 servo 10,P 30 delay 15 40 next P 50 for P=180 to 1 step -1 60 servo 10,P 70 delay 15 80 next P 90 goto 10

 以下のプログラムは、ボリュームの値によってサーボモーターの位置(角度)が変わります。

10 A=aRead(0)*9/51 20 servo 10,A 30 delay 15 40 goto 10

(練習) ボリュームの代わりに明るさセンサを使い、明るさで位置が変わるようにしなさい。

11.3 モーターを動かす

 下図のようにモーターを接続します。まず、「MOTOR」コマンドでモーターを動かしてみてください。左右のモーターの速度は0~255で指定します。

motor 255,100 motor 100,255 motor 255,255 motor 0,0

11.4 プログラムでモーターを動かす

 以下はボリュームの値によってモーターの回転速度が変わります。

10 A=aRead(4)/4 20 motor A,A 30 goto 10

 以下は音センサモジュールと組み合わせた例で、手を叩く(A6に接続した音センサーが400以上)と1秒間前進します。

10 if aRead(6)<400 goto 10 20 motor 255,255 30 delay 1000 40 motor 0,0 50 goto 10

 以下の例は、車の先端がテーブルから落ちそう(A7に接続した赤外線反射センサーが30以上)になると、少し後退し、右回転して、再び前進します。

10 motor 255,255 20 delay 100 30 if aRead(7)<500 goto 10 40 motor -200,-200 50 delay 500 60 motor 0,200 70 delay 500 80 goto 10

13. ビデオ表示

13.1 テレビに表示する

 下図左のようにテレビと接続します。写真のような先端に抵抗とジャンパピンを付けたケーブルを作っておくと便利です。

 パソコンの通信ソフトで操作している時はテレビにも同じ画面が表示されます。といっても、下の写真のように画面は80×48ドット(480バイトのVRAM)で、4×6ドットの文字フォントです。

 グラフィック表示のコマンドをいくつか試してみましょう。

plot 30,30,1 plot 40,40,1 line 0,0,79,0,1 line 79,0,79,47,1 circle 20,20,8,1 box 40,20,60,30,1

 以下は、正弦波形を描くプログラムです。

10 cls 20 for x=1 to 79 30 plot x,24-sin(x*9)/45,1 40 next x

 以下は、ランダムな場所に四角を表示・消去を繰り返すプログラムです。

10 cls 20 W=80: H=48 30 for i=1 to 10 40 x=rnd(W-5): y=rnd(H-5) 50 box x,y,x+5,y+5,1 60 delay 1000 70 box x,y,x+5,y+5,0 80 next i

 以下は、ボールを動かすプログラムです。

10 cls 20 for x=8 to 72 30 circle x,24,8,11 40 delay 50 50 circle x,24,8,10 60 next x

13.2 プログラムでビットマップ画像を表示する

 以下は64×48ドットのビットマップ画像を表示するプログラムです。画像を表示し、その後右にスライドします。適当な方法で、画像のピクセルパターンを用意する必要があります。

10 cls 20 bmp0,0,"ffffc00000007fff","ffffc00000001fff", "fffe0000000003ff","fffc0000000000ff", "fff80000000000ff","fff000000000007f", "ffe000000000003f","ff0000000000003f" 30 bmp0,8,"ff0000000000001f","ff2f00000000000f", "ff1fc0000000000f","ff000c000000000f", "ff003f000000000f","ff0020400000000f", "fc0000fc1800000f","fc00000f7f3c000f" 40 bmp0,16,"f80000037f7e000f","f8000000007e0003", "f01c00c0001cf7c3","f03e03c00000efcf", "f03e07c00e00000f","c03f1ff01f000003", "c03ffff03f800003","cf3ffff87fc380c3" 50 bmp0,24,"ce3fffffffc3c0c3","c03fc03fffe7f1cf", "c03f003ffffff3c0","ff3fff3fff0fff88", "ff1ffffffc0fff1c","ff9ffffff043ff3c", "ff8fffc03ff3ff38","ff8fffc03ff3fe00" 60 bmp0,32,"ffcfffff3ffffc01","ffc7fffffffffcff", "ffc3fffffffff8ff","fff3ff007ffff1ff", "fff1ff0c7fffc3ff","fff0ff0c7fffc7ff", "fffc7f0c7fff8fff","fffe3f807ffe1fff" 70 bmp0,40,"ffff3fc0fffe3fff","ffff07c3fff07fff", "fff023ffffe1ffff","ffe0203fff83ffff", "ff07e00000003fff","fc0fe3ffff8f0fff", "fc3fe3ffff8f0fff","f0ffe3ffff8fc1ff" 80 shift 1,3:delay 99:goto 80

13.3 ビデオゲーム「PONG」を作る

 以下のプログラムはPONGゲームのtinyBasic版です。A0ピンに接続したボリュームでラケットを動かします。このプログラムを入力した時点で残りのメモリー容量はあとわずかしかありませんので、作ることができるプログラムのサイズはこの程度です。

(変数の説明) WとHはそれぞれスクリーンの幅と高さ、AとBはそれぞれプレイヤーとコンピュータのスコア、XとYはボールの座標、DとEはX方向とY方向の移動方向、UとVはラケットの位置、QはBラケットのボール到着予定位置、Cは開始時の1秒カウンタです。

(プログラムの説明) 行120~230がボールの移動。行140~150が得点追加。行160~170がラケットにヒット。行240~280がプレーヤーのラケット移動。行290~370がコンピュータのラケット移動、特に行300~310がコンピュータのラケットが動き始めるまでの遅れを決めている部分で、ここで強弱を調整しています。ラリーが続くと強弱が変化するようにしても面白いでしょう。

 下の写真は、Arduino ProMiniを使い電池で動作させている様子です。ケースに入れれば、立派な(?)「ビデオゲーム機」となります。

10 cls:A=0:B=0:W=48:H=32 20 line 0,0,W-1,0,1:line W-1,0,W-1,H-1,1 30 line W-1,H-1,0,H-1,1:line 0,H-1,0,0,1 40 U=H/2-3:V=U 50 line W-5,U,W-5,U+5,2:line 4,V,4,V+5,2 60 cursor 8,1:print A:if A=9 stop 70 cursor 3,1:print B:if B=9 stop 80 D=1:E=1:if (U+V)&1 E=-1 90 X=5:Y=V+3:plot X,Y,1 100 C=50 110 if C>0 C=C-1: goto 240 120 plot X,Y,2 130 X=X+D 140 if X=0 A=A+1:goto 60 150 if X=W-1 B=B+1:goto 60 160 if X=W-5 if Y>=U if Y<=U+6 D=-D:tone 440,100 170 if X=4 if Y>=V if Y<=V+6 D=-D:tone 440,100 180 Y=Y+E 190 if Y=0 E=-E 200 if Y=H-1 E=-E 210 if X=W-5 if Y=U if E=1 E=-1 220 if X=W-5 if Y=U+5 if E=-1 E=1 230 plot X,Y,2 240 line W-5,U,W-5,U+5,2 250 U=H-1-aRead(0)/16 260 if U<0 U=0 270 if U>H-6 U=H-6 280 line W-5,U,W-5,U+5,2 290 if D=1 goto 400 300 if x>27 goto 400 310 if x=27 if A<=B goto 400 320 line 4,V, 4,V+5,2 330 if E=1 Q=Y+X-4:if Q>=H Q=64-H 340 if E=-1 Q=Y-X+4:if Q<0 Q=-Q 350 if Q<V+3 if V>0 V=V-1 360 if Q>V+3 if V<25 V=V+1 370 line 4,V,4,V+5,2 400 delay 20:goto 110 A:Playerスコア, B:コンピュータスコア, W:画面幅, H:画面高 枠線描画 枠線描画 U: Aラケット位置(画面右), V: Bラケット位置(画面左) ラケット描画 Aスコア表示(画面右) Bスコア表示(画面左) DはXの増分, EはYの増分 ボール位置(X,Y)にボール描画 サーブまでの待ち時間 サーブまで待つ ボール消去 Xの位置更新 Aが得点 Bが得点 Aラケット(画面右)にヒット Bラケット(画面左)にヒット Yの位置更新 上の壁の跳ね返り 下の壁の跳ね返り Aラケットの上隅で方向変化 Aラケットの下隅で方向変化 ボール表示 Aラケット消去 Aラケット位置はボリューム値 Aラケット位置を範囲内に修正 Aラケット位置を範囲内に修正 Aラケット表示 ボール右移動ならBラケット動かさない ボールが境界位置より遠ければBラケット動かさない ボールが境界位置でBが勝っていればBラケット動かさない Bラケット消去 ボール方向が下の時のBラケットボール到着予定位置Q ボール方向が上の時のBラケットボール到着予定位置Q Qに応じてBラケットを上に移動 Qに応じてBラケットを下に移動 Bラケット表示 20ms毎に繰り返す

回路図


PONGの表示画面


Arduino ProMiniを使い
完成後は電池で動作させた例

13.4 ビデオゲーム「シンプルシューター」を作る(学生の作品)

 以下のプログラムは学生の作品です。

シンプルシューター(製作: 山田駿太郎)
 外観は以下の写真のようになっている。また、ディスプレイに表示される画面にはプレイ画面とリザルト画面が あり、それぞれ以下のような画面である。
 プレイ画面の下側に表示された細長いものがプレイヤーの操作する自機である。ボリュームを回すことによって、左右に移動することができる。また、ボタンを押すと自機から真っ直ぐと画面上側に向かって弾が発射される。弾を発射すると、何かにぶつかるまで再び弾を発射することはできない。
 自機のほかに表示されている二つの大きな四角がエネミーである。エネミーは画面上部から出現し、画面下側へ向かってゆっくりと直進する。エネミーに自機の発射した弾が命中すると、そのエネミーは消失し、画面上部から新たなエネミーが出現する。また、エネミーは画面最下部に到着することでも消失し、その場合も新たに画面上部から出現する。この際、右上に表示される3つの小さな四角のうち、1つが消える。これは残りライフであり、すべて無くなるとゲームオーバーとなってリザルト画面に移行する。
 リザルト画面ではGAME OVERの文字とともにスコアが表示される。スコアは倒したエネミーの数×10点で計算される。より高いスコアを目指すことがこのゲームの目的であり、楽しみでもある。
10 cls: clear: w=48: h=48: s=0: i=0: e=0: a=0: n=0: c=0 20 line 0,0,w-1,0,1: line w-1,0,w-1,h,1 30 line w-1,h-1,0,h-1,1: line 0,h-1,0,0,1 40 if n<=2 plot 50,3,2: if n<=1 plot 50,5,2: if n<=0 plot 50,7,2 50 p=aread(0)/22 60 if p<3 p=4 70 if p>w-4 p=w-5 80 if s=1 plot x,y,2 90 box p,w-4,p,w-3,2: plot x,y,1: box r-1,l-1,r+1,l+1,11: box t-1,m-1,t+1,m+1,11: delay 20 100 box p,w-4,p,w-3,2: plot x,y,0: box r-1,l-1,r+1,l+1,10: box t-1,m-1,t+1,m+1,10 110 if a=0 a=1: goto 140 130 if a=1 l=l+1: m=m+1 : a=0 140 if dread(12)=0 i=1: goto 150 150 if dread(12)=1 i=0 160 if s=0 if i=1 x=p: y=h-4 : s=1 170 y=y-1 180 if y=0 s=0 190 if e=1 goto 220 200 r=rnd(40): r=r+4: e=1 210 l=3 220 if r>x-2 if r<x+2 if l>y-2 if l<y+2 e=0: s=0: y=-256: c=c+1 250 if l=48 e=0: n=n+1 260 if f=1 goto 290 270 t = rnd(40): t=t+4 : f=1 280 m=3 290 if t>x-2 if t<x+2 if m>y-2 if m<y+2 f=0: s=0 : y=-256: c=c+1 300 if m=48 f=0: n=n+1 400 if n>=3 goto 420 410 goto 20 420 cls: ? "GAME OVER" 430 ? "SCORE:",c*10

外観


プレイ画面


リザルト画面

(参考資料)

[1] George Gray, Half-byte tinyBasic, https://halfbyteblog.wordpress.com/
[2] TinyBasic を動かしてみる, http://ht-deko.com/arduino/tinybasic.html
[3] DSbasic, http://desktopstation.net/wiki/doku.php/dsbasic
[4] グラフで考える直流回路, http://siva.cc.hirosaki-u.ac.jp/usr/koyama/lecture/it/elec.html


(付録1) tinyBasicのArduinoへの書き込み

  1. Arduino 1.6.12をダウンロードし、インストールします。
  2. Arduinoを起動し、[ファイル][環境設定]で、スケッチブックの保存場所を確認し、その下にlibrariesフォルダを作ります。
  3. 液晶モジュールを利用するために、ST7032のライブラリ(ZIPファイル)をダウンロードし、解凍して上記librariesフォルダに置きます。
  4. NeoPixel LEDを利用するために、NeoPixelのライブラリ(ZIPファイル)をダウンロードし、解凍して上記librariesフォルダに置きます。
  5. [ツール][ボード]で「Arduino NANO」を選択し、[ツール][プロセッサ]で「ATmega328」を選択します。また、Arduino NANOを接続し、[ツール][シリアルポート]でポートを選択します。
  6. tinybasic20170303.zipを解凍します。ビデオ機能を利用しない場合はこのままでOKです。ビデオ表示やキーボードを接続する場合は、下表を参考に1行目のTVMODEの定義を1~4に変更します。有効にする機能によって作成できるプログラムのサイズ(概ね文字数)が異なります。12.212.4のように少し大きなプログラムの場合は、TVMODEを3にします。
    TVMODE 0 1 2 3 4
    I2C接続(液晶ディスプレイ,
    モータ, 音声合成, 温湿度センサ)
    - -
    NeoPixel - - - -
    ビデオ表示 -
    キーボード接続 - - -
    最大のプログラムサイズ(バイト)1022700584953837
  7. ビデオ表示を有効にする場合は、ArduinoIDEの「ライブラリを管理」でTVoutライブラリを組み込んでおきます。TVoutフォルダの中にTVoutfontsフォルダがあるので、librariesフォルダの下に移動します。また、video_gen.zipを解凍し、TVoutフォルダの中のvideo_gen.cppを上書きします。今のArduinoのバージョンではasm_macros.hで行っているアセンブラのマクロ定義がうまく展開されずエラーとなるため、マクロ定義を使わないようにしました。
  8. キーボードを接続する場合は、ArduinoIDEの「ライブラリを管理」でPS2Keyboardライブラリを組み込んだ後、日本語キーボードに対応するためにDECOさんの「PS/2キーボードを接続してみる」の下の方にあるps2keyaoard_240_mod.zipを解凍し、2つのファイルを上書きします。
  9. 以上の準備ができたらtinybasic.inoをArduino NANOに書き込みます。

 プログラム(tinybasic.ino)は、「Tyny Basic を動かしてみる[2]」のページの中ほどにある「HalfByteTinyBasic_mod.zip」を拡張したものです。拡張した主な内容は以下のとおりです。

  1. プログラム実行の中断(ブレーク)をESCキーで行うようにしたこと
  2. 入力ミスの修正にBS(BackSpace)キーを使えるようにしたこと
  3. 起動時にEEPROMに保存されているプログラムをLOADし、3秒後に自動実行するようにしたこと(プログラムが完成したらパソコン無しで動作する)
  4. %(剰余) &(and) |(or) !=(<>と同じ)の演算子を使えるようにしたこと
  5. dRead()で内部プルアップ抵抗を有効にしたこと
  6. PRINTコマンドで液晶ディスプレイ(ST7032)にも表示させるようにしたこと
  7. 温湿度センサ(HDC1000)用に「tRead」「hRead」関数を追加したこと
  8. サーボモーター制御用に「SERVO」コマンドを追加したこと
  9. モーター制御用に「MOTOR」コマンドを追加したこと
  10. 「TONE」コマンドを追加したこと
  11. 音声合成用に「TALK」コマンドを追加したこと
  12. NeoPixel LED制御用に「PIXEL」コマンドを追加したこと
  13. 7セグメントLED表示用の「NUMLED」コマンドを追加したこと
  14. 「PRINT」コマンドでビデオ(有効な場合)と液晶ディスプレイにも表示されるようにしたこと。関連して、「CLS」コマンドや「CURSOR」コマンドもそれらの表示デバイスに適用されるようにしたこと。
  15. ビデオ表示が有効な場合、「SET」「RESET」コマンドの代わりに「PLOT」コマンドを使い、パラメータで色指定するようにしたこと。また、ビットマップパターンをビデオ表示する「BMP」コマンドを追加したこと

 下図左のようにテレビやキーボードと接続すると、パソコンを使わずにプログラミングができます(このテキストでは扱っていません)。接続したキーボードとパソコンの通信ソフトのどちらからも操作できます。キーボードの接続については、「PS/2キーボードを接続してみる」を参考にしました。


テレビとキーボードの接続

テレビの表示画面

(付録2) 入出力に利用できるピン

 入出力に利用できるピンは下表のとおりです。D0~D13はすべてデジタル入力(Din)、デジタル出力(Dout)として利用できますが、アナログ出力(Aout)として利用できるピンは限られています。また、アナログ入力(Ain)として利用できるピンは専用に用意されています。

ピン AVR 通常 7seg
利用時
ビデオ
利用時
ビデオ・PS/2
キーボード
利用時
備考
D0PD0RX Din: dRead()で利用
(内部プルアップ抵抗有効)

Dout: dWriteで利用

Aout: aWriteで利用
D1PD1TX
D2PD2 Din Douta Din DoutPS2 Data
D3PD3 Din Dout Aoutb Din Dout Aout*PS2 Clock
D4PD4 Din Doutc Din Dout
D5PD5 Din Dout Aoutd Din Dout Aout
D6PD6 Din Dout Aoute Din Dout Aout
D7PD7 Din Doutf Video out
D8PB0 Din Doutg Din Dout
D9PB1 Din Dout Aout *Video sync
D10PB2 Din Dout Aout
D11PB3 Din Dout Aout *Audio out
D12PB4 Din Dout
D13PB5 Din Dout
A0/D14PC0 Ain Din Dout aRead() で利用
A1/D15PC1 Ain Din Dout
A2/D16PC2 Ain Din Dout
A3PC3 NeoPixel Pixelで利用
A4PC4 SDA I2Cデバイスを接続
tRead(), hRead(), motor, talk, LCD表示などで利用
A5PC5 SCL
A6 Ain aRead() で利用
A7 Ain

(付録3) tinyBasic リファレンス...グリーンが拡張箇所

□ コマンド

NEW
プログラムを初期化する。変数は「0」に初期化されないので注意。

CLEAR
A~Zのすべての変数の値を0にする。

MEM
メモリーの残量を表示する。

命令文
命令文を入力すると直ちに実行される(行末はEnterキー)。「A=1: B=2: PRINT A+B」のように「:」で区切って1行に複数の文を書いてもよい。

行番号 命令文
行番号をつけて命令文を入力すると、プログラムとして格納される(格納されたプログラムはRUNで実行)。

RUN
格納されたプログラムを先頭の行番号から実行する。

SAVE
EEPROMにプログラムを保存する。最大1000バイトで、プログラムは1つしか保存できない。

LOAD
EEPROMに保存したプログラムをロードする。起動時には自動的にLOADが行われる。

LIST
プログラムすべてを表示する。
LIST 行番号
行番号以降のプログラムを表示する。
LIST 行番号.
行番号のプログラムだけを表示する。

□ 数と変数と演算

-32767~32767の整数。16進表記の場合は、$FF, &FF, #FF のようにする。

定数(組み込み定数)
HIGH(またはHI)は「1」、LOW(またはLO)は「0」と同じ。

文字列
文字列は「"」で囲む。日本語も表示できる。

変数
変数名はA~Zで、大文字小文字は区別されない。変数には-32767~32767の整数を格納できる。
演算
+(加算) -(減算) *(乗算) /(除算) %(剰余) &(and) |(or) !(0なら1, 1なら0)
=(等しい) <>(等しくない) !=(等しくない) >(大きい) >=(以上) <(小さい) <=(以下)

□ 命令文

REM または #
以降行末までコメントとなる。メモリーを消費するので注意。

代入
変数に値を代入する。LETは省略してよい。
(例) LET A=100

POKE 番地, 値
@ 番地, 値
指定した番地に値を書き込む。
(例) POKE 1450,64...1450番地の値を64にする
(例) POKE TOP(0),"ABCDE"...メモリーのフリーエリアの先頭に文字列を"ABCDE"を格納する。文字列の末尾には改行コード(13)が付加される。
利用する時はPEEKを使い、何度も使う文字列で利用する。POKEとINKEYを組み合わせると、実行時に入力した文字列をしまっておくことができる。

FOR ループ
以下の例は1~20を表示する。 10 FOR X=1 TO 20 20 PRINT X 30 NEXT X 以下の例は10~100を表示する。 10 FOR X=0 TO 100 STEP 10 20 PRINT X 30 NEXT X ECHO 「FOR」と「NEXT」を同じ行に書くことはできない。

NEXT 変数
対応するFORループを終了する。

IF 式 文
式がtrueなら文を実行する。式に論理演算を使えないが、「IF A=1 IF B=2 PRINT A+B」のような方法で「AND」を表現できる。ELSEはない。

GOTO 行番号
指定した行番号にジャンプする。

GOSUB 行番号
指定した行番号のサブルーチンを呼び出す。サブルーチンは同じ処理を何度も実行する時に使う。

RETURN
サブルーチンを終了し、呼び出されたGOSUBの次の行に戻る。

STOP
プログラムを終了し、編集画面に戻る。

DELAY 時間
時間はms単位で指定する。

CLS
通信ソフトの画面、I2C液晶およびビデオ画面をクリヤーする。

PRINT 変数や文字列
? 変数や文字列
変数や文字列を通信ソフト(teraTerm等)の画面とI2C液晶とビデオ画面にに表示する。「,」で区切ると、複数の変数や文字列が連続して表示される。末尾に「;」を置くと改行されない。

INPUT 変数
変数に数値を入力する。 10 X=0 20 PRINT "X? ";: INPUT X 30 PRINT X

CURSOR X, Y
カーソル位置を(X,Y)にする。XとYは4×6ドットの文字単位で位置を指定。その後printで文字を表示する。

AWRITE ピン番号, 値
指定したピン(D3,D5,D6,D9,D10,D11)に値(0~255)を出力する。

DWRITE ピン番号, 値
指定したピン(D2~D13)に値(0または1)を出力する。これを使ってLEDやモーターをオンオフできる。

MOTOR 左速度, 右速度
左速度、右速度はそれぞれ-255~255。0で停止。

SERVO ピン番号, 角度
接続したピン(D2~D13)のサーボモーターを指定した角度(0~180)に動かす。

TONE ピン番号, 周波数, 時間
周波数はHz、時間はmsで指定する。ただし、以下に記すTVと接続した場合は、ピン番号は指定しない(D11ピン固定)。

NUMLED 数
D2~D8に接続した7セグメント数字表示LEDに「数(0~9)」を表示する。0~9以外の数を指定すると表示が消える。

PIXEL LED番号, R, G, B
指定したLED(0~9)に指定した色で点灯する。R(赤),G(緑),B(青)の明るさはそれぞれ0~255で指定する。なお、NeoPixelの制御信号(Din)はA3(D17)に接続する。

□ 関数

 括弧内に引数を与えると、値が返される。

ABS, POW, SIN, COS
ABS(絶対値)、POW(べき乗)など、関数として利用する。SIN, COSは「度」で角度を与え、1000倍した値が返される。COS(60)は500となる。

RND(数)
0から指定した数-1までの乱数を返す。RND(5)は0~4のいずれかの整数値を返す。

PEEK(番地)
指定した番地の値を返す。PEEKを使って、ユーザが入力した文字列を調べることができる。

TOP(0)
未使用メモリーの先頭番地を返す。文字列を格納する時に用いる。

CHR(文字コード)
指定した文字コードの文字を返す。「PRINT CHR(65)」で「A」が表示される。

INKEY(初期値)
キー操作のASCIIコードを返す。キーが押されるのを待つわけではない。 10 M=N 20 A=INKEY(0) 30 IF A=-1 GOTO 20 40 IF A=13 @M,13: RETURN 50 PRINT CHR(A); @M,A 60 M=M+1 70 GOTO 20 文字列を入力してもらう。キーが押されなければ、AはINKEYから0を受け取る。一度キーが押されると、そのASCIIコードがAの値となる。ENTERが押されると、メモリーに改行が格納される。メモリーの番地は+1される。

AREAD(ピン番号)
指定したアナログピン(A0~A7)から値を読み取る。0~1023の値が返される。

DREAD(ピン番号)
指定したピン(D2~D13)から値を読み取る。0か1の値が返される。0はLOW、1はHIGHを表す。

TREAD()
I2C接続した温湿度センサー(HDC1000)が計測した温度(℃)が返される。

HREAD()
I2C接続した温湿度センサー(HDC1000)が計測した湿度(%)が返される。

□ 命令文と関数(グラフィック)...TV表示が有効な場合

CURSOR X, Y
カーソル位置を(X,Y)にする。XとYは文字単位で位置を指定。その後printで文字を表示する。

PLOT X座標,Y座標,色
指定した座標(X,Y)にプロットする。色は0(黒), 1(白), 2(反転)で、以下についても同様。

LINE 始点のX座標, 始点のY座標, 終点のX座標, 終点のY座標, 色
画面に線を表示する。

BOX 始点のX座標, 始点のY座標, 終点のX座標, 終点のY座標, 色
画面に四角形を表示する。色は0(黒線), 1(白線), 2(線反転), 10(黒ベタ), 11(白ベタ), 12(反転ベタ), 他(白線黒ベタ)...反転表示に不具合あり!

CIRCLE 中心のX座標, 中心のY座標, 半径, 色
指定した座標に円を描く。色は0(黒線), 1(白線), 2(線反転), 10(黒ベタ), 11(白ベタ), 12(反転ベタ), 他(白線黒ベタ)...反転表示に不具合あり!

GET(X座標, Y座標)
座標(X,Y)の値(0または1)を返す。

SHIFT 移動量, 方向
表示画面を指定方向に指定量シフトする。方向は 0(UP), 1(DOWN), 2(LEFT), 3(RIGHT)。

INVERT
画面を白黒反転する。

BMP(X座標, Y座標, 文字列)
BMP(X座標, Y座標, 文字列, 文字列, ...)
座標(X,Y)からX方向に文字列で表したビットパターンを連続表示する。「0」~「F」の文字で4ピクセルの色(0:黒, 1:白)を表す(下表)。文字列を「,」で区切ると次のビットパターンは座標(X,Y+1)から表示される。
文字ピクセルパターン
(0:黒, 1:白)
文字ピクセルパターン
(0:黒, 1:白)
0
1
2
3
4
5
6
7
0000
0001
0010
0011
0100
0101
0110
0111
8
9
A
B
C
D
E
F
1000
1001
1010
1011
1100
1101
1110
1111

(付録4) 入出力モジュールの例

 ブレッドボードで利用できる入出力モジュールの製作例です。

モジュール回路図製作例
可変抵抗モジュール
明るさセンサーモジュール
温度センサーモジュール
赤外線反射モジュール
音量センサーモジュール
10連LEDモジュール
NeoPixel(DinはA3に接続)
モーターモジュール
右モーターはA0とA1をGNDに接続
音声合成モジュール

(付録5) 使用する主な部品

部品表
名称 外観 備考
ブレッドボード EIC-701 EIC-701 秋月 300円
ブレッドボード ジャンパーワイヤ 15cm   秋月 10本 300円
ブレッドボード ジャンパーワイヤ EIC-J-S   秋月 250円
LED 秋月 10個 120円
フルカラーシリアルLEDテープ SwitchScience 756円
数字表示LED C-551SR 秋月 40円
圧電スピーカー 秋月 2個 100円
プッシュスイッチ DS-660R-C 千石 84円
抵抗 1/4W 4.7Ω, 100Ω, 220Ω, 470Ω, 1kΩ, 10kΩ 秋月 100本 100円
CDS(光センサ)秋月 30円
温湿度センサ HDC1000秋月 680円
温度センサ LM61CIZ秋月 4個 200円
赤外線反射センサ LBR-127HLD   秋月 50円
サーボモーター SG-90   秋月 400円
液晶ディスプレイ AQM1602A   秋月 550円
モータードライバ DRV8830   秋月 170円
音声合成LSI ATP3011F4   秋月 850円
Arduino Nano 秋月 2780円
(相当品が300~400円で入手可)

koyama@hirosaki-u.ac.jp