UMEHOSHI ITA TOP PAGE    COMPUTER SHIEN LAB

esp32を付けた基板をUDPで遠隔操作

ロボットの構造

『ABS樹脂ケース(蝶番式・大) 112−TM−ABS(P-00278)』 に穴などの加工をして、
それに、 esp32を付けた基板esp32にMicroPython のファームウェアを書き込みしたものと、
「cheero (チーロ) CHE-061-IOT cheero Canvas IoT機器対応モバイルバッテリー【3200mAh】ホワイト」 を収納しています。

次のようにギア付きモータを両面テープでつけています。

真上からのイメージです。

この構成では、「CHE-061-IOT」のモバイルバッテリーを使っていますが、 「micro B」のUSBから供給されるこの5vを、モータ供給にも使うようにしています。
デフォルトの基板では、ホトカプラでモータ電源ラインは絶縁されているので、 モータ用のラインに供給できるように接続する配線が必要です。
その接続は、次の青マルのジャンパー用スルーホール(JMP3とJMP4)を半田付けします。

この配線により、「micro B」からのモータ用ラインにも供給されるので、このジャンパー配線を施した後、 モータを付けて動作させるコードを実行するとPCのUSB電源供給に負荷がかかります。
この変更基板の「micro B」とPCなどを接続した状態で、モータ制御は絶対に避けてください。
このページ最後のように、PCなどの接続を外して「micro B」にモバイルバッテリーを接続してWifiで制御できる状態になってから モータを動かすことを想定した変更基板です。
なお、PCと[UMEHOSHI ITA]の「USB Type-A」を接続した後で、「micro B」にモバイルバッテリーを接続する使い方があります。
この場合、PCとモバイルバッテリーの両方から電源が供給されるのですが、「USB Type-A」の方には このページで示したようにポリスイッチを付けることによって、PCなどの電源共有側に 対する保護が働きます。
PCとUSB接続状態で、モータ制御を試す場合は、このような構成で行えばよいでしょう。
(当方でこのような接続の動作確認をしていますが、PC側のUSBが壊れない保証はできません)

esp32に「アクセスポイント」と「UDPで[UMEHOSHI ITA]を制御」のプログラムを埋め込む

このページで示しているように、ampyを使って ESP32にファイルを転送して動作させることができている状態まで 進んでいる前提で説明します。
PCと[UMEHOSHI ITA]基板をUSBで繋ぐことになりますが、ESP32を追加した基板を使うには、大きなUSB電源供給能力が必要です。
このページの最後まで進むと、PCとUSBで接続する必要はなくなりますが、それまでは PCやスマフォと直接USB接続しないで、別電源付きのUSBハブを経由して接続した方が良いでしょう。

このマシンの動作は、UART1コマンドモードで動作させます。
UART1コマンドモードは、UARTで「UME専用Hexコマンド」を受け付けて実行る状態です。
このモードを利用すると、 ESP32内のMicroPythonのプログラムで、print()命令を使うと、 その出力は、[UMEHOSHI ITA]のUART1へ送られて、それが命令であれば、それで[UMEHOSHI ITA]を動かすことができます。
ですから、MicroPythonのプログラムで[UMEHOSHI ITA]の命令に関係ないprint()の出力はできなくなります。

そこで、このリンクで作成した、アクセスポイント用のプログラムのsetap.pyで 余計なprint()を除いて次のように変更します。

import time # setap.py
time.sleep(5)
import network
ap = network.WLAN(network.AP_IF) # create access-point interface
ap.config(essid='ESP-AP') # set the ESSID of the access point
ap.config(max_clients=10) # set how many clients can connect to the network
ap.ifconfig(('192.168.222.1', '255.255.255.0', '192.168.222.1', '8.8.8.8'))
# set (ip address, subnet mast, default gateway, dns ip address)
ap.active(True)         # activate the interface

このsetap.pyのファイルを、次の操作で、[UMEHOSHI ITA]へ転送します。
(pythonでampyが使える環境で行います。)

ampy -p COM8 -b 11500 put setap.py

そして、同時に次のようなboot.pyを、ampy -p COM8 -b 11500 put boot.pyで転送します。
このboot.pyが起動用スクリプトになっており、以下に内容を示します

# This file is executed on every boot (including wake-boot from deepsleep)
#import esp
#esp.osdebug(None)
#import webrepl
#webrepl.start()
import setap
#import udpsvrUme
#import udpsvrEsp

これは、元々あった記述にsetap.pyを起動時に実行せるため、import setapを追加しています。
これで、自動的にアクセスポイントとして起動し始めて接続できるようになって、 「192.168.222.1」のマシンとしても、使えるようになっています。
(PCなどで、このssidの'ESP-AP'のアクセスポイントに接続できることが確認できます。)
上記の最後の2行がはコメントにしてありますが、ESP32で動作予定のUSB受信プログラム(udpsvrUme.py)と [UMEHOSHI ITA]に転送するプログラム(udpsvrEsp.py)です。

UDPの受信プログラムでは、受信文字列に応じて、[UMEHOSHI ITA]の命令をprintで出力するように作ります。
この時、[UMEHOSHI ITA]はUART1コマンドモードにしておく必要があります。
このudpsvrEsp.py内のprintで[UMEHOSHI ITA]のエントリポイントを送ることで制御をしますが、 その[UMEHOSHI ITA]側のプログラムはあらかじめ転送する必要があり、そのプログラムがudpsvrUme.pyです。
これに埋め込む「ウメ・エディットプログラム」のソースコードは、 です。

このコードは、スマフォでの制御用に作ったコード(app_pwm.c)を 少し変更したものです。
変更点は次の4点です。
●左右のモータを交互に切り替えないように、「int oneCN6 =0」しています。
●一定時間経過したらモータと止めている箇所(timer2関数内のstop命令)をコメント化して、自動停止をしない
●_send_xxxのメソッドをコメントにします。(この出力はUSBですが、接続しないのでUSB出力を無しにします。)
●beep命令を追加し、PWMデューティー比のアップ/ダウンで変更可能範囲を超えてできない場合に音を出しました。
これをビルドした作ったhex情報を、udpsvrUme.pyに埋め込んで、[UMEHOSHI ITA]へ転送する訳です。
このコード(udpsvrUme.py)を、下記に示します。(日本語は使えません)

import time # udpsvrUme.py
time.sleep(2)

cmd='''S048000800000FFDF000083
S048000800400FFFF00007D
S04800080080000050000CC
S048000800C0032000000C1
S04800080100000000000D8
S04800080140000000000D4
S04800080180000000000D0
S1080005B0000F8FFBD270400BEAF21F0A00388BF023C05
S1080005B100000020324341043AC0180023C1080428CC3
S1080005B2000010043240180023C108043AC0180023CC7
S1080005B30001080438C0180023C0880428C2A106200B1
S1080005B4000050040140000000088BF033C20616294CC
S1080005B5000C47B027C206162A40180023C1880428C83
S1080005B60001C004104000000000180023C1480428CDB
S1080005B7000010044240180033C148064AC15004104CB
S1080005B8000000000000100042480BF033C00306294D9
S1080005B9000C47B827C003062A480BF033C0038629467
S1080005BA000C47B827C003862A42120000080BF033C72
S1080005BB00000366294C47B827C003662A480BF033C5A
S1080005BC00000346294C47B827C003462A41A17000884
S1080005BD000000000000180023C1880428C22004104D9
S1080005BE000000000000180023C0C80428C1E004018B8
S1080005BF000000000002120000080BF033C00306294D1
S1080005C0000C47B827C003062A480BF033C003862946F
S1080005C1000C47B827C003862A40100042480BF033C7B
S1080005C200000366294C47B827C003662A480BF033C69
S1080005C300000346294C47B827C003462A40180023C94
S1080005C40001480438C0180023C0C80428C2A104300A1
S1080005C500005004010000000000180023C1880438CE9
S0880005C60000180023C148043ACE6
S1080005C680021E8C0030400BE8F0800BD270800E00368
S0480005C780000000000BA
S108000500000E8FFBD271400BFAF1000BEAF21F0A00317
S10800050100088BF023C0C600324246143AC00A0023C8B
S108000502000C84042340080033C00506334000043ACD0
S1080005030000000000088BF023C00020324641043ACCB
S10800050400000A0023C504042340080033C005B6334D4
S108000505000000043AC80BF023CFF8F0334200843AC5E
S10800050600088BF023C00020324681043AC00A0023C9B
S108000507000B84042340000428C09F8400001000424D6
S10800050800080BF033C0008629401000424C47B827C92
S108000509000000862A40180023C108040AC88BF033C8E
S10800050A00020616294C47B027C206162A400A0023C95
S10800050B000F44142340000428C09F8400000000000D5
S10800050C00000A0023CF04142340000428C09F84000AF
S10800050D0002300042400A0023CFC414234000040ACB0
S10800050E00021E8C0031400BF8F1000BE8F0800E00363
S04800050F0001800BD278E
S108000560000E8FFBD271400BFAF1000BEAF21F0A00311
S1080005610000180023C0080428C001043240100023CE7
S1080005620002A10620008004010000000000180023C0D
S1080005630000080428C001043240180023C008043ACCB
S088000564000941500080000000033
S0880005648004017000C0000000027
S10800056500088BF023C2061428C008042300100422CAD
S108000566000FF00443088BF033C20616294C47B827C59
S108000567000206162A421E8C0031400BF8F1000BE8F65
S0C80005680001800BD270800E0030000000067
S108000580000E8FFBD271400BFAF1000BEAF21F0A0030F
S1080005810000180023C0480428C001043240100023CE1
S1080005820002A10620008004010000000000180023C0B
S1080005830000480428C001043240180023C048043ACC1
S088000584000141600080000000038
S0880005848004017000C0000000025
S10800058500088BF023C2061428C008042300100422CAB
S108000586000FF00443088BF033C20616294C47B827C57
S108000587000206162A421E8C0031400BF8F1000BE8F63
S0C80005880001800BD270800E0030000000065
S108000570000E8FFBD271400BFAF1000BEAF21F0A00310
S1080005710000180023C0080428C00F0422408004004DB
S108000572000000000000180023C0080428C00F04324E9
S1080005730000180023C008043ACD215000800000000DD
S0880005740004017000C000000002E
S10800057480088BF023C2061428C008042300100422CA5
S108000575800FF00443088BF033C20616294C47B827C51
S108000576800206162A421E8C0031400BF8F1000BE8F5D
S0C80005778001800BD270800E003000000005F
S108000590000E8FFBD271400BFAF1000BEAF21F0A0030E
S1080005910000180023C0480428C00F0422408004004D5
S108000592000000000000180023C0480428C00F04324E3
S1080005930000180023C048043AC5216000800000000E5
S0880005940004017000C000000002C
S10800059480088BF023C2061428C008042300100422CA3
S108000595800FF00443088BF033C20616294C47B827C4F
S108000596800206162A421E8C0031400BF8F1000BE8F5B
S0C80005978001800BD270800E003000000005D
S108000520000E8FFBD271400BFAF1000BEAF21F0A00315
S1080005210000180023C108040AC010004245817000CD7
S10800052200000000000010004247F17000C0000000018
S10800052300088BF023C2061428C008042300100422CB3
S108000524000FF00443088BF033C20616294C47B827C5F
S108000525000206162A421E8C0031400BF8F1000BE8F6B
S0C80005260001800BD270800E003000000006D
S108000530000E8FFBD271400BFAF1000BEAF21F0A00314
S1080005310000180023C108040ACFFFF04245817000C7F
S10800053200000000000FFFF04247F17000C00000000C0
S10800053300088BF023C2061428C008042300100422CB2
S108000534000FF00443088BF033C20616294C47B827C5E
S108000535000206162A421E8C0031400BF8F1000BE8F6A
S0C80005360001800BD270800E003000000006C
S108000540000E8FFBD271400BFAF1000BEAF21F0A00313
S1080005410000180023C108040AC010004245817000CD5
S10800054200000000000FFFF04247F17000C00000000BF
S10800054300088BF023C2061428C008042300100422CB1
S108000544000FF00443088BF033C20616294C47B827C5D
S108000545000206162A421E8C0031400BF8F1000BE8F69
S0C80005460001800BD270800E003000000006B
S108000550000E8FFBD271400BFAF1000BEAF21F0A00312
S1080005510000180023C108040ACFFFF04245817000C7D
S10800055200000000000010004247F17000C0000000015
S10800055300088BF023C2061428C008042300100422CB0
S108000554000FF00443088BF033C20616294C47B827C5C
S108000555000206162A421E8C0031400BF8F1000BE8F68
S0C80005560001800BD270800E003000000006A
S1080005D0000E8FFBD271400BFAF1000BEAF21F0A00303
S1080005D100000A0023CF44142340000428C09F84000A9
S1080005D20000000000000A0023CF04142340000428CD7
S1080005D3000E400042409F840000000000000A0023CD1
S1080005D4000FC414234000040AC21E8C0031400BF8F63
S1080005D50001000BE8F1800BD270800E00300000000A8
S1080005A0000F8FFBD270400BEAF21F0A0032110000052
S1080005A100080BF033C203462AC80BF033C203662AC4F
S1080005A200080BF033C203062AC80BF033C203862AC50
S1080005A300021E8C0030400BE8F0800BD270800E00375
S0480005A400000000000C7
S1080005D6000F8FFBD270400BEAF21F0A0030800C4AF07
S1080005D70000180023C108040AC80BF023C203840AC75
S1080005D800080BF023C203040AC0800C38F0100022489
S1080005D90000A006214000000000180023C0080428CDA
S1080005DA0002118400080BF023C203843AC80BF023C62
S1080005DB000203040AC7A170008000000000800C38FA8
S1080005DC000FFFF0224080062140000000080BF023C7B
S1080005DD000203840AC0180023C0080428C21184000A8
S1080005DE00080BF023C203043AC21E8C0030400BE8F3F
S0C80005DF0000800BD270800E003000000004C
S1080005DFC00F8FFBD270400BEAF21F0A0030800C4AFE4
S1080005E0C000180023C108040AC80BF023C203640AC6A
S1080005E1C0080BF023C203440AC0800C38F0100022478
S1080005E2C000A006214000000000180023C0480428CC9
S1080005E3C002118400080BF023C203643AC80BF023C5E
S1080005E4C00203440ACA1170008000000000800C38FA4
S1080005E5C00FFFF0224080062140000000080BF023C75
S1080005E6C00203640AC0180023C0480428C21184000A0
S1080005E7C0080BF023C203443AC21E8C0030400BE8F35
S0C80005E8C000800BD270800E0030000000046
S108000568C00008000801000000001000000FFDF0000D6
S108000569C00FFFF0000000500003200000010800080CA
S10800056AC000C00000000000000000000002222222212
R00800050000061
'''
print(cmd)

[UMEHOSHI ITA]で命令受け付けは、起動後4秒程度の初期設定モードがあるためすぐ受け付けません。
それで転送の開始をは、time.sleepで遅らせています。
また[UMEHOSHI ITA]と通信が有効である場合、Hexコマンドの応答メッセージが送られてきます。
それの受信を、以前は で行わせていました。

input("")はESPのUARTからの受信ですがフロー制御が正しくできないため、現在はこのコードを使っていません。


さて上記 udpsvrUme.pyは、次のように転送します。([UMEHOSHI ITA]をスルーモードにして行います)
ampy -p COM8 -b 11500 put udpsvrUme.py
起動時に自動実行させるMicroPythonプログラムではスレッド以外でループを作ることは危険なので避けた方がよいでしょう。
(仮に、ループが止まらないと、import udpsvrUmeで止まってしまい、ESP32が制御できなくなる可能性があるからです)
また、自動起動として設定する前に十分に検証すべきです。
検証は、USBでPCと接続して、[UMEHOSHI ITA]をスルーモードにして「Tera Term」などのターミナルを使った MicroPythonの会話モードで行います。(リセットしたい場合は、次の入力で可能です。)
import machine
machine.reset()
次のように、「import udpsvrUme」を入力すると、[UMEHOSHI ITA]に送るべき文字列が列挙されてプロンプトがでればOKです。
>>> import udpsvrUme
S048000800000FFDF000083
S048000800400FFFF00007D
・・・・省略・・・・・
R00800050000061

>>>
[UMEHOSHI ITA]がUART1コマンドモードであれば、このprintによる表示内容が、[UMEHOSHI ITA]への制御命令になります。
これにより、[UMEHOSHI ITA]に転送されていれて実行されていれば、app_pwm_esp.cによって定義される[UMEHOSHI ITA]の各実行は 次のようになります。
制御内容実行させる場合の[UMEHOSHI ITA]命令(絶対アドレスでの呼び出し表現)
前進("Forward")"R0080005200005F"
後進("Back")"R0080005300005E"
右回転("Right")"R0080005400005D"
左回転("Left")"R0080005500005C"
左のPWMのONのデューティー比をアップする("UpLeft")"R0080005600005B"
左のPWMのONのデューティー比をダウンする("DownLeft")"R0080005700005A"
右のPWMのONのデューティー比をアップする("UpRight")"R00800058000059"
右のPWMのONのデューティー比をダウンする("DownRight")"R00800059000058"
PWMのストップ("Stop")"R0080005A000050"
ブザーを鳴らす("Beep")"R0080005D00004D"
UDPで受信したカッコ内文字列で、[UMEHOSHI ITA]へ命令を送るためのESP32用のMicroPython用ファイル(udpsvrEsp.py)を下記に示します。
import setap # udpsvrEsp.py
import socket
sock=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
local_addr=("192.168.222.1", 3001)
sock.bind(local_addr) 
SIZE = 32

import _thread

def receive_loop():
    while True:
        msg, client_addr = sock.recvfrom(SIZE)
        s=msg.decode()
        if s.startswith("Forward") : 
            print("R0080005200005F")
        elif s.startswith("Back") : 
            print("R0080005300005E")
        elif s.startswith("Right") : 
            print("R0080005400005D")
        elif s.startswith("Left") : 
            print("R0080005500005C")
        elif s.startswith("UpLeft") : 
            print("R0080005600005B")
        elif s.startswith("DownLeft") : 
            print("R0080005700005A")
        elif s.startswith("UpRight") : 
            print("R00800058000059")
        elif s.startswith("DownRight") : 
            print("R00800059000058")
        elif s.startswith("Stop") : 
            print("R0080005A000050")
        elif s.startswith("ServerStop") : 
            break
        else:
            print("R0080005D00004D")
    #
    sock.close()

thread_id = _thread.start_new_thread(receive_loop, ()) 

上記のudpsvrEsp.pyのファイルを、次の操作でESP32へ転送します。

ampy -p COM8 -b 11500 put udpsvrEsp.py

転送後にUSBでPCと接続し直し、[UMEHOSHI ITA]をスルーモードで起動します。
(リセットのSW1を押しながらSW2を押して、SW1を離してから、LED1が消灯するまでSW2を押し続ける操作)
起動によってESP32は「192.168.222.1」のIPアドレスを持つアクセスポイントが起動するので PC側でwifiのssidの'ESP-AP'に接続しておきます。
そして、「Tera Term」などのターミナルを使った MicroPythonの会話モードで行います。
ここで、importでudpsvrEsp.pyを実行します。ここで、上記で示した文字列をUDPで送ることで、 対応する「UME専用Hexコマンド」の文字列が[UMEHOSHI ITA]に送られて、実行されます。
以下は、UDPでEP32に"Forward"を送ると、 "R0080005200005F"が[UMEHOSHI ITA]に送られて前進の動作が行われる時の表示です。

>>> import udpsvrEsp     # この行でUSB受信ループが始まります。
R0080005200005F

PC側で、UDPによるに"Forward"のよな制御文字を送信できる次のようなプログラム(pc_udp_esp32.py)で、 動作を確認するとよいでしょう。

import socket # (pc_udp_esp32.py)
import _thread

UDP_IP = "192.168.222.1"
UDP_PORT = 3001

def sendCommand(cmd):
    send_len=sockSend.sendto(cmd.encode('utf-8'), sever_addr)
    print(cmd.strip(), send_len, "byte 送信しました")

sockSend=socket.socket(socket.AF_INET, type=socket.SOCK_DGRAM)

sever_addr=(UDP_IP,UDP_PORT )#IPアドレスとポート番号
m_f=m_b=m_r=m_l=False   # 前進フラグ, 後進フラグ,  右回転フラグ, 左回転フラグ 
while True:
    s=input("W/A/X/D/S/+/-/y/h/u/j/QUIT/Q==>").upper()
    if s.startswith("W") :
        m_f=m_b=m_r=m_l=False
        m_f=True
    elif s.startswith("A") :
        m_f=m_b=m_r=m_l=False
        m_l=True
    elif s.startswith("X") :
        m_f=m_b=m_r=m_l=False
        m_b=True
    elif s.startswith("D") :
        m_f=m_b=m_r=m_l=False
        m_r=True
    elif s.startswith("S") :
        sendCommand("Stop\r\n")
        m_f=m_b=m_r=m_l=False
    elif s.startswith("+") :
        sendCommand("UpLeft\r\n")
        sendCommand("UpRight\r\n")
    elif s.startswith("-") :
        sendCommand("DownLeft\r\n")
        sendCommand("DownRight\r\n")
    elif s.startswith("Y") :sendCommand("UpLeft\r\n")
    elif s.startswith("H") :sendCommand("DownLeft\r\n")
    elif s.startswith("U") :sendCommand("UpRight\r\n")
    elif s.startswith("J") :sendCommand("DownRight\r\n")
    elif s.startswith("QUIT") :
        sendCommand("ServerStop\r\n")
    elif s.startswith("Q") :
        break
    else:
        sendCommand("Beep\r\n")
        continue
    #
    if m_f :
        sendCommand("Forward\r\n")
    elif m_l:
        sendCommand("Left\r\n")
    elif m_b :
        sendCommand("Back\r\n")
    elif m_r :
        sendCommand("Right\r\n")
    #

sockSend.close()

[UMEHOSHI ITA]をスルーモードでは、ターミナルに"R0080005200005F"のような 「UME専用Hexコマンド」が表示されて、モータが回るわけではありません。
[UMEHOSHI ITA]をUART1コマンドモードにして、行わなければなりません。
そして、そのためには udpsvrUme.pyとudpsvrEsp.pyがESP32で起動時に自動的に実行させる必要があります。
自動起動できるように、boot.pyの最後の3行を次のように変更します。

import setap
import udpsvrUme
import udpsvrEsp
これを、次の操作でesp32に転送します。
ampy -p COM8 -b 11500 put boot.py

以上で出来上がりです。
これで、PCからのUSB通信は不要になるので、PCのUSB接続は外して、USB(A-Type)をモバイルバッテリーに接続し直します。
そして、リセットのSW1を押しながらSW2を押し、SW1を離してLED1が消灯から点灯に変わるまでSW2を押し続けます。
これににより、UAER1コマンドモードになり、以下が自動的に実行されます。
setap.pyが実行されてESP32がアクセスポイントになり、 udpsvrUme.pyが実行されてUME専用Hexコマンドが転送されて実行されます。
(この転送が終わって実行されたタイミングで、ブザーが鳴ります。)
続いて、udpsvrEsp.pyが実行されてUDP受信ループの実行が始まります。

後は、PC側でWifiの(SSIDが「ESP-AP」でパスワードは無し)に接続し、 pc_udp_esp32.py起動してメニュー「("W/A/X/D/S/+/-/y/h/u/j/QUIT/Q==>")」に対する、キー入力操作で、 [UMEHOSHI ITA]を制御できるでしょう。
(コマンド以外を入力するとブザーが鳴ります)
以下は PC側でWifi接続して pc_udp_esp32.pyを実行し、PC側よりUDPによる文字列送信で遠隔操作をしている例です。

R:\MicroPythonFolder>python pc_udp_esp32.py
W/A/X/D/S/+/-/y/h/u/j/QUIT/Q==>w
Forward 9 byte 送信しました
W/A/X/D/S/+/-/y/h/u/j/QUIT/Q==>a
Left 6 byte 送信しました
W/A/X/D/S/+/-/y/h/u/j/QUIT/Q==>x
Back 6 byte 送信しました
W/A/X/D/S/+/-/y/h/u/j/QUIT/Q==>d
Right 7 byte 送信しました
W/A/X/D/S/+/-/y/h/u/j/QUIT/Q==>s
Stop 6 byte 送信しました
W/A/X/D/S/+/-/y/h/u/j/QUIT/Q==>q

R:\MicroPythonFolder>
動作を動画で紹介します このClickでYoutube 紹介動画へ移動できます。