はじめに
本ページは、金丸隆志著「実例で学ぶRaspberry Pi電子工作」(講談社ブルーバックス)の補足情報をまとめるためのページです。本書の内容が最新のRaspberry Pi OSで動作するようメンテナンスを続けています。補足情報
Raspberry PiのOSの更新に伴う内容の変更などを以下のページに記します。このページを書籍と合わせてごらんください。本書の演習を Raspberry Pi Pico シリーズで実行するという下記のコンテンツもおススメです!
日付 | バージョン | カーネルバージョン | OSバージョン |
2023/5/3 | Raspberry Pi OS 2023-05-03 | 6.1.21+ / 6.1.21-v7+ / 6.1.21-v7l+ / 6.1.21-v8+ | Bullseye (最終版) |
2023/10/10 | Raspberry Pi OS 2023-10-10 | 6.1.0-rpi4-rpi-{v6,v7,v7l,v8,2712} | Bookworm (Pi 5 は Bookworm 以降のみが動作) |
2023/12/5 | Raspberry Pi OS 2023-12-05 | 6.1.0-rpi7-rpi-{v6,v7,v7l,v8,2712} | |
- | 中略 | - | |
2025/5/13 | Raspberry Pi OS 2025-05-13 | 6.12.25-rpi7-rpi-{v6,v7,v7l,v8,2712} |
pi@raspbberrypi:~ $ここに見える「pi」はユーザー名を表しており、ユーザー名「pi」は2022年4月以前の古いOSで用いられていたデフォルトのユーザー名です。最新のOSを用いている方ならば、「pi」の部分に自分で決めたユーザー名が表示されているでしょう。私の場合、ユーザー名はいつも「kanamaru」としています。
python3 bb2-02-01-led.py
python3 bb2-03-01-3led.py python3 bb2-03-02-dice.py python3 bb2-03-03-dice-switch.py python3 bb2-03-04-dice-switch-delay.py
key = 'API_KEY'具体的には、5つのファイルの下記の行番号の位置に上の行があります。
key = '................................'書き換えたら、ファイルを上書き保存してください。この作業を、5つのファイル全てで行います。 以上でプログラム実行前の準備は完了です。
python3 bb2-04-01-weather.py本書の図4-7のように、入手した天気予報データを全て表示するコマンドです。表示された中身を理解する必要はなく、 「天気予報データを入手できた」ということを確認できればそれで十分です。
(省略) "weather": [ { "id": 804, "main": "Clouds", "description": "厚い雲", "icon": "04d" } ], "clouds": { "all": 100 }, "wind": { "speed": 2.42, "deg": 118, "gust": 2.49 }, "visibility": 10000, "pop": 0, "sys": { "pod": "d" }, "dt_txt": "2025-06-10 03:00:00" } ], "city": { "id": 1850144, "name": "東京都", "coord": { "lat": 35.6895, "lon": 139.6917 }, "country": "JP", "population": 12445327, "timezone": 32400, "sunrise": 1749065152, "sunset": 1749117228 } }なお、取得されるデータは本書の図4-7の形式とは全く異なります。予報のデータは上の「"main": "Clouds",」の部分を使っており、「"description": "厚い雲",」の部分は使っていません。 最終的に LCD に表示するうえで、"description" の部分はバリエーションが多すぎると思えたためです。
python3 bb2-04-02-forcast.py本書の図4-8のように、3日間の予報のみをデータを整理して表示します。
今日, 曇り, 26/28, 2025-06-05 06:00:00 明日, 曇り, 26/26, 2025-06-06 06:00:00 明後日, 曇り, 27/27, 2025-06-07 06:00:00なお、「お天気Webサービス」と異なり、「時々」や「のち」を含む結果は現れません。 「Clear(晴れ)」、「Clouds(曇り)」、「Rain(雨)」、「Snow(雪)」、「Thunderstorm(雷)」、「Drizzle(霧)」 の6種類の天気しか表示されません。その点はこのサイトの特性としてご了承ください。
python3 bb2-04-04-lcd-4modes.py python3 bb2-04-05-lcd-3modes.py実行結果は本書のプログラムと同じです。
i2cdetect -y 1このコマンドは、Raspberry Piに接続されたI2Cデバイスのアドレスを出力するものです。
lat = '35.6895' lon = '139.6917'これは緯度35.6895、経度139.6917を表しており、実はこれが東京を意味していた、というわけです。「東京」という地名で位置を指定したいところですが、それは無料枠の OpenWeather ではできない、ということです。
city = 'Tokyo'この行のTokyoの部分をSapporoに変更し、上書き保存します。
city = 'Sapporo'そして、bb2-04-00-checkcity.py を Thonny または下記のコマンドで実行してください。
python3 bb2-04-00-checkcity.pyすると、下記のように Sapporo の緯度と経度が得られます。
Sapporo lat = '43.0642' lon = '141.3469'このうち、下記の2行で bb2-04-01~bb2-04-05 のファイルの該当行を置き換えて保存すれば、それらのファイルは Sapporo の天気予報を出力するようになります。
lat = '43.0642' lon = '141.3469'
python3 bb2-05-01-TV.pyこれまで同様、タクトスイッチの利用に RPi.GPIO ではなく gpiozero を用いています。
sudo apt update sudo apt install python3-fastapip.160 の図5-12 の回路が準備できている状態で、FastAPI を用いて書かれた Web サーバーを下記の手順で実行します。
cd bb2-05-02-TVサンプルプログラムを bluebacks ディレクトリに展開した場合の移動コマンドは以下です。
cd bluebacks/bb2-05-02-TV移動したら、下記のコマンドで Web サーバーを実行します。
python3 app.pyこのコマンドが実行されたままの状態で、ブラウザで下記のアドレスにアクセスします。ブラウザは Raspberry Pi のものでも、同一ネットワーク内にある PC やスマートフォンのものでも構いません。
http://(Raspberry PiのIPアドレス):8000/5-2例えば、Raspberry PiのIPアドレスが 192.168.1.3 ならば、ブラウザのアドレス欄に「 http://192.168.1.3:8000/5-2 」を記入して Enter キーを押す、ということです。 Raspberry PiのIPアドレスの調べ方は、本書 p.336 に載っているように ifconfig コマンドを用いても良いですし、Raspberry Pi のデスクトップ右上の Wifi アイコンにマウスポインタを重ねることでも調べられると思います。
app.py : Pythonプログラム。Webサーバーの構築と、GPIO の利用など(ここではGPIOは用いていない) static/javascript.js:JavaScript プログラム。ブラウザからの指令などを app.py に送る templates/index.html:HTML ファイル。ページの見た目を決めている
sudo mousepad /boot/firmware/config.txtそして、末尾に追加されている設定を、下記のように先頭に「#」をつけることでコメントアウトし無効化します。
#dtoverlay=gpio-ir,gpio_pin=24 #dtoverlay=gpio-ir-tx,gpio_pin=25上書き保存して mousepad を終了し、Raspberrry Pi を再起動すれば GPIO 25 と 24 の利用設定は解除されています。
sudo apt remove lirc liblirc0 liblirc-client0
sudo mousepad /boot/firmware/config.txtそして、そのファイルの末尾に下記の一行を追加し、上書き保存してから mousepad を閉じてください。
dtoverlay=pwm-2chan
python3 bb2-06-01-zero.py
python3 bb2-06-02-pantilt.pyサーボの回転の向きを変えたい場合、プログラム中の下記の部分の2行目先頭の「#」を削除するか、
# 一般的なサーボモーターはこちらを有効に #duty = (servo_max-servo_min)*(val-val_min)/(val_max-val_min) + servo_minあるいは下図のように半固定抵抗の3.3VとGNDへの接続を入れ替えるかのどちらかの方法をとってください。
libcamera-hello -t 0libcamera-hello が存在しないと言われた場合、下記のコマンドでインストールできます。
sudo apt update sudo apt install libcamera-appsなお、libcamera-hello ではなく、第7章で使用する bb2-07-01-preview.py を実行しても良いです(ただし、OpenCVをインストールする必要があります)。
(1) sudo apt update (2) sudo apt install libjpeg-dev cmake libcamera-dev (3) git clone https://github.com/neuralpi/mjpg-streamer.git (4) cd mjpg-streamer/mjpg-streamer-experimental (5) make (6) cd (7) sudo mv mjpg-streamer/mjpg-streamer-experimental /opt/mjpg-streamerなお、OS として Bullseye やリリース直後の Bookworm をお使いの方は、上記の (5) で LibCamera.cpp のビルド中にエラーが起こると思います。その場合、エラーが出た状態から以下の 8 コマンドを一つずつ順に実行してください。この 8 コマンドが上記 (5)~(7) の代替、というイメージです。
rm -rf _build mkdir _build cd _build cmake -DLIBCAMERA_USES_TRANSFORM=ON .. cd .. make cd sudo mv mjpg-streamer/mjpg-streamer-experimental /opt/mjpg-streamer
opt_in = 'input_libcamera.so -camver 1 -fps 15 -r 640x480 -s 640x480'このうち
-camver 1の部分が、カメラモジュールのバージョンの数字を指定している部分です。お使いのカメラモジュールがバージョン 2 か 3 なら、この数字を 2 または 3 に変更し、それからファイルを上書き保存してください。
python3 bb2-06-03-stream.py &ブラウザでの動作確認は、本書 p.224 と同じです。
sudo apt update sudo apt install python3-fastapiそれ以外には、ハードウェアPWMを利用するために本ページの「p.192:ハードウェアPWMを用いるための準備」に基づき、 /boot/firmware/config.txt の末尾に「dtoverlay=pwm-2chan」を追記する必要があります。
cd bb2-06-05-pantiltサンプルプログラムを bluebacks ディレクトリに展開した場合の移動コマンドは以下です。
cd bluebacks/bb2-06-05-pantilt移動したら、下記のコマンドで Web サーバーを実行します。
python3 app.pyこのコマンドが実行されたままの状態で、ブラウザで下記のアドレスにアクセスします。ブラウザは Raspberry Pi のものでも、同一ネットワーク内にある PC やスマートフォンのものでも構いません。
http://(Raspberry PiのIPアドレス):8000/6-5例えば、Raspberry PiのIPアドレスが 192.168.1.3 ならば、ブラウザのアドレス欄に「 http://192.168.1.3:8000/6-5 」を記入して Enter キーを押す、ということです。 Raspberry PiのIPアドレスの調べ方は、本書 p.336 に載っているように ifconfig コマンドを用いても良いですし、Raspberry Pi のデスクトップ右上の Wifi アイコンにマウスポインタを重ねることでも調べられると思います。
app.py : Pythonプログラム。Webサーバーの構築と、GPIO の利用など static/javascript.js:JavaScript プログラム。ブラウザからの指令などを app.py に送る templates/index.html:HTML ファイル。ページの見た目を決めているなお、ブラウザ上のカメラの映像ですが、上述した mjpg-streamer が異常終了の問題が起こった場合、映像がそこで停止しているでしょう。その場合、ブラウザで再読み込みを実行すると、映像が再び流れ始めると思います。
python3 bb2-06-04-pantilt-pca9685.py
cd bb2-06-06-pantilt-pca9685サンプルプログラムを bluebacks ディレクトリに展開した場合の移動コマンドは以下です。
cd bluebacks/bb2-06-06-pantilt-pca9685移動したら、下記のコマンドで Web サーバーを実行します。
python3 app.pyこのコマンドが実行されたままの状態で、ブラウザで下記のアドレスにアクセスします。ブラウザは Raspberry Pi のものでも、同一ネットワーク内にある PC やスマートフォンのものでも構いません。
http://(Raspberry PiのIPアドレス):8000/6-6例えば、Raspberry PiのIPアドレスが 192.168.1.3 ならば、ブラウザのアドレス欄に「 http://192.168.1.3:8000/6-6 」を記入して Enter キーを押す、ということです。 Raspberry PiのIPアドレスの調べ方は、本書 p.336 に載っているように ifconfig コマンドを用いても良いですし、Raspberry Pi のデスクトップ右上の Wifi アイコンにマウスポインタを重ねることでも調べられると思います。 現れるページの見た目と機能は p.230 の図 6-19 と同じです。なお、「{"detail":"Not Found"}」というエラーが表示される場合、もう一度 raspi2-sample-pi5.zip をダウンロードして頂けますでしょうか?2024.02.25 にサンプルファイルを更新したためです。
sudo apt update sudo apt install libopencv-dev python3-opencv
python3 bb2-07-01-preview.py python3 bb2-07-02-binary.py python3 bb2-07-03-cannyedge.py python3 bb2-07-04-circle.py python3 bb2-07-05-face.py
python3 bb2-07-06-tracking-circle.py python3 bb2-07-07-tracking-face.py
python3 bb2-07-08-tracking-circle-pca9685.py python3 bb2-07-09-tracking-face-pca9685.py
sudo mousepad /boot/firmware/config.txtそして、第5章でテレビのリモコン操作をするための設定が末尾にあった場合、下記のように先頭に「#」をつけてコメントアウトして無効化します。
#dtoverlay=gpio-ir,gpio_pin=24 #dtoverlay=gpio-ir-tx,gpio_pin=25これで GPIO24 と GPIO25 をソフトウェアPWMの出力として利用できるようになります。
#dtoverlay=pwm-2chanこれで GPIO18 をソフトウェアPWMの出力として利用できるようになります。
LEG_F_R = NEUTRAL + 100 LEG_F_L = NEUTRAL - 100 LEG_B_R = NEUTRAL - 100 LEG_B_L = NEUTRAL + 1004つある数字の100が、脚の振り幅(モーターの動く角度)に対応する数値です。これを小さめの値、例えば下記のように60に変更します。
LEG_F_R = NEUTRAL + 60 LEG_F_L = NEUTRAL - 60 LEG_B_R = NEUTRAL - 60 LEG_B_L = NEUTRAL + 60変更して保存した後はそれぞれの app.py を再度実行し直す必要があります。
sudo apt update sudo apt install python3-fastapiそして、FastAPI を用いて書かれた Web サーバーを下記の手順で実行します。
cd bb2-08-06-pca9685サンプルプログラムを bluebacks ディレクトリに展開した場合の移動コマンドは以下です。
cd bluebacks/bb2-08-06-pca9685移動したら、下記のコマンドで Web サーバーを実行します。
python3 app.pyこのコマンドが実行されたままの状態で、ブラウザで下記のアドレスにアクセスします。ブラウザは Raspberry Pi のものでも、同一ネットワーク内にある PC やスマートフォンのものでも構いません。
http://(Raspberry PiのIPアドレス):8000/8-6例えば、Raspberry PiのIPアドレスが 192.168.1.3 ならば、ブラウザのアドレス欄に「 http://192.168.1.3:8000/8-6 」を記入して Enter キーを押す、ということです。 Raspberry PiのIPアドレスの調べ方は、本書 p.336 に載っているように ifconfig コマンドを用いても良いですし、Raspberry Pi のデスクトップ右上の Wifi アイコンにマウスポインタを重ねることでも調べられると思います。
app.py : Pythonプログラム。Webサーバーの構築と、GPIO の利用など static/javascript.js:JavaScript プログラム。ブラウザからの指令などを app.py に送る templates/index.html:HTML ファイル。ページの見た目を決めているなお、ソフトウェアPWMも用いる場合のプログラムは bb2-08-07-sw ディレクトリに格納されており、実行時に参照すべきアドレスは「http://(Raspberry PiのIPアドレス):8000/8-7」です。
mousepad web6legs.serviceすると、以下の内容がみられます。
[Unit] Description=Web6Legs After=network.target [Service] Type=simple WorkingDirectory=/home/pi/bb2-08-06-pca9685 #WorkingDirectory=/home/pi/bluebacks/bb2-08-06-pca9685 ExecStart=/usr/bin/python3 -m app TimeoutStopSec=5 StandardOutput=null [Install] WantedBy = multi-user.target変更しなければならないのは下記の部分、すなわち、プログラムが存在するディレクトリを指定している部分です。
WorkingDirectory=/home/pi/bb2-08-06-pca9685 #WorkingDirectory=/home/pi/bluebacks/bb2-08-06-pca96852行ありますが、2行目の先頭に「#」がついていますのでこちらは無効化された状態です。
sudo cp web6legs.service /etc/systemd/system/そうすると、ブラウザで6脚ロボットを操作するためのプログラムが「web6legs」という名前のサービスとして利用できるようになります。サービスの起動と終了を行うコマンドは以下の通りです。これらのコマンドは、どのディレクトリで実行しても構いません。
sudo service web6legs startサービス終了(python3 app.py の終了と同等)
sudo service web6legs stopなお、サービスが起動しているかどうかは、下記のように ps ax コマンドの出力から「python3」を含むものを抽出することで確認することができます。
kanamaru@raspberrypi:~ $ ps ax | grep python3 914 ? Ssl 0:00 /usr/bin/python3 -m app 1947 pts/0 S+ 0:00 grep --color=auto python31行目の「/usr/bin/python3 -m app」が、プログラム app.py が実行されていることを表します。
sudo systemctl enable web6legsこのコマンドを実行後、Raspberry Pi を再起動すると、自動で web6legs が起動されるようになります。ps ax コマンドで確認してみましょう。
sudo systemctl disable web6legs
sudo mousepad /etc/rc.localさて、このコマンドを実行したとき、既存ファイルである /etc/rc.local がテキストエディタ mousepad で開かれます。そうなった方は先に進んで構いません。
wget https://raw.githubusercontent.com/neuralassembly/raspi/refs/heads/master/rc.local chmod a+x rc.local sudo mv rc.local /etcこの3コマンドを実行してファイル /etc/rc.local を作成したら、上記の mousepad で /etc/rc.local を開くコマンドを実行し、以下に進みましょう。
python3 /home/pi/bb2-08-04-lcd.py $_IPなお、2022年4月にリリースされた OS よりデフォルトユーザー pi は廃止されています。そのため、上のコマンドの pi の部分は、皆さんが作成したユーザー名で置き換変える必要があります。すなわち、「kanamaru」というユーザーを作成したのなら、「python3 /home/kanamaru/bb2-08-04-lcd.py $_IP」となる、ということです。
python3 /home/pi/bluebacks/bb2-08-04-lcd.py $_IP上と同様に、「pi」は自分のユーザー名に置き換えてください。
python3 /home/pi/bb2-08-05-shutdown.py &なお、2022年4月にリリースされた OS よりデフォルトユーザー pi は廃止されています。そのため、上のコマンドの pi の部分は、皆さんが作成したユーザー名で置き換変える必要があります。すなわち、「kanamaru」というユーザーを作成したのなら、「python3 /home/kanamaru/bb2-08-05-shutdown.py &」となる、ということです。
python3 /home/pi/bluebacks/bb2-08-05-shutdown.py &上と同様に、「pi」は自分のユーザー名に置き換えてください。
python3 /home/pi/bb2-06-03-stream.py &なお、2022年4月にリリースされた OS よりデフォルトユーザー pi は廃止されています。そのため、上のコマンドの pi の部分は、皆さんが作成したユーザー名で置き換変える必要があります。すなわち、「kanamaru」というユーザーを作成したのなら、「python3 /home/kanamaru/bb2-06-03-stream.py &」となる、ということです。
python3 /home/pi/bluebacks/bb2-06-03-stream.py &上と同様に、「pi」は自分のユーザー名に置き換えてください。
cd bb2-08-08-pca9685サンプルプログラムを bluebacks ディレクトリに展開した場合の移動コマンドは以下です。
cd bluebacks/bb2-08-08-pca9685/移動したら、下記のコマンドで Web サーバーを実行します。
python3 app.pyこのコマンドが実行されたままの状態で、ブラウザで下記のアドレスにアクセスします。ブラウザは Raspberry Pi のものでも、同一ネットワーク内にある PC やスマートフォンのものでも構いません。
http://(Raspberry PiのIPアドレス):8000/8-8例えば、Raspberry PiのIPアドレスが 192.168.1.3 ならば、ブラウザのアドレス欄に「 http://192.168.1.3:8000/8-8 」を記入して Enter キーを押す、ということです。 Raspberry PiのIPアドレスの調べ方は、本書 p.336 に載っているように ifconfig コマンドを用いても良いですし、Raspberry Pi のデスクトップ右上の Wifi アイコンにマウスポインタを重ねることでも調べられると思います。 現れるページの見た目と機能は p.230 の図 6-19 と同じです。なお、「{"detail":"Not Found"}」というエラーが表示される場合、もう一度 raspi2-sample-pi5.zip をダウンロードして頂けますでしょうか?2024.02.25 にサンプルファイルを更新したためです。
sudo apt update sudo apt install avahi-daemonインストール後に再起動すると、自動的にavahiが起動し、「raspberrypi.local」でのアクセスが可能になります。
#電子工作 の実験。#RaspberryPiPico #6脚ロボット を実際に動かして見ました。概ね想定通りだと思います☺️。動作は「実例で学ぶ #RaspberryPi 電子工作」と #OttoDIY を参考にしています。ただ、まだぎこちない感じなので、少しずつ動作を改良したいなぁと考えています🤔。#RaspberryPi #ラズパイ pic.twitter.com/GibYrLPeof
— pass810 (@pass810) August 14, 2022
#電子工作 の実験。#ラズパイ 3A+に #USB #小型スピーカー を取り付けて見ました😄。アマゾンで600円位です。試しに #Julius を利用して #音声認識 をして見ました。「実例で学ぶ #RaspberryPi 電子工作」の補足情報と同じです。勉強になりました☺️。 pic.twitter.com/9SY2dFko7l
— pass810 (@pass810) October 3, 2021
Pythonで簡単なプログラムは書けるのだけれど、電子工作は全くわからない!となってしまう人向けの本でした。
— SSKGo💻️ (@SSKGo2) October 28, 2020
私自身が
抵抗はなぜ必要なの?
LEDを光らせるにはハンダが要るの?
ってレベルでしたが、これを読めばサクッと電子工作できました。https://t.co/IqAI41XZeI#ラズパイ
実例で学ぶRaspberry Pi電子工作
— KJ (@KinjiKamizaki) July 31, 2020
金丸隆志
電子工作もできる小型コンピュータ「Raspberry Pi」についての一冊。天気予報やリモコン化、六脚ロボットなど。
コードも結構混みいってるし、そこそこ複雑な回路も出てくるので初学者にはちょっと厳しいかも。ただ、その分実践的で面白い。ー#読了 pic.twitter.com/CirbzooFll
#電子工作 の実験。#RaspberryPi で #6脚ロボット を使って、#対象物追跡(#円追跡)です。意外としっかり追いかけてくれました😊。以前行った #OpenCV による対象物追跡に6脚ロボットの動きを追加しました。なるほどと思いました😃。 #PCA9685 #ラズパイ #SG90 pic.twitter.com/TcH95jwVOO
— pass810 (@pass810) 2019年2月28日
#電子工作 の実験。#RaspberryPi #サーボ 付 #カメラスタンド で #OpenCV を使って #円追跡(#TrackingCircle)です。「実例で学ぶRaspberryPi電子工作」を参考にしました。#PCA9685 を使い,#LCD(#ST7735)にも画像を表示している所が少し違います。勉強になりました😊。#Pillow pic.twitter.com/mGECHLA9Fd
— pass810 (@pass810) 2019年2月15日
#ESP32 で #6脚ロボット です。前進させてみました。「実例で学ぶ #RaspberryPi 電子工作」を参考にしました。滑り気味ですが、少しずつ進んでいます☺️。#Arduino #PCA9685 #SG90 pic.twitter.com/ykLx2CEwsl
— pass810 (@pass810) 2018年12月12日
これすごくいい。簡単に電子工作の楽しさがわかるので、入門に最適。夢が広がる〜。図書館で本を借りたけど、内容を確認しながらする作業が多いので、オススメはKindle版かな。
— daba | ケミカルリーマン (@chemryman) 2018年9月22日
実例で学ぶRaspberry Pi電子工作 作りながら応用力を身につける (ブルーバックス) https://t.co/5yt9RB79fP
こんなん作れたら楽しそうやんね😆
— IT暇人@実家ニート (@it31415) 2018年8月12日
ブルーバックス『実例で学ぶRaspberry Pi電子工作』 https://t.co/bc9SZRx7Xm @YouTubeより pic.twitter.com/nqnQJgSA5Y
金丸隆志「実例で学ぶraspberry pi電子工作」の6脚ロボを作ってみた。本のままだと動かない点が幾つかあったけどなんとか動くまでこぎつけた。ちょこちょこ歩く動作がなかなか愛らしい pic.twitter.com/CZe29YKgCN— 冨永 和吉 (@K_TOMI) 2016年4月18日
カメラ付き6脚ロボット— ひー (@freejog) 2017年7月5日
参考:金丸隆志『実例で学ぶRaspberry Pi電子工作 作りながら応用力を身につける』#RaspberryPi #ラズベリーパイ pic.twitter.com/BIQYxSWFfi
『実例で学ぶRaspberry Pi電子工作』を参考にして作成した6脚ロボット。アルカリ乾電池3本(1.5V×3本=4.5V)だとサーボモーターがうまく動かなかったので、エネループ4本(1.2V×4本=4.8V)にしたところ、期待通り動いた。 pic.twitter.com/7hiJWmNv0E— Syun'iti Honda (@Syun_itiHonda) 2017年11月27日
from machine import Pin from time import sleep, ticks_ms import random import _thread def switch_pressed(p): global prev_time pressed_time = ticks_ms() if pressed_time < prev_time + 200 : return prev_time = pressed_time _thread.start_new_thread(randomizeDice, ()) def randomizeDice(): delay = 0.1 sendLEDdata(LEDdata1, ser, rclk, srclk) sleep(delay) sendLEDdata(LEDdata2, ser, rclk, srclk) sleep(delay) sendLEDdata(LEDdata3, ser, rclk, srclk) sleep(delay) sendLEDdata(LEDdata4, ser, rclk, srclk) sleep(delay) sendLEDdata(LEDdata5, ser, rclk, srclk) sleep(delay) sendLEDdata(LEDdata6, ser, rclk, srclk) sleep(delay) dice = random.randint(1,6) if dice == 1: sendLEDdata(LEDdata1, ser, rclk, srclk) elif dice == 2: sendLEDdata(LEDdata2, ser, rclk, srclk) elif dice == 3: sendLEDdata(LEDdata3, ser, rclk, srclk) elif dice == 4: sendLEDdata(LEDdata4, ser, rclk, srclk) elif dice == 5: sendLEDdata(LEDdata5, ser, rclk, srclk) elif dice == 6: sendLEDdata(LEDdata6, ser, rclk, srclk) def sendLEDdata(data, ser, rclk, srclk): n = len(data) rclk.value(0) srclk.value(0) for i in range(n): if data[i] == 1: ser.value(1) else: ser.value(0) srclk.value(1) srclk.value(0) rclk.value(1) rclk.value(0) prev_time = ticks_ms() ser = Pin(16, Pin.OUT) rclk = Pin(17, Pin.OUT) srclk = Pin(18, Pin.OUT) switch = Pin(19, Pin.IN, Pin.PULL_UP) switch.irq(trigger=Pin.IRQ_FALLING, handler=switch_pressed) LEDdata0 = [0, 0, 0,0,0, 0, 0] LEDdata1 = [0, 0, 0,1,0, 0, 0] LEDdata2 = [0, 1, 0,0,0, 1, 0] LEDdata3 = [0, 1, 0,1,0, 1, 0] LEDdata4 = [1, 1, 0,0,0, 1, 1] LEDdata5 = [1, 1, 0,1,0, 1, 1] LEDdata6 = [1, 1, 1,0,1, 1, 1] sendLEDdata(LEDdata0, ser, rclk, srclk)
from time import sleep import network import requests import json ssid = 'YOUR_WIFI_SSID' password = 'YOUR_WIFI_PASSWORD' city = 'Tokyo' key = 'API_KEY' address = 'http://api.openweathermap.org/data/2.5/weather?units=metric&q={city}&APPID={key}'.format(city=city, key=key) wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(ssid, password) # Wait for connect or fail max_wait = 20 while max_wait > 0: if wlan.status() < 0 or wlan.status() >= 3: break max_wait -= 1 print('waiting for connection...') sleep(1) # Handle connection error if wlan.status() != 3: write_string('conn. failed') # showing on LCD raise RuntimeError('network connection failed') else: print('Connected') status = wlan.ifconfig() print( 'ip = ' + status[0] ) weather_json = requests.get(address).json() print(city) try: print('lat = \'{}\''.format(weather_json['coord']['lat'])) print('lon = \'{}\''.format(weather_json['coord']['lon'])) except KeyError: print('Not found.')まずはプログラム 6~7 行目の以下の部分です。これらの行には、それぞれ「皆さんの Wifi のネットワーク名 (SSID)」および「そのパスワード」を記入する必要があります。なお、2.4 GHz の周波数帯の Wifi アクセスポイントにしか接続できませんのでご注意ください。
ssid = 'YOUR_WIFI_SSID' password = 'YOUR_WIFI_PASSWORD'次にプログラム 10 行目の以下の部分です。この部分には皆さんが OpenWeather のサイトで取得した API キーを記入します。
key = 'API_KEY'イメージとしては下記のように編集する、ということです。
key = '................................'これらの3行は、4章のプログラム全てに存在しますので、全て皆さんの情報に置き換えてください。
Tokyo lat = '35.6895' lon = '139.6917'これは、Tokyo の緯度と経度を表示するプログラムなのでした。以下のプログラムでは、この緯度と経度の数値を利用して天気予報の取得を行っています。
city = 'Tokyo'利用可能な都市名は、こちらのサイトで調べることができます。ブラウザで位置情報の取得が許可されている必要があるようです。
from time import sleep import network import requests import json ssid = 'YOUR_WIFI_SSID' password = 'YOUR_WIFI_PASSWORD' lat = '35.6895' lon = '139.6917' units = 'metric' lang = 'ja' key = 'API_KEY' address = 'http://api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&appid={key}&units={units}&lang={lang}'.format(lat=lat, lon=lon, key=key, units=units, lang=lang) wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(ssid, password) # Wait for connect or fail max_wait = 20 while max_wait > 0: if wlan.status() < 0 or wlan.status() >= 3: break max_wait -= 1 print('waiting for connection...') sleep(1) # Handle connection error if wlan.status() != 3: raise RuntimeError('network connection failed') else: print('Connected') status = wlan.ifconfig() print( 'ip = ' + status[0] ) weather_json = requests.get(address).json() print(json.dumps(weather_json))先ほどと同様、
ssid = 'YOUR_WIFI_SSID' password = 'YOUR_WIFI_PASSWORD'および
key = 'API_KEY'の部分は皆さんの情報で置き換える必要があります。2.4 GHz の周波数帯の Wifi アクセスポイントにしか接続できませんのでご注意ください。また、必要に応じて下記の部分も皆さんの好みの緯度と経度に置き換えて構いません。
lat = '35.6895' lon = '139.6917'そのプログラムを Raspberry Pi Pico を指定した Thonny で記述し、main.py という名前で保存して実行します。
from time import sleep import network import requests import json ssid = 'YOUR_WIFI_SSID' password = 'YOUR_WIFI_PASSWORD' lat = '35.6895' lon = '139.6917' units = 'metric' lang = 'ja' key = 'API_KEY' address = 'http://api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&appid={key}&units={units}&lang={lang}'.format(lat=lat, lon=lon, key=key, units=units, lang=lang) wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(ssid, password) # Wait for connect or fail max_wait = 20 while max_wait > 0: if wlan.status() < 0 or wlan.status() >= 3: break max_wait -= 1 print('waiting for connection...') sleep(1) # Handle connection error if wlan.status() != 3: raise RuntimeError('network connection failed') else: print('Connected') status = wlan.ifconfig() print( 'ip = ' + status[0] ) weather_json = requests.get(address).json() dataLabel = ['今日', '明日', '明後日'] weather_dict = {'Clear':'晴れ', 'Clouds':'曇り', 'Rain':'雨','Snow':'雪', 'Thunderstorm':'雷', 'Drizzle':'霧'} for i in range(3): forecast = weather_json['list'][8*i] temp_min = round(forecast['main']['temp_min']) temp_max = round(forecast['main']['temp_max']) when = forecast['dt_txt'] try: weather = weather_dict[forecast['weather'][0]['main']] except KeyError: weather = '未定義' print('{}, {}, {}/{}, {}'.format(dataLabel[i], weather, temp_min, temp_max, when))先ほどと同様、
ssid = 'YOUR_WIFI_SSID' password = 'YOUR_WIFI_PASSWORD'および
key = 'API_KEY'の部分は皆さんの情報で置き換える必要があります。2.4 GHz の周波数帯の Wifi アクセスポイントにしか接続できませんのでご注意ください。また、必要に応じて下記の部分も皆さんの好みの緯度と経度に置き換えて構いません。
lat = '35.6895' lon = '139.6917'そのプログラムを Raspberry Pi Pico を指定した Thonny で記述し、main.py という名前で保存して実行します。
今日, 曇り, 22/22, 2025-06-12 15:00:00 明日, 曇り, 21/21, 2025-06-13 15:00:00 明後日, 雨, 21/21, 2025-06-14 15:00:00以上の動作チェックが終わったら、ブレッドボード上で回路を組みましょう。
from machine import Pin, I2C from time import sleep, ticks_ms import network import requests import json import sys ssid = 'YOUR_WIFI_SSID' password = 'YOUR_WIFI_PASSWORD' lat = '35.6895' lon = '139.6917' units = 'metric' lang = 'ja' key = 'API_KEY' def setup_st7032(): c_lower = (contrast & 0xf) c_upper = (contrast & 0x30)>>4 i2c.writeto_mem(address_st7032, register_setting, bytes([0x38, 0x39, 0x14, 0x70|c_lower, 0x54|c_upper, 0x6c])) sleep(0.2) i2c.writeto_mem(address_st7032, register_setting, bytes([0x38, 0x0d, 0x01])) sleep(0.001) def clear(): global position global line position = 0 line = 0 i2c.writeto_mem(address_st7032, register_setting, bytes([0x01])) sleep(0.001) def newline(): global position global line if line == display_lines-1: clear() else: line += 1 position = chars_per_line*line i2c.writeto_mem(address_st7032, register_setting, bytes([0xc0])) sleep(0.001) def write_string(s): for c in list(s): write_char(ord(c)) def write_char(c): global position byte_data = check_writable(c) if position == display_chars: clear() elif position == chars_per_line*(line+1): newline() i2c.writeto_mem(address_st7032, register_display, bytes([byte_data])) position += 1 def check_writable(c): if c >= 0x06 and c <= 0xff : return c else: return 0x20 # 空白文字 def read_adt7410(): word_data = int.from_bytes(i2c.readfrom_mem(address_adt7410, register_adt7410, 2), 'little', False) data = (word_data & 0xff00)>>8 | (word_data & 0xff)<<8 data = data>>3 # 13ビットデータ if data & 0x1000 == 0: # 温度が正または0の場合 temperature = data*0.0625 else: # 温度が負の場合、 絶対値を取ってからマイナスをかける temperature = ( (~data&0x1fff) + 1)*-0.0625 return temperature def switch_pressed(p): global prev_time pressed_time = ticks_ms() if pressed_time < prev_time + 200 : return prev_time = pressed_time global mode mode = (mode+1)%4 displayWeather() # 各天気をカタカナに def replaceWeather(weather): if weather.find('晴') != -1: return chr(0xca)+chr(0xda) # ハレ elif weather.find('曇') != -1: return chr(0xb8)+chr(0xd3)+chr(0xd8) # クモリ elif weather.find('雨') != -1: return chr(0xb1)+chr(0xd2) # アメ elif weather.find('雪') != -1: return chr(0xd5)+chr(0xb7) # ユキ else: return '--' # Webサービスより天気予報を取得 def getWeather(): global minmax, weather, minmax2, weather2 try: address = 'http://api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&appid={key}&units={units}&lang={lang}'.format(lat=lat, lon=lon, key=key, units=units, lang=lang) weather_json = requests.get(address).json() print('インターネットから天気予報データを入手しました') except requests.exceptions.RequestException: print('ネットワークエラー') if second==0: sys.exit() weather_dict = {'Clear':'晴れ', 'Clouds':'曇り', 'Rain':'雨','Snow':'雪', 'Thunderstorm':'雷', 'Drizzle':'霧'} for i in range(2): forecast = weather_json['list'][8*i] temp_min = round(forecast['main']['temp_min']) temp_max = round(forecast['main']['temp_max']) try: weather_tmp = weather_dict[forecast['weather'][0]['main']] except KeyError: weather_tmp = '未定義' if i==0: minmax = '{}/{}'.format(temp_min, temp_max) weather = replaceWeather(weather_tmp) else: minmax2 = '{}/{}'.format(temp_min, temp_max) weather2 = replaceWeather(weather_tmp) # 天気情報をLCDに表示 def displayWeather(): clear() if temperature != 999: s = '{0:.1f}'.format(temperature) # 小数点以下1桁 else: s = '' if mode==0: day = chr(0xb7)+chr(0xae)+chr(0xb3) # キョウ for i in range(chars_per_line-len(day)-len(s)): s = ' '+s write_string(day+s+weather) elif mode==1: day = chr(0xb7)+chr(0xae)+chr(0xb3) # キョウ for i in range(chars_per_line-len(day)-len(s)): s = ' '+s write_string(day+s+minmax) elif mode==2: day = chr(0xb1)+chr(0xbd) # アス for i in range(chars_per_line-len(day)-len(s)): s = ' '+s write_string(day+s+weather2) elif mode==3: day = chr(0xb1)+chr(0xbd) # アス for i in range(chars_per_line-len(day)-len(s)): s = ' '+s write_string(day+s+minmax2) i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000) i2c.scan() address_st7032 = 0x3e register_setting = 0x00 register_display = 0x40 contrast = 32 # 0から63のコントラスト。通常は32、文字が薄いときは40を推奨 chars_per_line = 8 # LCDの横方向の文字数 display_lines = 2 # LCDの行数 display_chars = chars_per_line*display_lines position = 0 line = 0 setup_st7032() prev_time = ticks_ms() switch = Pin(17, Pin.IN, Pin.PULL_UP) switch.irq(trigger=Pin.IRQ_FALLING, handler=switch_pressed) address_adt7410 = 0x48 register_adt7410 = 0x00 mode = 0 temperature = 999 minmax = '' weather = '' minmax2 = '' weather2 = '' second = 0 # プログラム起動時から経過した秒数(目安) hour = 0 # プログラム起動時から経過した時間(目安) prevhour = -1 # 前回天気予報を問い合わせた時間 wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(ssid, password) # Wait for connect or fail max_wait = 20 while max_wait > 0: if wlan.status() < 0 or wlan.status() >= 3: break max_wait -= 1 print('waiting for connection...') write_string('waiting for conn') #showing on LCD sleep(1) # Handle connection error if wlan.status() != 3: write_string('conn. failed') # showing on LCD raise RuntimeError('network connection failed') else: print('Connected') status = wlan.ifconfig() print( 'ip = ' + status[0] ) write_string(status[0]) #showing on LCD try: while True: if hour != prevhour: # 前回問い合わせより約1時間後 getWeather() # 天気予報取得 prevhour = hour try: temperature = read_adt7410() # 温度取得 except IOError: temperature = 999 # 温度取得失敗 displayWeather() # LCDに情報表示 second = second + 1 # 1秒進める hour = int(second/3600) # 秒を時間に変換(0,1,2,…のように整数のみ) sleep(1) except KeyboardInterrupt: passそして、書籍の bb2-04-05-lcd-3modes.py に該当するのが下記のプログラムです。
from machine import Pin, I2C from time import sleep, ticks_ms import network import requests import json import sys ssid = 'YOUR_WIFI_SSID' password = 'YOUR_WIFI_PASSWORD' lat = '35.6895' lon = '139.6917' units = 'metric' lang = 'ja' key = 'API_KEY' def setup_st7032(): c_lower = (contrast & 0xf) c_upper = (contrast & 0x30)>>4 i2c.writeto_mem(address_st7032, register_setting, bytes([0x38, 0x39, 0x14, 0x70|c_lower, 0x54|c_upper, 0x6c])) sleep(0.2) i2c.writeto_mem(address_st7032, register_setting, bytes([0x38, 0x0d, 0x01])) sleep(0.001) def clear(): global position global line position = 0 line = 0 i2c.writeto_mem(address_st7032, register_setting, bytes([0x01])) sleep(0.001) def newline(): global position global line if line == display_lines-1: clear() else: line += 1 position = chars_per_line*line i2c.writeto_mem(address_st7032, register_setting, bytes([0xc0])) sleep(0.001) def write_string(s): for c in list(s): write_char(ord(c)) def write_char(c): global position byte_data = check_writable(c) if position == display_chars: clear() elif position == chars_per_line*(line+1): newline() i2c.writeto_mem(address_st7032, register_display, bytes([byte_data])) position += 1 def check_writable(c): if c >= 0x06 and c <= 0xff : return c else: return 0x20 # 空白文字 def read_adt7410(): word_data = int.from_bytes(i2c.readfrom_mem(address_adt7410, register_adt7410, 2), 'little', False) data = (word_data & 0xff00)>>8 | (word_data & 0xff)<<8 data = data>>3 # 13ビットデータ if data & 0x1000 == 0: # 温度が正または0の場合 temperature = data*0.0625 else: # 温度が負の場合、 絶対値を取ってからマイナスをかける temperature = ( (~data&0x1fff) + 1)*-0.0625 return temperature def switch_pressed(p): global prev_time pressed_time = ticks_ms() if pressed_time < prev_time + 200 : return prev_time = pressed_time global mode mode = (mode+1)%3 displayWeather() # 各天気をカタカナに def replaceWeather(weather): if weather.find('晴') != -1: return chr(0xca)+chr(0xda) # ハレ elif weather.find('曇') != -1: return chr(0xb8)+chr(0xd3)+chr(0xd8) # クモリ elif weather.find('雨') != -1: return chr(0xb1)+chr(0xd2) # アメ elif weather.find('雪') != -1: return chr(0xd5)+chr(0xb7) # ユキ else: return '--' # Webサービスより天気予報を取得 def getWeather(): global minmax, weather, minmax2, weather2 try: address = 'http://api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&appid={key}&units={units}&lang={lang}'.format(lat=lat, lon=lon, key=key, units=units, lang=lang) weather_json = requests.get(address).json() print('インターネットから天気予報データを入手しました') except requests.exceptions.RequestException: print('ネットワークエラー') if second==0: sys.exit() weather_dict = {'Clear':'晴れ', 'Clouds':'曇り', 'Rain':'雨','Snow':'雪', 'Thunderstorm':'雷', 'Drizzle':'霧'} for i in range(2): forecast = weather_json['list'][8*i] temp_min = round(forecast['main']['temp_min']) temp_max = round(forecast['main']['temp_max']) try: weather_tmp = weather_dict[forecast['weather'][0]['main']] except KeyError: weather_tmp = '未定義' if i==0: minmax = '{}/{}'.format(temp_min, temp_max) weather = replaceWeather(weather_tmp) else: minmax2 = '{}/{}'.format(temp_min, temp_max) weather2 = replaceWeather(weather_tmp) # 天気情報をLCDに表示 def displayWeather(): clear() if temperature != 999: s = '{0:.1f}'.format(temperature) # 小数点以下1桁 else: s = '' if mode==0: day = chr(0xb7)+chr(0xae)+chr(0xb3) # キョウ for i in range(chars_per_line-len(day)-len(s)): s = ' '+s write_string(day+s+weather) elif mode==1: day = chr(0xb7)+chr(0xae)+chr(0xb3) # キョウ for i in range(chars_per_line-len(day)-len(s)): s = ' '+s write_string(day+s+minmax) elif mode==2: day = chr(0xb1)+chr(0xbd) # アス s = minmax2 for i in range(chars_per_line-len(day)-len(s)): s = ' '+s write_string(day+s+weather2) i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000) i2c.scan() address_st7032 = 0x3e register_setting = 0x00 register_display = 0x40 contrast = 32 # 0から63のコントラスト。通常は32、文字が薄いときは40を推奨 chars_per_line = 8 # LCDの横方向の文字数 display_lines = 2 # LCDの行数 display_chars = chars_per_line*display_lines position = 0 line = 0 setup_st7032() prev_time = ticks_ms() switch = Pin(17, Pin.IN, Pin.PULL_UP) switch.irq(trigger=Pin.IRQ_FALLING, handler=switch_pressed) address_adt7410 = 0x48 register_adt7410 = 0x00 mode = 0 temperature = 999 minmax = '' weather = '' minmax2 = '' weather2 = '' second = 0 # プログラム起動時から経過した秒数(目安) hour = 0 # プログラム起動時から経過した時間(目安) prevhour = -1 # 前回天気予報を問い合わせた時間 wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(ssid, password) # Wait for connect or fail max_wait = 20 while max_wait > 0: if wlan.status() < 0 or wlan.status() >= 3: break max_wait -= 1 print('waiting for connection...') write_string('waiting for conn') #showing on LCD sleep(1) # Handle connection error if wlan.status() != 3: write_string('conn. failed') # showing on LCD raise RuntimeError('network connection failed') else: print('Connected') status = wlan.ifconfig() print( 'ip = ' + status[0] ) write_string(status[0]) #showing on LCD try: while True: if hour != prevhour: # 前回問い合わせより約1時間後 getWeather() # 天気予報取得 prevhour = hour try: temperature = read_adt7410() # 温度取得 except IOError: temperature = 999 # 温度取得失敗 displayWeather() # LCDに情報表示 second = second + 1 # 1秒進める hour = int(second/3600) # 秒を時間に変換(0,1,2,…のように整数のみ) sleep(1) except KeyboardInterrupt: passどちらのプログラムもこれまでと同様、
ssid = 'YOUR_WIFI_SSID' password = 'YOUR_WIFI_PASSWORD'および
key = 'API_KEY'の部分は皆さんの情報で置き換える必要があります。2.4 GHz の周波数帯の Wifi アクセスポイントにしか接続できませんのでご注意ください。また、必要に応じて下記の部分も皆さんの好みの緯度と経度に置き換えて構いません。
lat = '35.6895' lon = '139.6917'そのプログラムを Raspberry Pi Pico を指定した Thonny で記述し、main.py という名前で保存して実行します。
from machine import Pin from time import sleep, ticks_ms import json from UpyIrTx import UpyIrTx tx_pin = Pin(19, Pin.OUT) tx = UpyIrTx(0, tx_pin, 38000, 30, 1) with open('TV.txt') as f: commands = json.load(f) def switch_pressed(p): global prev_time pressed_time = ticks_ms() if pressed_time < prev_time + 200 : return prev_time = pressed_time pin_id = int(str(p)[8:11].rstrip(",")) # expecting "Pin(GPIOXX, mode=IN, pull=PULL_UP)" if pin_id == 15: tx.send(commands['power']['signal']) elif pin_id == 14: tx.send(commands['cup']['signal']) elif pin_id == 13: tx.send(commands['cdown']['signal']) elif pin_id == 12: tx.send(commands['vup']['signal']) elif pin_id == 11: tx.send(commands['vdown']['signal']) prev_time = ticks_ms() pin_power = Pin(15, Pin.IN, Pin.PULL_UP) pin_power.irq(trigger=Pin.IRQ_FALLING, handler=switch_pressed) pin_cup = Pin(14, Pin.IN, Pin.PULL_UP) pin_cup.irq(trigger=Pin.IRQ_FALLING, handler=switch_pressed) pin_cdown = Pin(13, Pin.IN, Pin.PULL_UP) pin_cdown.irq(trigger=Pin.IRQ_FALLING, handler=switch_pressed) pin_vup = Pin(12, Pin.IN, Pin.PULL_UP) pin_vup.irq(trigger=Pin.IRQ_FALLING, handler=switch_pressed) pin_vdown = Pin(11, Pin.IN, Pin.PULL_UP) pin_vdown.irq(trigger=Pin.IRQ_FALLING, handler=switch_pressed)このプログラムを動かすための回路は下図になります。この回路は大きなブレッドボードでしか実現できませんのでご注意ください。赤外線 LED 部はデモアプリケーションで用いた回路と同じです。 回路が組めたら、Pico 上の main.py を実行し、ブレッドボート上の赤外線 LED をテレビの受光面に向け、タクトスイッチを押してみましょう。
import sys import network import socket from time import sleep from machine import Pin, I2C import json from UpyIrTx import UpyIrTx i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000) i2c.scan() tx_pin = Pin(19, Pin.OUT) tx = UpyIrTx(0, tx_pin, 38000, 30, 1) with open('TV.txt') as f: commands = json.load(f) ssid = 'YOUR_WIFI_SSID' password = 'YOUR_WIFI_PASSWORD' ##### for LCD def setup_st7032(): c_lower = (contrast & 0xf) c_upper = (contrast & 0x30)>>4 i2c.writeto_mem(address_st7032, register_setting, bytes([0x38, 0x39, 0x14, 0x70|c_lower, 0x54|c_upper, 0x6c])) sleep(0.2) i2c.writeto_mem(address_st7032, register_setting, bytes([0x38, 0x0d, 0x01])) sleep(0.001) def clear(): global position global line position = 0 line = 0 i2c.writeto_mem(address_st7032, register_setting, bytes([0x01])) sleep(0.001) def newline(): global position global line if line == display_lines-1: clear() else: line += 1 position = chars_per_line*line i2c.writeto_mem(address_st7032, register_setting, bytes([0xc0])) sleep(0.001) def write_string(s): for c in list(s): write_char(ord(c)) def write_char(c): global position byte_data = check_writable(c) if position == display_chars: clear() elif position == chars_per_line*(line+1): newline() i2c.writeto_mem(address_st7032, register_display, bytes([byte_data])) position += 1 def check_writable(c): if c >= 0x06 and c <= 0xff : return c else: return 0x20 # 空白文字 address_st7032 = 0x3e register_setting = 0x00 register_display = 0x40 contrast = 32 # 0から63のコントラスト。30から40程度を推奨 chars_per_line = 8 # LCDの横方向の文字数 display_lines = 2 # LCDの行数 display_chars = chars_per_line*display_lines position = 0 line = 0 setup_st7032() ##### end of LCD wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(ssid, password) html = """<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no"> <title>テレビのリモコン</title> <style>button { width: 7em; height: auto; background: #003366; font-weight: normal; font-size: 14pt; font-family: Arial, Helvetica, sans-serif; font-size: 14px; color: #ffffff; padding: 10px 20px; -moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px; border: 1px solid #003366; -moz-box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.5); -webkit-box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.5); box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.5); text-shadow: 0px -1px 0px rgba(000,000,000,0.7), 0px 1px 0px rgba(255,255,255,0.3); } button:active { position:relative; top:1px; } </style></head> <body> <div align="center"> <table border="0"> <tr> <td> <form><button name="input" value="pressed" type="submit" style="background: rgb(64 64 64); border: rgb(64 64 64);">入力切替</button></form> </td> <td> </td> <td> <form><button name="power" value="pressed" type="submit" style="background: rgb(249 25 25); border: rgb(249 25 25);">電源</button></form> </td> </tr> <tr> <td> <form><button name="ch1" value="pressed" type="submit">1</button></form> </td> <td> <form><button name="ch2" value="pressed" type="submit">2</button></form> </td> <td> <form><button name="ch3" value="pressed" type="submit">3</button></form> </td> </tr> <tr> <td> <form><button name="ch4" value="pressed" type="submit">4</button></form> </td> <td> <form><button name="ch5" value="pressed" type="submit">5</button></form> </td> <td> <form><button name="ch6" value="pressed" type="submit">6</button></form> </td> </tr> <tr> <td> <form><button name="ch7" value="pressed" type="submit">7</button></form> </td> <td> <form><button name="ch8" value="pressed" type="submit">8</button></form> </td> <td> <form><button name="ch9" value="pressed" type="submit">9</button></form> </td> </tr> <tr> <td> <form><button name="ch10" value="pressed" type="submit">10</button></form> </td> <td> <form><button name="ch11" value="pressed" type="submit">11</button></form> </td> <td> <form><button name="ch12" value="pressed" type="submit">12</button></form> </td> </tr> <tr> <td> <div align="center"> 音量 </div> </td> <td> </td> <td> <div align="center"> チャンネル </div> </td> </tr> <tr> <td> <form><button name="vup" value="pressed" type="submit" style="background: rgb(64 64 64); border: rgb(64 64 64);">↑</button></form> </td> <td> </td> <td> <form><button name="cup" value="pressed" type="submit" style="background: rgb(64 64 64); border: rgb(64 64 64);">↑</button></form> </td> </tr> <tr> <td> <form><button name="vdown" value="pressed" type="submit" style="background: rgb(64 64 64); border: rgb(64 64 64);">↓</button></form> </td> <td> </td> <td> <form><button name="cdown" value="pressed" type="submit" style="background: rgb(64 64 64); border: rgb(64 64 64);">↓</button></form> </td> </tr> </table> </div> </body> </html> """ # Wait for connect or fail max_wait = 20 while max_wait > 0: if wlan.status() < 0 or wlan.status() >= 3: break max_wait -= 1 print('waiting for connection...') write_string('waiting for conn') #showing on LCD sleep(1) # Handle connection error if wlan.status() != 3: write_string('conn. failed') # showing on LCD raise RuntimeError('network connection failed') else: print('Connected') status = wlan.ifconfig() print( 'ip = ' + status[0] ) write_string(status[0]) #showing on LCD # Open socket addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1] s = socket.socket() s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: s.bind(addr) except OSError as e: print(e) s.close() s = None write_string('socket error') #showing on LCD sys.exit() s.listen(1) print('listening on', addr) # Listen for connections, serve client while True: try: cl, addr = s.accept() #print('client connected from', addr) request = cl.recv(1024) #print("request:") #print(request) request = str(request) power_pressed = request.find('power=pressed') input_pressed = request.find('input=pressed') ch1_pressed = request.find('ch1=pressed') ch2_pressed = request.find('ch2=pressed') ch3_pressed = request.find('ch3=pressed') ch4_pressed = request.find('ch4=pressed') ch5_pressed = request.find('ch5=pressed') ch6_pressed = request.find('ch6=pressed') ch7_pressed = request.find('ch7=pressed') ch8_pressed = request.find('ch8=pressed') ch9_pressed = request.find('ch9=pressed') ch10_pressed = request.find('ch10=pressed') ch11_pressed = request.find('ch11=pressed') ch12_pressed = request.find('ch12=pressed') vup_pressed = request.find('vup=pressed') cup_pressed = request.find('cup=pressed') vdown_pressed = request.find('vdown=pressed') cdown_pressed = request.find('cdown=pressed') if power_pressed == 8: tx.send(commands['power']['signal']) elif input_pressed == 8: tx.send(commands['input']['signal']) elif ch1_pressed == 8: tx.send(commands['ch1']['signal']) elif ch2_pressed == 8: tx.send(commands['ch2']['signal']) elif ch3_pressed == 8: tx.send(commands['ch3']['signal']) elif ch4_pressed == 8: tx.send(commands['ch4']['signal']) elif ch5_pressed == 8: tx.send(commands['ch5']['signal']) elif ch6_pressed == 8: tx.send(commands['ch6']['signal']) elif ch7_pressed == 8: tx.send(commands['ch7']['signal']) elif ch8_pressed == 8: tx.send(commands['ch8']['signal']) elif ch9_pressed == 8: tx.send(commands['ch9']['signal']) elif ch10_pressed == 8: tx.send(commands['ch10']['signal']) elif ch11_pressed == 8: tx.send(commands['ch11']['signal']) elif ch12_pressed == 8: tx.send(commands['ch12']['signal']) elif vup_pressed == 8: tx.send(commands['vup']['signal']) elif cup_pressed == 8: tx.send(commands['cup']['signal']) elif vdown_pressed == 8: tx.send(commands['vdown']['signal']) elif cdown_pressed == 8: tx.send(commands['cdown']['signal']) response = html cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n') cl.send(response) cl.close() except OSError as e: print('connection closed') except KeyboardInterrupt as e: break s.close() s = Noneこのプログラムを動かすための回路は次の通りです。 見てわかる通り、赤外線 LED 以外に I2C 接続の小型 LCD も接続されています。これは、Pico W / Pico 2 W に割り当てられた IP アドレスやエラーメッセージを表示するためのものです。この LCD を接続しなくても演習は実行可能ですが、その場合、Pico W / Pico 2 W からのメッセージを Windows などの PC 上で見るしかなくなります。LCD を接続すること、すなわち Windows などの PC なしでも Pico W / Pico 2 W を動作させられるようにすることを強く推奨します。
ssid = 'YOUR_WIFI_SSID' password = 'YOUR_WIFI_PASSWORD'また、回路を LCD なしで実行する場合、プログラム中の下記の部分を消し、
##### for LCD (省略) ##### End of LCD下記の内容で差し替える必要があります。
def write_string(s): passさらに、プログラムの書き換え時は、Pico W / Pico 2 W 上で動作しているプログラムを Thonny の「STOP (停止)」ボタンから一旦停止する必要があることもご注意ください。
from machine import Pin, PWM NEUTRAL = 0.0675 # (0.035 + 0.1)/2 # 最小デューティ比3.5%と最大デューティ比10%の平均 NEUTRAL = int(65535*NEUTRAL) pwm0 = PWM(Pin(16)) pwm1 = PWM(Pin(17)) pwm2 = PWM(Pin(18)) pwm3 = PWM(Pin(19)) pwm4 = PWM(Pin(20)) pwm5 = PWM(Pin(21)) pwm0.freq(50) pwm1.freq(50) pwm2.freq(50) pwm3.freq(50) pwm4.freq(50) pwm5.freq(50) pwm0.duty_u16(NEUTRAL) pwm1.duty_u16(NEUTRAL) pwm2.duty_u16(NEUTRAL) pwm3.duty_u16(NEUTRAL) pwm4.duty_u16(NEUTRAL) pwm5.duty_u16(NEUTRAL)次に、6 脚ロボットが前進、後退、右回転、左回転の動作をそれぞれ一度ずつ実行し、最後に停止するプログラムが下記のものです。書籍では bb2-08-02-6legs-pca9685.py に対応します。
from machine import Pin, PWM from time import sleep def forwardPhase1(delay): pwm0.duty_u16(LEG_F_R) pwm1.duty_u16(LEG_F_R) pwm2.duty_u16(LEG_F_L) sleep(delay) pwm3.duty_u16(LEG_B_R) pwm4.duty_u16(LEG_B_L) pwm5.duty_u16(LEG_B_L) sleep(delay) pwm0.duty_u16(NEUTRAL) pwm1.duty_u16(NEUTRAL) pwm2.duty_u16(NEUTRAL) sleep(delay) pwm3.duty_u16(NEUTRAL) pwm4.duty_u16(NEUTRAL) pwm5.duty_u16(NEUTRAL) sleep(delay) def forwardPhase2(delay): pwm3.duty_u16(LEG_F_R) pwm4.duty_u16(LEG_F_L) pwm5.duty_u16(LEG_F_L) sleep(delay) pwm0.duty_u16(LEG_B_R) pwm1.duty_u16(LEG_B_R) pwm2.duty_u16(LEG_B_L) sleep(delay) pwm3.duty_u16(NEUTRAL) pwm4.duty_u16(NEUTRAL) pwm5.duty_u16(NEUTRAL) sleep(delay) pwm0.duty_u16(NEUTRAL) pwm1.duty_u16(NEUTRAL) pwm2.duty_u16(NEUTRAL) sleep(delay) def forwardMove(delay): forwardPhase1(delay) forwardPhase2(delay) def backwardPhase1(delay): pwm0.duty_u16(LEG_B_R) pwm1.duty_u16(LEG_B_R) pwm2.duty_u16(LEG_B_L) sleep(delay) pwm3.duty_u16(LEG_F_R) pwm4.duty_u16(LEG_F_L) pwm5.duty_u16(LEG_F_L) sleep(delay) pwm0.duty_u16(NEUTRAL) pwm1.duty_u16(NEUTRAL) pwm2.duty_u16(NEUTRAL) sleep(delay) pwm3.duty_u16(NEUTRAL) pwm4.duty_u16(NEUTRAL) pwm5.duty_u16(NEUTRAL) sleep(delay) def backwardPhase2(delay): pwm3.duty_u16(LEG_B_R) pwm4.duty_u16(LEG_B_L) pwm5.duty_u16(LEG_B_L) sleep(delay) pwm0.duty_u16(LEG_F_R) pwm1.duty_u16(LEG_F_R) pwm2.duty_u16(LEG_F_L) sleep(delay) pwm3.duty_u16(NEUTRAL) pwm4.duty_u16(NEUTRAL) pwm5.duty_u16(NEUTRAL) sleep(delay) pwm0.duty_u16(NEUTRAL) pwm1.duty_u16(NEUTRAL) pwm2.duty_u16(NEUTRAL) sleep(delay) def backwardMove(delay): backwardPhase1(delay) backwardPhase2(delay) def rotateRightPhase1(delay): pwm0.duty_u16(LEG_B_R) pwm1.duty_u16(LEG_B_R) pwm2.duty_u16(LEG_F_L) sleep(delay) pwm3.duty_u16(LEG_F_R) pwm4.duty_u16(LEG_B_L) pwm5.duty_u16(LEG_B_L) sleep(delay) pwm0.duty_u16(NEUTRAL) pwm1.duty_u16(NEUTRAL) pwm2.duty_u16(NEUTRAL) sleep(delay) pwm3.duty_u16(NEUTRAL) pwm4.duty_u16(NEUTRAL) pwm5.duty_u16(NEUTRAL) sleep(delay) def rotateRightPhase2(delay): pwm3.duty_u16(LEG_B_R) pwm4.duty_u16(LEG_F_L) pwm5.duty_u16(LEG_F_L) sleep(delay) pwm0.duty_u16(LEG_F_R) pwm1.duty_u16(LEG_F_R) pwm2.duty_u16(LEG_B_L) sleep(delay) pwm3.duty_u16(NEUTRAL) pwm4.duty_u16(NEUTRAL) pwm5.duty_u16(NEUTRAL) sleep(delay) pwm0.duty_u16(NEUTRAL) pwm1.duty_u16(NEUTRAL) pwm2.duty_u16(NEUTRAL) sleep(delay) def rotateRightMove(delay): rotateRightPhase1(delay) rotateRightPhase2(delay) def rotateLeftPhase1(delay): pwm0.duty_u16(LEG_F_R) pwm1.duty_u16(LEG_F_R) pwm2.duty_u16(LEG_B_L) sleep(delay) pwm3.duty_u16(LEG_B_R) pwm4.duty_u16(LEG_F_L) pwm5.duty_u16(LEG_F_L) sleep(delay) pwm0.duty_u16(NEUTRAL) pwm1.duty_u16(NEUTRAL) pwm2.duty_u16(NEUTRAL) sleep(delay) pwm3.duty_u16(NEUTRAL) pwm4.duty_u16(NEUTRAL) pwm5.duty_u16(NEUTRAL) sleep(delay) def rotateLeftPhase2(delay): pwm3.duty_u16(LEG_F_R) pwm4.duty_u16(LEG_B_L) pwm5.duty_u16(LEG_B_L) sleep(delay) pwm0.duty_u16(LEG_B_R) pwm1.duty_u16(LEG_B_R) pwm2.duty_u16(LEG_F_L) sleep(delay) pwm3.duty_u16(NEUTRAL) pwm4.duty_u16(NEUTRAL) pwm5.duty_u16(NEUTRAL) sleep(delay) pwm0.duty_u16(NEUTRAL) pwm1.duty_u16(NEUTRAL) pwm2.duty_u16(NEUTRAL) sleep(delay) def rotateLeftMove(delay): rotateLeftPhase1(delay) rotateLeftPhase2(delay) def stopMove(delay): pwm0.duty_u16(NEUTRAL) pwm1.duty_u16(NEUTRAL) pwm2.duty_u16(NEUTRAL) pwm3.duty_u16(NEUTRAL) pwm4.duty_u16(NEUTRAL) pwm5.duty_u16(NEUTRAL) sleep(delay) NEUTRAL = 0.0675 # (0.035 + 0.1)/2 # 最小デューティ比3.5%と最大デューティ比10%の平均 LEG_F_R = NEUTRAL + 0.024 LEG_F_L = NEUTRAL - 0.024 LEG_B_R = NEUTRAL - 0.024 LEG_B_L = NEUTRAL + 0.024 NEUTRAL = int(65535*NEUTRAL) LEG_F_R = int(65535*LEG_F_R) LEG_F_L = int(65535*LEG_F_L) LEG_B_R = int(65535*LEG_B_R) LEG_B_L = int(65535*LEG_B_L) DELAY = 0.15 pwm0 = PWM(Pin(16)) pwm1 = PWM(Pin(17)) pwm2 = PWM(Pin(18)) pwm3 = PWM(Pin(19)) pwm4 = PWM(Pin(20)) pwm5 = PWM(Pin(21)) pwm0.freq(50) pwm1.freq(50) pwm2.freq(50) pwm3.freq(50) pwm4.freq(50) pwm5.freq(50) pwm0.duty_u16(NEUTRAL) pwm1.duty_u16(NEUTRAL) pwm2.duty_u16(NEUTRAL) pwm3.duty_u16(NEUTRAL) pwm4.duty_u16(NEUTRAL) pwm5.duty_u16(NEUTRAL) forwardMove(DELAY) backwardMove(DELAY) rotateRightMove(DELAY) rotateLeftMove(DELAY) stopMove(DELAY)そして、最後に、ブラウザで 6 脚ロボットを操作するためのプログラムが下記です。Pico 上に main.py として保存して実行するためのものです。
from machine import Pin, PWM, I2C from time import sleep import network import socket import sys import _thread ssid = 'YOUR_WIFI_SSID' password = 'YOUR_WIFI_PASSWORD' ##### for LCD def setup_st7032(): c_lower = (contrast & 0xf) c_upper = (contrast & 0x30)>>4 i2c.writeto_mem(address_st7032, register_setting, bytes([0x38, 0x39, 0x14, 0x70|c_lower, 0x54|c_upper, 0x6c])) sleep(0.2) i2c.writeto_mem(address_st7032, register_setting, bytes([0x38, 0x0d, 0x01])) sleep(0.001) def clear(): global position global line position = 0 line = 0 i2c.writeto_mem(address_st7032, register_setting, bytes([0x01])) sleep(0.001) def newline(): global position global line if line == display_lines-1: clear() else: line += 1 position = chars_per_line*line i2c.writeto_mem(address_st7032, register_setting, bytes([0xc0])) sleep(0.001) def write_string(s): for c in list(s): write_char(ord(c)) def write_char(c): global position byte_data = check_writable(c) if position == display_chars: clear() elif position == chars_per_line*(line+1): newline() i2c.writeto_mem(address_st7032, register_display, bytes([byte_data])) position += 1 def check_writable(c): if c >= 0x06 and c <= 0xff : return c else: return 0x20 # 空白文字 i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000) i2c.scan() address_st7032 = 0x3e register_setting = 0x00 register_display = 0x40 contrast = 32 # 0から63のコントラスト。30から40程度を推奨 chars_per_line = 8 # LCDの横方向の文字数 display_lines = 2 # LCDの行数 display_chars = chars_per_line*display_lines position = 0 line = 0 setup_st7032() ##### end of LCD def forwardPhase1(delay): pwm0.duty_u16(LEG_F_R) pwm1.duty_u16(LEG_F_R) pwm2.duty_u16(LEG_F_L) sleep(delay) pwm3.duty_u16(LEG_B_R) pwm4.duty_u16(LEG_B_L) pwm5.duty_u16(LEG_B_L) sleep(delay) pwm0.duty_u16(NEUTRAL) pwm1.duty_u16(NEUTRAL) pwm2.duty_u16(NEUTRAL) sleep(delay) pwm3.duty_u16(NEUTRAL) pwm4.duty_u16(NEUTRAL) pwm5.duty_u16(NEUTRAL) sleep(delay) def forwardPhase2(delay): pwm3.duty_u16(LEG_F_R) pwm4.duty_u16(LEG_F_L) pwm5.duty_u16(LEG_F_L) sleep(delay) pwm0.duty_u16(LEG_B_R) pwm1.duty_u16(LEG_B_R) pwm2.duty_u16(LEG_B_L) sleep(delay) pwm3.duty_u16(NEUTRAL) pwm4.duty_u16(NEUTRAL) pwm5.duty_u16(NEUTRAL) sleep(delay) pwm0.duty_u16(NEUTRAL) pwm1.duty_u16(NEUTRAL) pwm2.duty_u16(NEUTRAL) sleep(delay) def forwardMove(delay): forwardPhase1(delay) forwardPhase2(delay) def backwardPhase1(delay): pwm0.duty_u16(LEG_B_R) pwm1.duty_u16(LEG_B_R) pwm2.duty_u16(LEG_B_L) sleep(delay) pwm3.duty_u16(LEG_F_R) pwm4.duty_u16(LEG_F_L) pwm5.duty_u16(LEG_F_L) sleep(delay) pwm0.duty_u16(NEUTRAL) pwm1.duty_u16(NEUTRAL) pwm2.duty_u16(NEUTRAL) sleep(delay) pwm3.duty_u16(NEUTRAL) pwm4.duty_u16(NEUTRAL) pwm5.duty_u16(NEUTRAL) sleep(delay) def backwardPhase2(delay): pwm3.duty_u16(LEG_B_R) pwm4.duty_u16(LEG_B_L) pwm5.duty_u16(LEG_B_L) sleep(delay) pwm0.duty_u16(LEG_F_R) pwm1.duty_u16(LEG_F_R) pwm2.duty_u16(LEG_F_L) sleep(delay) pwm3.duty_u16(NEUTRAL) pwm4.duty_u16(NEUTRAL) pwm5.duty_u16(NEUTRAL) sleep(delay) pwm0.duty_u16(NEUTRAL) pwm1.duty_u16(NEUTRAL) pwm2.duty_u16(NEUTRAL) sleep(delay) def backwardMove(delay): backwardPhase1(delay) backwardPhase2(delay) def rotateRightPhase1(delay): pwm0.duty_u16(LEG_B_R) pwm1.duty_u16(LEG_B_R) pwm2.duty_u16(LEG_F_L) sleep(delay) pwm3.duty_u16(LEG_F_R) pwm4.duty_u16(LEG_B_L) pwm5.duty_u16(LEG_B_L) sleep(delay) pwm0.duty_u16(NEUTRAL) pwm1.duty_u16(NEUTRAL) pwm2.duty_u16(NEUTRAL) sleep(delay) pwm3.duty_u16(NEUTRAL) pwm4.duty_u16(NEUTRAL) pwm5.duty_u16(NEUTRAL) sleep(delay) def rotateRightPhase2(delay): pwm3.duty_u16(LEG_B_R) pwm4.duty_u16(LEG_F_L) pwm5.duty_u16(LEG_F_L) sleep(delay) pwm0.duty_u16(LEG_F_R) pwm1.duty_u16(LEG_F_R) pwm2.duty_u16(LEG_B_L) sleep(delay) pwm3.duty_u16(NEUTRAL) pwm4.duty_u16(NEUTRAL) pwm5.duty_u16(NEUTRAL) sleep(delay) pwm0.duty_u16(NEUTRAL) pwm1.duty_u16(NEUTRAL) pwm2.duty_u16(NEUTRAL) sleep(delay) def rotateRightMove(delay): rotateRightPhase1(delay) rotateRightPhase2(delay) def rotateLeftPhase1(delay): pwm0.duty_u16(LEG_F_R) pwm1.duty_u16(LEG_F_R) pwm2.duty_u16(LEG_B_L) sleep(delay) pwm3.duty_u16(LEG_B_R) pwm4.duty_u16(LEG_F_L) pwm5.duty_u16(LEG_F_L) sleep(delay) pwm0.duty_u16(NEUTRAL) pwm1.duty_u16(NEUTRAL) pwm2.duty_u16(NEUTRAL) sleep(delay) pwm3.duty_u16(NEUTRAL) pwm4.duty_u16(NEUTRAL) pwm5.duty_u16(NEUTRAL) sleep(delay) def rotateLeftPhase2(delay): pwm3.duty_u16(LEG_F_R) pwm4.duty_u16(LEG_B_L) pwm5.duty_u16(LEG_B_L) sleep(delay) pwm0.duty_u16(LEG_B_R) pwm1.duty_u16(LEG_B_R) pwm2.duty_u16(LEG_F_L) sleep(delay) pwm3.duty_u16(NEUTRAL) pwm4.duty_u16(NEUTRAL) pwm5.duty_u16(NEUTRAL) sleep(delay) pwm0.duty_u16(NEUTRAL) pwm1.duty_u16(NEUTRAL) pwm2.duty_u16(NEUTRAL) sleep(delay) def rotateLeftMove(delay): rotateLeftPhase1(delay) rotateLeftPhase2(delay) def stopMove(delay): pwm0.duty_u16(NEUTRAL) pwm1.duty_u16(NEUTRAL) pwm2.duty_u16(NEUTRAL) pwm3.duty_u16(NEUTRAL) pwm4.duty_u16(NEUTRAL) pwm5.duty_u16(NEUTRAL) sleep(delay) def movingThread(): while True: if movingStatus == MOVE_FORWARD: forwardMove(DELAY) elif movingStatus == MOVE_BACKWARD: backwardMove(DELAY) elif movingStatus == ROTATE_RIGHT: rotateRightMove(DELAY) elif movingStatus == ROTATE_LEFT: rotateLeftMove(DELAY) elif movingStatus == MOVE_STOP: stopMove(DELAY) return wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(ssid, password) html = """<!DOCTYPE html><html> <head><meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no"> <title>六脚ロボットコントローラー</title> <style>button { width: auto; height: auto; background: #003366; font-weight: normal; font-size: 14pt; font-family: Arial, Helvetica, sans-serif; font-size: 14px; color: #ffffff; padding: 10px 20px; -moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px; border: 1px solid #003366; -moz-box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.5); -webkit-box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.5); box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.5); text-shadow: 0px -1px 0px rgba(000,000,000,0.7), 0px 1px 0px rgba(255,255,255,0.3); } </style></head> <p>押したボタンにより、六脚ロボットが動作します。中央のボタンをクリックしないと六脚ロボットは止まりません。</p> <div align="center"> <table border="0"> <tr align="center"> <td></td> <td><form><button name="forward" value="on" type="submit" style="background: %s;"> ↑ </button></form></td> <td></td> </tr> <tr align="center"> <td><form><button name="rot_l" value="on" type="submit" style="background: %s;"> ← </button></form></td> <td><form><button name="stop" value="on" type="submit" style="background: %s;"> 〇 </button></form></td> <td><form><button name="rot_r" value="on" type="submit" style="background: %s;"> → </button></form></td> </tr> <tr align="center"> <td></td> <td><form><button name="backward" value="on" type="submit" style="background: %s;"> ↓ </button></form></td> <td></td> </tr> </table> <br /><br /> </div> </body></html> """ # Wait for connect or fail max_wait = 20 while max_wait > 0: if wlan.status() < 0 or wlan.status() >= 3: break max_wait -= 1 print('waiting for connection...') write_string('waiting for conn') #showing on LCD sleep(1) # Handle connection error if wlan.status() != 3: write_string('conn. failed') # showing on LCD raise RuntimeError('network connection failed') else: print('Connected') status = wlan.ifconfig() print( 'ip = ' + status[0] ) write_string(status[0]) #showing on LCD # Open socket addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1] s = socket.socket() s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: s.bind(addr) except OSError as e: print(e) s.close() s = None write_string('socket error') #showing on LCD sys.exit() s.listen(1) print('listening on', addr) NEUTRAL = 0.0675 # (0.035 + 0.1)/2 # 最小デューティ比3.5%と最大デューティ比10%の平均 LEG_F_R = NEUTRAL + 0.024 LEG_F_L = NEUTRAL - 0.024 LEG_B_R = NEUTRAL - 0.024 LEG_B_L = NEUTRAL + 0.024 NEUTRAL = int(65535*NEUTRAL) LEG_F_R = int(65535*LEG_F_R) LEG_F_L = int(65535*LEG_F_L) LEG_B_R = int(65535*LEG_B_R) LEG_B_L = int(65535*LEG_B_L) DELAY = 0.15 pwm0 = PWM(Pin(16)) pwm1 = PWM(Pin(17)) pwm2 = PWM(Pin(18)) pwm3 = PWM(Pin(19)) pwm4 = PWM(Pin(20)) pwm5 = PWM(Pin(21)) pwm0.freq(50) pwm1.freq(50) pwm2.freq(50) pwm3.freq(50) pwm4.freq(50) pwm5.freq(50) pwm0.duty_u16(NEUTRAL) pwm1.duty_u16(NEUTRAL) pwm2.duty_u16(NEUTRAL) pwm3.duty_u16(NEUTRAL) pwm4.duty_u16(NEUTRAL) pwm5.duty_u16(NEUTRAL) MOVE_FORWARD = 0 MOVE_BACKWARD = 1 ROTATE_RIGHT = 2 ROTATE_LEFT = 3 MOVE_STOP = 4 onCol = '#26a1ff' offCol = '#003366' move_strs = (offCol, offCol, onCol, offCol, offCol) isMoving = False movingStatus = MOVE_STOP # Listen for connections, serve client while True: try: cl, addr = s.accept() #print('client connected from', addr) request = cl.recv(1024) #print("request:") #print(request) request = str(request) forward_on = request.find('forward=on') rot_l_on = request.find('rot_l=on') stop_on = request.find('stop=on') rot_r_on = request.find('rot_r=on') backward_on = request.find('backward=on') if forward_on == 8: move_strs = (onCol, offCol, offCol, offCol, offCol) movingStatus = MOVE_FORWARD if rot_l_on == 8: move_strs = (offCol, onCol, offCol, offCol, offCol) movingStatus = ROTATE_LEFT if stop_on == 8: move_strs = (offCol, offCol, onCol, offCol, offCol) movingStatus = MOVE_STOP isMoving = False if rot_r_on == 8: move_strs = (offCol, offCol, offCol, onCol, offCol) movingStatus = ROTATE_RIGHT if backward_on == 8: move_strs = (offCol, offCol, offCol, offCol, onCol) movingStatus = MOVE_BACKWARD if ((movingStatus == MOVE_FORWARD or movingStatus == MOVE_BACKWARD or movingStatus == ROTATE_LEFT or movingStatus == ROTATE_RIGHT) and isMoving == False): isMoving = True _thread.start_new_thread(movingThread, ()) response = html % move_strs cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n') cl.send(response) cl.close() except OSError as e: print('connection closed') except KeyboardInterrupt as e: break s.close() s = None上記のプログラムを実行するための注意をいくつか記します。下記のように、用いている Wifi の SSID (アクセスポイント名) とそのパスワードを記す項目があります。 ここを皆さんの環境における SSID とパスワードに変更しないとこのプログラムは動作しませんのでご注意ください。なお、2.4 GHz の周波数帯の Wifi アクセスポイントにしか接続できませんのでご注意ください。
ssid = 'YOUR_WIFI_SSID' password = 'YOUR_WIFI_PASSWORD'また、回路を LCD なしで実行する場合、プログラム中の下記の部分を消し、
##### for LCD (省略) ##### End of LCD下記の内容で差し替える必要があります。
def write_string(s): passさらに、プログラムの書き換え時は、Pico W / Pico 2 W 上で動作しているプログラムを Thonny の「STOP (停止)」ボタンから一旦停止する必要があることもご注意ください。