回路配線図の PDF と応用PDF は Raspberry Pi 上のブラウザでも見られますが、ブラウザ上の「↓」(ダウンロード)ボタンでダウンロードし、ファイルマネージャーで PDF ファイルを右クリックし「アプリケーションで開く」→「アクセサリ」→「ドキュメントビューア」などで開いてもよいでしょう。
「選択したアプリケーションをこのファイルタイプのデフォルトのアクションとする」にチェックを入れればそのアプリケーションで開くのがデフォルト動作となります。
OSである Raspberry Pi OS のインストールから設定の流れは、書籍執筆時から少しずつ変更されております。
最新のインストールおよび設定方法は、
「Raspberry Piではじめる機械学習 補足情報」内にある「Raspberry PiへのOSのインストール方法」をご覧ください。本書2章と同等の内容をアップデートされた状態で見ることができますので、参考にしてください。
p.128, Bookworm (Raspberry Pi OS 2023-10-10 以降) および Bullseye (Raspberry Pi OS 2021-10-30 以降) でのカメラの利用について
Raspberry Pi OS 2023-10-10 からはじまったバージョン Bookworm および
Raspberry Pi OS 2021-10-30 からはじまったバージョン Bullseye では
カメラモジュールの利用方法に大きな変更が加えられ、libcamera というライブラリを用いるようになりました。
これは、本書でカメラを用いるプログラムは、そのままでは動かなくなるということを意味します。
ただし、Bullseye では「Legacy Camera」という、これまでと互換性のあるカメラの利用方法が可能になっています。この Lecgacy Camera を用いれば本書でカメラを用いるプログラムはそのまま動作しますが、
残念ながら Bookworm では Legacy Camera は削除されてしまいました。
<2020-12-02版およびそれ以降の Raspberry Pi OS をご利用の場合(Bullseye)>
2020-12-02版およびそれ以降の Raspberry Pi OS をご利用で、なおかつイヤフォンジャックから音が鳴らない場合、raspi-configというコマンドで音声の出力先をイヤフォンジャックに切り替えます。
以下の手順に従ってください。
ターミナルで「 sudo raspi-config 」コマンドを実行し、設定画面を開く
キーボードの「Enter」キーを押し、「1 System Options」に入る
キーボードの「↓」キーを一回押し、「S2 Audio」にフォーカスを合わせる
キーボードの「Enter」キーを押し、「S2 Audio」の設定画面に入る
キーボードの「↓」キーを一回押し、「1 Headphones」にフォーカスを合わせる
キーボードの「Enter」キーを押し、「1 Headphones」を選択する
キーボードの「TAB」キー二回を押し、「Finish」にフォーカスを合わせる
キーボードの「Enter」キーを押し、raspi-config の設定画面を終了する
以上で、下記コマンドで
mplayer test.mp3
イヤフォンジャックから音声が出るようになります。
<2020-5-27版および2020-8-20版の Raspberry Pi OS をご利用の場合>
次は、2020-5-27版および2020-8-20版の Raspberry Pi OS をご利用の場合で、なおかつイヤフォンジャックから音が鳴らない場合です。
音声を再生するコマンドを以下に変えるとイヤフォンジャックから音が鳴ります。
「-ao alsa:device=hw=1,0」が「イヤフォンジャックからの再生」を意味します。「-ao alsa:device=hw=0,0」なら「HDMIからの再生」です。
なお、NOOBS 3.2.1 (Raspbian 2019-09-26) より前の OS ではテキストエディタとしてleafpadではなくmousepadを用います。
sudo leafpad /etc/rc.local
p.287, IPアドレスをLCDに表示するために /etc/rc.local に記入するコマンド
python3 /home/pi/07-02-LCD.py $_IP
なお、2022年4月にリリースされた OS よりデフォルトユーザー pi は廃止されています。そのため、上のコマンドの pi の部分は、皆さんが作成したユーザー名で置き換変える必要があります。すなわち、「kanamaru」というユーザーを作成したのなら、「python3 /home/kanamaru/07-02-LCD.py $_IP」となる、ということです。
さて、ここで紹介するコマンドは、libcamera を用いない場合すなわち、Bullseye の Legacy Camera モードでカメラを利用する場合です。
コピーの際、先頭の「(1)」、「(2)」などの数字を含めないよう注意してください。
(1) sudo apt update
(2) sudo apt install libjpeg-dev cmake
(3) git clone https://github.com/neuralassembly/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 を用いている方は、(2) のコマンドを
(2) sudo apt install libjpeg8-dev cmake
に変更しなければならいない場合があります。
また、 Bullseye を用いている方は、本ページの
「p.128, Bookworm (Raspberry Pi OS 2023-10-10 以降) および Bullseye (Raspberry Pi OS 2021-10-30 以降) でのカメラの利用について」の項目を参考に、Legacy Camera モードを有効にする必要がありますのでその点もご注意ください。
さらに、64-bit版 Bullseye を用いている方は、実行時に「本書の演習をウェブカメラで実行する方法」で配布しているサンプルファイル内の 10-02-stream-webcam.sh を用いる必要があります。
なお、2022年4月にリリースされた OS よりデフォルトユーザー pi は廃止されています。そのため、上のコマンドの pi の部分は、皆さんが作成したユーザー名で置き換変える必要があります。すなわち、「kanamaru」というユーザーを作成したのなら、「sh /home/kanamaru/10-02-stream.sh」となる、ということです。
なお、2022年4月にリリースされた OS よりデフォルトユーザー pi は廃止されています。そのため、上の記述の pi の部分は、皆さんが作成したユーザー名で置き換変える必要があります。すなわち、「kanamaru」というユーザーを作成したのなら、「/home/kanamaru/bluebacks」となる、ということです。
p.310, nano の設定ファイル .nanorc に記す内容
set tabsize "4"set tabstospaces
p.310, vi の設定ファイル .vimrc に記す内容
set expandtab
set tabstop=4set softtabstop=4set shiftwidth=4
なお、2022年4月にリリースされた OS よりデフォルトユーザー pi は廃止されています。そのため、上のコマンドの pi の部分は、皆さんが作成したユーザー名で置き換変える必要があります。すなわち、「kanamaru」というユーザーを作成したのなら、「python3 /home/kanamaru/07-03-LCD-temp.py &」となる、ということです。
Raspberry Pi OS 2023-10-10 からはじまったバージョン Bookworm および
Raspberry Pi OS 2021-10-30 からはじまったバージョン Bullseye では
カメラモジュールの利用方法に大きな変更が加えられ、libcamera というライブラリを用いるようになりました。
これは、本書でカメラを用いるプログラムがそのままでは動かなくなる、ということを意味します。
ただし、Bullseye では「Legacy Camera」という、これまでと互換性のあるカメラの利用方法が可能になっています。この Lecgacy Camera を用いれば本書でカメラを用いるプログラムはそのまま動作しますが、
残念ながら Bookworm では Legacy Camera モードは削除されてしまいました。
そこで、本ページでは、Legacy Camera に頼らずに最新の OS で本書の演習を実行する方法を解説します。具体的には、libcamera ライブラリを利用する Python モジュールである picamera2 を使ってプログラムを実行します。
サポート環境は Bookworm および Bullseye 以の 32-bit または 64-bit 版の Raspberry Pi OS です。Bullseye の場合は、なるべく新しいバージョンを用いましょう。そうしないと、本ページで利用する picamera2 (python3-picamera2) がインストールされていないことがあるからです。
また、Bullseye の場合、Legacy Camera モードは無効にしておく必要があります。OS インストール直後の状態ではあらかじめ無効になっています。一度有効にしてしまった方は、
ターミナルを開いて以下の手順に従うことで、raspi-config により Legacy Camera モードを無効にしましょう。
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
一方、2023年3月、Wifi 機能付きの Raspberry Pi Pico W (以下 Pico W)が日本で販売開始されました。
さらに、2025年3月にはその高機能版である Raspberry Pi Pico 2 W (以下 Pico 2 W)が日本で販売開始されました。
Pico W や Pico 2 W を用いると、 Wifi 接続を用いる 9 章および 10 章の演習をある程度再現できるようになります。
具体的には、本ページでは下記の内容を取り扱います。
9章の内容: PC やスマートフォンのブラウザで、Pico W / Pico 2 W に接続された LED / I2C温度センサ / RGB フルカラー LED / DCモータ / サーボモータ を制御
10章の内容: PCやスマートフォンのブラウザで、Pico W / Pico 2 W を搭載したキャタピラ式模型を制御
10章のキャタピラ式模型を Pico W で実現した様子を示したのが下図です。もともと Raspberry Pi が配置されていたスペースが空いていますが、これは 回路と Pico W を全てブレッドボード上に配置できるからです。回路動作用のバッテリーが、大電流を流せないものでも良い、というメリットもあります。
そして、このキャタピラ式模型の動作の様子が下記の YouTube 動画です。
なお、Wifi 経由での Pico W / Pico 2 W の制御には欠点もあり、回路への制御信号の送信が、ブラウザのページ再読み込みのタイミングでしか行えないため、ブラウザに対して行ったアクションへのレスポンスが悪い、という問題があります。
さて、本ページでは以上の内容の実現方法を解説していきます。
その内容に進む前に、「本書の電子工作パーツを Raspberry Pi Pico シリーズで利用する」を参考に、
下記の内容を進めておいてください。Pico W や Pico 2 W でも、Pico 用の演習をすべて実行することができます。Pico W や Pico 2 W を購入できるサイトもリンク先で紹介されています。
Pico W / Pico 2 W への MicroPython 環境のインストール (自分が用いているバージョンの Pico の UF2 ファイルを用いることに注意)
Windows などへの、開発環境 Thonny のインストール
Pico W / Pico 2 W で Python プログラムを実行する方法の習得
1. ブラウザのボタンによるLEDの点灯
まずは、PCやスマートフォンのブラウザから、Pico W / Pico 2 W に接続された LED のオン / オフを切り替える演習を行ってみましょう。9.3 章の内容です。必要な回路は下図の通りです。
見てわかる通り、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 を動作させられるようにすることを強く推奨します。
さらに、プログラムの書き換え時は、Pico W / Pico 2 W 上で動作しているプログラムを Thonny の「STOP (停止)」ボタンから一旦停止する必要があることもご注意ください。
記述できたら Pico W / Pico 2 W でプログラムを実行してみましょう。LCD 上に(または Thonny のコンソール上に)Pico W に割り当てられた IP アドレス(例えば、 192.168.1.40 のようなもの)が表示されれば、実行が成功した可能性が高いでしょう。下図のような状態です。
そうしたら、Pico W / Pico 2 W と同じネットワーク内に存在する PC やスマートフォンのブラウザでアドレス(例えば 192.168.1.40 の場合、 http://192.168.1.40/ )にアクセスしてみましょう。下図のようなページが現れるはずです。
ブラウザ上のボタンを押すことで Pico W / Pico 2 W に接続された LED の点灯/消灯が切り替わります。LED 点灯時の画面の様子は下記の通りで、ボタンの色が変わります。これは、本書の Raspberry Pi 版のプログラムと同じ挙動です。ただし、上で既に述べたように、回路のアクションはブラウザ上のページの再読み込みのタイミングでしか起こりませんので、反応が遅いという問題がありますのでご了承ください。これは、本ページの以下のプログラム全てに当てはまります。
PCやスマートフォンのブラウザに、Pico W / Pico 2 W に接続された I2C 温度センサの値を表示する演習を行ってみましょう。9.4 章の内容です。必要な回路は下図の通りです。
そして、この回路上で実行すべきプログラムは下記の通りです。
import time
import sys
import network
import socket
from time import sleep
from machine importPin, I2C
ssid ='YOUR_WIFI_SSID'
password ='YOUR_WIFI_PASSWORD'
i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000)
i2c.scan()##### for temperature sensordef read_adt7410():
word_data =int.from_bytes(i2c.readfrom_mem(address_adt7410, register_adt7410,4),'little')
data =(word_data &0xff00)>>8|(word_data &0xff)<<8
data = data>>3# 13ビットデータif data &0x1000==0:# 温度が正または0の場合
temperature = data*0.0625else:# 温度が負の場合、 絶対値を取ってからマイナスをかける
temperature =((~data&0x1fff)+1)*-0.0625return temperature
address_adt7410 =0x48
register_adt7410 =0x00##### end of temperature sensor##### for LCDdef 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 +=1def check_writable(c):if c >=0x06and c <=0xff:return c
else:return0x20# 空白文字
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)# refresh per 5 sec.
html ="""<!DOCTYPE html><html>
<head><meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no">
<meta http-equiv="refresh" content="5">
<title>2 - I2C温度センサ(ADT7410)による温度の取得</title>
</head>
<div align="center">
<p>温度: %s</p>
</div>
</body></html>
"""# Wait for connect or fail
max_wait =20while max_wait >0:if wlan.status()<0or wlan.status()>=3:break
max_wait -=1print('waiting for connection...')
write_string('waiting for conn')#showing on LCD
time.sleep(1)# Handle connection errorif wlan.status()!=3:
write_string('conn. failed')# showing on LCDraiseRuntimeError('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)exceptOSErroras 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 clientwhileTrue:try:
cl, addr = s.accept()#print('client connected from', addr)
request = cl.recv(1024)#print("request:")#print(request)
request = str(request)
inputValue = read_adt7410()
inputStr ='{:.1f}'.format(inputValue)
response = html % inputStr
cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
cl.send(response)
cl.close()exceptOSErroras e:print('connection closed')exceptKeyboardInterruptas e:break
s.close()
s =None
なお、「1. ブラウザのボタンによるLEDの点灯」で述べたように、YOUR_WIFI_SSID と YOUR_WIFI_PASSWORD を自分の環境のものに変更することと、
LCD なしでプログラムを実行したい場合は「# for LCD ~ # End of LCD」の部分の差し替えが必要ですのでご注意ください。
さらに、プログラムの書き換え時は、Pico W / Pico 2 W 上で動作しているプログラムを Thonny の「STOP (停止)」ボタンから一旦停止する必要があることもご注意ください。
PCやスマートフォンのブラウザで、Pico W / Pico 2 W に接続された RGBフルカラーLED の色を制御する演習を行ってみましょう。9.5 章の内容です。
なお、Rapsberry Pi を対象とした本書では、RGBフルカラーLEDの色の制御に、ブラウザのスライダを用いました。
しかし、Pico W / Pico 2 W ではスライダを用いるのは難しそうに思えましたので(専門的に言えば、JavaScript で取得したスライダの値を回路に反映する方法が思いつかない)、簡易的にボタンによる制御を行うことにしました。
なお、「1. ブラウザのボタンによるLEDの点灯」で述べたように、YOUR_WIFI_SSID と YOUR_WIFI_PASSWORD を自分の環境のものに変更することと、
LCD なしでプログラムを実行したい場合は「# for LCD ~ # End of LCD」の部分の差し替えが必要ですのでご注意ください。
さらに、プログラムの書き換え時は、Pico W / Pico 2 W 上で動作しているプログラムを Thonny の「STOP (停止)」ボタンから一旦停止する必要があることもご注意ください。
PCやスマートフォンのブラウザで、Pico W / Pico 2 W に接続された DC モーターの回転速度を制御する演習を行ってみましょう。9.6 章の内容です。
なお、Rapsberry Pi を対象とした本書では、DC モーターの速度制御に、ブラウザのタッチイベントを用いました。
しかし、Pico W / Pico 2 W ではタッチイベントを用いるのは難しそうに思えましたので(専門的に言えば、JavaScript で取得したタッチイベントの情報を回路に反映する方法が思いつかない)、簡易的にボタンによる制御を行うことにしました。
必要な回路は下図の通りです。
import time
import sys
import network
import socket
from time import sleep
from machine importPin, PWM, I2C
pwm1 = PWM(Pin(16))
pwm2 = PWM(Pin(17))
pwm1.freq(100)
pwm2.freq(100)
pwm1.duty_u16(0)
pwm2.duty_u16(0)
i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000)
i2c.scan()
ssid ='YOUR_WIFI_SSID'
password ='YOUR_WIFI_PASSWORD'##### for LCDdef 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 +=1def check_writable(c):if c >=0x06and c <=0xff:return c
else:return0x20# 空白文字
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>4 - ボタンによるDCモーターの速度制御</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">
<form><button name="vel_n2" value="on" type="submit" style="background:%s;"> -2 </button>
<button name="vel_n1" value="on" type="submit" style="background:%s;"> -1 </button>
<button name="vel_0" value="on" type="submit" style="background:%s;"> 0 </button>
<button name="vel_p1" value="on" type="submit" style="background:%s;"> +1 </button>
<button name="vel_p2" value="on" type="submit" style="background:%s;"> +2 </button>
</form>
<br /><br />
</div>
</body></html>
"""# Wait for connect or fail
max_wait =20while max_wait >0:if wlan.status()<0or wlan.status()>=3:break
max_wait -=1print('waiting for connection...')
write_string('waiting for conn')#showing on LCD
time.sleep(1)# Handle connection errorif wlan.status()!=3:
write_string('conn. failed')# showing on LCDraiseRuntimeError('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)exceptOSErroras e:print(e)
s.close()
s =None
write_string('socket error')#showing on LCD
sys.exit()
s.listen(1)print('listening on', addr)
onCol ='#26a1ff'
offCol ='#003366'
vel_strs =(offCol, offCol, onCol, offCol, offCol)
duty_max =0.7# Listen for connections, serve clientwhileTrue:try:
cl, addr = s.accept()#print('client connected from', addr)
request = cl.recv(1024)#print("request:")#print(request)
request = str(request)
vel_n2_on = request.find('vel_n2=on')
vel_n1_on = request.find('vel_n1=on')
vel_0_on = request.find('vel_0=on')
vel_p1_on = request.find('vel_p1=on')
vel_p2_on = request.find('vel_p2=on')if vel_n2_on ==8:
vel_strs =(onCol, offCol, offCol, offCol, offCol)
pwm2.duty_u16(0)
pwm1.duty_u16(int(65535*duty_max))if vel_n1_on ==8:
vel_strs =(offCol, onCol, offCol, offCol, offCol)
pwm2.duty_u16(0)
pwm1.duty_u16(int(65535*duty_max/2))if vel_0_on ==8:
vel_strs =(offCol, offCol, onCol, offCol, offCol)
pwm1.duty_u16(0)
pwm2.duty_u16(0)if vel_p1_on ==8:
vel_strs =(offCol, offCol, offCol, onCol, offCol)
pwm1.duty_u16(0)
pwm2.duty_u16(int(65535*duty_max/2))if vel_p2_on ==8:
vel_strs =(offCol, offCol, offCol, offCol, onCol)
pwm1.duty_u16(0)
pwm2.duty_u16(int(65535*duty_max))
response = html % vel_strs
cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
cl.send(response)
cl.close()exceptOSErroras e:print('connection closed')exceptKeyboardInterruptas e:break
s.close()
s =None
なお、「1. ブラウザのボタンによるLEDの点灯」で述べたように、YOUR_WIFI_SSID と YOUR_WIFI_PASSWORD を自分の環境のものに変更することと、
LCD なしでプログラムを実行したい場合は「# for LCD ~ # End of LCD」の部分の差し替えが必要ですのでご注意ください。
さらに、プログラムの書き換え時は、Pico W / Pico 2 W 上で動作しているプログラムを Thonny の「STOP (停止)」ボタンから一旦停止する必要があることもご注意ください。
PCやスマートフォンのブラウザで、Pico W / Pico 2 W に接続されたサーボモーターの角度を制御する演習を行ってみましょう。9.7 章(付録 PDF)の内容です。
なお、Rapsberry Pi を対象とした本書では、サーボモーターの角度制御に、ブラウザのスライダを用いました。
しかし、Pico W / Pico 2 W ではスライダを用いるのは難しそうに思えましたので(専門的に言えば、JavaScript で取得したスライダの値を回路に反映する方法が思いつかない)、簡易的にボタンによる制御を行うことにしました。
必要な回路は下図の通りです。
import time
import sys
import network
import socket
from time import sleep
from machine importPin, PWM, I2C
def servo_duty_hwpwm_web(val):
val_min =0
val_max =4
servo_min =0.035# 最小デューティ比3.5%
servo_max =0.1# 最大デューティ比10%if val < val_min:
val = val_min
duty =(servo_min-servo_max)*(val-val_min)/(val_max-val_min)+ servo_max
# 一般的なサーボモーターはこちらを有効に#duty = (servo_max-servo_min)*(val-val_min)/(val_max-val_min) + servo_minreturn duty
pwm1 = PWM(Pin(16))
pwm1.freq(50)
pwm1.duty_u16(int(65535*servo_duty_hwpwm_web(2)))
i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000)
i2c.scan()
ssid ='YOUR_WIFI_SSID'
password ='YOUR_WIFI_PASSWORD'##### for LCDdef 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 +=1def check_writable(c):if c >=0x06and c <=0xff:return c
else:return0x20# 空白文字
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>7 - サーボモーターの制御</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">
<form><button name="pos0" value="on" type="submit" style="background:%s;"> 0 </button>
<button name="pos1" value="on" type="submit" style="background:%s;"> 1 </button>
<button name="pos2" value="on" type="submit" style="background:%s;"> 2 </button>
<button name="pos3" value="on" type="submit" style="background:%s;"> 3 </button>
<button name="pos4" value="on" type="submit" style="background:%s;"> 4 </button>
</form>
<br /><br />
</div>
</body></html>
"""# Wait for connect or fail
max_wait =20while max_wait >0:if wlan.status()<0or wlan.status()>=3:break
max_wait -=1print('waiting for connection...')
write_string('waiting for conn')#showing on LCD
time.sleep(1)# Handle connection errorif wlan.status()!=3:
write_string('conn. failed')# showing on LCDraiseRuntimeError('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)exceptOSErroras e:print(e)
s.close()
s =None
write_string('socket error')#showing on LCD
sys.exit()
s.listen(1)print('listening on', addr)
onCol ='#26a1ff'
offCol ='#003366'
pos_strs =(offCol, offCol, onCol, offCol, offCol)# Listen for connections, serve clientwhileTrue:try:
cl, addr = s.accept()#print('client connected from', addr)
request = cl.recv(1024)#print("request:")#print(request)
request = str(request)
pos0_on = request.find('pos0=on')
pos1_on = request.find('pos1=on')
pos2_on = request.find('pos2=on')
pos3_on = request.find('pos3=on')
pos4_on = request.find('pos4=on')if pos0_on ==8:
pos_strs =(onCol, offCol, offCol, offCol, offCol)
pwm1.duty_u16(int(65535*servo_duty_hwpwm_web(0)))if pos1_on ==8:
pos_strs =(offCol, onCol, offCol, offCol, offCol)
pwm1.duty_u16(int(65535*servo_duty_hwpwm_web(1)))if pos2_on ==8:
pos_strs =(offCol, offCol, onCol, offCol, offCol)
pwm1.duty_u16(int(65535*servo_duty_hwpwm_web(2)))if pos3_on ==8:
pos_strs =(offCol, offCol, offCol, onCol, offCol)
pwm1.duty_u16(int(65535*servo_duty_hwpwm_web(3)))if pos4_on ==8:
pos_strs =(offCol, offCol, offCol, offCol, onCol)
pwm1.duty_u16(int(65535*servo_duty_hwpwm_web(4)))
response = html % pos_strs
cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
cl.send(response)
cl.close()exceptOSErroras e:print('connection closed')exceptKeyboardInterruptas e:break
s.close()
s =None
なお、「1. ブラウザのボタンによるLEDの点灯」で述べたように、YOUR_WIFI_SSID と YOUR_WIFI_PASSWORD を自分の環境のものに変更することと、
LCD なしでプログラムを実行したい場合は「# for LCD ~ # End of LCD」の部分の差し替えが必要ですのでご注意ください。
さらに、プログラムの書き換え時は、Pico W / Pico 2 W 上で動作しているプログラムを Thonny の「STOP (停止)」ボタンから一旦停止する必要があることもご注意ください。
なお、「1. ブラウザのボタンによるLEDの点灯」で述べたように、YOUR_WIFI_SSID と YOUR_WIFI_PASSWORD を自分の環境のものに変更することと、
LCD なしでプログラムを実行したい場合は「# for LCD ~ # End of LCD」の部分の差し替えが必要ですのでご注意ください。
さらに、プログラムの書き換え時は、Pico W / Pico 2 W 上で動作しているプログラムを Thonny から一旦停止する必要があることもご注意ください。