masimaro blog

menu

home > 電子工作:M5Stack > M5stackで2足歩行ロボットの作成⑥「micropythonで液晶画面に表情をつけてサーボモーターと連動させる」

M5stackで2足歩行ロボットの作成⑥「micropythonで液晶画面に表情をつけてサーボモーターと連動させる」


M5Stack
2021-07-23


今回は、M5stackの画面に表情を付けるプログラムを組んでいきたいと思います。

サムネイルにあるとおり質素なものですので、ずっと同じ表情だと少しあれなんでサーボモーターが動作した時に口をぱくぱくさせる感じに仕上げます。

それでは、あらかじめロボット(サーボモーター)、pca9685、m5stack、電池などのハードウェアをPCとつなげておきます。

プログラムは、前回作成したserver.pyに追加していきます。

server.py

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に変更し、ハードウェアを全部脳みそに詰め込んで、表示部だけを外側から見えるようにして完成に持っていきたいと思います。

コメントを残す

お名前

コメント



コメント一覧

コメントはまだありません
MENU