Pythonプログラミング ものづくり(Seeeduino XIAO版)

小山智史(koyama88@cameo.plala.or.jp)

コンピュータの仕組み ATtiny2313版 - - - - -
Arduinoでものづくり ATtiny4313版 ATmega328P版 NANO版 - ESP8266版 XIAO版
tinyBasicでものづくり - - NANO版 ProMicro版 ESP8266版 XIAO版
Pythonでものづくり - - - - - XIAO版
Javascriptでものづくり - - - ProMicro版 - XIAO版

目次


0. 準備
1. コマンド操作
2. デジタル出力(1): LEDの点滅
3. デジタル入力: スイッチ操作でLEDのオンオフ
4. アナログ出力: LEDの明るさを変える
5. アナログ入力
6. デジタル出力(2): 数字表示LED
7. デジタル出力(3): 液晶ディスプレイ
8. センサーの利用
9. ブザー音で演奏
10. 合成音声でおしゃべり
11. イルミネーション
12. 赤外線リモコン
13. 動くものを作る
14. 押しボタン信号機
15. シリアル通信教材(光通信)
16. ビデオ表示
(付録1) 入出力モジュール
(付録2) 使用する主な部品
(付録3) 入出力に利用できるピン
(付録4) tinyBasicの書き込み
(付録5) tinyBasic リファレンス
(付録6) 文字と文字コード


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

☆ 本テキストは実習時に適宜解説や補足を行うことを前提にしています。自学自習には適していないかもしれません。

0. 準備

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

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

(外観) (内部の接続)

 ブレッドボードにXIAOを差し込み、GNDと3V3の端子を図のように接続します。USBケーブルをパソコンに接続すると、電源(3.3V)がパソコンから供給されます。XIAOに見やすいラベルを貼っておくことをお勧めします(テプラファイル)。

(回路図) (実体配線図)

 初めてブレッドボードを使う場合は、練習としてLEDと抵抗を下図左のように接続して点灯を確認しましょう。電源はパソコンからUSB端子を通じて供給され、電流の流れは下図右のようになります。ブレッドボードの内部の結線も含めて「電流が流れる経路」を意識することが重要です。

(回路図) (実体配線図) (電流の流れ)

0.2 CircuitPythonの準備

 付録4を参考に、Seeeduino XIAOにCircuitPythonのプログラムを書き込みます。

 CircuitPythonのアプリケーションプログラムは、パソコンのCIRCUITPYTHONドライブ上のcode.pyという名前のファイルとして、テキストエディタで直接編集することができます。

 このプログラムは電源投入時に自動的に読み込まれてプログラムが自動実行されます。

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

0.3 通信ソフトTeraTermの準備

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

 通信ソフトで接続すると「HalfByte...Hitany key!」の表示が現れるので、何かキーを押します。そして、「0.2 ArduinoにPythonを準備」の後初めて使う場合に一度だけ「new」「save」の操作をしてください。


1. コマンド操作

1.1 コマンド操作(Python命令の実行)

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


パソコンの通信ソフト(TeraTerm)の表示画面
 はじめに、コマンドで直接指示を与える方法を試してみましょう。
print(100)
これは、「100を表示しなさい」という「命令」を表していて、この1行が「命令文」になっています。「文」といっても、疑問文や感嘆文があるわけではありません。コンピュータ(Python)に対して指示を与える「命令文」があるだけです。

 では、次のようにするとどうでしょうか。
print(100*200/50+2) print(30000+2000) print(30000+8000)
それぞれ計算結果が表示されますが、最後の命令文は計算結果が扱える数「-32767~32767」を越えてしまったため、正しく表示されません。

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

print("ABC") print("青森県") print("100+200") print("100+200=",100+200)

1.2 変数

 「変数」は値をしまっておく入れ物です。変数には整数(-1073741824~1073741823)や浮動小数点数や文字列を格納できます。

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


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

2.1 LEDを点灯・消灯する

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

(回路図) (実体配線図)

 以下、「#」以降行末まではコメントですので、入力する必要はありません。

from arduino import * # 各種定義を読み込む led=pinMode(D9, OUTPUT) # ledはD9で出力 while True: # 以下を繰り返す led.value=HIGH # ledの値をHIGH(点灯) sleep(1) # 1秒待つ led.value=LOW # ledの値をLOW(消灯) sleep(1) # 1秒待つ

 マイコンにプログラムを保存し、LEDが点滅することを確かめてください。プログラムに誤りがあると、teraTermの画面にエラーメッセージが表示されます。

 以下、プログラムの内容について簡単に説明します。

 下図のようにマイコン内部のスイッチがプログラムで切り替えられ、LEDが点滅すると考えればわかりやすいと思います。スイッチがHIGHになるとLEDに電流が流れ、LOWになると電流が流れません。

 この例のように、「Arduino流」CircuitPythonのプログラムは以下のように作ります。

  1. はじめに各種定義が記載されたarduino.pyを読み込みます。
  2. 2行目以降に電源投入後1度だけ実行したいプログラムを記載します。
  3. 繰り返して実行したいプログラムはWhile以下に記載します。
  4. 各行の「#」以降はコメントとなります。
  5. HIGHやLOWやOUTPUTは「組み込み定数」で、あらかじめ値が定義されています(付録1)。
  6. pinMode()はあらかじめ定義された「組み込み関数」です(付録1)。

(練習) LEDを1秒毎に交互に点滅させ、D9の電圧をテスターで測りなさい。

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

2.2 LEDを高速に点滅させる

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

 7行目と9行目を「sleep(1)」→「sleep(0.1)」→「sleep(0.02)」→「sleep(0.01)」→「sleep(0.001)」と変えて、点滅の様子を確認してください。「sleep(0.01)」の時は10ms点灯、10ms消灯を繰り返し、50Hzの点滅になります。「sleep(0.02)」(25Hz)の時は点滅しているのがわかりますが、「sleep(0.01)」(50Hz)では点滅を感じなくなります。人間の目が感じる「点滅のちらつき」はフリッカと呼ばれ、高速の点滅(50~60Hz)ではちらつきを感じなくなります。

 通常の蛍光灯は100Hz(50Hzの2倍)で点滅しますが、蛍光管の状態によっては発光にムラが生じ50Hzで点滅し、その場合はちらつきが気になることがあります。インバーター式の蛍光灯はちらつきを抑えるために超高速(数10kHz)で点滅させています。また、ブラウン管テレビが毎秒30フレーム(実際にはインターレースで60Hzのフリッカ)、映画のフィルムが毎秒24フレーム(実際には同じフレームを2度映すことで48Hzのフリッカ)になっているのは、このような目の特性を考慮してのことです。

(練習) LEDの点滅速度を30Hz, 40Hz, 50Hz, 60Hzと変化させ、ちらつきが感じられるかどうかを、ブレッドボードを静止した場合と、左右に振った場合の両方で調べなさい。

(練習) 緑色LEDと1kΩの抵抗をD8に追加接続し、そのLEDを1秒毎に点滅させなさい。また、赤色LEDと緑色LEDが1秒毎に「交互に」点灯するようにしなさい。


3. デジタル入力: スイッチ操作でLEDのオンオフ

3.1 スイッチが押されているかどうか調べる

 下図左のようにD2にスイッチを接続し、プログラム中で「pinMode(D2, INPUT_PULLUP)」とすると、下図右のようにマイコンに内蔵されたD2の抵抗が有効になります。すると、D2はスイッチをオンにすると低い電圧(0V)になり、スイッチをオフにすると高い電圧(3.3V)になります。この抵抗を「プルアップ抵抗」といいます。「高い電圧に引っ張り上げる抵抗」というような意味です。


スイッチを接続

内蔵プルアップ抵抗

 D2が高い電圧(1)か低い電圧(0)かは「digitalRead(D2)」で読み取ります。

(練習) 上図のようにスイッチを接続し、以下のプログラムを実行しなさい。スイッチを押した時と離した時のそれぞれについて、D2の電圧をテスターで計りなさい。

from arduino import * pinMode(D2, INPUT_PULLUP)

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

 以下のプログラムは、buttonがLOW(0)ならば(スイッチが押されていれば)ledを点灯し、HIGH(1)ならばledを消灯します。

 「button.value」がHIGH(高い電圧)かLOW(低い電圧)かを調べ、LOWであれば「led.value=HIGH」、そうでなければ「led.value=LOW」が実行されます。

from arduino import * # 各種定義を読み込む button=pinMode(D2, INPUT_PULLUP)# buttonはD2で入力(プルアップ抵抗有効) led=pinMode(D9, OUTPUT) # ledはD9で出力 while True: # 以下を繰り返す if button.value==LOW: # buttonの値がLOW(押されている)なら led.value=HIGH # ledの値をHIGH(点灯) else: # buttonの値がHIGH(押されていない)なら led.value=LOW # ledの値をLOW(消灯)
回路図

 次のようにしても同じです。

from arduino import * button=pinMode(D2, INPUT_PULLUP)# buttonはD2で入力(プルアップ抵抗有効) led=pinMode(D9, OUTPUT) # ledはD9で出力 while True: # 以下を繰り返す led.value=not button.value # ledの値はbuttonの値がLOW(押されている)ならHIGH(点灯) # HIGH(押されていない)ならLOW(消灯)
回路図

3.3 スイッチ操作でLEDを点灯・消灯する

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

 「buttonState=button.value」で、現在のbuttonの状態(HIGHまたはLOW)を読み取り、buttonStateに代入します。次に、それがlastState(直前のbuttonの状態)と違っていて、かつそれがHIGHなら、ledState(ledの状態)を反転します。そしてledStateをledに出力します。

from arduino import * # 各種定義を読み込む button=pinMode(D2, INPUT_PULLUP)# buttonはD2で入力(プルアップ抵抗有効) led=pinMode(D9, OUTPUT) # ledはD9で出力 buttonState=HIGH # 現在のbuttonの状態 lastState=HIGH # 直前のbuttonの状態 ledState=LOW # ledStateをLOWにする(現在の点灯状態は消灯) while True: # 以下を繰り返す buttonState=button.value # buttonの値をbuttonStateに代入 if buttonState!=lastState and buttonState==LOW: # 前の状態と違いかつLOWなら ledState=not ledState # ledStateを反転(HIGHならLOW, LOWならHIGH) led.value=ledState # ledにledStateを出力 lastState=buttonState # lastStateを更新 sleep(0.01) # チャタリングを考慮し少し待つ
回路図

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

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

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

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

 上の例ではledStateを記憶し、スイッチが押されるとその値を反転させていましたが、ここではCounterにスイッチが押された回数を記憶します。「buttonState!=lastState and buttonState==LOW」つまりbuttonState(現在のbuttonの状態)がlastState(直前のbuttonの状態)と違っていてかつそれがHIGHなら、次行でCounterの値に1を加えます。そして「Counter%4==0」つまりCounterの値を4で割った余りが0であれば、ledにHIGHを出力し、そうでなければLOWを出力します。

from arduino import * # 各種定義を読み込む button=pinMode(D2, INPUT_PULLUP)# buttonはD2で入力(プルアップ抵抗有効) led=pinMode(D9, OUTPUT) # ledはD9で出力 counter=0 # counter(スイッチ操作回数)を0にする buttonState=HIGH # 現在のbuttonの状態をHIGHにする lastState=HIGH # 直前のbuttonの状態をHIGHにする while True: # 以下を繰り返す buttonState=button.value # buttonの値をbuttonStateに代入 if buttonState!=lastState and buttonState==LOW: # buttonの状態が前と違いかつLOWなら counter=counter+1 # counterの値に+1 lastState=buttonState # lastStateを更新 if counter%4==0: led.value=HIGH # ledを点灯 else: led.value=LOW # ledを消灯 sleep(0.01) # チャタリングを考慮し少し待つ
回路図

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

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

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

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

from arduino import * # 各種定義を読み込む button=pinMode(D2, INPUT_PULLUP)# buttonはD2で入力(プルアップ抵抗有効) led=pinMode(D9, OUTPUT) # ledはD9で出力 while True: # 以下を繰り返す if button.value==LOW: # buttonが押されていれば led.value=HIGH # ledを点灯 sleep(3) # 3秒待つ else: # そうでなければ led.value=LOW # ledを消灯
回路図

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

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

4.1 LEDの明るさを変える

 1.2ではLEDの点灯時間と消灯時間を変えてどのように見えるかを確認しました。ここでは7行目と9行目を「sleep(0.001)」にしてみます。

from arduino import * # 各種定義を読み込む led=pinMode(D9, OUTPUT) # ledはD9で出力 while True: # 以下を繰り返す led.value=HIGH # ledを点灯 sleep(0.001) # 1ms待つ led.value=LOW # ledを消灯 sleep(0.001) # 1ms待つ

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

PWM波形

 次に、はじめの「sleep(0.001)」はそのままにして次の「sleep(0.001)」を「sleep(0.009)」としてみてください。こうすることにより、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)。

from arduino import * # 各種定義を読み込む led=pinMode(D9, OUTPUT_ANALOG)# ledはD9でANALOG出力(PWM) while True: analogWrite(led, 10)

(練習) 「analogWrite(led, 10)」の「10」を0~255の間でいくつか変えて、LEDの明るさを観察しなさい。また、その時の波形をオシロスコープで観察して記録しなさい。

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

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

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

PWM波形

 以下は、デューティ比を0%から100%まで少しずつ変化させるプログラムです。暗い状態から次第に明るくなり、その後次第に暗くなり、これを繰り返します。「analogWrite(led, bright)」でbrightの値をアナログ出力し、「bright=bright+fade」でbrightの値をfadeだけプラスしています。

from arduino import * # 各種定義を読み込む led=pinMode(D9, OUTPUT_ANALOG)# ledはD9でANALOG出力(PWM) bright=5 # brightを5にする fade=5 # fadeを5にする while True: # 以下を繰り返す analogWrite(led, bright) # ledの値をbrightにする bright=bright+fade # brightの値にfadeの値を足し込む if bright==0 or bright==255:# brightの値が0または255だったら fade=-fade # fadeの符号を逆にする sleep(0.03) # 30ms待つ

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


5. アナログ入力

5.1 アナログ値の入力

 analogRead(A1)は、A1の電圧(0~3.3V)に応じて0~1023の値をとります。以下のプログラムはこれを利用し、ボリュームの値に応じてLEDの点滅時間が変化します。

from arduino import * # 各種定義を読み込む led=pinMode(D9, OUTPUT) # ledはD9で出力 vr=pinMode(A1, INPUT_ANALOG) # vrはA1でANALOG入力 while True: # 以下を繰り返す val=analogRead(vr) # val: 0-1023 led.value=HIGH # ledを点灯 sleep(val/1023) # val/1023秒待つ led.value=LOW # ledを消灯 sleep(val/1023) # val/1023秒待つ
回路図

5.2 アナログ値に応じてLEDを点灯・消灯

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

from arduino import * # 各種定義を読み込む led=pinMode(D9, OUTPUT) # ledはD9で出力 vr=pinMode(A1, INPUT_ANALOG) # vrはA1でANALOG入力 while True: # 以下を繰り返す val=analogRead(vr) # val: 0-1023 if val<400: # valが400未満なら led.value=HIGH # ledを点灯 else: # そうでなければ led.value=LOW # ledを消灯
回路図

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

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

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

from arduino import * # 各種定義を読み込む led=pinMode(D9, OUTPUT_ANALOG)# ledはD9でANALOG出力 vr=pinMode(A1, INPUT_ANALOG) # vrはA1でANALOG入力 while True: # 以下を繰り返す val=analogRead(vr) # val: 0-1023 analogWrite(led, val//4) # 0-255 sleep(0.03) # 30ms待つ
回路図

6. デジタル出力(2): 数字表示LED

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

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

回路図

 D4~D10はLEDのa~gのセグメントに接続されていて、それぞれ HIGH(1)を出力した時に点灯、LOW(0)を出力した時に消灯する回路となっています。下表は、各数字を表示する際にa~gの各々に何を出力すればよいかと、その16進数値を示したものです。

数字 D10
g
D9
f
D4
e
D5
d
D6
c
D7
b
D8
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」を表示するものです。

from arduino import * # 各種定義を読み込む a=pinMode(D8, OUTPUT) # aはD8で出力 b=pinMode(D7, OUTPUT) # bはD7で出力 c=pinMode(D6, OUTPUT) # cはD6で出力 d=pinMode(D5, OUTPUT) # dはD5で出力 e=pinMode(D4, OUTPUT) # eはD4で出力 f=pinMode(D9, OUTPUT) # fはD9で出力 g=pinMode(D10, OUTPUT) # gはD10で出力 while True: # 以下を繰り返す a.value=HIGH # aを点灯 b.value=LOW # bを消灯 c.value=HIGH # cを点灯 d.value=HIGH # dを点灯 e.value=LOW # eを消灯 f.value=HIGH # fを点灯 g.value=HIGH # gを点灯
回路図

 以下は、上の表をもとに0~9の7セグメントパターンをsegdataに用意し、これを使って表示するようにしたものです。

from arduino import * # 各種定義を読み込む a=pinMode(D8, OUTPUT) # aはD8で出力 b=pinMode(D7, OUTPUT) # bはD7で出力 c=pinMode(D6, OUTPUT) # cはD6で出力 d=pinMode(D5, OUTPUT) # dはD5で出力 e=pinMode(D4, OUTPUT) # eはD4で出力 f=pinMode(D9, OUTPUT) # fはD9で出力 g=pinMode(D10,OUTPUT) # gはD10で出力 segdata=( # 0-9のセグメントデータ 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x27,0x7f,0x67 ) while True: # 以下を繰り返す for pattern in segdata: a.value=pattern&0x01 b.value=pattern&0x02 c.value=pattern&0x04 d.value=pattern&0x08 e.value=pattern&0x10 f.value=pattern&0x20 g.value=pattern&0x40 sleep(1)
回路図

 以下は、予め定義したnumLed(a~gのpinMode設定)とnumdisp(0~9の表示)を使って表示させるようにしたものです。

from arduino import * # 各種定義を読み込む LED=numLed() # LEDは(a,...,g) while True: # 以下を繰り返す numDisp(5, LED) # LEDに5を表示 sleep(1) # 1秒待つ
回路図

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

6.2 数字表示LEDにカウントアップ表示する

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

from arduino import * # 各種定義を読み込む LED=numLed() # LEDは(a,...,g) while True: # 以下を繰り返す for i in range(10): # i: 0-9 numDisp(i, LED) # LEDにiを表示 sleep(1) # 1秒待つ
回路図

(練習) 数字表示が9~0にカウントダウンするプログラムを作りなさい。

(練習) 1秒毎に「H」「E」「L」「L」「O」を表示し、その後3秒間消え、再びこれを繰り返すプログラムを作りなさい。また、「E」「r」「r」「o」「r」の表示にしてみなさい。また、自分の名前を表示できるか工夫してみなさい。

6.3 スイッチを押すと数字表示をカウントアップする

 スイッチを押すと数字表示をカウントアップするには次のようにします。

from arduino import * # 各種定義を読み込む LED=numLed() # LEDは(a,...,g) button=pinMode(D2, INPUT_PULLUP)# buttonはD2で入力(プルアップ抵抗有効) count=0 # 表示する数 while True: # 以下を繰り返す if button.value==LOW: # buttonがLOWなら count=(count+1)%10 # countの値をプラス1する(9の次は0) sleep(0.3) # チャタリングを考慮し少し待つ numDisp(count, LED) # LEDにcountを表示
回路図

(練習) スイッチを追加し、一方のスイッチでカウントアップ、もう一方のスイッチでカウントダウンするプログラムを作りなさい。

(練習) 1秒毎ではなく10ms毎に高速でカウントアップするようにしなさい。また、スイッチが押されるとカウントを停止するようにしなさい。

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

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

from arduino import * # 各種定義を読み込む LED=numLed() # LEDは(a,...,g) vr=pinMode(A1, INPUT_ANALOG) # vrはA1でANALOG入力 while True: # 以下を繰り返す val=analogRead(vr)//103 # val: 0-9 numDisp(val, LED) # LEDにvalを表示 sleep(0.3) # チャタリングを考慮し少し待つ
回路図

6.5 数字の2桁表示

 数字表示LEDに2桁以上の数字を表示するにはどうしたらいいでしょうか。LED毎にa~gをつなげばいいのですが(スタティック点灯)、Arduinoのピンが不足してしまいます。このような場合はダイナミック点灯と呼ばれる方法が用いられます。この方法は、身近にあるさまざまな表示装置に使われています。

 2個の数字表示LEDのa~gに表示したい数字のパターンを出力し、D2を「LOW(低い電圧)」にすると「10の桁」に、D3を「LOW(低い電圧)」にすると「1の桁」に表示されます。

from arduino import * # 各種定義を読み込む LED=numLed() # LEDは(a,...,g) digit10=pinMode(D2, OUTPUT) digit1=pinMode(D3, OUTPUT) while True: # 以下を繰り返す numDisp(2, LED) # a-gに「2」のパターンを出力 digit10.value=LOW; sleep(1); digit10.value=HIGH # 左のLEDに1秒表示 numDisp(3, LED) # a-gに「3」のパターンを出力 digit1.value=LOW; sleep(1); digit1.value=HIGH # 右のLEDに1秒表示

 ここで、2箇所の「sleep(1)」を「sleep(0.1)」→「sleep(0.02)」→「sleep(0.01)」と変えてみてください。1.2の時と同様にフリッカが私たちの目にはわからなくなります。でも、ブレッドボードを手に持って左右に動かすとフリッカがわかります。

 次に、0~99の数を表示するにはどうしたらいいでしょうか。今、以下のように表示させたい数「23」を変数Tに入れておくことにします。10の位は「T//10」、1の位は「T%10(Tを10で割った余り)」で求め、出力しています。他は前と同じです。

from arduino import * # 各種定義を読み込む LED=numLed() # LEDは(a,...,g) digit10=pinMode(D2, OUTPUT) digit1=pinMode(D3, OUTPUT) T=23 # 表示させたい数 while True: # 以下を繰り返す numDisp(T//10, LED) # a-gに「10の位」のパターンを出力 digit10.value=LOW; sleep(0.01); digit10.value=HIGH # 左のLEDに0.01秒表示 numDisp(T%10, LED) # a-gに「1の位」のパターンを出力 digit1.value=LOW; sleep(0.01); digit1.value=HIGH # 右のLEDに0.01秒表示

6.6 アナログ値を数字2桁で表示

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

from arduino import * # 各種定義を読み込む LED=numLed() # LEDは(a,...,g) digit10=pinMode(D2, OUTPUT) digit1=pinMode(D3, OUTPUT) vr=pinMode(A1, INPUT_ANALOG) # vrはA1でANALOG入力 while True: # 以下を繰り返す val=analogRead(vr)//11 # val: 0-99 numDisp(val//10, LED) # a-gに「10の位」のパターンを出力 digit10.value=LOW; sleep(0.01); digit10.value=HIGH # 左のLEDに0.01秒表示 numDisp(val%10, LED) # a-gに「1の位」のパターンを出力 digit1.value=LOW; sleep(0.01); digit1.value=HIGH # 右のLEDに0.01秒表示
回路図

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

7.1 液晶ディスプレイに文字や数字を表示する

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

 CLS(画面クリア)、CURSOR(カーソル位置指定)、PRINT(表示)の3つのコマンドは、通信ソフト(teraTerm等)と液晶の両方の表示に適用されます。ビデオ表示を利用している場合はそちらにも適用されます。

from arduino import * lcd=pinMode(I2C, ST7032) lcd.clear() print("COUNT: ") print(100)

7.2 液晶ディスプレイにカウントアップ表示する

 以下のプログラムは、1秒毎にカウントアップ表示します(通信ソフトの画面にも表示されます)。

from arduino import * lcd=pinMode(I2C, ST7032) count=0 while True: lcd.clear() lcd.print("COUNT: ") lcd.print(count) sleep(1) count=count+1
回路図

 以下のプログラムは、スイッチを押すとカウントアップするプログラムです(通信ソフトの画面にも表示されます)。

from arduino import * button=pinMode(D2, INPUT_PULLUP) lcd=pinMode(I2C, ST7032) count=0 buttonState=HIGH lastButton=HIGH while True: lcd.clear() lcd.print("COUNT: ") lcd.print(count) buttonState=button.value if lastButton==HIGH and buttonState==LOW: count=count+1 lastButton=buttonState sleep(0.05)
回路図

7.3 アナログ値を液晶ディスプレイに表示する

 以下のプログラムは、ボリュームのアナログ値(0~1023)を液晶ディスプレイに表示します。

from arduino import * vr=pinMode(A3, INPUT_ANALOG) lcd=pinMode(I2C, ST7032) while True: lcd.clear() lcd.print("VAL: ") lcd.print(analogRead(vr)) sleep(0.1)
回路図

8. センサーの利用

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

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

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

8.2 明るさセンサー(CDS)

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

from arduino import * # 各種定義を読み込む led=pinMode(D9, OUTPUT) # ledはD9で出力 vr=pinMode(A1, INPUT_ANALOG) # vrはA1でANALOG入力 while True: # 以下を繰り返す val=analogRead(vr) # val: 0-1023 if val<400: # valが400未満なら led.value=HIGH # ledを点灯 else: # そうでなければ led.value=LOW # ledを消灯
回路図

 境界値付近ではledが点灯したり消灯したり不安定になるので、これを避けるためには、以下のように「ヒステリシス特性」を持たせます。「ledState==LOW and val<400」つまりledが消灯(ledStateがLOW)している時に400以下の明るさになったら、次行でledを点灯(ledStateをHIGH)し、「ledState==HIGH and val>=500」つまりledが点灯(ledStateがHIGH)している時に500以上の明るさになったら、次行でledを消灯(ledStateをLOW)します。

from arduino import * # 各種定義を読み込む led=pinMode(D9, OUTPUT) # ledはD9で出力 vr=pinMode(A1, INPUT_ANALOG) # vrはA1でANALOG入力 ledState=LOW # ledは初め消灯状態 while True: # 以下を繰り返す val=analogRead(vr) # val: 0-1023 if ledState==LOW and val<400: # 消灯状態でvalが400未満なら ledState=HIGH # 点灯状態に elif ledState==HIGH and val>=500: # 点灯状態でvalが500以上なら ledState=LOW # 消灯状態に if ledState==HIGH: # 点灯状態なら led.value=HIGH # ledを点灯 else: # そうでなければ led.value=LOW # ledを消灯
回路図

(練習) 「400」と「500」の値を変えて、適切な値をみつけなさい。

8.3 温度センサー(サーミスタ)

 CDSの代わりに温度センサー(サーミスタ)をつないでみましょう。温度が高くなるほどサーミスタの抵抗値が低くなり、A3の電圧は高くなります。温度がある値以上になるとLEDが点灯します。プログラムは上と同様です。ただし、「400」の値は適切な値に変更する必要があります。

from arduino import * # 各種定義を読み込む led=pinMode(D9, OUTPUT) # ledはD9で出力 vr=pinMode(A1, INPUT_ANALOG) # vrはA1でANALOG入力 ledState=LOW # ledは初め消灯状態 while True: # 以下を繰り返す val=analogRead(vr) # val: 0-1023 if ledState==LOW and val<400: # 消灯状態でvalが400未満なら ledState=HIGH # 点灯状態に elif ledState==HIGH and val>=500: # 点灯状態でvalが500以上なら ledState=LOW # 消灯状態に if ledState==HIGH: # 点灯状態なら led.value=HIGH # ledを点灯 else: # そうでなければ led.value=LOW # ledを消灯
回路図

(練習) ヒステリシス特性を持たせたプログラムの動作を確かめなさい。また、「400」と「500」の値を変えて、適切な値をみつけなさい。

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

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

from arduino import * # 各種定義を読み込む LED=numLed() # LEDは(a,...,g) vr=pinMode(A1, INPUT_ANALOG) # vrはA1でANALOG入力 while True: # 以下を繰り返す val=analogRead(vr)//103 # val: 0-9 numDisp(count, LED) # LEDにcountを表示 sleep(0.3) # チャタリングを考慮し少し待つ
回路図

8.5 温度の計測(数字表示)

 以下のプログラムは、温度を0~9の数字で表示させます。プログラムは6.4と同様です。ただし、A1の電圧は温度が変化してもあまり大きく変化しません。このような場合は、9行目を以下のように変更し、220~280の値valを0~9に変換します。

val=val/30;  ↓ val=map(val, 220,280, 0,9);
char LED[]={ // 配列LEDに値を入れる 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x27, 0x7f, 0x67 }; void setup(){ DDRD=0x7f; // PD0~PD6を出力にする } void loop(){ int val=analogRead(A1); // valにA1のアナログ値(0~1023)を代入 val=val/30; // 0~9の値になるようにvalの値を30で割る PORTD=LED[val]; // val(0~9)に応じたPD0~PD6をPORTDに出力 }
回路図

8.6 音を検知する: 音量センサー

 以下のプログラムは、音を検知したらLEDを3秒点灯するものです。

from arduino import * # 各種定義を読み込む led=pinMode(D9, OUTPUT) # ledはD9で出力 sound=pinMode(A1, INPUT_ANALOG) # vrはA1でANALOG入力 while True: # 以下を繰り返す if analogRead(sound)>=400: # soundの値が400以上なら led.value=HIGH # ledを点灯 sleep(3) # 3秒待つ else: # そうでなければ led.value=LOW # ledを消灯
回路図

(練習) 6.3を参考に、手をたたくとカウントアップするプログラムを作りなさい。

8.7 赤外線反射センサー

 赤外線の反射光で数mm程度の距離に反射物を検知するセンサーです。9.7のモーターカーと組み合わせると、ライントレースや衝突回避などが可能になります。以下のプログラムは、数mm程度の距離に反射物を検知したらLEDを点灯するものです。ただし、「250」の値は適切な値に変更する必要があります。

int led=9; // 変数ledに9を代入する 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 音を出す

 LEDの点滅プログラムでLEDと同じ箇所に圧電ブザー(Bzz)を接続すると、ブザーからカチカチと音が聞こえます。ここで、点灯時間と消灯時間を以下のように変えてみます。

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 音程を変える

 指定した周波数の音を、指定した時間、出力するtone()という関数が利用できます。以下は時報の音を出すプログラムです。

from arduino import * # 各種定義を読み込む bzzObj=pinMode(D9, OUTPUT_ANALOG) while True: tone(bzzObj, 784, 0.2) # bzzに784Hzの音を0.2秒出力する(ポッ) sleep(0.8) # 0,8秒待つ tone(bzzObj, 784, 0.2) # bzzに784Hzの音を0.2秒出力する(ポッ) sleep(0.8) # 0,8秒待つ tone(bzzObj, 784, 0.2) # bzzに784Hzの音を0.2秒出力する(ポッ) sleep(0.8) # 0,8秒待つ tone(bzzObj, 1046,1.2) # bzzに1046Hzの音を1.2秒出力する(ポーン) sleep(1.8) # 1,8秒待つ

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

(練習) 上のプログラムを参考に、簡単な曲を演奏させるプログラムを作りなさい。

(練習) 時計のアラーム音、信号機の音、風呂や電気ポットの湯沸かし音など、身近にある電子音を録音し、その音をプログラムで再現しなさい。

9.3 楽譜を演奏する

 以下のプログラムは、配列として記述した楽譜情報を参照しながら演奏します。NOTE_C4などの周波数値はpitches.hファイルで定義されています。

#include "pitches.h" // pitches.hファイルを読み込む int bzz=6; // 変数bzzに6を代入する 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(){ }

 楽譜はさまざまな方法で表現することができます。以下は、少し長い曲を演奏するプログラムです。

from arduino import * # 各種定義を読み込む C=262; D=294; E=330; F=349; G=392; A=440; B=494 c=523; d=587; e=659; f=698; g=784; R=1 notes=[ 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] Tempo=0.3 # Tempoを0.3秒にする bzz=pinMode(D9, OUTPUT_ANALOG); # bzzをANALOG出力にする while True: # 以下を繰り返す i=0 while True: int note=notes[i] # 変数noteにひとつ読み if note==0 break # noteが0なら1曲演奏終了 elif not note==R tone(bzz, note, Tempo); # noteがR(休符)でもなければ # bzzにnote[Hz]の周波数の音をTempo[ms]出力する sleep(Tempo*1.1) # Tempoだけ待つ i=i+1 sleep(Tempo*8) # しばらくしてから演奏を繰り返す

(練習) メロディー(notes[]の部分)を変え、あるいは音階を拡張し、好きな曲の電子オルゴールを作りなさい。

9.4 楽器を作る

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

int bzz=6; // 変数bzzに6を代入する int button=0; // 変数buttonに0を代入する void setup(){ pinMode(bzz,OUTPUT); // bzzピンを出力にする pinMode(button, INPUT_PULLUP); // buttonピンをプルアップ入力にする } void loop(){ // 以下を繰り返す if(digitalRead(button)==LOW){ // buttonがLOWなら int val=analogRead(A3);// valにA3のアナログ値(0~1023)を代入する tone(bzz,val,200); // valの周波数を200ms delay(200); // 200ms待つ } }

 上のプログラムで一応楽器として使えるのですが、ドレミ...の音を丁度よく出すのは難しいと思います。そこで、0~1023のアナログ値をmap関数を使って0~11の値に変換し、notes[ ]配列の周波数を参照するようにしたのが次のプログラムです。

int bzz=9; // 変数bzzに9を代入する int button=0; // 変数buttonに0を代入する // ド レ ミ ファ ソ ラ シ ド レ ミ ファ ソ 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(A3); // valにA3のアナログ値(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 「合いの手」楽器(この項は作成中です)

 音量センサーと楽譜の演奏を組み合わせ、「合いの手」楽器を作ってみます。

#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=7; int bzz=6; 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()<lastmillis+tempo) if(digitalRead(mic)==LOW) s=1; return s; } } void setup(){ pinMode(mic, INPUT_PULLUP); pinMode(bzz, OUTPUT); } void loop(){ for(;;){ b(R,1000); // 1秒待つ while(digitalRead(mic)==HIGH); // チョ(音を待つ) b(R,L8); if(!b(R,L4)) continue; // チョイ if(b(R,L8)) continue; // の if(!b(R,L4)) continue; // チョイ if(b(R,L8)) continue; b(SIL,L16);b(RAL,L16);b(SOuL,L16);b(RAL,L32);// シラソ#ラ if(!b(R,L4)) continue; // チョン(ド) b(0,L32); b(RE,L16);b(DO,L16);b(SIL,L16);b(DO,L32); // レドシド if(!b(R,L4)) continue; // チョン(ミ) b(0,L32); b(FA,L16);b(MI,L16);b(REu,L16);b(MI,L16); // ファミレ#ミ b(SI,L16);b(RA,L16);b(SOu,L16);b(RA,L16); // シラソ#ラ b(SI,L16);b(RA,L16);b(SOu,L16);b(RA,L32); // シラソ#ラ if(!b(R,L4)) continue; // チョン(ド) b(0,L32); b(RA,L8);b(DOH,L8); // ラド b(SI,L8);b(RA,L8);b(SO,L8);b(RA,L8); // シラソラ b(SI,L8);b(RA,L8);b(SO,L8);b(RA,L8); // シラソラ b(SI,L8);b(RA,L8);b(SO,L8);b(FAu,L16); // シラソファ# if(!b(R,L4)) continue; // チョン(ミ) b(DO,L16);b(MI,L16);b(SO,L16); b(SO,L16);b(R,L16);b(MI,L16);b(SO,L4); } }

(練習) 違うメロディーやリズムにしてみなさい。


10. 合成音声でおしゃべり

10.1 合成音声でおしゃべり

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

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

from arduino import * # 各種定義を読み込む while True: # 以下を繰り返す talk("ju'nbi/o'-kei") # 「準備OK!」 sleep(2) # 2秒待つ

(練習) 10行目を変えて、違う言葉をしゃべらせてみなさい。

10.2 「九九」の読み上げ

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

from arduino import * # 各種定義を読み込む for x in range(1,10): # x: 1-9 for y in range(1,10): # y: 1-9 talk(x,"kakeru",y,"wa",x*y)

 以下は、外部スイッチが押されると「九九」をランダムに読み上げるプログラムです。

from arduino import * # 各種定義を読み込む button=pinMode(D2, INPUT_PULLUP) while True: # 以下を繰り返す if button.value==LOW: x=randint(1,9) y=randint(1,9) talk(x,"kakeru",y,"wa",x*y)

(練習) 利用者に適したスイッチを使い、合成音声の読み上げで支援するさまざまなツール(VOCA)を作ることができます。2個のスイッチを接続し、それぞれ「はい」「いいえ」の合成音声が出るようなVOCAを作りなさい。


11. イルミネーション

11.1 NeoPixelの利用

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

 LEDの番号(0~11)と色相(0~255)と明るさ(0~255)を指定することもできます。明るさの指定を省略すると255として扱われます。

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


HUE04285128170212255
シアンマジェンタ

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

from arduino import * pixelObj=NeoPixel(D10, 10, brightness=0.3) pixelObj[0]=(255,0,0) pixelObj[1]=(0,255,0) pixelObj[2]=(0,0,255) pixelObj[0]=(255,255,0) pixelObj[1]=(0,255,255) pixelObj[2]=(255,255,255) pixelObj[0]=(0,0,0) pixelObj[1]=(0,0,0) pixelObj[2]=(0,0,0) pixelObj[0]=0 pixelObj[1]=42 pixelObj[3]=85 pixelObj[0]=0,50 pixelObj[1]=42,50 pixelObj[2]=85,50 LED0のRGBを(255,0,0)にする LED1のRGBを(0,255,0)にする LED2のRGBを(0,0,255)にする LED0のRGBを(255,255,0)にする LED1のRGBを(0,255,255)にする LED2のRGBを(255,255,255)にする LED0を消す LED1を消す LED2を消す LED0のHUEを0(赤)にする LED1のHUEを42(黄)にする LED2のHUEを85(緑)にする LED0のHUEを0(赤)明るさ50にする LED1のHUEを42(黄)明るさ50にする LED2のHUEを85(緑)明るさ50にする

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

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

from arduino import * # 各種定義を読み込む N=10 # LEDは10個 pixelObj=NeoPixel(D10, N, brightness=0.3) while True: pixelObj[0]=(255,0,0) # LED0 を赤に sleep(1) # 1秒待つ pixelObj[0]=(0,0,0) # LED0 を消灯 sleep(1) # 1秒待つ


pixelObj[0]=(255,0,0) → pixelObj[0]=(0,255,0) とすると緑で点滅します。

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

from arduino import * # 各種定義を読み込む N=10 # LEDは10個 pixelObj=NeoPixel(D10, N, brightness=0.3) while True: for i in range(N): pixelObj[i]=(255,0,0) # N個のLEDを赤に sleep(1) # 1秒待つ for i in range(N): pixelObj[i]=(0,0,0) # N個のLEDを消灯 sleep(1) # 1秒待つ

LEDの点灯時間、つまり前半の待ち時間を

sleep(0.1) と短くすると短時間点灯する「フラッシュ」になります。

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

11.3 ワイプ

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

from arduino import * # 各種定義を読み込む N=10 # LEDは10個 pixelObj=NeoPixel(D10, N, brightness=0.3) while True: for i in range(N): pixelObj[i]=(0,0,0) # N個のLEDを消灯 sleep(1) # 1秒待つ for i in range(N): pixelObj[i]=(255,0,0) # i番目のLEDを点灯 sleep(1) # 1秒待つ

pixelObj[i]=(255,0,0) とすると順に消灯します。

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

11.4 スイープ

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

from arduino import * # 各種定義を読み込む N=10 # LEDは10個 pixelObj=NeoPixel(D10, N, brightness=0.3) while True: for i in range(N): pixelObj[i]=(255,0,0)# i番目のLEDを点灯 sleep(1) # 1秒待つ pixelObj[i]=(0,0,0) # i番目のLEDを消灯

11.5 ウェーブ

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

from arduino import * # 各種定義を読み込む N=10 # LEDは10個 pixelObj=NeoPixel(D10, N, brightness=0.3) def PixelWave(r, g, b): for j in range(20,0,-1): for i in range(N): if (i+j)%3==0: pixelObj[i]=(r,g,b) else: pixelObj[i]=(0,0,0) sleep(0.08) while True: # 以下を繰り返す PixelWave(255,255,255) # 白のウェーブ PixelWave(255, 0, 0) # 赤のウェーブ PixelWave( 0,255, 0) # 緑のウェーブ PixelWave( 0, 0,255) # 青のウェーブ

(練習) 「1個おき」「3個おき」のウェーブにしてみなさい。

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

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

from arduino import * # 各種定義を読み込む N=10 # LEDは10個 pixelObj=NeoPixel(D10, N, brightness=0.3) while True: for i in range(256): pixelObj[0]=(i,0,0); sleep(0.01) # だんだん明るく for i in range(255,0,-1): pixelObj[0]=(i,0,0); sleep(0.01) # だんだん暗く

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

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

11.7 クロスフェード

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

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

from arduino import * # 各種定義を読み込む N=10 # LEDは10個 pixelObj=NeoPixel(D10, N, brightness=0.3) while True: for i in range(N): for r in range(256): pixelObj[i]=(255-r,0,0); pixelObj[(i+1)%N]=(r,0,0); sleep(0.01)

11.8 フルカラー表示

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

from arduino import * # 各種定義を読み込む N=10 # LEDは10個 pixelObj=NeoPixel(D10, N, brightness=0.3) while True: for r=255;r>=0;r--: pixelObj[0]=(r,255-r,0); sleep(0.05)

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

from arduino import * # 各種定義を読み込む N=10 # LEDは10個 pixelObj=NeoPixel(D10, N, brightness=0.3) while True: for W in range(256): if W<85: B=0; G=W; R=85-G elif W<170: R=0; B=W-85; G=85-B else: G=0; R=W-170; B=85-R for i in range(N): pixelObj[i]=(R,G,B); sleep(0.01)

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

from arduino import * # 各種定義を読み込む N=10 # LEDは10個 pixelObj=NeoPixel(D10, N, brightness=0.3) while True: for j in range(0,256,8): for i in range(N): W=(i*256//10+j)%256 if W<85: B=0; G=W; R=85-G elif W<170: R=0; B=W-85; G=85-B else: G=0; R=W-170; B=85-R pixelObj[i]=(R,G,B) sleep(0.01)

 以下のプログラムは、ボリュームを回すと色が変化します。

from arduino import * # 各種定義を読み込む N=10 # LEDは10個 vr=pinMode(A1, INPUT_ANALOG) pixelObj=NeoPixel(D10, N, brightness=0.3) while True: W=analogRead(vr)//4 if W<85: B=0; G=W; R=85-G elif W<170: R=0; B=W-85; G=85-B else: G=0; R=W-170; B=85-R for i in range(N): pixelObj[i]=(R,G,B); sleep(0.01)

(練習) ボリュームの代わりにCDS(明るさセンサ)を使ってみなさい。

11.9 イルミネーションの例

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

from arduino import * # 各種定義を読み込む N=10 # LEDは10個 pixelObj=NeoPixel(D10, N, brightness=0.3) while True: n=randint(6,N); for i in range(n-1): u=(i+1)*10 # uは最大の明るさ for b in range(u+1): # 明るさbを0~u pixelObj[i]=(u-b,u-b,u-b); # LED iをだんだん暗く pixelObj[(i+1)%10]=(b,b,b); # LED i+1をだんだん明るく sleep(0.003) sleep((10-n)*0.01) # 速さ調整 for b in range(u,256): # 明るさbを最大までだんだん明るくする(キラリ) pixelObj[n-1]=(b,b,b); sleep(0.003) for b in range(255,0,-1): # 明るさbを0までだんだん暗くする pixelObj[n-1]=(b,b,b) sleep(0.003) sleep(random()*2); # 次のドロップまで0~2秒待つ

 以下は、NeoPixelリング(12個)を使い、ランダムに点灯させるようにした例です。オブジェの作り方はngo-tecさんのページを参考にしました。点灯位置と点灯色と点灯時間間隔をランダムにし、点灯後はだんだん暗くしています。

from arduino import * # 各種定義を読み込む N=12 # LEDは12個 pixelObj=NeoPixel(D10, N, brightness=0.3) hue=[0,0,0,0,0,0,0,0,0,0,0,0] # 色 u=[0,0,0,0,0,0,0,0,0,0,0,0] # 明るさ t=1 # t*50ms後にfire while True: for i in range(N): if hue[i]<85: B=0;G=hue[i]*3*u[i]//255; R=(255-G)*u[i]//255 elif hue[i]<170: R=0;B=(hue[i]-85)*3*u[i]//255; G=(255-B)*u[i]//255 else: G=0;R=(hue[i]-170)*3*u[i]//255;B=(255-R)*u[i]//255 pixelObj[i]=(R,G,B) u[i]=u[i]*95//100 sleep(0.05) # 処理は50ms毎 t=t-1 if t==0: p=randint(0,N-1); # ランダムなLED u[p]=255 # 明るさを255 hue[p]=randint(0,255) # 色はランダムに t=1+randint(0,20)+randint(0,20) # 0~2秒後にfireするようにtをランダムに決める

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


12. 赤外線リモコン

12.1 赤外線リモコンを作る

 テレビなどのリモコンは、ボタンを押すと赤外線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ボタンを押すと各社リモコンの「チャンネルダウン」信号が次々出力されます。実際には各社の信号を出す必要はありません。

from arduino import * UpButton=pinMode(D2, INPUT_PULLUP) DownButton=pinMode(D4, INPUT_PULLUP) pwmPin=pwmio.PWMOut(D9, frequency=38000, duty_cycle=2**15) pulseout=pulseio.PulseOut(pwmPin) Toshiba =GenericTransmit(header=[9000,4500],one=[550,1700],zero=[550,550],trail=550) Panasonic=GenericTransmit(header=[3600,1600],one=[500,1200],zero=[500,400],trail=500) Sony =GenericTransmit(header=[2600,600], one=[1200,600],zero=[600,600],trail=0) def reverse(n): result=0 for i in range(8): result<<=1; result|=n&1; n>>=1 return result while True: if UpButton.value==LOW: Toshiba.transmit( pulseout, [reverse(n) for n in [0x40,0xbf,0x1b,0xe4]]) Panasonic.transmit(pulseout, [reverse(n) for n in [0x02,0x20,0x80,0x00,0x34,0xb4]]) Sony.transmit( pulseout, [reverse(n) for n in [0x90,0x00]], repeat=2, delay=0.022, nbits=12) time.sleep(0.2) if DownButton.value==LOW: Toshiba.transmit( pulseout, [reverse(n) for n in [0x40,0xbf,0x1f,0xe0]]) Panasonic.transmit(pulseout, [reverse(n) for n in [0x02,0x20,0x80,0x00,0x35,0xb5]]) Sony.transmit( pulseout, [reverse(n) for n in [0x91,0x00]], repeat=2, delay=0.022, nbits=12) time.sleep(0.2)
回路図

12.2 なんでもリモコン

 以下の回路とプログラムは、1個のスイッチ操作で電源・CHup・CHdnをコントロールできる「なんでもリモコン」のPython版です(オリジナルはこちら)。ブザー音の「ドに電源」「レにCHup」「ミにCHdn」が割り当ててあります。スイッチを押すと「ドーレーミー」とブザー音が鳴るので、希望の箇所でもう一度スイッチを押します。このプログラムはパナソニックTV用です。他社の場合は、変更が必要です。


13. 動くものを作る

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

13.1 モーターカー(この項は作成中です)

 DCモータを2個使ったモーターカーです。ボタン操作で前進・後退・左ターン・右ターンします。

void setup(){ } void stop(){ // 停止 } void fwd(){ // 前進 } void bwd(){ // 後退 } void right(){ // 右旋回 } void left(){ // 左旋回 } void loop(){ }

 以下は、赤外線反射センサーが「崖」を検知すると、少し後退し、右回転して、再び前進します。

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°)を制御するように作られています。

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

from arduino import * # 各種定義を読み込む servoObj=pinMode(D9, OUTPUT_SERVO) while True: for pos in range(0, 180, 1):# posを0~179まで1度ずつ変える servoObj.angle=pos # サーボモータをposの位置にする sleep(0.05) # 少し待つ for pos in range(180, 0,-1):# posを180~1まで1度ずつ変える servoObj.angle=pos # サーボモータをposの位置にする sleep(0.05) # 少し待つ

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

from arduino import * # 各種定義を読み込む vr=pinMode(A1, INPUT_ANALOG) # vrはA1でANALOG入力 servoObj=pinMode(D9, OUTPUT_SERVO) while True: pos=analogRead(vr)*179/1023 # posは0-179 servoObj.angle=pos # サーボモータをposの位置にする sleep(0.05) # 少し待つ

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


14. 押しボタン信号機

 押しボタン信号機の制御プログラムです。

from arduino import * # 各種定義を読み込む buttonPin=pinMode(D2, INPUT_PULLUP) # D2は押しボタン RPin=pinMode(D9, OUTPUT) # D9は車赤のLED YPin=pinMode(D8, OUTPUT) # D8は車黄のLED GPin=pinMode(D7, OUTPUT) # D7は車緑のLED RRPin=pinMode(D6, OUTPUT) # D6は歩行者赤のLED GGPin=pinMode(D4, OUTPUT) # D4は歩行者緑のLED lastState=HIGH # 直前のbuttonの状態 def carsig(r, y, g): # 車 赤・黄・緑の点灯・消灯 RPin.value=r # RPinにrを出力 YPin.value=y # YPinにyを出力 GPin.value=g # GPinにgを出力 def mansig(r, g): # 歩行者 赤・緑の点灯・消灯 RRPin.value=r # RRPinにrを出力 GGPin.value=g # GGPinにgを出力 carsig(LOW,LOW,HIGH); mansig(HIGH,LOW) # 初期状態は 車青・歩行者赤 while True: button=buttonPin.value # 押しボタンスイッチの状態を読み取り if button!=lastState and button==LOW: # 押されたら sleep(4) # 4秒待って carsig(LOW,HIGH,LOW); sleep(4) # 車黄 4秒 carsig(HIGH,LOW,LOW); mansig(LOW,HIGH) # 車赤・歩行者青 16秒 sleep(16) for i in range(8): # 歩行者青 点滅 8秒 mansig(LOW,HIGH); sleep(0.5) mansig(LOW,LOW); sleep(0.5) mansig(HIGH,LOW); sleep(4) # 歩行者赤 4秒 carsig(LOW,LOW,HIGH) # 車青 lastState=button # buttonを記憶

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

from arduino import * # 各種定義を読み込む buttonPin=pinMode(D2, INPUT_PULLUP) # D2は押しボタン RPin=pinMode(D9, OUTPUT) # D9は車赤のLED YPin=pinMode(D8, OUTPUT) # D8は車黄のLED GPin=pinMode(D7, OUTPUT) # D7は車緑のLED RRPin=pinMode(D6, OUTPUT) # D6は歩行者赤のLED GGPin=pinMode(D4, OUTPUT) # D4は歩行者緑のLED bzzPin=pinMode(D5, OUTPUT # D5はブザー lastState=HIGH # 直前のbuttonの状態 Tempo=200 e=659 c=523 def carsig(r, y, g): # 車 赤・黄・緑の点灯・消灯 RPin.value=r # RPinにrを出力 YPin.value=y # YPinにyを出力 GPin.value=g # GPinにgを出力 def mansig(r, g): # 歩行者 赤・緑の点灯・消灯 RRPin.value=r # RRPinにrを出力 GGPin.value=g # GGPinにgを出力 carsig(LOW,LOW,HIGH); mansig(HIGH,LOW) # 初期状態は 車青・歩行者赤 while True: button=buttonPin.value # 押しボタンスイッチの状態を読み取り if button!=lastState and button==LOW: # 押されたら sleep(4) # 4秒待って carsig(LOW,HIGH,LOW); sleep(4) # 車黄 4秒 carsig(HIGH,LOW,LOW); mansig(LOW,HIGH) # 車赤・歩行者青 16秒 for i in range(5): # カッコー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 in range(8): # 歩行者青 点滅 8秒 mansig(LOW,HIGH); sleep(0.5) mansig(LOW,LOW); sleep(0.5) mansig(HIGH,LOW); sleep(4) # 歩行者赤 4秒 carsig(LOW,LOW,HIGH) # 車青 lastState=button # buttonを記憶

15. 応用例

15.1 I2Cデバイスを使う

 最近は、SDAとSCLの2本の信号線(GndとV+を加えれば4本)で接続するさまざまなI2Cデバイスが入手できます。例えば液晶ディスプレイやモータードライバーや温湿度センサーや加速度センサーなどがあります。

 ここでは、液晶ディスプレイに表示する温湿度計を紹介します。この例ではI2Cのプルアップ抵抗はSHT31に内蔵されています。

from arduino import * sht=SHTinit() LCDinit() while True: temp=round(sht.temperature,1) # 温度 LCDcursor(0,0); LCDprint(str(temp)+" \337C") # 温度を表示 humi=round(sht.relative_humidity) # 湿度 LCDcursor(0,1); LCDprint(str(humi)+" %RH") # 湿度を表示 sleep(1) # 1秒待つ


(参考資料)

[1] CircuitPython, https://circuitpython.org/
[2] Arduino to CircuitPython, https://learn.adafruit.com/arduino-to-circuitpython


(付録1) 入出力モジュール

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

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

部品表
名称 外観 備考
Seeeduino XIAO 秋月 580円
ブレッドボード EIC-301 秋月 200円
ブレッドボード ジャンパーワイヤ 15cm   秋月 10本 350円
ブレッドボード ジャンパーワイヤ EIC-J-S   秋月 300円
LED 秋月 10個 120円
フルカラーシリアルLEDテープ SwitchScience 770円
数字表示LED C-551SR 秋月 40円
圧電スピーカー SPT08 秋月 2個 100円
圧電サウンダ FGT-31T-3.7A1 秋月 70円
スピーカ UGSM23A 秋月 100円
プッシュスイッチ DS-660R-C 千石 84円
抵抗 1/4W 4.7Ω, 100Ω, 220Ω, 470Ω, 1kΩ, 10kΩ 秋月 100本 100円
CDS(光センサ)秋月 30円
温湿度センサ SHT31秋月 950円
赤外線反射センサ LBR-127HLD   秋月 50円
人感センサ(焦電センサ)
ブレッドボード ジャンパーワイヤ 15cm
  秋月 400円
秋月 10本 220円
サーボモーター SG-90   秋月 400円
液晶ディスプレイ AQM1602A   秋月 550円
モータードライバ DRV8830   秋月 230円
音声合成LSI ATP3011F4/M6   秋月 900円

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

(1) XIAOで使用できるピン

ピン 変数名 備考
デジタル入出力アナログ出力(PWM)アナログ入力
0 D0DACA0
1 D1-A1
2 D2A2
3 D3A3
4 D4A4I2C SDA
5 D5A5I2C SCL
6 D6A6TX
7 D7A7RX
8 D8A8SCK
9 D9A9MISO
10 D10A10MOSI
22(仮想ピン)-TEMPSHT31/SCD30 温度
23(仮想ピン)-HUMISHT31/SCD30 湿度
24(仮想ピン)-CO2SCD30 CO2濃度
設定値または取得値0 / 10 - 2550 - 1023


(付録4) CircuitPythonの書き込み

  1. XIAO用のCircuitPythonのuf2ファイルをここからダウンロードします。
  2. ジャンパーワイヤを使って、RSTピンを2回すばやくショートさせます。
  3. Arduinoドライブが現れるので、そこにダウンロードしたuf2ファイルをコピーします。すると、さっきまでのArduinoドライブがCIRCUITPYドライブになります。
  4. CIRCUITPYドライブの中にcode.pyというファイルがあるのでこれをテキストエディタで開きます。中は空です。これがCircuitPythonのプログラムファイルなので、直接編集してプログラムを作ります。
  5. エディタで保存操作をすると直ちにプログラムが実行されます。
  6. ここでは、共通して利用する変数や関数をarduino.pyに記述しているので、code.pyの冒頭で from arduino import * のように読み込みます。


(付録5) arduino.py

from time import sleep from board import * from busio import I2C from microcontroller import * from pwmio import PWMOut from pulseio import PWMOut from adafruit_motor import servo from digitalio import DigitalInOut, Direction, Pull from analogio import AnalogIn from random import * #from neopixel import * #from adafruit_irremote import GenericTransmit from adafruit_sht31d import * LOW=0; HIGH=1 INPUT=0; OUTPUT=1; INPUT_PULLUP=2; INPUT_ANALOG=3 # 入出力モード OUTPUT_ANALOG=4; OUTPUT_SERVO=5 isI2C=False def pinMode(pin, mode): # pinの入出力モードをmodeにしてピンオブジェクトを返す if mode==INPUT_ANALOG: pinObj=AnalogIn(pin) elif mode==OUTPUT_ANALOG: pinObj=pwmio.PWMOut(pin, variable_frequency=True) elif not mode==OUTPUT_SERVO: pinObj=DigitalInOut(pin) if mode==OUTPUT: pinObj.direction=Direction.OUTPUT elif mode==INPUT: pinObj.direction=Direction.INPUT elif mode==INPUT_PULLUP: pinObj.direction=Direction.INPUT; pinObj.pull=Pull.UP elif mode==OUTPUT_SERVO: pwm=pulseio.PWMOut(pin, duty_cycle=2**15, frequency=50) pinObj=servo.Servo(pwm) return pinObj def tone(pinObj, freq, dur): # pinObjにfreq(Hz)でdur(秒)トーン出力 TONEOFF=0; TONEON=65535//2 if freq>0: pinObj.frequency=freq; pinObj.duty_cycle=TONEON sleep(dur) pinObj.duty_cycle=TONEOFF def analogWrite(pinObj, val): # pinObj にval(0-255)をアナログ出力 pinObj.frequency=1000; pinObj.duty_cycle=65535*val//255 def analogRead(pinObj): # pinObjのアナログ入力(0-1023) return pinObj.value//64 def numLed(): # D4-D10に接続した数字表示LEDのpinObjの配列ledObjを返す ledObj=[] ledObj.append(pinMode(D8, OUTPUT)); ledObj.append(pinMode(D7, OUTPUT)) ledObj.append(pinMode(D6, OUTPUT)); ledObj.append(pinMode(D5, OUTPUT)) ledObj.append(pinMode(D4, OUTPUT)); ledObj.append(pinMode(D9, OUTPUT)) ledObj.append(pinMode(D10,OUTPUT)) return ledObj def numDisp(num, ledObj): # ledObj(数字表示LED)にnum(0-9)を表示, segdata=(0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x27,0x7f,0x67) for i in range(7): ledObj[i].value=segdata[num]&(0x01<<i) def talk(*args): # sをAQUESTALKで発声 # print(args) global i2c, isI2C if not isI2C: i2c=I2C(SCL,SDA); isI2C=True AQUESTALK=0x2e while not i2c.try_lock(): pass # print([hex(x) for x in i2c.scan()]) for s in args: if type(s) is int: if s<0: s=-s; s="mainasu"+"<NUMK VAL="+str(s)+">" else: s="<NUMK VAL="+str(s)+">" i2c.writeto(AQUESTALK, bytes([ord(x) for x in s+"\r"])) result=bytearray(5) while True: i2c.readfrom_into(AQUESTALK, result) if result[0]==ord('>'): break sleep(0.1) # print(chr(result[0])) i2c.unlock() # i2c.deinit() def LCDwrite(cmd, data): global i2c, isI2C if not isI2C: i2c=I2C(SCL,SDA); isI2C=True buf=bytearray(2); buf[0]=cmd; buf[1]=data while not i2c.try_lock(): pass i2c.writeto(0x3e, buf) i2c.unlock() def LCDinit(): global i2c, isI2C if not isI2C: i2c=I2C(SCL,SDA); isI2C=True LCDcontrast=5 # 0-15 while not i2c.try_lock(): pass i2c.writeto(0x3e, bytes([0x38, 0x39, 0x14, 0x73, 0x56, 0x6c, 0x38, 0x0c, 0x01])) i2c.writeto(0x3e, bytes([0x00, 0x39, 0x00, 0x70+LCDcontrast])) # contrast i2c.unlock() def LCDclear(): LCDwrite(0x00,0x01); sleep(0.01); LCDwrite(0x00,0x02); sleep(0.01) def LCDcursor(x, y): LCDwrite(0x00,0x80+y*0x40+x) def LCDprint(s): for c in str(s): LCDwrite(0x40, ord(c)) def SHTinit(): global i2c, isI2C if not isI2C: i2c=I2C(SCL,SDA); isI2C=True return SHT31D(i2c)