前回、WEBサーバーとpca9685制御のmicropythonプログラムを合体させました↓
今回は、M5stackの画面に表情を付けるプログラムを組んでいきたいと思います。
サムネイルにあるとおり質素なものですので、ずっと同じ表情だと少しあれなんでサーボモーターが動作した時に口をぱくぱくさせる感じに仕上げます。
それでは、あらかじめロボット(サーボモーター)、pca9685、m5stack、電池などのハードウェアをPCとつなげておきます。
プログラムは、前回作成したserver.pyに追加していきます。
server.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
import network import machine import time import socket import re from machine import Pin,I2C import servo import pca9685 from m5stack import lcd ESSID = 'M5stack' PASSWORD = '11223344' IP = '192.168.5.1' i2c = I2C(0, scl=Pin(22), sda=Pin(21)) sev = servo.Servos(i2c) sv ="p" posi = {"lx":90, "ly":90, "lr":10, "rx":230, "ry":90, "rr":10, "ux":110, "uy":170} lcd.clear(lcd.BLACK) def nomale_lcd(): lcd.circle(posi["lx"], posi["ly"], posi["lr"], lcd.WHITE, lcd.WHITE) lcd.circle(posi["rx"], posi["ry"], posi["rr"], lcd.WHITE, lcd.WHITE) lcd.rect(posi["ux"], posi["uy"], 100, 5, lcd.WHITE, lcd.WHITE) #サーボモーター制御 def servoset(): global sv if sv == "st": lcd.rect(posi["ux"], posi["uy"], 100, 30, lcd.WHITE, lcd.BLACK) sev.position(0, 97) time.sleep_ms(1000) sev.position(1, 90) time.sleep_ms(1000) sev.position(2, 100) time.sleep_ms(1000) sev.position(3, 90) time.sleep_ms(1000) elif sv == "fw": lcd.rect(posi["ux"], posi["uy"], 100, 30, lcd.WHITE, lcd.BLACK) sev.position(3, 70) sev.position(1, 70) time.sleep_ms(500) sev.position(0, 77) sev.position(2, 80) time.sleep_ms(500) sev.position(3, 90) sev.position(1, 90) time.sleep_ms(500) sev.position(3, 110) sev.position(1, 110) time.sleep_ms(500) sev.position(0,117) sev.position(2, 120) time.sleep_ms(500) sev.position(3, 90) sev.position(1, 90) time.sleep_ms(500) elif sv == "lt": lcd.rect(posi["ux"], posi["uy"], 100, 30, lcd.WHITE, lcd.BLACK) sev.position(3, 40) sev.position(1, 60) time.sleep_ms(500) sev.position(0, 117) time.sleep_ms(500) sev.position(3, 90) time.sleep_ms(500) sev.position(1, 90) time.sleep_ms(500) sev.position(0, 97) time.sleep_ms(500) elif sv == "bk": lcd.rect(posi["ux"], posi["uy"], 100, 30, lcd.WHITE, lcd.BLACK) sev.position(3, 110) sev.position(1, 110) time.sleep_ms(500) sev.position(0, 77) sev.position(2, 80) time.sleep_ms(500) sev.position(3, 90) sev.position(1, 90) time.sleep_ms(500) sev.position(3, 70) sev.position(1, 70) time.sleep_ms(500) sev.position(0,117) sev.position(2, 120) time.sleep_ms(500) sev.position(3, 90) sev.position(1, 90) time.sleep_ms(500) elif sv == "rt": lcd.rect(posi["ux"], posi["uy"], 100, 30, lcd.WHITE, lcd.BLACK) sev.position(3, 120) sev.position(1, 140) time.sleep_ms(500) sev.position(2, 80) time.sleep_ms(500) sev.position(1, 90) time.sleep_ms(500) sev.position(3, 90) time.sleep_ms(500) sev.position(2, 100) time.sleep_ms(500) #html def home_page(): html = """<html lang="ja"> <head> <meta charset="utf-8"> <title>サーボテスト</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> * { margin: 0px; padding: 0px; } body { max-width: 600px; font-size: 25px; width: 100%; } main { height: 40vh; background: skyblue; } ul { display: block; height: 40vh; list-style: none; padding-top: 10px; } .bc { display: flex; } .a, .bc, .d { height: 12vh; } li { width: 100px ; height: 90% ; margin-left: auto; margin-right: auto; background: yellow; } .b { margin-left: auto; } .c { margin-right: auto; } ul li { text-align: center; } .a li, .b li, .c li, .d li { border: solid 1px; } .n li { background: yellow; } a:active { color: #ff2020; } .a li, .b li, .c li, .d li, .n li { border: solid 1px; } .n { margin-right: 3px; margin-left: 3px; } </style> </head> <body> <main> <ul> <div class="a"> <a href="/svm_aa"> <li id="forward">前進</li> </a></div> <div class="bc"> <div class="b"> <a href="/svm_bb"> <li id="left">左旋回</li> </a></div> <div class="n"> <a href="/svm_ee"> <li id="set">SET</li> </a></div> <div class="c"> <a href="/svm_cc"> <li id="right">右旋回</li> </a></div> </div> <div class="d"> <a href="/svm_dd"> <li id="backward">後退</li> </a></div> </ul> </main> </body> </html>""" return html #テキストとして値を返す nomale_lcd() #WIFI APの立ち上げ ap = network.WLAN(network.AP_IF) ap.config(essid=ESSID, authmode=3, password=PASSWORD) ap.ifconfig((IP,'255.255.255.0',IP,'8.8.8.8')) ap.active(True) print("AP OK") #WEBサーバーの立ち上げ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('', 80)) s.listen(5) #クライアント接続 while True: conn, addr = s.accept() request = str(conn.recv(1024)) m = re.search(r'svm_(\D\D\s)', request) #print(request) #ボタン操作反映 if m != None : svm = m.group(0) if svm == 'svm_aa ': sv = "fw" elif svm == 'svm_bb ': sv = "lt" elif svm == 'svm_cc ': sv = "rt" elif svm == 'svm_dd ': sv = "bk" elif svm == 'svm_ee ': sv = "st" servoset() #サーボ関数呼び出し sv ="p" lcd.clear(lcd.BLACK) nomale_lcd() response = home_page() #html text格納 conn.send(response) #クライアントへhtmlデータ送信 conn.close() #scket破棄 print("Data Received") |
from m5stack import lcd のライブラリで、表示関係の関数が使えるようになります。下記にドキュメントへのリンクを張っておくので参考にしてください。↓
https://github.com/m5stack/M5Stack_MicroPython/blob/master/README.md#lcd
posiの変数に、両目と口の表示位置を格納しています。M5stackは320×240なので、xとy(横軸と縦軸)で座標を決めておきます。今回lは左目、rは右目、uは口の座標としています。
lcd.clearのメソッドは、引数に背景色を選択できます。
続いて表情を表示させる関数nomale_lcdを定義しています。lcd.circle は、指定した半径の円状のグラフィックを表示させるメソッドになります。引数の第一、第二に座標、第三に半径、第四が枠の色で第五が塗りつぶしの色になります。lcd.recth は、四角のグラフィックを表示できます。座標を決めて、第三引数にwhidth,第四にheightを指定します。
クライアントがWEBサーバーに接続後のwhileに入る前にnomale_lcdを呼び出して、M5stackの電源投入後に表情が表示されるようにしています。
あとはサーボモーターの関数が実効された場合、口を開ける動作にしたいので、前後左右それぞれのプログラムの先頭にlcd.recthを入れています。
サーボの動作終了後に口が開きっぱなしになるので、一旦クリアして、再度デフォルトの状態のnomale.lcdを呼び出しています。
こんな感じになります。↓
次回はこのプログラムをmain.pyに変更し、ハードウェアを全部脳みそに詰め込んで、表示部だけを外側から見えるようにして完成に持っていきたいと思います。