私的AI研究会 > WebIOPi演習
WebIOPi を使うと「Webブラウザから Raspberry Pi をリモート操作する」「Raspberry Pi を操作するプログラムを書く」「Raspberry Pi のGPIOを操作する」ことができる。
MJPG-streamerを使ってカメラの動画を見ながら操作するキャタピラーロボットを作ってみる。
/home/pi/webiopi ├── bb │ ├── 01 │ │ ├── index.html │ │ ├── javascript.js │ │ ├── js │ │ │ └── require.js │ │ ├── script.py │ │ └── styles.css │ ├── 04 │ │ ├── img │ │ │ └── DCMotorController.png │ │ ├── index.html │ │ ├── javascript.js │ │ ├── js │ │ │ └── require.js │ │ ├── script.py │ │ └── styles.css │ ├── 05 │ │ ├── img │ │ │ └── CrawlerController.png │ │ ├── index.html │ │ ├── javascript.js │ │ ├── js │ │ │ └── require.js │ │ ├── script.py │ │ └── styles.css │ ├── 06 │ │ ├── img │ │ │ └── CrawlerControllerTrans.png │ │ ├── index.html │ │ ├── javascript.js │ │ ├── js │ │ │ └── require.js │ │ ├── script.py │ │ └── styles.css │ ├── 07-02-LCD.py │ ├── 10-01-sw-poweroff.py │ └── 10-02-stream.sh └── callmacro_leds ├── index.html └── script.py
● GPIO LED関連・モーター関連
● 回路図 fig.10, fig.11
● LCD追加 (ブレッドボードのモータードライバの位置を1段下げる)
● LCDボード SDA → GPIO 2, SCL → GPIO 3
● モーターへの配線+/- 入れ替え
● モーターの端子間に0.01μFのコンデンサを追加(ノイズ対策)
● 左記の「Aibot Project」のハードウエアを利用してWebIPOi プログラミングを検証する。
● 3つのLED の点滅をコントロールする。
● 出力ピンは、GPIO 16,17,18
● GPIOピン番号に注意。(以前の回路から変更)
<script type="text/javascript" src="/webiopi.js"></script>
webiopi().callMacro( "マクロ関数名", 引数 );
$ vi script.py #!/usr/bin/env python # -*- coding: utf-8 -*- import webiopi GPIO = webiopi.GPIO LED1PIN = 21 LED2PIN = 13 LED3PIN = 12 g_led1active = 0 g_led2active = 0 g_led3active = 0 g_speed = 5 def setup(): GPIO.setFunction( LED1PIN, GPIO.OUT ) GPIO.setFunction( LED2PIN, GPIO.OUT ) GPIO.setFunction( LED3PIN, GPIO.OUT ) def loop(): # speed | 1 10 # interval[ sec] | 0.5 0.01 interval = 0.5 + (g_speed - 1) * (0.01 - 0.5) / (10-1) if g_led1active: GPIO.digitalWrite( LED1PIN, True ) if g_led2active: GPIO.digitalWrite( LED2PIN, True ) if g_led3active: GPIO.digitalWrite( LED3PIN, True ) webiopi.sleep( interval ) GPIO.digitalWrite(LED1PIN, False ) GPIO.digitalWrite(LED2PIN, False ) GPIO.digitalWrite(LED3PIN, False ) webiopi.sleep( interval ) @webiopi.macro def ChangeLedActive( led, active ): global g_led1active, g_led2active, g_led3active if 1 == int(led): g_led1active = int(active) if 2 == int(led): g_led2active = int(active) if 3 == int(led): g_led3active = int(active) @webiopi.macro def ChangeSpeed( speed ): global g_speed g_speed = int(speed)
$ vi index.html <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title></title> <script type="text/javascript" src="/webiopi.js"></script> <script type="text/javascript"> webiopi().ready( function() { // Initialize webiopi().callMacro( "ChangeLedActive", [1, 0] ); webiopi().callMacro( "ChangeLedActive", [2, 0] ); webiopi().callMacro( "ChangeLedActive", [3, 0] ); webiopi().callMacro( "ChangeSpeed", 5 ); } ); function onCheckboxLed( led ) { if( 1 == led ) { webiopi().callMacro( "ChangeLedActive", [1, document.getElementsByName("led1")[0].checked ? 1 : 0] ); return; } if( 2 == led ) { webiopi().callMacro( "ChangeLedActive", [2, document.getElementsByName("led2")[0].checked ? 1 : 0] ); return; } if( 3 == led ) { webiopi().callMacro( "ChangeLedActive", [3, document.getElementsByName("led3")[0].checked ? 1 : 0] ); return; } } function onRadioSpeed( speed ) { webiopi().callMacro( "ChangeSpeed", speed ); } </script> </head> <body> LED Active :<br> LED1<input type="checkbox" name="led1" onclick="onCheckboxLed(1)"> タクトSW<br> LED2<input type="checkbox" name="led2" onclick="onCheckboxLed(2)"> 左目<br> LED3<input type="checkbox" name="led3" onclick="onCheckboxLed(3)"> 右目<br> <br> Blink Speed :<br> 1<input type="radio" name="speed" onclick="onRadioSpeed(1)"> 遅い<br> 2<input type="radio" name="speed" onclick="onRadioSpeed(2)"><br> 3<input type="radio" name="speed" onclick="onRadioSpeed(3)"><br> 4<input type="radio" name="speed" onclick="onRadioSpeed(4)"><br> 5<input type="radio" name="speed" onclick="onRadioSpeed(5)" checked><br> 6<input type="radio" name="speed" onclick="onRadioSpeed(6)"><br> 7<input type="radio" name="speed" onclick="onRadioSpeed(7)"><br> 8<input type="radio" name="speed" onclick="onRadioSpeed(8)"><br> 9<input type="radio" name="speed" onclick="onRadioSpeed(9)"><br> 10<input type="radio" name="speed" onclick="onRadioSpeed(10)"> 早い<br> </body> </html>
$ sudo vi /etc/webiopi/config
[SCRIPTS] セクションの「myscript =」に、作成したスクリプトファイルを指定。
[SCRIPTS] # Load custom scripts syntax : # name = sourcefile # each sourcefile may have setup, loop and destroy functions and macros #myscript = /home/pi/webiopi/examples/scripts/macros/script.py myscript = /home/pi/webiopi/callmacro_leds/script.py
[HTTP] セクションの「doc-root =」、「welcome-file =」に、作成したHTMLファイルを指定。
[HTTP] # HTTP Server configuration enabled = true port = 8000 # File containing sha256(base64("user:password")) # Use webiopi-passwd command to generate it passwd-file = /etc/webiopi/passwd # Use doc-root to change default HTML and resource files location #doc-root = /home/pi/webiopi/examples/scripts/macros doc-root = /home/pi/webiopi/callmacro_leds/ # Use welcome-file to change the default "Welcome" file welcome-file = index.html
$ sudo systemctl start webiopi
http://192.168.xxx.xxx:8000/認証ダイアログが表示された場合は、
$ sudo systemctl stop webiopi
$ sudo service webiopi start
$ sudo service webiopi stop
$ ps ax |grep webiopi
1612 ? Ssl 0:00 /usr/bin/python3 -m webiopi -l /var/log/webiopi -c /etc/webiopi/config 1619 pts/0 S+ 0:00 grep --color=auto webiopi
1619 pts/0 S+ 0:00 grep --color=auto webiopi
$ sudo systemctl enable webiopi
$ sudo systemctl disable webiopi
[SCRIPTS] セクションの「myscript =」に、サービスを開始したときに実行するスクリプトファイルを指定。
[SCRIPTS] # Load custom scripts syntax : # name = sourcefile # each sourcefile may have setup, loop and destroy functions and macros #myscript = /home/pi/webiopi/examples/scripts/macros/script.py myscript = /home/pi/work/webiopi/xxxxxxxx/XXXXXXXX.py
[HTTP] セクションの「doc-root =」、「welcome-file =」に、Webブラウザからアクセスしたときに表示作成されるHTMLファイルを指定。
[HTTP] # HTTP Server configuration enabled = true port = 8000 # File containing sha256(base64("user:password")) # Use webiopi-passwd command to generate it passwd-file = /etc/webiopi/passwd # Use doc-root to change default HTML and resource files location #doc-root = /home/pi/webiopi/examples/scripts/macros doc-root = /home/pi/webiopi/xxxxxxxx/ # Use welcome-file to change the default "Welcome" file #welcome-file = index.html welcome-file = XXXXXXXX.html
ブラウザのボタンによるLEDの点灯
$ cd bluebacks/raspi1a-sample $ ls 04-01-led.py 05-03-sw-pd-event.py 06-01-print.py 07-01-temp.py 07-05-acc.py 08-01-led.py 08-05-servo-swpwm.py 10-02-stream.sh 04-02-led.py 05-04-sw-camera.py 06-02-led.py 07-02-LCD.py 07-06-acc.py 08-02-rgbled.py 08-06-2servos.py test.mp3 05-01-sw.py 05-05-sw-mp3.py 06-03-volume.py 07-03-LCD-temp.py 07-07-acc-gyro-mag.py 08-03-dcmotor.py 09-samples 05-02-sw-pd.py 05-06-sw-poweroff.py 06-04-spidev.py 07-04-temp.py 07-08-pressure-temp.py 08-04-servo.py 10-01-sw-poweroff.py $ cp -r 09-samples/bb /home/pi/webiopi
$ cd /home/pi/webiopi/bb/01 $ ls index.html javascript.js js script.py styles.css
$ vi script.py import webiopi import time # デバッグ出力を有効に webiopi.setDebug() # GPIOライブラリの取得 GPIO = webiopi.GPIO LED = 12 STATE = GPIO.LOW # WebIOPiの起動時に呼ばれる関数 def setup(): webiopi.debug("Script with macros - Setup") # GPIOのセットアップ GPIO.setFunction(LED, GPIO.OUT) GPIO.digitalWrite(LED, STATE) # WebIOPiにより繰り返される関数 def loop(): webiopi.sleep(5) # WebIOPi終了時に呼ばれる関数 def destroy(): webiopi.debug("Script with macros - Destroy") # GPIO関数のリセット(入力にセットすることで行う) GPIO.setFunction(LED, GPIO.IN) # 自作のマクロ。JavaScriptから呼ぶことができる @webiopi.macro def toggleLED(gpio): global STATE STATE = not STATE GPIO.digitalWrite(int(gpio), STATE) return STATE
$ vi index.html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no"> <title>1 - LEDの点灯制御</title> <script src="js/require.js"></script> <script> require(["/webiopi.js", "javascript.js"], function(){ webiopi().ready( initialize_webiopi ); }); </script> </head> <body> <p>下のボタンを押すごとに、LEDの点灯状態が変化します。</p> <div align="center"> <button id="gpio12" onClick="toggleLED(12)">LED</button> </div> </body> </html>
$ sudo vi /etc/webiopi/config[SCRIPTS] セクション
: myscript = /home/pi/webiopi/bb/01/script.py :[HTTP] セクション
: doc-root = /home/pi/webiopi/bb/01/ : welcome-file = index.html :
$ sudo systemctl start webiopi
http://192.168.xxx.xxx:8000/
※以上で251ページまで完了
タッチイベントの利用~DCモーターの速度制御
● 左記の「Aibot Project」のハードウエアを利用してWebIPOi プログラミングを検証する。
● AOUT1,AOUT2に接続したDCモーターをコントロールする。
● 出力ピンは、AIN1→GPIO 25, AIN2→GPIO 24 (本書オリジナルのまま)
● モータードライバをMODE0(PWM)でコントロールする。(ハードウェア設定)
● DRV8835 取扱説明書
● 別のドライバ基板()Pololu DRV8835 Dual Motor Driver Carrier)を使用する場合。接続ピンに若干の変更あり。
● ドライバ基板の説明
● モータードライバをMODE0(PWM)でコントロールする。(ハードウェア設定)
$ cd /home/pi/webiopi/bb/04 $ ls index.html javascript.js js script.py styles.css
$ vi script.py import webiopi import time # デバッグ出力を有効に webiopi.setDebug() # GPIOライブラリの取得 GPIO = webiopi.GPIO PWM1 = 25 PWM2 = 24 # WebIOPiの起動時に呼ばれる関数 def setup(): webiopi.debug("Script with macros - Setup") # GPIOのセットアップ GPIO.setFunction(PWM1, GPIO.PWM) GPIO.setFunction(PWM2, GPIO.PWM) # 初期のデューティー比を0%に(静止状態) GPIO.pwmWrite(PWM1, 0) GPIO.pwmWrite(PWM2, 0) # WebIOPiにより繰り返される関数 def loop(): webiopi.sleep(5) # WebIOPi終了時に呼ばれる関数 def destroy(): webiopi.debug("Script with macros - Destroy") # GPIO関数のリセット(入力にセットすることで行う) GPIO.setFunction(PWM1, GPIO.IN) GPIO.setFunction(PWM2, GPIO.IN) # 2つのPWMにデューティー比をまとめてセットするためのマクロ # commandIDは、iOSのSafariでPOSTがキャッシュされることへの対策 @webiopi.macro def pwm2Write(duty1, duty2, commandID): GPIO.pwmWrite(PWM1, float(duty1)) GPIO.pwmWrite(PWM2, float(duty2))
// タッチのサポート状況のチェック用変数 var support = { pointer: window.navigator.pointerEnabled, mspointer: window.navigator.msPointerEnabled, touch: 'ontouchstart' in window }; // タッチの場合わけ。pointer系:IE11以降、MSPointer系:IE10、touch系:android、iPhone、iPad var touchStart = support.pointer ? 'pointerdown' : support.mspointer ? 'MSPointerDown' : 'touchstart'; var touchMove = support.pointer ? 'pointermove' : support.mspointer ? 'MSPointerMove' : 'touchmove'; var touchEnd = support.pointer ? 'pointerup' : support.mspointer ? 'MSPointerUp' : 'touchend'; function initialize_webiopi(){ // webiopiの準備が終わってからstyles.cssを適用する applyCustomCss('styles.css'); // タッチエリアの設定 var touchArea = $("#touchArea")[0]; // タッチイベントのイベントリスナーの登録 touchArea.addEventListener(touchStart, touchEvent, false); touchArea.addEventListener(touchMove, touchEvent, false); touchArea.addEventListener(touchEnd, touchEndEvent, false); // クリックイベントのイベントリスナーの登録 touchArea.addEventListener("click", clickEvent, false); // GPIOの状態を監視しない webiopi().refreshGPIO(false); } // 前に送信したデューティー比を覚えておく var rate25Prev = 0; var rate24Prev = 0; // デューティー比がth (0.0~1.0) 以上変化した時のみ値を送信 var th = 0.1; // 命令送信ごとに増加するIDを作成(iOSのSafariでPOSTがキャッシュされることの対策) var commandID=0; // タッチ開始時とタッチ中のイベントリスナー function touchEvent(e){ e.preventDefault(); // タッチ中のイベントのみ捕捉(IE) if(support.pointer || support.mspointer){ if(e.pointerType != 'touch' && e.pointerType != 2){ return; } } var touch = (support.pointer || support.mspointer) ? e : e.touches[0]; var width = document.getElementById("touchArea").offsetWidth; if(touch.pageX<width/2){ var rate = 0.7*(width/2-touch.pageX)/(width/2); if(Math.abs(rate-rate24Prev)>th){ webiopi().callMacro("pwm2Write", [0, rate, commandID++]); rate25Prev = 0; rate24Prev = rate; } }else{ var rate = 0.7*(touch.pageX-width/2)/(width/2); if(Math.abs(rate-rate25Prev)>th){ webiopi().callMacro("pwm2Write", [rate, 0, commandID++]); rate25Prev = rate; rate24Prev = 0; } } } // タッチ終了時のイベントリスナー function touchEndEvent(e){ e.preventDefault(); webiopi().callMacro("pwm2Write", [0, 0, commandID++]); rate25Prev = 0; rate24Prev = 0; } // クリック時のイベントリスナー(主にPC用) function clickEvent(e){ e.preventDefault(); // タッチによるクリックは無視(IE) if(support.pointer || support.mspointer){ if(e.pointerType == 'touch' || e.pointerType == 2){ return; } } var width = document.getElementById("touchArea").offsetWidth; if(e.pageX>=2*width/5 && e.pageX<3*width/5){ webiopi().callMacro("pwm2Write", [0, 0, commandID++]); rate25Prev = 0; rate24Prev = 0; }else if(e.pageX<width/2){ var rate = 0.7*(width/2-e.pageX)/(width/2); webiopi().callMacro("pwm2Write", [0, rate, commandID++]); rate25Prev = 0; rate24Prev = rate; }else{ var rate = 0.7*(e.pageX-width/2)/(width/2); webiopi().callMacro("pwm2Write", [rate, 0, commandID++]); rate25Prev = rate; rate24Prev = 0; } } function applyCustomCss(custom_css){ var head = document.getElementsByTagName('head')[0]; var style = document.createElement('link'); style.rel = "stylesheet"; style.type = 'text/css'; style.href = custom_css; head.appendChild(style); }
$ sudo vi /etc/webiopi/config[SCRIPTS] セクション
: myscript = /home/pi/webiopi/bb/04/script.py :[HTTP] セクション
: doc-root = /home/pi/webiopi/bb/04/ : welcome-file = index.html :
$ sudo systemctl start webiopi
http://192.168.xxx.xxx:8000/
※以上で 9.6節 271ページまで完了
WebIOPiを用いたキャタピラ模型の製作
● 左記の「Aibot Project」のハードウエアを利用してWebIPOi プログラミングを検証する。
● AOUT1,AOUT2,BOUT1,BOUT2に接続したDCモーターをコントロールする。
● 出力ピンは、AIN1→GPIO 25, AIN2→GPIO 24, BIN1→GPIO 23, BIN2→GPIO 22 (本書オリジナルのまま)
● モータードライバをMODE0(PWM)でコントロールする。(ハードウェア設定)
● DRV8835 取扱説明書
● 別のドライバ基板()Pololu DRV8835 Dual Motor Driver Carrier)を使用する場合。接続ピンに若干の変更あり。
● ドライバ基板の説明
● モータードライバをMODE0(PWM)でコントロールする。(ハードウェア設定)
$ cd /home/pi/webiopi/bb/05 $ ls index.html javascript.js js script.py styles.css
$ vi script.py mport webiopi import time # デバッグ出力を有効に webiopi.setDebug() # GPIOライブラリの取得 GPIO = webiopi.GPIO PWM1 = 25 PWM2 = 24 PWM3 = 23 PWM4 = 22 # WebIOPiの起動時に呼ばれる関数 def setup(): webiopi.debug("Script with macros - Setup") # GPIOのセットアップ GPIO.setFunction(PWM1, GPIO.PWM) GPIO.setFunction(PWM2, GPIO.PWM) GPIO.setFunction(PWM3, GPIO.PWM) GPIO.setFunction(PWM4, GPIO.PWM) # 初期のデューティー比を0%に(静止状態) GPIO.pwmWrite(PWM1, 0) GPIO.pwmWrite(PWM2, 0) GPIO.pwmWrite(PWM3, 0) GPIO.pwmWrite(PWM4, 0) # WebIOPiにより繰り返される関数 def loop(): webiopi.sleep(5) # WebIOPi終了時に呼ばれる関数 def destroy(): webiopi.debug("Script with macros - Destroy") # GPIO関数のリセット(入力にセットすることで行う) GPIO.setFunction(PWM1, GPIO.IN) GPIO.setFunction(PWM2, GPIO.IN) GPIO.setFunction(PWM3, GPIO.IN) GPIO.setFunction(PWM4, GPIO.IN) # 4つのPWMにデューティー比をまとめてセットするためのマクロ # commandIDは、iOSのSafariでPOSTがキャッシュされることへの対策 @webiopi.macro def pwm4Write(duty1, duty2, duty3, duty4, commandID): GPIO.pwmWrite(PWM1, float(duty1)) GPIO.pwmWrite(PWM2, float(duty2)) GPIO.pwmWrite(PWM3, float(duty3)) GPIO.pwmWrite(PWM4, float(duty4)) - JavaScriptファイル~ $ vi javascript.js // タッチのサポート状況のチェック用変数 var support = { pointer: window.navigator.pointerEnabled, mspointer: window.navigator.msPointerEnabled, touch: 'ontouchstart' in window }; // タッチの場合わけ。pointer系:IE11以降、MSPointer系:IE10、touch系:android、iPhone、iPad var touchStart = support.pointer ? 'pointerdown' : support.mspointer ? 'MSPointerDown' : 'touchstart'; var touchMove = support.pointer ? 'pointermove' : support.mspointer ? 'MSPointerMove' : 'touchmove'; var touchEnd = support.pointer ? 'pointerup' : support.mspointer ? 'MSPointerUp' : 'touchend'; function initialize_webiopi(){ // webiopiの準備が終わってからstyles.cssを適用する applyCustomCss('styles.css'); resize_canvas(); // タッチエリアの設定 var touchArea = $("#touchArea")[0]; // タッチイベントのイベントリスナーの登録 touchArea.addEventListener(touchStart, touchEvent, false); touchArea.addEventListener(touchMove, touchEvent, false); touchArea.addEventListener(touchEnd, touchEndEvent, false); // クリックイベントのイベントリスナーの登録 touchArea.addEventListener("click", clickEvent, false); // Firefoxで画面の回転を検出 var mqOrientation = window.matchMedia("(orientation: portrait)"); mqOrientation.addListener(function() { resize_canvas(); }); // ウインドウサイズの変更を検出 window.addEventListener('resize', function (event) { resize_canvas(); }); // GPIOの状態を監視しない webiopi().refreshGPIO(false); } // iOSで画面の回転を検出 window.onorientationchange = function() { var iR = Math.abs( window.orientation ); if ( iR == 0 || iR == 90 ){ resize_canvas(); } } // 前に送信したデューティー比を覚えておく var rate25Prev = 0; var rate24Prev = 0; var rate23Prev = 0; var rate22Prev = 0; // デューティー比がth (0.0~1.0) 以上変化した時のみ値を送信 var th = 0.1; // モーターの最大速度 (0.0~1.0)。モーターを保護する意味で1.0にはしない方が良い var maxSpeed = 0.7; // 命令送信ごとに増加するIDを作成(iOSのSafariでPOSTがキャッシュされることの対策) var commandID = 0; var mTouchWidth; var mTouchHeight; var mTouchOffsetTop; var mTouchOffsetLeft; function touchEvent(e){ e.preventDefault(); // タッチ中のイベントのみ捕捉(IE) if(support.pointer || support.mspointer){ if(e.pointerType != 'touch' && e.pointerType != 2){ return; } } var touch = (support.pointer || support.mspointer) ? e : e.touches[0]; // エリア外のタッチを無視 if(touch.pageX < mTouchOffsetLeft || touch.pageX >= mTouchOffsetLeft + mTouchWidth || touch.pageY < mTouchOffsetTop || touch.pageY >= mTouchOffsetTop + mTouchHeight){ return; } if(touch.pageX < mTouchOffsetLeft + mTouchWidth/3){ // 左旋回 var rate = maxSpeed*(mTouchOffsetLeft + mTouchWidth/3-touch.pageX)/(mTouchWidth/3); // 前回送信時と値が大きく違うときのみ送信 if(Math.abs(rate-rate24Prev)>th || Math.abs(rate-rate23Prev)>th){ webiopi().callMacro("pwm4Write", [0, rate, rate, 0, commandID++]); rate25Prev = 0; rate24Prev = rate; rate23Prev = rate; rate22Prev = 0; } }else if(touch.pageX < mTouchOffsetLeft + 2*mTouchWidth/3){ // 前後移動 // 左右の車輪の速さの違いの補正 var modL = (1.2-0.8)*(touch.pageX - mTouchOffsetLeft - mTouchWidth/3)/(mTouchWidth/3) + 0.8; var modR = (0.8-1.2)*(touch.pageX - mTouchOffsetLeft - mTouchWidth/3)/(mTouchWidth/3) + 1.2; if(touch.pageY < mTouchOffsetTop + mTouchHeight/2){ var rate = maxSpeed*(mTouchHeight/2 + mTouchOffsetTop - touch.pageY)/(mTouchHeight/2); modL *= rate; modR *= rate; if(modL > 1.0){ modL = 1.0; } if(modR > 1.0){ modR = 1.0; } // 前回送信時と値が大きく違うときのみ送信 if(Math.abs(modL-rate25Prev)>th || Math.abs(modR-rate23Prev)>th){ webiopi().callMacro("pwm4Write", [modL, 0, modR, 0, commandID++]); rate25Prev = modL; rate24Prev = 0; rate23Prev = modR; rate22Prev = 0; } }else{ var rate = maxSpeed*(touch.pageY - mTouchOffsetTop - mTouchHeight/2)/(mTouchHeight/2); modL *= rate; modR *= rate; if(modL > 1.0){ modL = 1.0; } if(modR > 1.0){ modR = 1.0; } // 前回送信時と値が大きく違うときのみ送信 if(Math.abs(modL-rate24Prev)>th || Math.abs(modR-rate22Prev)>th){ webiopi().callMacro("pwm4Write", [0, modL, 0, modR, commandID++]); rate25Prev = 0; rate24Prev = modL; rate23Prev = 0; rate22Prev = modR; } } }else{ // 右旋回 var rate = maxSpeed*(touch.pageX - mTouchOffsetLeft - 2*mTouchWidth/3)/(mTouchWidth/3); // 前回送信時と値が大きく違うときのみ送信 if(Math.abs(rate-rate25Prev)>th || Math.abs(rate-rate22Prev)>th){ webiopi().callMacro("pwm4Write", [rate, 0, 0, rate, commandID++]); rate25Prev = rate; rate24Prev = 0; rate23Prev = 0; rate22Prev = rate; } } } // タッチ終了時のイベントリスナー function touchEndEvent(e){ e.preventDefault(); webiopi().callMacro("pwm4Write", [0, 0, 0, 0, commandID++]); rate25Prev = 0; rate24Prev = 0; rate23Prev = 0; rate22Prev = 0; } // クリック時のイベントリスナー(主にPC用) function clickEvent(e){ e.preventDefault(); // タッチによるクリックは無視(IE) if(support.pointer || support.mspointer){ if(e.pointerType == 'touch' || e.pointerType == 2){ return; } } // エリア外のクリックを無視 if(e.pageX < mTouchOffsetLeft || e.pageX >= mTouchOffsetLeft + mTouchWidth || e.pageY < mTouchOffsetTop || e.pageY >= mTouchOffsetTop + mTouchHeight){ return; } if(e.pageX < mTouchOffsetLeft + mTouchWidth/3){ // 左旋回 var rate = maxSpeed*(mTouchOffsetLeft + mTouchWidth/3 - e.pageX)/(mTouchWidth/3); webiopi().callMacro("pwm4Write", [0, rate, rate, 0, commandID++]); rate25Prev = 0; rate24Prev = rate; rate23Prev = rate; rate22Prev = 0; }else if(e.pageX < mTouchOffsetLeft + 2*mTouchWidth/3){ // 前後移動 // 左右の車輪の速さの違いの補正 var modL = (1.2-0.8)*(e.pageX - mTouchOffsetLeft - mTouchWidth/3)/(mTouchWidth/3) + 0.8; var modR = (0.8-1.2)*(e.pageX - mTouchOffsetLeft - mTouchWidth/3)/(mTouchWidth/3) + 1.2; if(e.pageY >= mTouchOffsetTop + 2*mTouchHeight/5 && e.pageY < mTouchOffsetTop + 3*mTouchHeight/5){ webiopi().callMacro("pwm4Write", [0, 0, 0, 0, commandID++]); rate25Prev = 0; rate24Prev = 0; rate23Prev = 0; rate22Prev = 0; }else if(e.pageY < mTouchOffsetTop + mTouchHeight/2){ var rate = maxSpeed*(mTouchOffsetTop + mTouchHeight/2 - e.pageY)/(mTouchHeight/2); modL *= rate; modR *= rate; if(modL > 1.0){ modL = 1.0; } if(modR > 1.0){ modR = 1.0; } webiopi().callMacro("pwm4Write", [modL, 0, modR, 0, commandID++]); rate25Prev = modL; rate24Prev = 0; rate23Prev = modR; rate22Prev = 0; }else{ var rate = maxSpeed*(e.pageY - mTouchOffsetTop - mTouchHeight/2)/(mTouchHeight/2); modL *= rate; modR *= rate; if(modL > 1.0){ modL = 1.0; } if(modR > 1.0){ modR = 1.0; } webiopi().callMacro("pwm4Write", [0, modL, 0, modR, commandID++]); rate25Prev = 0; rate24Prev = modL; rate23Prev = 0; rate22Prev = modR; } }else{ // 右旋回 var rate = maxSpeed*(e.pageX - mTouchOffsetLeft - 2*mTouchWidth/3)/(mTouchWidth/3); webiopi().callMacro("pwm4Write", [rate, 0, 0, rate, commandID++]); rate25Prev = rate; rate24Prev = 0; rate23Prev = 0; rate22Prev = rate; } } function resize_canvas(){ if($(window).width() < $(window).height()){ isPortrait = true; }else{ isPortrait = false; } if(isPortrait){ mTouchWidth = 0.95*$(window).width(); mTouchHeight = mTouchWidth; }else{ mTouchHeight = 0.95*$(window).height(); mTouchWidth = mTouchHeight; } mTouchOffsetLeft = ($(window).width()-mTouchWidth)/2; mTouchOffsetTop = $("#touchArea").offset().top; $( "#touchImage" ).width(mTouchWidth); $( "#touchImage" ).height(mTouchHeight); } function applyCustomCss(custom_css){ var head = document.getElementsByTagName('head')[0]; var style = document.createElement('link'); style.rel = "stylesheet"; style.type = 'text/css'; style.href = custom_css; head.appendChild(style); }
$ sudo vi /etc/webiopi/config[SCRIPTS] セクション
: myscript = /home/pi/webiopi/bb/05/script.py :[HTTP] セクション
: doc-root = /home/pi/webiopi/bb/05/ : welcome-file = index.html :
$ sudo systemctl start webiopi
http://192.168.xxx.xxx:8000/
※以上で 10章 285ページまで完了
$ sudo systemctl enable webiopi Synchronizing state of webiopi.service with SysV service script with /lib/systemd/systemd-sysv-install. Executing: /lib/systemd/systemd-sysv-install enable webiopi Created symlink /etc/systemd/system/multi-user.target.wants/webiopi.service → /etc/systemd/system/webiopi.service.再起動後、前節のブラウザで操作できれば設定完了。
サンプルファイルをコピーする。
$ cd ~/bluebacks/raspi1a-sample 04-01-led.py 05-03-sw-pd-event.py 06-01-print.py 07-01-temp.py 07-05-acc.py 08-01-led.py 08-05-servo-swpwm.py 10-02-stream.sh 04-02-led.py 05-04-sw-camera.py 06-02-led.py 07-02-LCD.py 07-06-acc.py 08-02-rgbled.py 08-06-2servos.py test.mp3 05-01-sw.py 05-05-sw-mp3.py 06-03-volume.py 07-03-LCD-temp.py 07-07-acc-gyro-mag.py 08-03-dcmotor.py 09-samples 05-02-sw-pd.py 05-06-sw-poweroff.py 06-04-spidev.py 07-04-temp.py 07-08-pressure-temp.py 08-04-servo.py 10-01-sw-poweroff.py $ cp 07-02-LCD.py ~/webiopi/bb pi@raspberrypi-mas:~/bluebacks/raspi1a-sample $ cd ~/webiopi/bb $ ls 01 04 05 07-02-LCD.py
● Raspberry Pi の「設定」アプリケーションでI2Cを有効にする。
$ i2cdetect -y 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- 3e -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- --
「3e」の表示があればOK.
$ vi 07-02-LCD.py # -*- coding: utf-8 -*- import smbus import sys from time import sleep def setup_st7032(): trials = 5 for i in range(trials): try: c_lower = (contrast & 0xf) c_upper = (contrast & 0x30)>>4 bus.write_i2c_block_data(address_st7032, register_setting, [0x38, 0x39, 0x14, 0x70|c_lower, 0x54|c_upper, 0x6c]) sleep(0.2) bus.write_i2c_block_data(address_st7032, register_setting, [0x38, 0x0d, 0x01]) sleep(0.001) break except IOError: if i==trials-1: sys.exit() def clear(): global position global line position = 0 line = 0 bus.write_byte_data(address_st7032, register_setting, 0x01) sleep(0.001) def newline(): global position global line if line == display_lines-1: clear() else: line += 1 position = chars_per_line*line bus.write_byte_data(address_st7032, register_setting, 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() bus.write_byte_data(address_st7032, register_display, byte_data) position += 1 def check_writable(c): if c >= 0x06 and c <= 0xff : return c else: return 0x20 # 空白文字 bus = smbus.SMBus(1) 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() if len(sys.argv)==1: # アルファベットと記号は「''」でくくってそのまま表示可能 write_string('Hello World') # カタカナや特殊記号は文字コードを一文字ずつ入力 # 以下は「ラズベリー パイ」と表示する例 #s = chr(0xd7)+chr(0xbd)+chr(0xde)+chr(0xcd)+chr(0xde)+chr(0xd8)+chr(0xb0)+' '+chr(0xca)+chr(0xdf)+chr(0xb2) #write_string(s) else: write_string(sys.argv[1])
$ python3 07-02-LCD.py $ python 07-02-LCD.py $ python 07-02-LCD.py 'test'
python2,python3 いずれでも実行可能。
$ sudo vi /etc/rc.local #!/bin/sh -e # # rc.local # # This script is executed at the end of each multiuser runlevel. # Make sure that the script will "exit 0" on success or any other # value on error. # # In order to enable or disable this script just change the execution # bits. # # By default this script does nothing. # Print the IP address _IP=$(hostname -I) || true if [ "$_IP" ]; then printf "My IP address is %s\n" "$_IP" fi python3 /home/pi/webiopi/bb/07-02-LCD.py $_IP exit 0
● ネットワーク接続後に実行するように設定用アプリケーションで「システム」タブの「ネットワークブート」の項目の「ネットワークを待つ」にチェックを入れる。
● 再起動後IPアドレスを表示することを確認する。
$ cd ~/bluebacks/raspi1a-sample $ cp 10-01-sw-poweroff.py ~/webiopi/bb pi@raspberrypi-mas:~/bluebacks/raspi1a-sample $ cd ~/webiopi/bb $ ls 01 04 05 07-02-LCD.py 10-01-sw-poweroff.py
$ vi 10-01-sw-poweroff.py import RPi.GPIO as GPIO from time import sleep import subprocess # 2020/07/16 GPIO 20 ext-pulldown SWITCH=20 state = 0 GPIO.setmode(GPIO.BCM) GPIO.setup(SWITCH, GPIO.IN) try: while True: if GPIO.input(SWITCH)==GPIO.HIGH: if state == 2: state = 0 args = ['sudo', 'poweroff'] subprocess.Popen(args) else: state += 1 else: state = 0 sleep(0.5) except KeyboardInterrupt: pass GPIO.cleanup()
$ sudo vi /etc/rc.local #!/bin/sh -e # # rc.local # # This script is executed at the end of each multiuser runlevel. # Make sure that the script will "exit 0" on success or any other # value on error. # # In order to enable or disable this script just change the execution # bits. # # By default this script does nothing. # Print the IP address _IP=$(hostname -I) || true if [ "$_IP" ]; then printf "My IP address is %s\n" "$_IP" fi python3 /home/pi/webiopi/bb/07-02-LCD.py $_IP python3 /home/pi/webiopi/bb/10-01-sw-poweroff.py & exit 0
キャタピラ式模型にカメラを搭載しよう
● GPIO出力ピンは、AIN1→GPIO 25, AIN2→GPIO 24, BIN1→GPIO 23, BIN2→GPIO 22 (本書オリジナルのまま)
● Raspberry Pi に搭載されたカメラを使用し、ロボット前面の様子を見ながらコントロールできるようにする。
● DRV8835 取扱説明書
● 別のドライバ基板()Pololu DRV8835 Dual Motor Driver Carrier)を使用する場合。接続ピンに若干の変更あり。
● ドライバ基板の説明
● モータードライバをMODE0(PWM)でコントロールする。(ハードウェア設定)
$ cp -r ~/bluebacks/ ~/webiopi/bb/raspi1a-sample/09-samples/06 ~/webiopi/bb $ cd /home/pi/webiopi/bb/06 $ ls index.html javascript.js js script.py styles.css
$ vi script.py import webiopi import time # デバッグ出力を有効に webiopi.setDebug() # GPIOライブラリの取得 GPIO = webiopi.GPIO PWM1 = 25 PWM2 = 24 PWM3 = 23 PWM4 = 22 # WebIOPiの起動時に呼ばれる関数 def setup(): webiopi.debug("Script with macros - Setup") # GPIOのセットアップ GPIO.setFunction(PWM1, GPIO.PWM) GPIO.setFunction(PWM2, GPIO.PWM) GPIO.setFunction(PWM3, GPIO.PWM) GPIO.setFunction(PWM4, GPIO.PWM) # 初期のデューティー比を0%に(静止状態) GPIO.pwmWrite(PWM1, 0) GPIO.pwmWrite(PWM2, 0) GPIO.pwmWrite(PWM3, 0) GPIO.pwmWrite(PWM4, 0) # WebIOPiにより繰り返される関数 def loop(): webiopi.sleep(5) # WebIOPi終了時に呼ばれる関数 def destroy(): webiopi.debug("Script with macros - Destroy") # GPIO関数のリセット(入力にセットすることで行う) GPIO.setFunction(PWM1, GPIO.IN) GPIO.setFunction(PWM2, GPIO.IN) GPIO.setFunction(PWM3, GPIO.IN) GPIO.setFunction(PWM4, GPIO.IN) # 4つのPWMにデューティー比をまとめてセットするためのマクロ # commandIDは、iOSのSafariでPOSTがキャッシュされることへの対策 @webiopi.macro def pwm4Write(duty1, duty2, duty3, duty4, commandID): GPIO.pwmWrite(PWM1, float(duty1)) GPIO.pwmWrite(PWM2, float(duty2)) GPIO.pwmWrite(PWM3, float(duty3)) GPIO.pwmWrite(PWM4, float(duty4))
$ vi javascript.js // タッチのサポート状況のチェック用変数 var support = { pointer: window.navigator.pointerEnabled, mspointer: window.navigator.msPointerEnabled, touch: 'ontouchstart' in window }; // タッチの場合わけ。pointer系:IE11以降、MSPointer系:IE10、touch系:android、iPhone、iPad var touchStart = support.pointer ? 'pointerdown' : support.mspointer ? 'MSPointerDown' : 'touchstart'; var touchMove = support.pointer ? 'pointermove' : support.mspointer ? 'MSPointerMove' : 'touchmove'; var touchEnd = support.pointer ? 'pointerup' : support.mspointer ? 'MSPointerUp' : 'touchend'; function initialize_webiopi(){ // webiopiの準備が終わってからstyles.cssを適用する applyCustomCss('styles.css'); mCanvas = document.getElementById("canvas"); mCtx = mCanvas.getContext('2d'); resize_canvas(); // タッチエリアの設定 var touchArea = $("#touchArea")[0]; // タッチイベントのイベントリスナーの登録 touchArea.addEventListener(touchStart, touchEvent, false); touchArea.addEventListener(touchMove, touchEvent, false); touchArea.addEventListener(touchEnd, touchEndEvent, false); // クリックイベントのイベントリスナーの登録 touchArea.addEventListener("click", clickEvent, false); // Firefoxで画面の回転を検出 var mqOrientation = window.matchMedia("(orientation: portrait)"); mqOrientation.addListener(function() { resize_canvas(); }); // ウインドウサイズの変更を検出 window.addEventListener('resize', function (event) { resize_canvas(); }); // GPIOの状態を監視しない webiopi().refreshGPIO(false); } // iOSで画面の回転を検出 window.onorientationchange = function() { var iR = Math.abs( window.orientation ); if ( iR == 0 || iR == 90 ){ resize_canvas(); } } // 前に送信したデューティー比を覚えておく var rate25Prev = 0; var rate24Prev = 0; var rate23Prev = 0; var rate22Prev = 0; // デューティー比がth (0.0~1.0) 以上変化した時のみ値を送信 var th = 0.1; // モーターの最大速度 (0.0~1.0)。モーターを保護する意味で1.0にはしない方が良い var maxSpeed = 0.7; // 命令送信ごとに増加するIDを作成(iOSのSafariでPOSTがキャッシュされることの対策) var commandID = 0; var mCount = 0; var mCanvas; var mCtx; var mImg1; var mImg2; var mImgArrow; var mWidth = 640; var mHeight = 480; var host = location.host; var hostname = host.split(":")[0]; var port= 9000; var URL1 = 'http://' + hostname + ':' + port + '/?action=snapshot'; //var URL2 = 'http://' + hostname + ':8000/bb/06/img/CrawlerControllerTrans.png'; var URL2 = 'http://' + hostname + ':8000/img/CrawlerControllerTrans.png'; var mTouchWidth; var mTouchHeight; var mTouchOffsetTop; var mTouchOffsetLeft; function imageSetup(){ mImg1 = new Image(); mImg2 = new Image(); mImgArrow = new Image(); mImg1.src = URL1 +'&'+(mCount++); mImgArrow.src = URL2; mImg1.onload = function() { mImg2.src = URL1 + '&' + (mCount++); mCtx.drawImage(mImg1, 0, 0, mWidth, mHeight); mCtx.drawImage(mImgArrow, 0, 0, mWidth, mHeight); }; mImg2.onload = function() { mImg1.src =URL1 + '&' + (mCount++); mCtx.drawImage(mImg2, 0, 0, mWidth, mHeight); mCtx.drawImage(mImgArrow, 0, 0, mWidth, mHeight); }; } function touchEvent(e){ e.preventDefault(); // タッチ中のイベントのみ捕捉(IE) if(support.pointer || support.mspointer){ if(e.pointerType != 'touch' && e.pointerType != 2){ return; } } var touch = (support.pointer || support.mspointer) ? e : e.touches[0]; // エリア外のタッチを無視 if(touch.pageX < mTouchOffsetLeft || touch.pageX >= mTouchOffsetLeft + mTouchWidth || touch.pageY < mTouchOffsetTop || touch.pageY >= mTouchOffsetTop + mTouchHeight){ return; } if(touch.pageX < mTouchOffsetLeft + mTouchWidth/3){ // 左旋回 var rate = maxSpeed*(mTouchOffsetLeft + mTouchWidth/3-touch.pageX)/(mTouchWidth/3); // 前回送信時と値が大きく違うときのみ送信 if(Math.abs(rate-rate24Prev)>th || Math.abs(rate-rate23Prev)>th){ webiopi().callMacro("pwm4Write", [0, rate, rate, 0, commandID++]); rate25Prev = 0; rate24Prev = rate; rate23Prev = rate; rate22Prev = 0; } }else if(touch.pageX < mTouchOffsetLeft + 2*mTouchWidth/3){ // 前後移動 // 左右の車輪の速さの違いの補正 var modL = (1.2-0.8)*(touch.pageX - mTouchOffsetLeft - mTouchWidth/3)/(mTouchWidth/3) + 0.8; var modR = (0.8-1.2)*(touch.pageX - mTouchOffsetLeft - mTouchWidth/3)/(mTouchWidth/3) + 1.2; if(touch.pageY < mTouchOffsetTop + mTouchHeight/2){ var rate = maxSpeed*(mTouchHeight/2 + mTouchOffsetTop - touch.pageY)/(mTouchHeight/2); modL *= rate; modR *= rate; if(modL > 1.0){ modL = 1.0; } if(modR > 1.0){ modR = 1.0; } // 前回送信時と値が大きく違うときのみ送信 if(Math.abs(modL-rate25Prev)>th || Math.abs(modR-rate23Prev)>th){ webiopi().callMacro("pwm4Write", [modL, 0, modR, 0, commandID++]); rate25Prev = modL; rate24Prev = 0; rate23Prev = modR; rate22Prev = 0; } }else{ var rate = maxSpeed*(touch.pageY - mTouchOffsetTop - mTouchHeight/2)/(mTouchHeight/2); modL *= rate; modR *= rate; if(modL > 1.0){ modL = 1.0; } if(modR > 1.0){ modR = 1.0; } // 前回送信時と値が大きく違うときのみ送信 if(Math.abs(modL-rate24Prev)>th || Math.abs(modR-rate22Prev)>th){ webiopi().callMacro("pwm4Write", [0, modL, 0, modR, commandID++]); rate25Prev = 0; rate24Prev = modL; rate23Prev = 0; rate22Prev = modR; } } }else{ // 右旋回 var rate = maxSpeed*(touch.pageX - mTouchOffsetLeft - 2*mTouchWidth/3)/(mTouchWidth/3); // 前回送信時と値が大きく違うときのみ送信 if(Math.abs(rate-rate25Prev)>th || Math.abs(rate-rate22Prev)>th){ webiopi().callMacro("pwm4Write", [rate, 0, 0, rate, commandID++]); rate25Prev = rate; rate24Prev = 0; rate23Prev = 0; rate22Prev = rate; } } } // タッチ終了時のイベントリスナー function touchEndEvent(e){ e.preventDefault(); webiopi().callMacro("pwm4Write", [0, 0, 0, 0, commandID++]); rate25Prev = 0; rate24Prev = 0; rate23Prev = 0; rate22Prev = 0; } // クリック時のイベントリスナー(主にPC用) function clickEvent(e){ e.preventDefault(); // タッチによるクリックは無視(IE) if(support.pointer || support.mspointer){ if(e.pointerType == 'touch' || e.pointerType == 2){ return; } } // エリア外のクリックを無視 if(e.pageX < mTouchOffsetLeft || e.pageX >= mTouchOffsetLeft + mTouchWidth || e.pageY < mTouchOffsetTop || e.pageY >= mTouchOffsetTop + mTouchHeight){ return; } if(e.pageX < mTouchOffsetLeft + mTouchWidth/3){ // 左旋回 var rate = maxSpeed*(mTouchOffsetLeft + mTouchWidth/3 - e.pageX)/(mTouchWidth/3); webiopi().callMacro("pwm4Write", [0, rate, rate, 0, commandID++]); rate25Prev = 0; rate24Prev = rate; rate23Prev = rate; rate22Prev = 0; }else if(e.pageX < mTouchOffsetLeft + 2*mTouchWidth/3){ // 前後移動 // 左右の車輪の速さの違いの補正 var modL = (1.2-0.8)*(e.pageX - mTouchOffsetLeft - mTouchWidth/3)/(mTouchWidth/3) + 0.8; var modR = (0.8-1.2)*(e.pageX - mTouchOffsetLeft - mTouchWidth/3)/(mTouchWidth/3) + 1.2; if(e.pageY >= mTouchOffsetTop + 2*mTouchHeight/5 && e.pageY < mTouchOffsetTop + 3*mTouchHeight/5){ webiopi().callMacro("pwm4Write", [0, 0, 0, 0, commandID++]); rate25Prev = 0; rate24Prev = 0; rate23Prev = 0; rate22Prev = 0; }else if(e.pageY < mTouchOffsetTop + mTouchHeight/2){ var rate = maxSpeed*(mTouchOffsetTop + mTouchHeight/2 - e.pageY)/(mTouchHeight/2); modL *= rate; modR *= rate; if(modL > 1.0){ modL = 1.0; } if(modR > 1.0){ modR = 1.0; } webiopi().callMacro("pwm4Write", [modL, 0, modR, 0, commandID++]); rate25Prev = modL; rate24Prev = 0; rate23Prev = modR; rate22Prev = 0; }else{ var rate = maxSpeed*(e.pageY - mTouchOffsetTop - mTouchHeight/2)/(mTouchHeight/2); modL *= rate; modR *= rate; if(modL > 1.0){ modL = 1.0; } if(modR > 1.0){ modR = 1.0; } webiopi().callMacro("pwm4Write", [0, modL, 0, modR, commandID++]); rate25Prev = 0; rate24Prev = modL; rate23Prev = 0; rate22Prev = modR; } }else{ // 右旋回 var rate = maxSpeed*(e.pageX - mTouchOffsetLeft - 2*mTouchWidth/3)/(mTouchWidth/3); webiopi().callMacro("pwm4Write", [rate, 0, 0, rate, commandID++]); rate25Prev = rate; rate24Prev = 0; rate23Prev = 0; rate22Prev = rate; } } function resize_canvas(){ if($(window).width() < 4*$(window).height()/3){ isPortrait = true; }else{ isPortrait = false; } if(isPortrait){ mTouchWidth = 0.95*$(window).width(); mTouchHeight = 3*mTouchWidth/4; }else{ mTouchHeight = 0.95*$(window).height(); mTouchWidth = 4*mTouchHeight/3; } mTouchOffsetLeft = ($(window).width()-mTouchWidth)/2; mTouchOffsetTop = $("#touchArea").offset().top; mWidth = mTouchWidth; mHeight = mTouchHeight; mCanvas.width = mWidth; mCanvas.height = mHeight imageSetup(); } function applyCustomCss(custom_css){ var head = document.getElementsByTagName('head')[0]; var style = document.createElement('link'); style.rel = "stylesheet"; style.type = 'text/css'; style.href = custom_css; head.appendChild(style); }
$ sudo vi /etc/webiopi/config[SCRIPTS] セクション
: myscript = /home/pi/webiopi/bb/06/script.py :[HTTP] セクション
: doc-root = /home/pi/webiopi/bb/06/ : welcome-file = index.html :
$ sudo systemctl start webiopi
http://192.168.xxx.xxx:8000/
※以上で 10章 288ページまで本書の演習は完了
$ vi ~/webiopi/sw-poweroff.py import RPi.GPIO as GPIO from time import sleep import subprocess # 2020/07/16 GPIO 20 ext-pulldown SWITCH=20 state = 0 GPIO.setmode(GPIO.BCM) GPIO.setup(SWITCH, GPIO.IN) try: while True: if GPIO.input(SWITCH)==GPIO.HIGH: if state == 2: state = 0 cmd = "python /home/pi/webiopi/i2c-lcd.py Shutdown" subprocess.call(cmd.split()) args = ['sudo', 'poweroff'] subprocess.Popen(args) else: state += 1 else: state = 0 sleep(0.5) except KeyboardInterrupt: pass GPIO.cleanup()
$ vi ~/webiopi/i2c-lcd.py # -*- coding: utf-8 -*- import smbus import sys from time import sleep def setup_st7032(): trials = 5 for i in range(trials): try: c_lower = (contrast & 0xf) c_upper = (contrast & 0x30)>>4 bus.write_i2c_block_data(address_st7032, register_setting, [0x38, 0x39, 0x14, 0x70|c_lower, 0x54|c_upper, 0x6c]) sleep(0.2) bus.write_i2c_block_data(address_st7032, register_setting, [0x38, 0x0d, 0x01]) sleep(0.001) break except IOError: if i==trials-1: sys.exit() def clear(): global position global line position = 0 line = 0 bus.write_byte_data(address_st7032, register_setting, 0x01) sleep(0.001) def newline(): global position global line if line == display_lines-1: clear() else: line += 1 position = chars_per_line*line bus.write_byte_data(address_st7032, register_setting, 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() bus.write_byte_data(address_st7032, register_display, byte_data) position += 1 def check_writable(c): if c >= 0x06 and c <= 0xff : return c else: return 0x20 # 空白文字 bus = smbus.SMBus(1) 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() if len(sys.argv)==1: # アルファベットと記号は「''」でくくってそのまま表示可能 write_string('Hello World') # カタカナや特殊記号は文字コードを一文字ずつ入力 # 以下は「ラズベリー パイ」と表示する例 #s = chr(0xd7)+chr(0xbd)+chr(0xde)+chr(0xcd)+chr(0xde)+chr(0xd8)+chr(0xb0)+' '+chr(0xca)+chr(0xdf)+chr(0xb2) #write_string(s) else: write_string(sys.argv[1])
$ vi ~/webiopi/streamer-start.py #!/bin/bash if pgrep mjpg_streamer > /dev/null then echo "mjpg_streamer already running" else LD_LIBRARY_PATH=/opt/mjpg-streamer/ /opt/mjpg-streamer/mjpg_streamer -i "input_raspicam.so -fps 15 -q 50 -x 640 -y 480" -o "output_http.so -p 9000 -w /opt/mjpg-streamer/www" > /dev/null 2>&1& echo "mjpg_streamer started" fi
$ sudo vi /etc/rc.local #!/bin/sh -e # # rc.local # # This script is executed at the end of each multiuser runlevel. # Make sure that the script will "exit 0" on success or any other # value on error. # # In order to enable or disable this script just change the execution # bits. # # By default this script does nothing. sleep 10 # Print the IP address _IP=$(hostname -I) || true if [ "$_IP" ]; then printf "My IP address is %s\n" "$_IP" fi python3 /home/pi/webiopi/i2c-lcd.py $_IP python3 /home/pi/webiopi/sw-poweroff.py & sh /home/pi/webiopi/streamer-start.sh exit 0