[フレッシャーズ特別企画]
初めの一歩!ラズパイPicoマイコン×PythonでLチカ入門

16連LED“NeoPixel”の点灯/消灯/色/明るさをプログラミングで自由自在

著者:田口 海詩 / 企画:ZEPエンジニアリング /

本稿の実験で使う見本Pythonプログラム

第1章 今こそマイコン入門!2大ツール“Pico”と“Python”

Pythonが使える500円マイコン・ボード“Pico”

動作クロックが高く,メモリ容量が大きい

2021年1月20日,ラズベリーパイ財団が500円のマイコン・ボード「Raspberry Pi Pico」(写真1)をリリースしました.本稿では,Pico(ピコ)と呼びます.

写真1 ラズベリーパイ財団が発表したマイコン・ボード“Raspberry Pi Pico”
搭載されているマイコンは“RP2040”

ラズベリーパイ財団のPico開発スタートページでは,Picoのプログラム開発言語として最初に“Python”を推奨しています.さらに,Python開発環境として,エストニアのTartu(タルトゥ)大学が開発した“Thonny”(トニーまたはソニーと読む)を勧めています(図1).

図1 Pythonプログラミング開発環境“Thonny”

マイコン・ボード Picoの脳ミソ“RP2040”

Picoに搭載されているマイコン“RP2040”の中央には,ラズベリーパイのロゴ・マークが印刷されています(写真2).です.ここで,RP2040マイコンが備える機能を説明しましょう.

図2に示すのは,RP2040マイコンのブロック・ダイヤグラムです.

プロセッサ,メモリ,GPIOなど外部回路との接続用回路(インターフェース),$\mathrm{ I^{2}C}$通信回路などの周辺回路(ペリフェラル)を内蔵しています.

RP2040の仕様を次に示します.

  1. ARM Cortex M0+デュアル・コア
  2. 最大動作周波数 133 MHz
  3. USBを介しマスストレージを使ったドラッグ・アンド・ドロップによるプログラム書き込み可能
  4. SRAM:264Kバイト
  5. フラッシュ・メモリ:2Mバイト
  6. USB 1.1 ホスト/デバイス両対応
  7. 多機能GPIOピン 26チャネル
  8. SPI/$\mathrm{ I^{2}C}$/UART 各2チャネル
  9. A-Dコンバータ(分解能12ビット)
  10. PWMチャネル 16チャネル
  11. 高速な浮動小数点ライブラリを搭載
  12. プログラマブルI/O:8チャネル
写真2 Picoに搭載されているマイコン“RP2040”
ラズベリーパイ財団が設計したデュアル・コアARMマイコン
図2 RP2040のブロック・ダイヤグラム

世界の定番!プログラミング言語“Python”

Pythonは,他のプログラミング言語と比べて短い行数で動作記述できる効率のよい言語です.また動作に直接関係しない暗黙のルールをあまり覚える必要もありません.

Pythonユーザは,全世界に無数にいます.使い方がわからなくても,インターネットで検索すればたくさんの答えがヒットします.

プログラミング言語の分厚い教科書は,やる気をなくさせます.でもPython言語なら,少しの文法を覚えるだけで,コンピュータを操縦できます.

プログラミングの上達のこつは,簡単なプログラムをたくさん書くことですが,行き詰ることも多いでしょう.いまどきは,インターネットで調べればたくさんの答えが得られることでしょう(図3図4).

A-Dコンバータ,D-Aコンバータ,GPIO,$\mathrm{ I^{2}C}$インターフェース回路など,ハードウェアを制御する制御プログラムもすでに用意されています.これらのライブラリを利用すれば,効率よくプログラミングできるでしょう.

第2章 プログラミング開発環境の準備

Python-機械語翻訳ファームウェアMicroPythonを使った最新開発環境

従来のプログラミング開発

従来のC言語を利用したマイコン・プログラミングでは,パソコンの開発環境(コンパイラ)を使って,数百~数千行もある一連のプログラムを一括で機械語に変換してから,マイコンに書き込みます.

具体的には,次の手順を繰り返しています.

  1. 処理全体のソースコードを記述する
  2. ソースコード全体をコンパイルして機械語に変換する
  3. USB経由でマイコン内のメモリに転送する
  4. プログラムを実行する
  5. ソースコードのミスを修正する

これら一連の作業は,「統合開発環境」(IDE:Integrated Development Environment)というアプリケーション・ソフトウェアをパソコンにインストールして利用して行っています.

MicroPythonを使ったプログラミング開発

MicroPythonは,Python言語を機械語に翻訳するマイコン用ファームウェアの名称であり、Python言語と同じ文法に従います.パソコン上で統合開発環境“Thonny”上で,Python言語を使ってソースコードを記述し,Picoに転送してプログラムを実行します.

RP2040に“MicroPython”という通訳ファームウェアを書き込んだら,パソコンでPythonプログラムを1行書いて,USB経由でRP2040に送信してみてください,MicroPythonは,その1行のPython言語を翻訳して,CPUが理解できる言葉(機械語)に変換してくれます.

この仕組みのおかげで,ソースコードを1行書いたら直ぐにマイコンで動かしてみることができます.プログラムの一部をマイコンに投げかけて,反応を見ることができるのです.このような開発環境のことを“REPL(Read-Eval-Print Loop)”と呼びます.

MicroPythonを使えば,従来の一連のソースコードを書き終えてから一括で機械語に変換する「コンパイル作業」が不要になります.プログラム・コード片を1行ずつマイコンに直接投入すると,そのたびに応答して,マイコンの内部回路を動かしてくれるのです.ただし,処理速度がかなり遅くなるので,実用性があるとは言いにくいです.

MicroPythonを利用するプログラミング開発環境のセットアップ

ラズベリー・パイ財団が推奨する開発環境 “Thonny”

ラズベリーパイ財団は,Picoのプログラミング開発用として,Python言語専用の統合開発環境“Thonny”を推奨しています.図5に示すのは,Picoのプログラミングを行うための開発環境全体像を示します.

図5 ラズベリーパイ財団が推奨するPicoのプログラミング環境
動画1 Python-機械語の翻訳ファームウェア“MicroPython”をRP2040に書き込むまで

(1)PicoをBOOTSELモードで起動し,RP2040ページにアクセス

Pythonプログラミング開発環境の構築方法は,ラズベリーパイ財団のRP2040ホームページ(図3)に解説があります.

写真3に示すように,Picoの[BOOTSEL]ボタンを押しながらUSBケーブルをパソコンに接続すると,PicoはBOOTSELモードで起動します.

BOOTSELモードで起動すると,パソコンのファイル・ブラウザからPicoのドライブ(RPI-RP2)を見ることができるようになります.RPI-RP2フォルダにある“index.htm”ををクリックすると,図6に示すRaspberry PiのRP2040ページにアクセスできます.

(a)BOOTSELモードで起動 (b)BOOTSELモードのPicoドライブ
写真3 PicoをBOOTSELモードで起動する
Picoの[BOOTSEL]ボタンを押しながら,パソコンにUSBケーブルを接続すると,PicoはBOOTSELモードで起動する
図6 Picoの心臓部「RP2040マイコン」の開発スタートページ

(2)MicroPythonファームウェアをダウンロードして,Picoにコピー

Raspberry PiのRP2040ページにアクセスして,図6に示すMicroPythonのスタートページのタグをクリックします.MicroPythonファームウェア(UF2ファイル)をダウンロードします.

ダウンロードしたMicroPythonファームウェアのUF2ファイルをPicoのRPI-RP2ドライブにコピーします.これで,MicroPythonファームウェアはRP2040に書き込まれました.

(3)統合開発環境Thonnyをダウンロードしセットアップ

Pythonの統合開発環境“Thonny”をセットアップします.

図7に示すようにThonnyのホームページにアクセスします.ページの右上にダウンロード先を示すリンクが表示されています.自分のOSにあったソフトウェアをダウンロードします.“Thonny-3.3.x.exe”をダウンロードし,指示にしたがってThonnyをインストールします.

図8に示すように,インストール後Thonnyを起動すると,上下に2つのフレームがあるウインドウが開きます.

上部フレームを「コードエディタ」と呼び,Pythonプログラムを記述・実行するときに用います.下部フレームを「シェル」と呼び,Picoに直接コマンドを送るときやプログラム実行結果を表示するときに用います.シェルの領域にプロンプト“>>>”が表示されたら,Picoとの通信ができる状態になっています.

“>>>”が表示されない場合は,通信設定が“MicroPython(Raspberry Pi Pico)”になっていることを確認して,[STOP]アイコンを押してください.

Thonnyには,「ファイルのSave・Open機能」や「プログラムのStart・STOP機能」のボタンがあります.また,[View]-[ファイル]を選ぶと,ファイル・ブラウザが開きパソコン上のファイルやPicoのフラッシュ・メモリ上のファイルを確認したり操作できたりします.

図7 統合開発環境Thonnyの準備
Thonnyは,Ver.3.3.3からPicoを標準でサポートし始めた

図8 Pythonの開発環境“Thonny”の起動画面
Thonnyの起動画面.シェルに“>>>”が表示されていたら,開発環境は正常である

初めてのマイコン・プログラミング(プログラム名 L_chika.py)

準備ができたら,リスト1に示すLチカ・プログラムを動かしてみましょう.

プログラムの説明

1行目は,Lチカに必要なモジュール(ライブラリ)の読み込みを行います.

machineモジュールのI/Oピン制御命令でLEDを点滅させます.utimeモジュールは,時間を管理するための命令です.

2行目は,LEDに接続されているGPIO25ピンを出力端子にして,“led”と命名します.

3行目以降は,実際にLEDを点滅するための命令です.

4行目は,GPIO25ピンをONにしてLEDを発光させてます.4行目で0.5秒待ち,6行目でLEDを消します.

7行目で,再度0.5秒待ち,4行目に戻り,これを延々と繰り返します.

リスト1 Picoに実装されたLEDを点滅させる「Lチカ」プログラム

第3章 初めてのコンピュータ制御「Lチカ」

[やってみよう1]LEDの点灯/消灯を繰り返す

写真4のように,Picoには緑色LEDが1個付いています.

このLEDは,マイコンのGPIO25ピンに接続されています.プログラムでGPIO25ピンの電圧をを“H”にしたり“L”にしたりすると,同時に,LEDが点灯したり消灯したりします.

リスト2に示すのは,LEDを0.5秒ごとにON(LED点灯)OFF(LED消灯)を繰り返すプログラムです.これがいわゆる「Lチカ」のプログラムです.6行目の待ち時間で点灯時間を,また8行目の待ち時間で消灯時間を決めています.

図9に,プログラムで設定したON/OFF繰り返し波形を示します.

ON時間を$t_{on}$,OFF時間を$t_{off}$と定義します.周期(T)は,$t_{on}$と$t_{off}$を足し合わせた時間です.周期Tの逆数($1/T$)は繰り返し周波数($f$)です.そして,周期(T)のうON時間が占める割り合いを「デューティ比」と呼びます.図9(b)に示す式で表されます.

では,次の2パターンで点灯時間を変更して,LEDの点滅の違いを確認してみてください.

  • デューティ比10%「点灯時間:0.1秒,点灯時間:0.9秒
  • デューティ比90%「点灯時間:0.9秒,点灯時間:0.1秒
写真4 動作確認などにも使うRaspberry Pi PicoのLED
Raspberry Pi Picoに実装されているLEDは,RP2040のGPIO25ピンに接続されている.プログラムで,GPIO25ピンの電位を“H”,または“L”に制御することで,点灯させたり消灯させたりできる

リスト2 LEDを点滅(Lチカ)させるプログラム
図9 LEDを駆動するPWM信号のデューティ比を変える

[やってみよう2]LED点滅の周波数を変えてみる

リスト2に示したLチカ・プログラムは,周期Tが1秒の点滅プログラムです.周期Tを1秒(1Hz)から0.1秒(10Hz),0.01秒(100Hz)と変化させるとLEDの発光はどのように変化していくのでしょうか?

6行目と8行目のsleep(0.5)の数字を0.05,0.005と変化させてみてください.0.05秒(周期0.1秒:周波数10Hz)まではLEDの点滅を目で確認できるでしょう.しかし,0.005秒(周期0.01秒:周波数100Hz)になると,目が追いつかなくなります.

点滅が見えなくなる周波数100Hzで,今度はデューティ比を変えてみましょう.次のような条件にプログラムを変更すると,LEDの光の強さが変わって見えます.

  • デューティ比10%:点灯時間:0.001秒,点灯時間:0.009秒
  • デューティ比90%:点灯時間:0.009秒,点灯時間:0.001秒

これを「パルス幅変調(PWM:Pulse Width Modulation)調光」と呼びます.

[やってみよう3]明るさをふんわり変えられる“PWM”

リスト2のLチカ・プログラムでは,GPIO端子の出力信号をプログラムで直接制御する方法を用いていましたが,Picoにはパルス幅制御を専用に行うPWM機能が周辺回路(ペリフェラル)として組み込まれています.

リスト3にPWM機能を使用してLEDを点滅するプログラムを記述します.PWM機能を使用すれば,わずか数行でLEDのデューティ比を制御するプログラムを記述できます.

プログラムの説明

1行目は必要なモジュールをインポートします.GPIOピンとPWM機能が必要なのでmachineモジュールの中からPinとPWMの機能をインポートします.

3行目でPWM波形をGPIO25ピンから出力できるようにするための設定を行い,pwmという名前を付けます.4行目の“pwm.freq(10)”はPWM波形の繰り返し周波数を設定します.

リスト2では10Hzで設定しています.

“pwm.freq(100)”を変更すれば周波数を100Hzに設定できます.PWM機能の周波数の設定下限は8Hzです.

5~6行目でデューティ比の設定を行います.5行目の変数“duty”でデューティ比を[%]で指定します.

6行目のpwm.duty_u16()がPWMのデューティ比を設定するメソッドです.()の中は16ビット(0~65535)の整数値を引数として入れてデューティ比を設定します.引数が0のときは0%,65535のときは100%になります.

引数“int(65535 *duty/100)”の計算で,デューティ比に合った16ビットの整数値を算出しています.計算の中に割算“/”が入っているため,計算結果が浮動小数点型になります.そこで,int関数を使用して整数値に変換しています.

リスト1のLチカ・プログラムと同じようにPWMの周波数を10Hzから100Hzに変更すると,LEDの点滅を目で確認できなくなります.そして,デューティ比を変えることで光の発光強度を変えることができます.

リスト3 PWM機能を利用してLEDをふんわり点灯させるPythonプログラム

[やってみよう4]パルス幅変調調光を用いて発光強度を変えよう

LEDの輝度をランダムに変えてみましょう.リスト4にプログラムを示します.

1行目は,machineモジュールからPinとPWMの機能をインポートしています.

2行目で時間管理のutimeモジュールと,乱数関数のrandomモジュールをインポートします.

3行目は,PWM機能をGPIO25ピンから出力するように“pwm”という名前で設定を行っています.4行目では,PWMの繰り返し周波数を100Hzと設定します.

6行目以降は,繰り返しwhile文です.Whileの条件文を“True”にすることで,7~10行目のインデントされた制御範囲部分を無限で繰り返します.

7行目は,ランダム関数を用いて,0~1.0の乱数を作り変数xに代入します.

8行目は.PWMのデューティを設定するメソッドです.PWMのデューティ値は16ビットで設定するので,範囲は“:0~65535(0x0000~0xffff)”の整数値で設定します.プログラムは,最大値65535に乱数(0~1.0)の値を掛け算し,int関数を用いて整数値に変換します.

9行目では,0.1秒プログラムを休止します.

10行目は,変数xの値を確認できるように,print関数でThonnyのシェルに表示させています.

リスト4 ランダム関数を使うとLEDをふんわり点灯させることができる

コラム ろうそく風のゆらゆらLチカのプログラミング

鍵は“$1/f$”

光がゆらゆら揺れる「ろうそく型LEDライト」が,100円ショップなどで買えます. この動きは,心地よいリズムを出す「$1/f$ゆらぎ」で実現されています.

マイコンを使って疑似的に$1/f$ゆらぎを実現するときは,「間欠カオス法」を利用します.現在の発光強度x(t)が0.5未満かそれ以上で,次の発光強度を求める演算方法を変えます(図10).x(t)が,完全OFF(0)や完全ON(1.0)に近づくと,発光強度の値 “x(t+1)”が変化しにくくなり貼り付くため,ランダム関数を使って値を設定しなおすのです.

リスト5に実際のプログラムを示します.

プログラムの説明

1~4行目は,リスト4のプログラムと同じです.5行目は,LEDの発光強度初期値を0.5に設定しています.

6行目以下はwhile文による繰り返しです.

7~12行目は,“if~elif~else”文を用いて,現在の発光強度x(t)の値を見て,次の演算方法を分岐させています(図10).いわゆる間欠カオス動作を実現する部分です.

発光強度xが5%以上50%未満の場合,次のxの値を$x+2x^{2}$で演算します.発光強度xが50%以上95%未満の場合次のxの値を$x-2(1-x)^{2}$で演算します.発光強度xがそれ以外の場合は,ランダム関数を使って再度,発光強度xを設定します.

13行目でデューティ比設定を行い14行目で,0.1秒待ちます.

15行目で,発光強度xの値をThonnyのシェルに表示して確認します.


図10 間欠カオス法によるゆらぎの演算
リスト5 ろうそく風ゆらぎ発光(間欠カオス発光)のプログラム

第4章 16連LEDモジュール NeoPixelをグルグル光らせる実験
【Neoピカ 準備編】

準備

NeoPixelの動作のしくみ

写真5(a)に示す部品は,赤,緑,青の単色LEDと制御回路が1パッケージに内蔵されたフルカラーLEDです.外部から色をディジタル信号で制御できます.

NeoPixelには,テープ状,リング状,パネル状などさまざな形態の製品があります[写真5(b)].

MicroPythonが備えるNeoPixelを制御するライブラリを利用すれば,NeoPixel制御プログラムを簡単に作ることができます.マイコン・ビギナの第一歩といえば「Lチカ」が定番ですが,これからは「NEOピカ」が定番になるかもしれませんね.

写真5 16連LEDリング“NeoPixel”のLEDをPythonで自在に点灯させる
たくさんのLEDが色とりどりに輝く電飾デバイス.赤,緑,青のLED素子と制御回路を内蔵 したカラーLEDチップを搭載している

NeoPixelが搭載するLED制御IC WS2812B(Worldsemi製)は,$V_{DD}$,$\mathrm{D_{OUT}}$,$V_{SS}$,$\mathrm{D_{IN}}$の4端子を備えています.

図11に示すように,搭載された全LEDの$V_{DD}$(+5V)と$V_{SS}$(GND)端子を電源につなぎ,1番目LEDの$\mathrm{D_{OUT}}$端子を次LEDの$\mathrm{D_{IN}}$端子を繋ぎます.同ようにして,次のLEDにもシリアルで接続していきます.最初のLEDを0として,順に番号が割り振られます.LEDは,最大1024個まで接続できます.

図12に示すのは,NeoPixel上の各LEDに制御データが転送される仕組みです.1つのLEDに送る制御データは,赤色,緑色,青色の各々8ビット,計24ビットです.ディジタル回路で0,1信号といえば,“L”(0V),“H”(3.3V)のように,電圧の違いで区別するのか普通ですが,図12に示すように,NeoPixelはパルス幅のデューティ比の違いで,0と1を識別します.

制御信号の受け渡し方法を説明します.

0番目のカラーLEDは,$\mathrm{D_{IN}}$端子から入力されたシリアル制御データの内の先頭24ビット分だけを自分の制御データとして取り込み,その他の制御データを自分の$\mathrm{D_{OUT}}$端子から次のLEDの$\mathrm{D_{IN}}$端子へと送り出します.

次のカラーLEDも同様の動作で,先頭24ビット分の制御データを取り込み,その他の制御データを後段のLEDに送り出します.

以上の仕組みで各LEDに目的の制御データを届けます.

各LEDに届いた制御データは内蔵の制御回路で解釈して,赤,緑,青のLEDの発光量を制御しフルカラーで発光させます.一連のLED制御データを送るときに,各制御データの間隔を50μs以上開かないようにします.送られてくるデータの間隔が50μs以上になると,各LEDチップはデータ転送が終了したと判断します.

図11に示すように,NeoPixelをPicoへ接続するときは,$V_{SS}$端子をGNDピン(38ピン),$V_{DD}$端子を$V_{BUS}$(40ピン)につなぎ,$\mathrm{D_{IN}}$端子をGPIO端子に接続します.今回PicoのGPIO端子はGP0(1ピン)に接続します.写真6に示すように,PicoとNeoPixelは3本の配線で接続します.

図11 16連LEDリング“NeoPixel”の接続回路図
PicoにUSBから+5V電源を加えると,RP2040の$V_{BUS}$端子(40ピン)からこの5Vが出力されるので,これをNeoPixelに供給すればいい

図12 PicoとNeoPixelの通信
NeoPixelは,赤,緑,青のLEDと制御を一体化したカラーLED.シリアル接続でデータを送る

写真6 PicoとNepPixelの接続
PicoとNeoPixelは,GND,5V電圧($V_{BUS}$),DI信号(GP0)の3本で接続する

ws2812bライブラリの入手,セットアップ方法

Picoに書き込む通訳ファームウェア“MicroPython”は,NeoPixelを制御する関数を備えていません.別途ライブラリを入手して,使えるようにセットアップします.

PicoのMicroPython公式ガイド“Get started with MicroPython”に.PicoでNeoPixelを制御するライブラリの入手先が記載されています.URLに“hsmag.cc/pico-ws2812b”と入力すると,図13に示すGithubのNeoPixelライブラリ“pico_python_ws2812b”のページにアクセスできます.

図13に示すように,GithubページのCodeタグからDownload ZIPを選んでファイル一式をダウンロードします.解凍すると,図14に示すファイルが現れます.この中の“ws2812b.py(Python file)”がNeoPixelのライブラリです.パソコン上のわかりやすい場所に保存しておいてください.

次に,“ws2812b.py”をPicoのフラッシュ・メモリにアップロードします.

図15に示す,Thonnyのブラウザ機能を利用して,ファイルをアップロードします.ファイル・ブラウザは,Thonny起動画面の[表示(View)]-[ファイル(file)]を選ぶと,Thonnyの左端に現れます.ファイル・ブラウザを用いると,パソコン上とPicoのフラッシュ・メモリ上の両方のファイルを確認できます.

ファイル・ブラウザで,パソコン上“This computer”に保存した“ws2812b.py”を選んで右クリックするとメニュー・タグが開きます.メニュー・タグの中から“Upload to/”を選ぶと,Picoのフラッシュ・メモリにファイルがコピーされます.ファイル・ブラウザのRaspberry Pi Pico側のフォルダの内容に“ws2812b.py”が追加されたことを確認してください.

Githubからダウンロードしたexamplesフォルダの中に,いくつかのNeoPixelサンプル・プログラムが収録されています.

動画2 NeoPixelライブラリのセットアップ
図13 Githubにあるws2812bライブラリ

図14 ws2812bライブラリ・ファイル

図15 Picoのフラッシュ・メモリにws2812bをアップロードする

NeoPixelライブラリws2812b.pyの使い方

表1に示すのは,NeoPixelライブラリ“ws2812b.py”の使い方です.

最初に,ws2812bモジュールをインポートします.インポート方法は,MicroPython標準モジュールと同じです.

次に,NeoPixelやPico制御ピン番号の状態を設定します.この設定処理のことを「オブジェクト生成」または「インスタンス化」と呼びます.オブジェクトを作るときには,同時に固有の名前も付けます.今回“np”と命名しました.

オブジェクト指向プログラミングでは,オブジェクトを制御する命令を「メソッド」と呼びます.NeoPixelを発光させる主なメソッドは,“set_pixel()”,“set_pixel_line()”,“fill()”,“show()”です.

例えば,オブジェクトnpのp番目のLEDを(r,g,b)のカラー・コードで示す色で光らせたいときは“ np. set_pixel(p,r,g,b)”メソッドを利用します.メソッドのパラメータ“p”はLEDの位置番号です.最初のLEDは0番になり,1,2,3番と制御したいLED番号を設定します.

次に続くパラメータ“r”,“g”,“b”はそれぞれ,赤色,緑色,青色の発光強度値です.それぞれ,0~255の間で設定します.

表1 NeoPixelを制御する“ws2812b.py”ライブラリの使い方
最初に,モジュールの読み込みとオブジェクトの生成を行い,制御メソッドを使ってLEDの発光色の制御を行う

第5章 16連LEDモジュール NeoPixelをグルグル光らせる実験
【Neoピカ やってみよう編】

実験1 NeoPixelを光らせる(プログラム名:Neopixel1.py)

リスト6(a)は,NeoPixelの0番目位置のLEDを赤く光らせるプログラムです.

プログラムはわずか4行で,1~2行目はNeoPixel制御を行うための初期設定です.1行目でNeoPixelモジュール“ws2812b.py”を使うことを宣言します.

2行目で,NeoPixelを動作させるためのオブジェクトを“np”と言う名前で生成します.オブジェクト生成に必要なパラメータは「LEDの数,ステートマシンID,GPIOピン番号」です.

今回実験で用いるNeoPixelのLED数は16個です.GPIO0ピンからNeoPixelを制御する波形を出力します.ステートマシンIDは,NeoPixel制御で使用する0/1パルス幅信号を生成するためのプログラマブルIO(PIO)内のステートマシン番号です.ステートマシンIDは,0または4のどちらかを選択できます.

3~4行目は,NeoPixelのLEDを制御するメソッドです.

3行目のnp.set_pixel(0,255,0,0)メソッドで発光位置と色の指定を行います.最初のパラメータLED位置で0~15を選びます.カラー・コード(赤コード,緑コード,青コード)は発光させる色に合わせてそれぞれ8ビット(0~255)で指定します.

4行目のnp.show()メソッドは,3行目で指定したLEDの発光でデータをNeoPixelに送信する命令です.データ送信と同時にLEDが反応します.


(a)Neopixel発光プログラム
(b)シェルを用いた直接制御
リスト6 1行ずつRP2040を操作できるThonnyのシェル機能を利用して,NeoPixelのLEDの点灯/消灯を試してみる
写真7 LEDを選んで点灯させる
machineモジュールを利用する
動画3 NeoPixelのLEDを時計回りに順番に点灯させる

実験2 シェル機能を使って直接NeoPixelを制御する

リスト6(a)のプログラムを実行すると,0番目のLEDが赤色に発光します.次に,この状態でシェルから直接NeoPixelを制御するメソッドを入力してみましょう.

リスト6(b)のように,“np.set_pixel(2,0,0,255)”を入力してリターンし,次の行で“np.show()”を入力してリターンしてください.すると,2番目の位置のLEDが青色に発光します(写真7).

同じようにいくつかのLEDも発光させてみてください.シェルを用いると直接マイコンにメソッドを送って処理結果を観察できます.

“np.fill(0,0,0)”は,赤:0,緑:0,青:0のカラー・コードつまり黒(消灯)を意味しています.このメソッドと,np.show()メソッドを実行すれば,すべてのLEDが消灯します.

コラム シェルの便利機能

シェルのプロンプト“>>>”に文字を入力するときに,キーボードの“↑”キーと“↓”キーを押すと,以前に入力した文字列が再度表示されます.同じ文字列を繰り返し入力したいときに利用します.

コラム RGBカラー・コードの調べ方

LEDを希望の色で光らせるために,赤,緑,青のバランスをどのように設定したらよいのでしょう?

図16に示すように,Windowsアプリケーションの「ペイント」を利用すると,カラー・コードを調べることができます.目的の色をパレットから選択すれば,赤,緑,青のカラー・コードが得られます.ここに示された値をそのまま“set_pixel(r,g,b)”メソッドのパラメータに入力すると希望の色で光ります.

図16 RGBカラー・コードの取得
Windowsの付属アプリケーション・ソフトウェア「ペイント」を利用して,表現したい色とRGBカラー・コードの関係を調べることができる

実験3 発光を回転させる(プログラム名:Neopixel2.py)

あらまし

カラーLEDの発光位置を1つずつ変えていくプログラムを作ります.

NeoPixelリングのLEDがグルグル回転するように光ります(写真8).LEDの発光は,実験1で利用した“set_pixel(p,r,g,b)”メソッドを用います.このメソッドの最初のパラメータ“p”は発光するLEDの位置を示しています.

NeoPixelリングには,16個のカラーLEDがシリアル信号線で接続されており,1番目のカラーLEDが0,次のLEDが1,2,…と16番目のLEDが15と定義されています.

発光するLEDを変えたいときは,逐次,引数を0から15まで変更します.

リスト7に示すのは,NeoPixelリングの発光位置を時計回り回転させる「ラウンド・サークル」プログラムです.LEDの発光位置を変えるときは,for文を利用します.

プログラムは,通常上から下に命令を実行されます.for文,if文,while文などの制御文を用いた分岐を利用することで,複雑な処理プログラムに対応することができます.

リスト7 LEDの光りがぐるぐる回る「ラウンド・サークル」プログラム
LEDの発光位置を順番に移動させるプログラム.for文を用いてLED発光位置を移動させる
写真8 NeoPixel上にあるLEDが時計回りに順番に光る

プログラムの説明

1~2行目は,実験1でも記述があった通りNeoPixelの初期設定です.

4~9行目は,代表的な色のカラー・コードを指定色として利用できるように記述しています.

11行目は,LEDに発光させる色の設定を行っています.colorの指定色を変えることでラウンド・サークルの色が変化します.

12~19行目は,LEDを回転させるプログラムです.

13~19行目は,LEDの点灯を1周させるプログラムです.for文を用いて,変数iがrange()関数で示す範囲で変化します.具体的にrange(0,16,1)のように引数を設定することで,0から15までカウントアップするように動作します.

15~16行目は,いったん全LEDを消灯させています.

17~18行目は,変数iの示す位置でLEDを発光させます.

19行目では,0.1秒待ちます.

以降,変数iが15になるまで繰り返します.12行目にもfor文があり,range()関数の引数を3に設定して,3回転させています.

21~27行目にもう一度for文が出てきます.これら一連のプログラムで,カメラのフラッシュのような光り方を実現できます.21行目から以降を2回繰り返す制御文です.22行目のnp.fill()メソッドを用いて,全LEDを指定色で発光させます.24行目で0.1秒待ち,25行目ですべてのLEDを消します.これを2回繰り返します.

実験4 回転方向を逆に変えてみよう

LEDの点灯を逆回転させます.

LEDに色を設定するメソッドは“np.set_pixel(p,r,g,b)なので,引数“p”,つまり「LED位置」の値を15から0にカウントダウンします.

リスト7において,時計方向に回転させている箇所は13行目のfor文です.変数iを“range(0,16,1)”関数で,0から15にカウントアップしています.

このfor文を“for i in range(16,0,-1):”に変えます.NeoPixelライブラリのws2812bはLED位置のパラメータを0~15の範囲でしか受け付けません.このままプログラムを起動するとエラーが出ます.

“for i in range(16,0,-1):”の記述は,16~1までのカウントダウンです.

試しに,リスト8に示したようにThonnyのシェルから,“for i in range(16,0,-1):”を実行して変数iの変化を確認してみてください.16から1までカウントダウンしているのがわかります.そのため,LED位置パラメータ“p”に“16”が設定されて,エラーが発生したことがわかります.

LED位置パラメータが“15”から“0”にカウントダウンするように,for文のrange関数の引数を探してください.

正解は,“for i in range(15,-1,-1):”という記述です.

リスト8 今度は,NeoPixelのLEDの点灯を反時計回りに回してみよう

実験5 NeoPixelディジタル温度計(プログラム名:Neopixel4.py)

カラフルなディジタル温度計を作ろう!

RP2040マイコンが内蔵する温度センサで室内を測り,NeoPixelで表現します.

この温度センサの実体はダイオードで,その順方向電圧$V_{BE}$の温度依存性を利用しています.$V_{BE}$は,マイコン内部のA-Dコンバータの5番目のチャネル(AINSEL=4)目に 接続されており,プログラムで指定すれば$V_{BE}$電圧を読み取ることができます(図17).

$V_{BE}$は,27℃のとき0.706Vで,-1.721mV/℃の割合で変化します.次式を使って,A-Dコンバータで取得したアナログ電圧$V_{ADC}$[V]から温度$T$[℃]を算出します.

\begin{align} T=27℃-\frac{ V_{ADC} - 0.706\mathrm{V} } {0.001721 \mathrm{V/℃}} \end{align}

写真9に示すのは,RP2040内の温度センサの値を読み取って,NeoPixelに表示しているところです.

図17 RP2040マイコンが内蔵するA-D変換回路を動かして,温度センサの値を読み取ってみよう
RP2040マイコン内部の温度センサは,A-D変換機能のチャネル4につながっている.プログラムでチャネル4の電圧をA-Dコンバータで読み込む.ディジタル温度計が完成!

写真9 NeoPixel温度計
RP2040が内蔵する温度センサの値を,同じくRP2040が内蔵するA-Dコンバータで読み取って,NeoPixelに表示する.RP2040を指で触れると,体温で内蔵センサが温められて, LEDの表示が変化する

A-D変換機能を使う

RP2040は,12ビットA-D変換機能が備わっており,4チャネルのアナログ電圧と温度センサ電圧$V_{ADC}$を読み取ることができます.ただし,ADCチャネル番号3(AINSEL=3)のアナログ電圧は,Pico基板の外部ピンには接続されていないので,ユーザが使えるA-Dコンバータは,3チャネル(AINSEL=0~2)です.温度センサのアナログ電圧は,ADCチャネル番号4(AINSEL=4)を指定すれば読み取れます.

次の3ステップで,温度センサ電圧をA-D変換します.

  1. import machine # machineライブラリを準備(ADC機能が入ってる)
  2. sensor = machine.ADC(4)# ADCチャネル番号4のオブジェクト生成
  3. reading = sensor.read_u16()# ADC変換メソッドの実施

温度センサ電圧以外のA-D変換も同様のステップで行えます.2行目の“machine.ADC(AINSEL)”の“AINSEL”の値を0,1,2に変えることによって,Picoの指定端子からアナログ電圧をA-D変換できます.

プログラムの説明

リスト9に温度計プログラムを示します.全部で16行です.1行目から5行目までがプログラムの初期設定を行い,7行目のwhile文以降が温度読み取りと表示を行うくり返し部分です.

1行目のimport文は,プログラムに必要なモジュールを宣言しています.

2行目で,温度センサ電圧を読み取るチャネル番号4(AINSEL=4)の“ADC”のオブジェクトを生成します.名前は“sensor_temp”としました.

3行目は,A-D変換の1LSB分の電圧を“conversion_factor”という名前で前もって演算しておきます.A-D変換は,0~3.3V間のアナログ電圧をディジタル値に変換します.RP2040マイコンが内蔵するA-Dコンバータの分解能は12ビットですが,PythonのA-D変換命令は16ビット・データ(下位4ビットは無効な値)で出力します.1LSB分の電圧を演算するのに用いる値は65535(=$2^{16}-1$)を使います.

4行目は,NeoPixelのオブジェクト生成です.5行目の“temp_base”は,NeoPixelの0番目の位置の温度を定義しています.NeoPixelは16連なので,0番目のLEDを10℃に設定すると,10~25℃の温度範囲で表示できます.表示温度がこの範囲に収まらないときは,“temp_base”の値を変えます.

6行目のwhile文以降インデントされた部分が繰り返し範囲になります.

7行目で温度センサ電圧の読み取りを行っています.“read_u16()”は,A-D変換を実行するメソッドです.“sensor_temp”オブジェクトをA-D変換するので,温度センサのアナログ電圧のADCディジタル値が得られます.1LSBの電圧値“conversion_factor”をかけ合わせるので,“reading”値は温度センサの電圧値になります.

8行目は,温度センサ電圧値を温度値に変換する計算です.温度センサはダイオードの$V_{BE}$電圧温度特性を利用している式(1)を用いて換算します.

9行目で,温度値をint関数を用いて整数値に変換しています.22.8311℃の場合は“22”になります.

10行目は,温度値よりNeoPixelに表示するLEDの個数を決定します.

11行目から14行目までは,NeoPixelに表示するためのメソッドです.

11行目の“np.fill(r,g,b)”は,すべてのLEDを塗りつぶすメソッドです.(0,0,0)はすべてのLEDを黒(消灯)を意味しています.

12行目の“np.set_pixel_line(p1,p2,r,g,b)”は,指定した間のLEDを同じ色に塗りつぶ すメソッドです.0番目のLEDからtemp_pixelで示すLEDの1つ前までをカラー・コード(30,5,0)(うすい橙)で塗りつぶします.

13行目の“set_pixel(p,r,g,b)”は,おなじみの指定した位置のLED発光メソッドです.temp_pixel位置のLEDを(255,0,0)(赤色)に設定します.

14行目の“np.show()”は,11行目から13行目まで設定した発光データをLEDに送ります.

15行目は測定した温度値をThonnyのシェルに表示する命令です.

16行目は2秒待ちメソッドです.16行目までの処理が終わったら,もう一度7行目に戻って同じ処理を繰り返します.

リスト9 PicoとNeoPixelで作るディジタル温度計のPythonプログラム

コラム Thonnyのプロッタ機能

リスト9では,Picoの温度センサで読み取った温度値をThonnyのシェルにも表示しています.

図18に示すように,プログラムのprint(temperature)命令が行われると,そのびにシェルに温度が表示されます.

Thonnyは,図18に示す描画機能を備えています.Thonnyの表示[View]-[Plotter]を選ぶと,シェルの右側にフレームが開き,グラフで確認できます.

図18 温度測定の表示,プロット
Thonnyの[表示(View)]-[Plotter]を選ぶと,シェルに表示した値をグラフ化して見せてくれる

実験6 色を微調整できるカラー・モデルを使う(プログラム名:Neopixel6.py)

あらまし

ここまでは,RGBカラー・コードを使って,NeoPixelの色を指定してきました.

RGBで色設定を行うときは,Windowsアクセサリのペイントなどのカラー・コード表を利用します.細かく色を調整したいときは,カラー・コード表と見比べながら,RGBカラー・コードを決定します.これは,人間の目が赤,緑,青の色を識別するからですが,感覚的ではありません.

図19に示すのは,人間の感覚に合った色表現の方法“HSBカラー・モデル”です.人の感覚に近いパラメータです.

  • 色相(Hue)
  • 彩度(Saturation)
  • 明度(Brightness)

色相(Hue)は,赤色を0°,緑色を120°,青色を240°というふうに角度で表現します.

彩度(Saturation)は色のあざやかさです.パラメータは,0(にごり)~1.0(あざやか)の間で表現されます.0に設定すると,選べるのは白色~灰色~黒色だけです.1に設定すると,0(暗い)~1.0(明るい)の間で表現できます(図19).

HSBカラー・モデルは,アニメーション会社ピクサーの共同設立にかかわったアルヴィ・レイ・スミス(Alvy Ray Smith)が1978年に考案しました.コンピュータ・グラフィックの 世界の定番技術です.

図19 アートのようなキメ細かい色合い調整を望むなら「HSBカラー・モデル」がお勧め
色相,彩度,明度という人間の感覚に近いパラメータで色を指定できる

HSB‐RGB変換関数

人間の感覚に近いHSBカラー・モデルをマイコンがわかるRGBコードに変換する

NeoPixelのLEDを制御する信号は,RGBカラー・コードで記述が行われています.

リスト10に示すのは,HSBカラー・モデルで表現したパラメータをRGBカラー・コードに変換する関数“hsb2rgb.py”です.HSBカラー・モデルのパラメータ「色相(0~360),彩度(0~1.0),明度(0~1.0)」を入力すると,RGBカラー・コード「赤色(0~255),緑色(0~255),青色(0~255)」に変換します.

変換プログラムは40行程度で,11行目まではHSB関数の引数が適用範囲に入るように範囲処理を行っています.

12~14行目は,R,G,B値最大値,最小値など,8ビット(0~255)整数値に正規化する処理です.あとはhue値を60°ごとに分けてそれぞれ定義された変換演算を行います.40行目で演算されたRGBカラー・コードのR,G,B値を変数名colorcodeのリスト型変数に変換します.関数の戻り値は1つの値しか取れないので,R,G,Bの3つの値をリスト変数に変換します.

Pythonで記述したHSB‐RGB変換関数“hsb2rgb.py”をダウンロードして,Thonnyのファイル・ブラウザでPicoのフラッシュ・メモリにアップロードしてください.

RP2040のフラッシュ・メモリに,“hsb2rgb.pyファイル”が確認できれば,HSB‐RGB変換関数を使用できます.

リスト10 HSB‐RGB変換プログラム
しきい値“Hue”によって演算式を使い分け,R,G,Bの値を算出する

使い方

HSB‐RGB変換関数は,次のHSBカラー・モデルのパラメータ値をRGBカラー・コード「赤色(0~255),緑色(0~255),青色(0~255)」に変換します.

  • 色相(0~360)
  • 彩度(0~1.0)
  • 明度(0~1.0)

Pythonで関数を使う手順を次に示します.

  1. Hsb2rgbモジュールをインポートする
  2. RGB値を格納する変数を準備する.例えばcolor
  3. color = hsb2rgb.HSB(h(色相),s(彩度),b(明度))を記述する
  4. RGBの値は変数colorの中に「r(赤色),g(緑色),b(青色)」順番で格納する
  5. 変数colorの各色値は「r:color[0], g:color[1], b:color[2]」に分解する

NeoPixel 16リングでカラー・ホイール表示する

HSBカラー・モデルを用いると,簡単にカラー・ホイールを作ることができます(図19).

HSBカラー・モデルは色相(0~360),彩度(0~1.0),明度(0~1.0)のパラメータで色を設 定します.

色は色相で設定します.

16LEDのNeoPixelリングを用いた場合は,LEDの配置の角度と同じ値を色相パラメータ値と すれば,HSBカラー・モデルの色を設定できます.16LEDのNeoPixelリングを使うときは, 各LED間の角度はそれぞれ22.5°(=360÷16)です.for文を用いて色相パラメータの値を 算出します.

プログラムの説明

リスト11に示すプログラムを実行すると,LEDの点灯がぐるぐる回転します(写真10).リング状のNeoPixelにカラー・ホイールを表示して,位置をずらしていきます.

プログラムには,HSBカラー・モデル・パラメータをRGBカラー・コードに変換する関数“hsb2rgb.py”を使って記述しています.わずか17行です.

1行目で必要なライブラリ・モジュールをインポートします.

2行目はNeoPixelのオブジェクト生成を行います.

4~9行目は,カラー・ホイールを表示する関数です.関数の定義は“def”関数名(引数):文を用いて行い,インデントで区切られた部分が関数の範囲です.

4行目でカラー・ホイールを表示する“colorwheel(a,s,b)”関数を定義しています.関数の引数は,次の3つのパラメータです.

  • a:表示角度(0~15)
  • s:彩度(0~1.0)
  • b:明度(0~1.0)

カラー・ホイールの表示角度を変えるには,引数aの値を変えて“colorwheel”関数を実行します.

5行目は,for文を用いた繰り返し実行で.16個のLEDの色情報を作ります.

6行目は,LEDの発光位置を決める演算です.各LEDは“22.5×p”が色相(hue)の値になり発光色が決まります.

7行目は,HSBカラー・モデルのパラメータをRGBカラー・コードに変換する関数を実行します.変数colorにHSBカラー・モデルのパラメータから変換されたRGBカラー・コードが入ります.

8行目は,RGBカラー・コードを使ってNeoPixelの色を設定しています.

9行目は,NeoPixelに設定データを送っています.

11~17行目は,レインボ・サークルを表示するプログラムです.

12~15行目は,カラー・ホイールを1回転させるfor文です.

“Colorwheel(a,s,b)”関数の最初の引数aを15から0まで変化させると,カラー・ホイールが時計周りに回転します.12行目のfor文を13行目のfor文に置き換えると,引数aが0から15に変化し,反時計周りに回転します.11行目のfor文は,1回転を5回繰り返す命令です.

16~17行目は,NeoPixelのLEDをすべて消す命令です.

リスト11 レインボ・サークルのPythonプログラム
HSBカラー・モデルの色相,彩度,明度のパラメータをRGBカラー・コードに変換してからNeoPixelに送り出す
写真10 レインボ・サークルの発光のようす


参考文献

  1. https://www.raspberrypi.org/documentation/rp2040/getting-started/
  2. https://micropython.org/
  3. https://www.raspberrypi.org/documentation/rp2040/getting-started/
  4. https://hackspace.raspberrypi.org/books/micropython-pico
  5. https://github.com/micropython/micropython/wiki
  6. WS2812Bデータシート,Worldsemi

(c)2021 Uta Taguchi All Right Reserved.