UMEHOSHI ITA TOP PAGE    COMPUTER SHIEN LAB

Raspberry PI Zero WととUmehoshi ITAをUSB接続し、Wifi接続してWebブラウザ制御 兼 TCP制御作品その2(EEPROM化作品)

前に紹介したページの作品は、HTTPサーバー起動時に[UMEHOSHI ITA]基板のRAM領域へ モータ制御プログラム転送し、他のPCなどからブラウザでロボットを制御できる仕様になっています。
ここで使うDHCP、HTTP、TCPサーバーは、「Raspberry PI Zero W」のLinuxで行っています。
(PI Zero Wは、このリンク先で紹介したraspbianをインストールしています。)
そして、HTTPサーバーやモータ制御プログラム転送のpythonプログラムをsystemctlで起動しています。
このロボットは、起動してブラウザで接続して制御できるまでに、約2分程度の時間が必要でした。http://192.168.100.1/index.htm

このページではこの起動時間を短縮するため、[UMEHOSHI ITA]基板のRAM領域へモータ制御プログラム転送をせず、 プログラムEEPROMに書き込んで使う改良を行った資料です。
(「Raspberry PI Zero W」と[UMEHOSHI ITA]基板と接続詳細情報は、 別ページなど参照してください。)

機能は、改良前と同じです。
起動すると、自身をWifiのアクセスポイントになります。
(アクセスポイント SSID:pizero、パスプレーズ:abcd1234)
そして、DHCPサーバーで、接続してきた相手に 192.168.100.2〜のIPアドレスを配信します。
このロボットのIPアドレスは192.168.100.1で 起動時は、Webサーバーとして起動します。
ロボットのカメラ画像配信ページ: http://192.168.100.1/index.html
ロボットの遠隔操作ページ: http://192.168.100.1/index.htm
ロボットの撮影画像閲覧URL: http://192.168.100.1/imgs
ロボットの撮影画像を消去するページ: http://192.168.100.1/clear.jpg
ロボットのWebサーバーのプログラムを終了ページ: http://192.168.100.1/quit.jpg
上記操作でWebサーバーが終了すると、 TCP制御サーバーが起動します。 このサーバーへPC側よりでpython umeclient2.pyで接続して'M'/'f'/'quit'/w/x/a/d/Q/Z/E/C/T>の メニュー操作で制御できます。
この umeclient2.py のリンク先にこのソースを紹介しています。

[UMEHOSHI ITA]基板のEEPROM領域にモータ制御プログラムを書き込んで制御するための変更

起動させた後、PCからのWifiを介した遠隔操作で操作するので、PCが接続するアクセスポイントをロボットの アクセスポイント(SSID:pizero, パスフレーズ「abcd1234」)で接続します。

「ssh pi@192.168.100.1」で接続して、操作します。 (このページでセットアップしていれば[abc123]のパスワードで接続)
 Are you sure you want to continue connecting (yes/no/[fingerprint])?でyesを入力します。
 Host key verification failed.のエラーであれば「ssh-keygen -R 192.168.0.123」入力で、該当のIPアドレス情報を消して再接続します。)


これまで、 UMEHOSI ITA のRAM領域に初期コードを転送して動作させる手法は、このサービスから
/home/pi/umehoshi/umehoshi.pyを起動させていました。
umehoshi.py中で[UMEHOSHI ITA]のRAMに、モータ制御プログラムを転送していました。これを転送しないでROM利用するプログラムに変更した
/home/pi/umehoshi2/umehoshi2.pyを使う変更です。

そこでまず、次のコマンド操作でサービスを停止します。
sudo systemctl disable umehoshi.service
新しいサービスをumehoshi2で作るので、必要な umeusb.pyumetcp.pyをumehoshフォルダからコピーします。
それ以外にも、必要なファイル次のようにコピーします。 これらは、このリンク先で作成したWebサーバー用ファイルです。
(myweb.pyは以前と同じWebサーバー用モジュールです)
またWebサーバー時に使うumeutil.pyもcgi-binフォルダを作ってumeutil.pyもコピーしておきます。
(umeutil.pyはUSBでコマンドファイルを転送する関数定義ファイルです)
他に、受信ファイルを格納するためのdatasフォルダや、カメラ画像を格納するimgsフォルダを作っています。
pi@raspberrypi:~ $ mkdir -p ~/umehoshi2
pi@raspberrypi:~ $ cd ~/umehoshi2
pi@raspberrypi:~/umehoshi2 $ cp ~/umehoshi/umeusb.py .
pi@raspberrypi:~/umehoshi2 $ cp ~/umehoshi/umetcp.py .
pi@raspberrypi:~/umehoshi2 $ cp ~/umehoshi/myweb.py .
pi@raspberrypi:~/umehoshi2 $ cp ~/umehoshi/index.html .
pi@raspberrypi:~/umehoshi2 $ cp ~/umehoshi/index.htm .
pi@raspberrypi:~/umehoshi2 $ cp ~/umehoshi/quit.jpg .
pi@raspberrypi:~/umehoshi2 $ mkdir cgi-bin
pi@raspberrypi:~/umehoshi2 $ cp ~/umehoshi/cgi-bin/umeutil.py ./cgi-bin/umeutil.py
pi@raspberrypi:~/umehoshi2 $ mkdir datas
pi@raspberrypi:~/umehoshi2 $ mkdir imgs
これでumetcp.pyを起動するとTCPのサーバーが起動し、
対応する後述のPC側プログラム(umehosi2.pyh)を使えばファイルを送り込むことができます。
umetcp.pyの実行前にumetcp.pyを編集してのmainのip変数の設定にを、そのRaspberry PI Zeroに合わせる必要があります。
(nano umetcp.pyを編集して、ip = "192.168.100.1"と設定しました。)
そして次の操作により、後述する『umehosi2.pyh』のファイルを送信し、そのファイルを~/umehoshi/に移動しています。
(一部に表示を省略しています)
Pi Zero のumetcp.py実行側PCでのumeclient2.py実行側
pi@raspberrypi:~/umehoshi2 $ python3 umetcp.py
Serverの情報: ('192.168.100.1', 59154)
接続要求を待つ
start receiveData
umehoshi2.py 2510
recieve file:umehoshi2.py,size:2510

pi@raspberrypi:~/umehoshi2 $ mv datas/umehoshi2.py .

D:raspi_zero>python umeclient2.py
IP (defualt:192.168.100.1)Address>
接続成功
start receiveData
送信したい情報を選択入力'M'/'f'/'quit'/w/x/a/d/Q/Z/E/C/T>f
[ ・・・・カレントディレクトリの表示・・・・・]
送信したいファイル名入力>app_pwm_esp32.umh
b'fapp_pwm_esp32.umh 5715\r\n'  送信byte: 5715
送信したい情報を選択入力'M'/'f'/'quit'/w/x/a/d/Q/Z/E/C/T>quit
  終了時のエラー表示は無視してください

上記で転送した『umehosi2.py』ファイルは、umehosi.pyの代わり使う後述するROM領域利用のサーバープログラムです。
このumehosi2.pyを次の操作により起動して、『app_pwm_esp32.umh』のファイルを送信します。(一部に表示を省略しています)

Pi Zero のumehosi2.py実行側PCでのumeclient2.py実行側
pi@raspberrypi:~/umehoshi2 $ python3 umehoshi2.py
Serial(port='/dev/ttyACM0', baudrate=115200, bytesize=8, parity='N', stopbits=1, timeout=None, xonxoff=False, rtscts=False, dsrdtr=False)
Serverの情報: raspberrypi ('192.168.100.1', 59154)
接続要求を待つ
接続を待って、接続してきたら許可
接続相手: ('192.168.100.5', 53057)
start receiveData
app_pwm_esp32.umh 5715
S109D02001000E8FFBD271400BFAF1000BEAF21F0A00304


 ・・・・・書き込み応答が続く


S109D020A400000904234080040AC21E8C0030400BE8F73
S0C9D020A50000800BD270800E003000000004E

 クライアントがquitで終了すると、エラーの表示がでるが無視
接続を待って、接続してきたら許可
 Ctrl+C で強制終了させてください。
KeyboardInterrupt
pi@raspberrypi:~/umehoshi2 $
D:raspi_zero>python umeclient2.py
IP (defualt:192.168.100.1)Address>
接続成功
start receiveData
送信したい情報を選択入力'M'/'f'/'quit'/w/x/a/d/Q/Z/E/C/T>f
[ ・・・・カレントディレクトリの表示・・・・・]
送信したいファイル名入力>app_pwm_esp32.umh
b'fapp_pwm_esp32.umh 5715\r\n'  送信byte: 5715
S109D02001000E8FFBD271400BFAF1000BEAF21F0A00304
SET:9D020010
・・・・ 書き込みと応答の表示が続く・・・・・
S0C9D020A50000800BD270800E003000000004E
SET:9D020A50
 転送が止まったら、SPACE,ENTERを入力してメニューを出す。
送信したい情報を選択入力'M'/'f'/'quit'/w/x/a/d/Q/Z/E/C/T>quit
	・・・・・・
ConnectionAbortedError: [WinError 10053] 
確立された接続がホスト コンピューターのソウトウェアによって中止されました。
  この終了時のエラー表示は無視してください
D:raspi_zero>

上記はWifiを介したファイルapp_pwm_esp32.umhの送信でSSHで行っている操作です。
この送信したファイルは、 [UMEHOSHI ITA]基板のEEPROM領域に書き込むにモータ制御プログラムで、このEEPROM用のコードは、このリンク先ページで 紹介したものです。
umehoshiEditの開発ツールで直接に書き込むこともできますが、上記方法でこの翻訳済みファイルのapp_pwm_esp32.umh を送信するだけで、同様にEEPROMへの書き込み処理が完了します。

次の各種エントリーポイントの「.umh」を用意し、転送による実行で各モータなどの動作を確認できます。
(モータの制御前に、EEPROM領域にあるモータ制御の初期化が必要で、起動時に一回だけuStartInit.umhを実行させる必要があります。)
ファイル名内容概要
uBeep.umh
Beep
R009D020D00003B
音パターン[・・・・]を登録
uStartInit.umh
StartInit
R009D020010004E
下記初期設定と調整(PWDなどの初期化で、下記モータ制御を行う前に一回だけ実行しなければならない。)
uForward.umh
Forward
R009D020200004D
モータ前進
uBack.umh
Back
R009D020300004C
モータ後進
uRight.umh
Right
R009D020400004B
モータ右回転
uLeft.umh
Left
R009D020500004A
モータ左回転
uLeftUp.umh
UpLeft
R009D0206000049
モータ左デューティ幅アップルーチン
uLeftDown.umh
DownLeft
R009D0207000048
モータ左デューティ幅ダウンルーチン
uRightUp.umh
UpRight
R009D0208000047
モータ右デューティ幅アップ
uRightDown.umh
DownRight
R009D0209000046
モータ右デューティ幅ダウン
uStop.umh
Stop
R009D020A00003E
モータストップ


上記のファイル転送してモータを動作させる例を下記に示します
(以下は、モータ制御初期化の転送実行と、モータ前進処理の転送実行を実行です。)
Pi Zero のumehosi2.py実行側PCでのumeclient2.py実行側


pi@raspberrypi:~/umehoshi2 $ python3 umehoshi2.py
Serial(port='/dev/ttyACM0', baudrate=115200, bytesize=8, parity='N', stopbits=1, timeout=None, xonxoff=False, rtscts=False, dsrdtr=False)
Serverの情報: raspberrypi ('192.168.100.1', 59154)
接続要求を待つ
接続を待って、接続してきたら許可
接続相手: ('192.168.100.5', 53129)
start receiveData
uStartInit.umh 28
R009D020010004E

uForward.umh 26
R009D020200004D



 クライアントがquitで終了すると、エラーの表示がでるが無視
接続を待って、接続してきたら許可
 Ctrl+C で強制終了させてください。
KeyboardInterrupt
pi@raspberrypi:~/umehoshi2 $
D:raspi_zero>python umeclient2.py
IP (defualt:192.168.100.1)Address>
接続成功
start receiveData
送信したい情報を選択入力'M'/'f'/'quit'/w/x/a/d/Q/Z/E/C/T>f
[ ・・・・カレントディレクトリの表示・・・・・]
送信したいファイル名入力>uStartInit.umh
b'fuStartInit.umh 28\r\n'  送信byte: 28
R009D020010004E
START:9D020010

送信したい情報を選択入力'M'/'f'/'quit'/w/x/a/d/Q/Z/E/C/T>f
[ ・・・・カレントディレクトリの表示・・・・・]
送信したいファイル名入力>uForward.umh
b'fuForward.umh 26\r\n'  送信byte: 26
R009D020200004D
START:9D020200

送信したい情報を選択入力'M'/'f'/'quit'/w/x/a/d/Q/Z/E/C/T>quit
	・・・・・・
ConnectionAbortedError: [WinError 10053] 
確立された接続がホスト コンピューターのソウトウェアによって中止されました。
  この終了時のエラー表示は無視してください


Systemdのサービスで使うPi Zero用の制御サーバープログラム(umehoshi2.py)の内容(使い方は上で紹介)

次に示すumehoshi2.pyは、[UMEHOSHI ITA]基板のEEPROM領域にあるモータ制御をTCPで制御するコードです。
(赤の所が、以前と異なる部分で、EEPROM領域にあるモータ制御の初期化関数を呼び出しています。 上述の"uStartInit.umh"を『umehosi2.py』を使って、ファイルを転送しておく必要があります。)
#!/usr/bin/python3
# -*- coding: utf-8 -*-
#   TCPサーバ側「sudo python3 umehosi2.py」で動かす
#   Webサーバ、TCPサーバー実行「sudo python3 umehoshi2.py SV」で動かす(myweb.py内os.chmod利用のためsudo権限が必要)
import os
import umetcp
from umetcp import send_file, send_message, recieve_file, recieve_message, \
   receiveData , path_datas
import socket
import umeusb
import traceback
import sys
#import time

sock = None # クライアント側のソケット

os.chdir("/home/pi/umehoshi2/") # カレントディレクトリ変更

umeusb.init_sub()

def my_tcp_receive_file_func(filename):
    ''' 受信したumehoshiアプリ用「.umh」のファイルより、
     「UME専用Hexコマンド」の文字列をusbへ出力する'''
    name,ext = os.path.splitext(filename)
    if not ext == ".umh" : return
    with open(path_datas + "/" + filename) as f: ss=f.readlines()
    ss="".join(ss[1:])
    print(ss)
    umeusb.send_cmd(ss) # TCPで受信した 「.umh」データを[UMEHOSHI ITA]へ送る

my_tcp_receive_file_func( "uStartInit.umh" ) # このロボット制御用の[UMEHOSHI ITA EEPROM]用初期化エントリーファイル実行


if len(sys.argv) > 1:
      argument = sys.argv[1]
      #print(f"受け取った引数: {argument}")
      #======================= myweb start
      umetcp.camera.close()
      umeusb.usb.close()
      import myweb # ここで、Webサーバを起動して、サーバを使い終わたら、改めてデバイスを初期化して使う。
      umeusb.init_sub()
      import picamera
      umetcp.camera = picamera.PiCamera() 
      #======================= myweb end

def my_usb_receive_func(bin):
    'usb受信のイベントで実行するデフォルト処理'
    global sock
    #print("-----------",bin)
    ss = bin.decode('utf-8')
    a=ss.split('\n')
    for s in a:
        s = s.strip()
        if sock != None:
            send_message(sock, s) # [UMEHOSHI ITA]からの応答メッセージをTCPで返す
        else: print( s )

umeusb.usb_receive_func = my_usb_receive_func # USB受信データの処理を置き換える。

umetcp.tcp_receive_file_func = my_tcp_receive_file_func # tcp受信データの処理を置き換え

def my_tcp_recieve_message(msg):
   umeusb.send_cmd(msg)

umetcp.tcp_receive_message_func=my_tcp_recieve_message  # ★ umehoshi.py に対して新しく追加(20250720)

ip = "192.168.100.1" #[Raspberry PI Zero WH]の設定に合わせてください
portnumber=59154
hostname=socket.gethostname()

server_addr =(ip, portnumber)
print("Serverの情報:",hostname, server_addr)
serversock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversock.bind(server_addr)  # IPとポート番号を指定します
print("接続要求を待つ")
serversock.listen()
while True:
   print("接続を待って、接続してきたら許可")
   sock, address = serversock.accept()#サーバの接続受け入れ
   print("接続相手:",address)
   try:
      receiveData( sock ) # 受信ループ
   except Exception as e:
      print(f"例外の種類: {type(e)}")
      print(f"スタックトレース: {traceback.format_exc()}")
      print(e, "sock.close()")
   sock.close()

上記のumehoshi2.pyをWebサーバーとして動かすため、"/cgi-bin/ume.py"を変更

「sudo python3 umehoshi2.py SV」で上記を動かすと、import mywebでWebサーバーを起動します。
ここで使っているモジュール(myweb.py)は、 このリンク先で示すソースを使っています。

閲覧用のindex.htmlの内容や、 制御用のindex.htmの内容は各リンク先にありますが、以前と変更していません。
ロボットのカメラを単純に閲覧するだけでであれば、これまで変更で見えているはずです。(このイメージはこのリンク先と同じです)
ですが、モータ制御を行うページのindex.htm(操作イメージ)でume.pyを変更せずにボタン操作すると失敗します。
index.htm内のボタン操作で、"/cgi-bin/ume.py"を呼び出しで「UMEHOSHI ITA」基板のモータ制御関数のエントリーポイントを起動するようになっていますが、 このアドレスが以前のRAM領域のままでは正しく実行できないことになりります。
以前のume.pyの内容を、以下の赤のように変更したume.pyを作成したファイルを用意します。
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# このume.pyは、 http://192.168.100.1/cgi-bin/ume.py?PWD=abc123&CMD=Beep のような呼び出しが可能
import serial
import cgi
import os
import sys
import io
import cgitb
cgitb.enable()

sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') # print命令のエンコードを指定
print("Content-Type: text/html; charset=utf-8\n\n")

import serial
cmd="X"
rtn="None"
try:
    serial = serial.Serial(port ='/dev/ttyACM0' , baudrate = 115200,timeout = 1)

    import umeutil

    # フォームから値を取得
    form = cgi.FieldStorage()
    pwd = ""
    if 'PWD' in form:
        pwd = form['PWD'].value 
    if 'CMD' in form and pwd == "abc123":
        cmd = form['CMD'].value
        if cmd=='Forward':
            rtn=umeutil.send_cmd(serial, "R009D020200004D")
            rtn=str(rtn)
        elif cmd=='Backword':
            rtn=umeutil.send_cmd(serial, "R009D020300004C")
            rtn=str(rtn)
        elif cmd=='Left':
            rtn=umeutil.send_cmd(serial, "R009D020500004A")
            rtn=str(rtn)
        elif cmd=='Right':
            rtn=umeutil.send_cmd(serial, "R009D020400004B")
            rtn=str(rtn)
        elif cmd=='UL':
            rtn=umeutil.send_cmd(serial, "R009D0206000049")
            rtn=str(rtn)
        elif cmd=='DL':
            rtn=umeutil.send_cmd(serial, "R009D0207000048")
            rtn=str(rtn)
        elif cmd=='UR':
            rtn=umeutil.send_cmd(serial, "R009D0208000047")
            rtn=str(rtn)
        elif cmd=='DR':
            rtn=umeutil.send_cmd(serial, "R009D0209000046")
            rtn=str(rtn)
        elif cmd=='Stop':
            rtn=umeutil.send_cmd(serial, "R009D020A00003E")
            rtn=str(rtn)
        elif cmd[:1]=='R' or cmd[:1]=='S' or cmd[:1]=='G': 
            rtn=umeutil.send_cmd(serial, cmd)
            rtn=str(rtn)
        else: # cmd=='Beep'
            rtn=umeutil.send_cmd(serial, "R009D020D00003B")
            rtn=str(rtn)
        #
        serial.close()
except Exception as e:
    print(e)

print(cmd + ":" + rtn)
上記ume.pyファイルは、次の操作でファイルを転送して、cgi-binのディレクトリへ移動し、実行権限を与えます。
Pi Zero のumetcp.py実行側PCでのumeclient2.py実行側
pi@raspberrypi:~/umehoshi2 $ python3 umetcp.py
Serverの情報: ('192.168.100.1', 59154)
接続要求を待つ
start receiveData
ume.py 2047
recieve file:ume.py,size:2047

pi@raspberrypi:~/umehoshi2 $ mv datas/ume.py cgi-bin/ume.py
pi@raspberrypi:~/umehoshi2 $ chmod +x cgi-bin/ume.py
pi@raspberrypi:~/umehoshi2 $ 
D:raspi_zero>python umeclient2.py
IP (defualt:192.168.100.1)Address>
接続成功
start receiveData
送信したい情報を選択入力'M'/'f'/'quit'/w/x/a/d/Q/Z/E/C/T>f
[ ・・・・カレントディレクトリの表示・・・・・]
送信したいファイル名入力>ume.py
b'fume.py 2047\r\n'  送信byte: 2047
送信したい情報を選択入力'M'/'f'/'quit'/w/x/a/d/Q/Z/E/C/T>quit
  終了時のエラー表示は無視してください

以上で、Webサーバー関連の準備は終了です。次のように検証します。
まず、Webサーバーを次のように起動します。
pi@raspberrypi:~/umehoshi2 $ sudo python3 myweb.py
のURLでカメラ画像が見えればOKです。
http://192.168.100.1/index.html
続いて、上記で作成したcgi-bin/ume.pyの検証で、次のブラウズでビープ音が出ればOKです。
http://192.168.100.1/cgi-bin/ume.py?PWD=abc123&CMD=Beep
なおモータを動作させる場合は、先にuStartInit.umhを実行させなければなりません。
それを起動に行っているサーバーがumehoshi2.pyで、この中でmyweb.pyを起動しています。
(次のhttp://192.168.100.1/QUIT.jpgを閲覧でWebサーバーを終了できます。)
Web兼制御サーバープログラムのumehoshi2.pyを起動するコマンドは、次のように起動します。
pi@raspberrypi:~/umehoshi2 $ sudo python3 umehoshi2.py SV
次のURLでボタン操作でモータうぃ制御できるページが開きます。
http://192.168.100.1/index.htm
このWebサーバーを終了は、次のhttp://192.168.100.1/QUIT.jpgの閲覧で行います。
Webサーバーが終了終了すると、TCP制御サーバーが始まります。

このサーバーは、PC側で上記でも紹介しているように、python umeclient2.pyで接続してM'/'f'/'quit'/w/x/a/d/Q/Z/E/C/T> のメニュー操作で制御できます。


Systemdのサービスにumehoshi2.pyを登録

umehoshi2.pyをSystemdを使ったサービスとして、電源投入時に、実行させる設定です。
まず、umehoshi2.pyに実行権限を次のコマンドで与えます。
sudo chmod +x /home/pi/umehoshi2/umehoshi2.py
これをサービスなどの設定を行うファイル「 Unitファイル」を次のように作ります。
ユーザ作成のUnitファイルの置き場は、「/etc/systemd/system」と決まって、次のファイルを作ります。
「/etc/systemd/system/umehoshi2.service」をsudoの編集操作で操作します。

[Unit]
Description = umehoshiserver2.

[Service]
ExecStart=/usr/bin/python3 /home/pi/umehoshi2/umehoshi2.py SV
Type=simple

[Install]
WantedBy=multi-user.target
これは、次のコマンド操作で動作を確認します。(Webサーバーの動作説明はこのページ先頭で紹介しています。TCP制御サーバーは下記で後述しています)
sudo systemctl start umehoshi2.service     (自動実行の登録前の検証時の実行で使う)
次のコマンド操作でサービスの実行状態を確認します。
sudo systemctl status umehoshi2.service

動作の確認後、このサービスを次のコマンド操作で登録します。
sudo systemctl enable umehoshi2.service

以上で、サービス登録が終了です。
sudo rebootまたは、シャットダウンして、再度電源コネクタを抜き差しして、動作を確かめます。


前に紹介したページの作品は、HTTPサーバー起動時に[UMEHOSHI ITA]基板のRAM領域へ モータ制御プログラムを転送し、 約2分程度の時間が必要でした。
対して、モータ制御プログラムをEEPROMに書き込んで、起動時はその初期化関数を呼び出すだけに改良しています。
この初期化関数の起動時にブザーが鳴るようになっており、そこまでの時間は約42秒掛かりました。
なお、PCでロボットのWifiアクセスポイントが見えて接続できるようになるまでには、約1分20秒を要しました。

アクセスポイント SSID:pizero、パスプレーズ:abcd1234 のWifiのアクセスポイントにPCで接続します。
始めはWebサーバーが起動しているので、そして、http://192.168.100.1/index.htmに接続します。
このブラウザ閲覧で、モーター動作を確認します。

その後、quit.jpg閲覧で、Webサーバーの代わりに、TCP制御サーバーの状態にします。(次の行のクリック操作)
「Webサーバー終了してTCP制御サーバー起動」の隠し操作
これは、http://192.168.100.1/quit.jpgの閲覧で終了し、TCPの制御サーバーが動作を始めます。
このTCP制御サーバーに対応するクライアントソフト(umeclient2.pyなど)をPC側で動かして制御を確かめます。

PC側でこのロボットを制御するためのクライアントプログラム例:umeclient2.py

PC側よりでpython umeclient2.pyの実行でロボットのTCP制御サーバーに 接続して'M'/'f'/'quit'/w/x/a/d/Q/Z/E/C/T>の メニュー操作で制御できます。
このumeclient2.pyは、このリンク先で紹介したumeclient.pyのエントリーポイントのファイル(.umh)を変更し、 別モジュールであったumetcp.pyを一つのソースにまとめた次のコードです。
また、メニューの T Enter の操作でロボットに撮影を要求し、応答した画像は'serverpicture.jpg'の名前で保存していますが、 この表示処理(show_imageメソッド)を、メインスレッドで行うように変更しています。
import socket

import os
import sys
import time
import threading
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt

def send_message(sock,msg):
    bin=('M'+msg+"\r\n").encode("utf-8")#binaryへ変換
    sock.sendall(bin)#一括送信

def send_file(sock, filepath):
    filesize = os.path.getsize(filepath)
    filename = os.path.basename(filepath)
    s = "f{} {}".format(filename, filesize)
    bin=(s+"\r\n").encode("utf-8")#binaryへ変換
    print(bin, end="  ")
    sock.sendall(bin) #filename filesize 送信
    with open(filepath, "rb") as f:
        bin = f.read(filesize)
    # print("送信data:" , bin )
    sock.sendall(bin) # ファイル内容一括送信
    print("送信byte:" , len(bin) )

serverpicture_receive_filepath = None # 受信画像ファイル

def show_image(): # serverpicture_receive_filepath の画像を表示
    global serverpicture_receive_filepath
    if serverpicture_receive_filepath == None: return
    img = Image.open(serverpicture_receive_filepath) # 元となる画像の読み込み
    serverpicture_receive_filepath = None
    np_img = np.array( img )
    plt.imshow( np_img ) # 各OSの標準ビューアが開く。
    plt.show( block= True ) 
    plt.close()

def recieve_file(sock):
    global serverpicture_receive_filepath
    buf=b""
    while True:
        bin = sock.recv(1)#1byte受信
        if len(bin) != 1: break 
        buf += bin
        if buf[-2:] == b"\r\n":# 1行の文字列受信終了?
            s = buf[0:-2].decode('utf-8')#バイナリから
            #print(s)
            a=s.split(' ')
            filename, filesize = a[0], int(a[1])
            print(f"recieve file:{filename},size:{filesize}")# 受信したファイル名とサイズを表示  
            #bin = sock.recv(filesize) # ファイルの一括受信
            bin=b""
            while len(bin) < filesize:
                bin += sock.recv(filesize-len(bin)) # ファイルの受信
            #print( bin )
            with open( filename, "wb") as f:
                f.write(bin)
            if filename == 'serverpicture.jpg': serverpicture_receive_filepath = filename
            break
        #
    

def recieve_message(sock):
    buf=b""
    while True:
        bin = sock.recv(1)#1byte受信
        if len(bin) != 1: break
        buf += bin
        #print("----", buf)
        if buf[-2:] == b"\r\n":# 1行の文字列受信終了?
            s = buf[0:-2].decode('utf-8')#バイナリから
            print(s) # 受信文字列表示
            break

def receiveData(sock):
    try :
        print( "start receiveData" )
        while True:
            bin = sock.recv(1)#1byte受信
            if len(bin) != 1: break
            if bin == b"f" : recieve_file(sock)
            elif bin == b"M" : recieve_message(sock)
            else:
                print(bin , end="")
        #
        print( "receiveData ended." )
    #
    except Exception as e:
        print(f"例外: {e}")

portnumber=59154
ip="192.168.100.1" #[Raspberry PI Zero WH]の設定に合わせてください
s = input("IP (defualt:{})Address>".format(ip))
if s != "": ip = s
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((ip, portnumber))
print("接続成功")
t_id = threading.Thread(target=receiveData, args=(sock,) )
t_id.start()
loopFlag = True
cmd=""
path_datas = '.'
while loopFlag:
    show_image() # serverpicture_receive_filepathがあれば表示
    s = input("送信したい情報を選択入力'M'/'f'/'quit'/w/x/a/d/Q/Z/E/C/T>")
    if s != "": cmd = s
    if cmd == 'M':
        msg = input("送信したい文字列入力>")
        send_message(sock,msg+"\r\n")
    elif cmd == 'f':
        print(os.listdir(path_datas))
        filename = input("送信したいファイル名入力>")
        if not os.path.isfile(path_datas + "/" + filename):
            print( filename, "no exist")
            continue
        send_file(sock, path_datas + "/" + filename)
    elif cmd == 'quit': loopFlag=False
    elif cmd == 'b': # ビープ音要求 (隠しコマンド)
        send_message(sock,"R009D020D00003B\r\n")
    elif cmd == 'w':
        send_file(sock, path_datas + "/" + "uForward.umh")
    elif cmd == 'x':
        send_file(sock, path_datas + "/" + "uBack.umh")
    elif cmd == 'a':
        send_file(sock, path_datas + "/" + "uLeft.umh")
    elif cmd == 'd':
        send_file(sock, path_datas + "/" + "uRight.umh")
    elif cmd == 'Q':
        send_file(sock, path_datas + "/" + "uLeftUp.umh")
    elif cmd == 'Z':
        send_file(sock, path_datas + "/" + "uLeftDown.umh")
    elif cmd == 'E':
        send_file(sock, path_datas + "/" + "uRightUp.umh")
    elif cmd == 'C':
        send_file(sock, path_datas + "/" + "uRightDown.umh")
    elif cmd == 'T':
        sock.sendall( ('T'+"\r\n").encode("utf-8") )# 写真を撮って、そのファイルを要求
        cmd = "" 
    #
    time.sleep(0.5)

sock.close()
sys.exit(0)
上記のコードでは、わざとsend_fileメソッドで、各種"〜.umh"ファイルを送信しているので、そのファイルがカレントディレクトリに 配置しておく必要があります。
上記のコードのsend_message(sock,"R009D020D00003B\r\n")のビープ音要求のように、「UME専用Hexコマンド」の文字列を直接送る方法に改良すれば "〜.umh"ファイル群の配置は必要無くなります。
このように 「UME専用Hexコマンド」の文字列を直接送る方法にしたJavaのコードをこのリンク先で紹介しています。