TOP PAGE

ESP32でのMicroPythonで、遠隔制御用のTCPサーバーを構築する

以下で例えば右のように、ESP32へ電源さえ供給すれば、Wifiを介してPCから遠隔操作するサーバーを構築する方法を紹介します。
(ESP32のMicroPythonのパワーON時の起動ファイルが boot.py に決まっており、 サーバの起動はboot.pyからsetap.pyとserver.pyを呼びだすことで実現しています。)

ESP32側には、このリンク先で紹介したように、MicroPythonのインストールが済んでいることが前提です。
遠隔操作の目標はファイルの送信ですが、他にこのリンク先で 紹介したファイル操作(ファイルリスト取得、テキストファイル内容の表示、ファイルダウンロード、Pythonファイルのimport起動)が目標です。

左の写真で、USBコネクタのすぐ左(上)にあるスイッチがENボタンと呼ばれるリセットボタンです。
左の写真で、USBコネクタのすぐ右(下)にあるスイッチがBootボタンと呼ばれ、 Bootボタンを押しながら、ENボタンを押してリセットすることで、Download Boot(シリアル通信でプログラム書込モード)に手動で切り替えることができます。

ESP32をアクセスポイントにして、接続したPCからファイルのアップロードまで

ESP32にMicroPythonのインストールされて、PCとUSBで接続して、PC側でターミナルエミュレータの「Tera Term」画面で、MicroPythonの会話モードが 可能になっている状況から、この構築を始めます。
そのためには、ESP32とPCをUSBで接続後、PCのデバイスマネージャーで、接続したポート(COMとLPT)を確認しておく必要があります。
私の場合、デバイスマネージャーのの画面からCOM5が使われていると分かります。
(このCOM番号が得られない場合、ドライバーのインストールが必要です)
以降はESP3のUSB接続がCOM5の場合の例です。
「Tera Term」では、次のように「Serial Port...」を選択してUSB接続で分かったCOM番号など、次のように設定してOKします。

以上で、次のようなMicroPythonの会話モードの画面になります。

この画面に、MicroPythonのコードを貼り付けて実行できます。例えば、次のコードを貼り付けると、 ESP32がアクセスポイントとなり、ESP32のIPアドレスを192.168.220.1にしてDHCPサーバーが起動するためのsetap.pyファイルが生成されます。
(貼り付けるとき、行数が多いと貼り付けで失敗することがあるので、1〜6行程度に分けてコピー貼り付けすると上手く出来るでしょう。)
fw = open('setap.py', 'w')
fw.write("import time # setap.py\n")
fw.write("time.sleep(5)\n")
fw.write("import network\n")
fw.write("ap = network.WLAN(network.AP_IF) # create access-point interface\n")
fw.write("ap.config(essid='ESP32D-AP') # set the ESSID of the access point\n")

fw.write("ap.config(max_clients=10) # set how many clients can connect to the network\n")
fw.write("ap.ifconfig(('192.168.220.1', '255.255.255.0', '192.168.220.1', '8.8.8.8'))\n")
fw.write("# set (ip address, subnet mast, default gateway, dns ip address)\n")
fw.write("ap.active(True)         # activate the interface\n")
fw.close()
上記の貼り付け実行で、正しく'setap.py'のファイルが出来上っているかは、次のコードの実行で確認出来ます。
with open('setap.py', 'r') as fr:
    for s in fr.readlines():print(s, end="")
'setap.py'の実行は、次のコードを貼り付けることで行います。
import setap
上記の実行後、WindowsなどのWifiを確認して、'ESP32D-AP'のアクセスポイントが見えて接続できればOKです。

次は、TCPでファイルを受信するサーバーをfilerec.pyの名前で作るコードです。
次のコードをコピーして「Tera Term」画面に貼り付けで実行させます。
fw = open('filerec.py', 'w')
fw.write("import socket\n")
fw.write("portnumber=59154\n")
fw.write("ip = '192.168.220.1'\n")
fw.write("server_addr =(ip, portnumber)\n")
fw.write("serversock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n")
fw.write("serversock.bind(server_addr)  # IP and port number\n")
fw.write("serversock.listen(1)\n")
fw.write("print(f'{serversock}:\\n start server')\n")
fw.write("sock, address = serversock.accept()\n")
fw.write("print('client address:',address)\n")
fw.write("\n")

fw.write("buf=b'' # For receiving file names and sizes\n")
fw.write("sock.recv(1)        # receieve 'F'\n")
fw.write("while True:\n");
fw.write("    bin = sock.recv(1)        # receieve 1byte\n")
fw.write("    if len(bin) != 1: break   # close or error ?\n")
fw.write("    buf += bin\n")
fw.write("    if buf[-2:] == b'\\r\\n': # end line ?\n")
fw.write("        s = buf[0:-2].decode('utf-8')# binarry to str\n")
fw.write("        print(s)\n")
fw.write("        a=s.split(' ')\n")
fw.write("        filename, filesize = a[0], int(a[1])\n")
fw.write("        print('filename:',  filename, ',  filesize:', filesize) \n")

fw.write("        with open( filename, 'wb') as f:\n")
fw.write("            for n in range(filesize):\n")
fw.write("                bin = sock.recv(1)\n")
fw.write("                f.write(bin)\n")
fw.write("        print('received:', filesize, 'byte.')\n")
fw.close()
上記のファイル受信するサーバーは、接続したクライアントから、次のフォーマットの受信で、そのファイルをESP32側に保存するように作られています。

F「ファイル名」 「ファイルサイス」改行「ファイルサイスに対応するバイナリのバイト列」
先頭にFの文字があり、続けて「受け取るファイル名」、直後に半角スペースが1つ、その後に「ファイルサイス」の10進文字列と改行が在ります。
ここまでがテキスト情報で、この直後に「ファイルサイス」に対応する受信するバイナリのバイト列が並ぶイメージです。

上記の貼り付け実行で、正しく'filerec.py'のファイルが出来上っているかは、次のコードの貼り付け、実行で確認出来ます。
with open('filerec.py', 'r') as fr:
    for s in fr.readlines():print(s, end="")

次は上記のファイル受信サーバーに対して、ファイルを送信するクライアントをPC側で作ります
次の内容で、filesnd.pyの名前のファイルを作ります。
import socket # filesnd.py
import os
import time
def sendFile(sock: socket, filename:str):
    filesize = os.path.getsize(filename)
    s = "F{} {}".format(filename, filesize)
    bin=(s+"\r\n").encode("utf-8") # to binary
    print(bin, end="  ")
    sock.sendall(bin) # send filename filesize
    with open(filename, "rb") as f:
        bin = f.read(filesize)
    # print("send data:" , bin )
    sock.sendall(bin) # send all file
    print("send byte:" , len(bin) )

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
portnumber=59154
ip="192.168.220.1"
sock.connect((ip, portnumber))
print(f"{sock}:接続しました。")
filename = input("送信したい文字ファイル名入力>");
sendFile(sock, filename) #ファイルを送信
time.sleep(1)
sock.close()
以上までの操作で、ESP32にファイルを送信する準備ができました。
以下の操作で、ファイルを送信します。 (一度「ESP32-DevKitC-32D」を再起動してから行うとよいでしょう。)
まず、「Tera Term」画面でESP32側のfilerec.pyモジュールをimportで実行させます。
>>> import setap
>>> import filerec
<socket>:
 start server

この実行結果で、サーバーが起動しているのが分かります。
次は、PC側でファイル送信のクライアント(filesnd.py)を起動です。
その前にPC側のWifi接続画面で、'ESP32D-AP'のアクセスポイントに接続します。
そして、PC側のipconfigコマンドなどで192.168.220.0のネットワークに接続できたことを確認します。
以下は、PC側でクライアント(filesnd.py)を起動して、filesnd.pyのファイルをESP32に送信している例です。
D:\esp32ロボット1\ESP32-DevKitC-32D>python filesnd.py
<socket.socket fd=396, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.220.2', 56245), raddr=('192.168.220.1', 59154)>:接続しました。
送信したい文字ファイル名入力>filesnd.py
b'Ffilesnd.py 803\r\n'  send byte: 803

D:\esp32ロボット1\ESP32-DevKitC-32D>
上記は、PC側の803バイトのfilesnd.pyのファイルを送信した場合のコンソール画面です。
そして、以下はESP32側の「Tera Term」画面が次のように変化します。
>>> import setap
>>> import filerec
<socket>:
 start server
client address ('192.168.220.2', 56245)
filesnd.py 803
filename: filesnd.py ,  filesize: 803
received: 803 byte.
>>>
上記で、803 byteの受信データで、filesnd.pyのファイルを生成したのが分かります。
なお上記の受信処理は、一つのファイルを受信し終わると終了します。
再び受信をしたい場合は、次のように'filerec'モジュールを開放する必要があります。
>>> import sys
>>> del sys.modules['filerec']
この後で、import filerec の入力により、再びfilerecモジュールが起動できます。

ESP32をアクセスポイントにして、遠隔制御用のTCPサーバーが自動的に起動するまでの設定

前述の操作では、USBでESP32とPCを接続し、 PC側で「Tera Term」画面のMicroPythonの会話モードで、 import setapimport filerecの入力操作をしなければモジュールを実行しません。
またfilerec.pyのモジュールでは、1回の実行で1つのファイル受信しかできません。
そこで、1回の実行で複数のファイル受信や各種ファイル操作を行うための次に示す新しいサーバー(server.py)をESP32に転送して使えるようにします。
import os   # server.py ESP32用 (UME用を除いたコード)
import sys
import socket
import _thread
import time
#import esp
#esp.osdebug(None) # ベンダ O/S デバッグメッセージをオフにする

path_datas = "./"
flag_listen_loop=True
sock2=None # TCPクライアントと通信するソケット

def tcp_send_message(sock,msg): # sockのクライアント側にメッセージ送信
    bin=('M'+msg+"\r\n").encode("utf-8")
    if sock : sock.sendall(bin)

def send(msg):# sock2のクライアント側にメッセージ送信
    msg=f"{msg}" # 文字列へ変換
    if sock2 is None: return
    a=msg.split("\n")
    for s in a:
        tcp_send_message(sock2,s)

def ls_filelist(sock,path): # ファイルリストの文字列をクライアントに送信
    tcp_send_message(sock,"ls_filelist:" + path)
    if path == "": path="."
    try:
        for f_name in os.listdir(path) :
            file_info = os.stat(f_name)
            file_size = file_info[6]
            d_inf = 'dir' if (file_info[0] & 0x4000 ) else '   ' # ディレクトリ判定
            msg = f"{d_inf} {file_size:7}  {f_name}"
            tcp_send_message(sock, msg)
    except Exception as e:
        tcp_send_message(sock,"ls_filelist Error:" + str(e))

def cat_filepath(sock, filepath): # filepathのテキスト内容をクライアントに送信
    tcp_send_message(sock, "cat_filepath:" + filepath)
    try:
        with open(filepath, 'r', encoding='utf-8') as fr:
            ss = fr.readlines()
        for s in ss: 
            if s[-1] == '\n' : s=s[:-1] # 改行を除く
            tcp_send_message(sock, s)
    except Exception as e:
        tcp_send_message(sock,"cat_filepath Error"+ str(e))    

def del_filepath(sock, filepath): # filepathのファイルを削除
    tcp_send_message(sock, "del_filepath:" + filepath)
    try:
        os.remove(filepath)
    except Exception as e:
        tcp_send_message(sock,"del_filepath Error:"+ str(e))    

def import_name(sock, name):# nameのモジュールのPythonソースを実行
    tcp_send_message(sock, "import_name:" + name)
    try:
        mod=__import__(name) # 動的なインポートで実行
        del sys.modules[name] # 再度インポートできるように開放
    except Exception as e:
        tcp_send_message(sock,"import_name Error:"+ str(e))

def send_file(sock, filepath):# filepathのファイルをクライアントに送信
    try:
        idx_last_sep = filepath.rfind('\\')
        filename = filepath if idx_last_sep == -1 else filepath[idx_last_sep+1:]
        file_info = os.stat(filepath)
        filesize = file_info[6]
        s = "f{} {}".format(filename, filesize)
        bin=(s+"\r\n").encode("utf-8")
        sock.sendall(bin) #send filename filesize
        if filesize > 0:
            with open(filepath, "rb") as f:
                bin = f.read(filesize)
            sock.sendall(bin) # send binary file
        #
        tcp_send_message(sock,"server:send_file:"+ filepath)  
    except Exception as e:
        tcp_send_message(sock,"server:send_file Error:"+ str(e))    

def recieve_file(sock):# TCPでファイルを受信(1byte受信の繰り返し)
    buf=b""
    while True:
        bin = sock.recv(1)
        if bin == b'': break
        buf += bin
        if buf[-2:] == b"\r\n":
            s = buf[0:-2].decode('utf-8')
            a=s.split(' ')
            filename, filesize = a[0], int(a[1])
            f=open( path_datas + filename, "wb") # 受信ファイルの保存
            n=0
            tcp_send_message(sock,f"Starting to receive {filename}:size={filesize} Please wait.")
            while n < filesize:
                bin = sock.recv(1)
                if bin == b'': break
                f.write(bin)
                n += 1
                if n % 10000 == 0: tcp_send_message(sock,f"\n{n}bytes were received.")
            #
            f.close()
            tcp_send_message(sock,f"\n{filename} size:{filesize}, {n}bytes receive end.")
            if filename.endswith(".umh"):
                send_cmdfile(sock,filename) # ファイルのコマンド内容をUMEHOSHI ITAへ送信
            break
        #
    #print(filename + ":size=" + str(filesize)) # debug ---------------

def recieve_file_Max(sock):# TCPでファイルを受信(バッファを最大して受信)
    buf=b""
    while True:
        bin = sock.recv(1)
        if len(bin) != 1: break
        buf += bin
        if buf[-2:] == b"\r\n":
            s = buf[0:-2].decode('utf-8')
            a=s.split(' ')
            filename, filesize = a[0], int(a[1])
            bin=b""
            while len(bin) < filesize:
                bin += sock.recv(filesize-len(bin))
            with open( path_datas + filename, "wb") as f: # 受信ファイルの保存
                f.write(bin)
            tcp_send_message(sock,f"Server:'{filename}' {len(bin)}bytes received.")
            if filename.endswith(".umh"):
                send_cmdfile(sock,filename) # ファイルのコマンド内容をUMEHOSHI ITAへ送信
            break
        #
    #print(filename + ":size=" + str(filesize)) # debug ---------------

def recieve_message(sock): # 受信メッセージに対する処理
    global flag_listen_loop
    buf=b""
    while True:
        bin = sock.recv(1)
        if len(bin) != 1:
            flag_listen_loop = False
            return False
        #           
        buf += bin
        if buf[-2:] == b"\r\n":
            s = buf[0:-2].decode('utf-8').strip()
            if s == "end_listen_loop": # 受信文字列がこれと一致するなら終了
                flag_listen_loop = False
                return False
            #
            if s.startswith("ls -l"): # ファイルリスト要求に対する送信処理
                ls_filelist(sock, s[5:].strip() )
                return True
            #
            if s.startswith("cat "):# ファイル内容表示要求に対する送信処理
                cat_filepath(sock, s[4:] )
                return True
            #
            if s.startswith("del "):# ファイル削除要求に対する処理
                del_filepath(sock, s[4:] )
                return True
            #
            if s.startswith("import "):# Pythonファイルの実行要求に対する処理
                import_name(sock, s[7:] )
                return True
            #
            if s.startswith("get "): # ファイル要求に対するファイル送信
                send_file(sock, s[4:] )
                return True
            #
            if s.startswith("exc "): # .umhファイルの内容で、[UMEHOSHI ITA]のUART1に出力する。
                send_cmdfile(sock, s[4:] )
                return True
            #
            if len(s)>1 and s[0] in "SRG": 
                print( s ) # 受信文字列を、UME専用Hexコマンドと判断して、UMEHOSHI ITAへ送信
            else:
                tcp_send_message(sock,s) # 受信文字列を、TCPクライアントへECHO送信
            #
            return True

def receiveData(sock): # 受信処理ループ
    # tcp_send_message(sock,"start sever receiveData" )
    while True:
        bin = sock.recv(1)#1byte recieve
        if len(bin) != 1: break
        if bin == b"f" : recieve_file(sock)
        elif bin == b"F" : recieve_file_Max(sock)
        elif bin == b"M" : recieve_message(sock)
        else:
            tcp_send_message(sock,"server receive error:" + str(bin) )
    #

# UME用で除いた箇所の部分がここに存在していた。

# ------------- Main -------------------
portnumber=59154
#ip = "192.168.220.1"
#ip = "192.168.222.1"
ip = "192.168.0.110"
server_addr =(ip, portnumber)
serversock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversock.bind(server_addr)
#time.sleep(3.1) # 3.1秒待機 ([UMEHOSHI ITA]の初期でUME用で不要)
#thread_id = _thread.start_new_thread(input_loop, ()) #UME用で入力用スレッドのUME用で不要
serversock.listen(1) # TCPサーバ受け入れ待機
while flag_listen_loop:
    #
    sock2, address = serversock.accept() # クライアントの接続許可
    serversock.close()
    tcp_send_message(sock2, "\nConnect!") # TCPクライアントへのメッセージ
    try: 
        receiveData( sock2 ) # 受信応答処理のループ
    except: pass
    # print(cmd) # ― ― ― ― の音で終了処理で、UME用により不要
    sock2.close()

上記のコードをserver.pyの名前で作ります。
このコードは、UMEHOSHI ITA基板に取り付けたESP32用のserver.pyから余計なコードを除いた処理です。
これは、ファイル受信以外に、 ファイルリスト取得、テキストファイル内容の表示、ファイルダウンロード、Pythonファイルのimport起動などを可能にするサーバーのコードです。

このをserver.pyをPCからESP32へ送信します。
それには、前述で行ったように「Tera Term」画面でESP32側のfilerec.pyモジュールをimportで実行させます。次の操作です。
>>> import setap
>>> import filerec
<socket>:
 start server

次にPC側のWifi接続画面で、'ESP32D-AP'のアクセスポイントに接続します。
そして、次のようにPC側でファイル送信のクライアント(filesnd.py)を起動し、上で作ったserver.pyを送信します。
以下は、PC側でクライアント(filesnd.py)を起動して、server.pyのファイルをESP32に送信している例です。
D:\esp32ロボット1\ESP32-DevKitC-32D>python filesnd.py
<socket.socket fd=348, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.220.2', 59693), raddr=('192.168.220.1', 59154)>:接続しました。
送信したい文字ファイル名入力>server.py
b'Fserver.py 8673\r\n'  send byte: 8673

D:\esp32ロボット1\ESP32-DevKitC-32D>
この実行で、server.pyのファイルががESP32にコピーされまます。この時のサーバ側の「Tera Term」画面は次のようになるでしょう。
>>> import setap
>>> import filerec
<socket>:
 start server
client address ('192.168.220.2', 59693)
server.py 8673
filename: server.py ,  filesize: 8673
received: 8673 byte.
>>>

次に、ESP32をアクセスポイントにする「setap.py」と上記で送った遠隔制御用のTCPサーバーの「 server.py」を起動時に自動的に起動させます。
そのために、boot.pyで起動モジュールimportします。そのboot.pyをPC側で、次の内容で作成します。
import setap
import server
上記で作成したboot.pyを転送する方法を示します。
一度、ESP32を再起動させます。
「Tera Term」画面でESP32側のserver.pyモジュールをimportで実行させます。次の操作です。(filerecの代わりにserverを使った例です)
>>> import setap
>>> import server
以上でserver.pyを起動しました。
そしてPC側のWifi接続画面で、'ESP32D-AP'のアクセスポイントに接続します。
これでPC側からfilesnd.pyで次のようにファイルを送ることができます。
(server.pyはfilerec.pyと同じように、filesnd.pyより送られたファイルを受け取って記憶することができます。)
D:\esp32ロボット1\ESP32-DevKitC-32D>python filesnd.py
<socket.socket fd=372, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.220.2', 49793), raddr=('192.168.220.1', 59154)>:接続しました。
送信したい文字ファイル名入力>boot.py
b'Fboot.py 27\r\n'  send byte: 27

D:\esp32ロボット1\ESP32-DevKitC-32D>
正しくboot.pyが転送できたかは、次の操作でboot.pyを表示させて確認することができます。
with open('boot.py', 'r') as fr:
   for s in fr.readlines():print(s, end="")
正しい内容であれば、ESP32の基板のUSBケーブルをPCから抜いて、代わりにUSBの電源アダプタに繋いで起動します。
これで、Wifiアクセスポイントとしてsetap.pyが起動し、192.168.220.1の遠隔制御用サーバーのserver.pyが起動します。

上記で実行したESP32の遠隔制御用サーバーを制御するクライアント(client.py)の作成と、実行例

ESP32の基板をUSBの電源アダプタに繋いで起動します。
前述の操作が終わっていれば、この電源を供給で、ESP32がWifiアクセスポイントになっています。
そしてESP32は、192.168.220.1:59154の遠隔制御用サーバー(server.py)が起動します。
この上記サーバー(server.py)は、MicroPythonで動作しており、ESP32への任意ファイルアップロード、ダウンロード、 ルートディレクトリのリスト情報出力、任意テキストファイル内容の情報出力、ファイルの削除、pythonモジュールの実行、サーバーの実行終了 をクライアントから要求できるように作っています。
以下にこのサーバーを制御するコンソール用のクライアントソフト(client.py)を紹介します。
このクライアントは、UMEHOSHI ITA基板に取り付けたESP32のサーバーに対するクライアントソフトから、余計なコードを除いて作成したコードです。
import os 		# client_esp32.py
import socket
import sys
import time
import threading

path_datas="./"

# ----- 受信関連 ---------------------------------
def recieve_file(sock):
    buf=b""
    while True:
        bin = sock.recv(1)
        if len(bin) != 1: break
        buf += bin
        if buf[-2:] == b"\r\n":
            s = buf[0:-2].decode('utf-8')
            a=s.split(' ')
            filename, filesize = a[0], int(a[1])
            bin=b""
            while len(bin) < filesize:
                bin += sock.recv(filesize-len(bin))
            with open( path_datas + filename, "wb") as f:
                f.write(bin)
            #tcp_send_message(sock,filename + ":size=" + str(len(bin)))
            break
    print(filename + ":size=" + str(filesize)) # debug ---------------

def recieve_message(sock): # TCP文字列メッセージ受信
    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)
            break

def receiveData(sock): # 全てのTCP受信のスレッド用関数
    #print( "start receiveData" )
    while True:
        try:
            bin = sock.recv(1)#1byte受信
        except Exception as e: break
        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." )

# ----- 送信関連 ---------------------------------
def send_file(sock, filepath):
    try:
        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) )
    except Exception as e:
        print( "send_file error:" + str(e))

def send_file_Max(sock, filepath):
    try:
        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) )
    except Exception as e:
        print( "send_file_Max error:" + str(e))

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

# ----- メイン ---------------------------------
portnumber=59154
ip="192.168.220.1"
s = input(f"IP (defualt:{ip})Address>")
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()
time.sleep(1.0)
loopFlag = True
cmd=""
while loopFlag:
    #s = input("入力:M/F/'quit'/w/a/s/d/b/?>")
    s = input("入力:M/F/'quit'?>")
    if s != "": cmd = s
    if cmd == 'M':
        # msg = input("UME HEX Command /exc /ls -l/cat /get /del /import >")
        msg = input("ls -l/cat /get /del /import >")
        send_message(sock,msg)
        cmd=' '
    elif cmd == 'f' or cmd == 'F':
        s='F'
        print(os.listdir(path_datas))
        filename = input("送信したいファイル名入力>")
        if not os.path.isfile(path_datas + "/" + filename):
            print( filename, "no exist")
            continue
        if cmd == 'f' : send_file(sock, path_datas + "/" + filename)
        if cmd == 'F' : send_file_Max(sock, path_datas + "/" + filename)
        cmd=' '
    elif cmd == 'quit':
        send_message(sock,'end_listen_loop')
        loopFlag=False
    else:
        pass # 追加コマンド処理
    #
    time.sleep(1.0)

sock.close()
sys.exit(0)

上記コード実行例(使い方を示しています。)

ここでは、"Hello World"をクライアントのPCコンコール画面に表示するtest.pyをESP32にアップロードして、それを実行する例を示します。
この時に使うtest.pyは、PC側で次のように作ります。
import server

server.send("Hello World");
この状態で、ESP32の基板をUSBの電源アダプタに繋いで起動します。
そしてPC側のWifi接続画面で、'ESP32D-AP'のアクセスポイントに接続します。
そして、PC側のコマンドラインで、client.pyを実行して次のように操作します。
(操作は、'test.py'をESP32へアップロードし、ESP32側のファイルリストを表示し、アップロードした'test.py'内容を表示してから その'test.py'を実行し、次に'test.py'を削除してから再びファイルリストを表示し、最後にESP32のサーバーを終了しています。)
D:\esp32ロボット1\ESP32-DevKitC-32D>python client.py
IP (defualt:192.168.220.1)Address>		Enterのキー入力で、デフォルトのサーバに接続を要求

Connect!	サーバに接続したことを知らせる表示
入力:M/F/'quit'?>F		ESP32へアップロード指示のキー入力
['boot.py', 'client.py', 'filesnd.py', 'info', 'server.py', 'servertest', 'test.py']	クライアント側のディレクトリ表示
送信したいファイル名入力>test.py		ESP32へtest.pyをアップロードするのキー入力
Server:'test.py' 46bytes received.
入力:M/F/'quit'?>M		他の操作のメニュー表示を指示するキー入力
ls -l/cat /get /del /import >ls -l	ESP32側のファイルリスト表示を指示するキー入力
Send:ls -l
ls_filelist:
         27  boot.py
        962  filerec.py
        803  filesnd.py
       8673  server.py
        448  setap.py
        937  tcpfrec.py
         46  test.py
入力:M/F/'quit'?>M		他の操作のメニュー表示を指示するキー入力
ls -l/cat /get /del /import >cat test.py	他ESP32側のフtest.pyを表示を指示するキー入力
Send:cat test.py
cat_filepath:test.py
import server

server.send("Hello World");
入力:M/F/'quit'?>M		他の操作のメニュー表示を指示するキー入力
ls -l/cat /get /del /import >import test	testモジュールを実行させるキー入力
Send:import test
import_name:test
Hello World	testの実行で、得られた表示文字列
入力:M/F/'quit'?>M		他の操作のメニュー表示を指示するキー入力
ls -l/cat /get /del /import >get setap.py		setap.pyのファイルをPC側にダウンロード指示するキー入力
入力:M/F/'quit'?>M
ls -l/cat /get /del /import >del test.py		ESP32側のtest.pyを削除するキー入力
Send:del test.py
del_filepath:test.py
入力:M/F/'quit'?>M		
ls -l/cat /get /del /import >ls -l	ファイルリスト表示のキー入力で、削除できたことを確認
Send:ls -l
ls_filelist:
         27  boot.py
        962  filerec.py
        803  filesnd.py
       8673  server.py
        448  setap.py
        937  tcpfrec.py
入力:M/F/'quit'?>quit		終了を指示するキー入力
Send:end_listen_loop
receiveData ended.

D:\esp32ロボット1\ESP32-DevKitC-32D>
上記操作で、'test.py'をESP32へアップロードし、ESP32側のファイルリストを表示し、 アップロードした'test.py'のテキスト内容を表示してから その'test.py'を実行し、次に'test.py'を削除してから再びファイルリストを表示し、最後にESP32のサーバーを終了しています。

さて上記でESP32で実行させたtest.pyの内容は次の内容でした。
import server

server.send("Hello World");
このESP32の実行で、server.py内の.sendによってクライアント側に"Hello World"の文字列をTCP送信し、クライアント側での表示を実現しています。
つまり、ESP32で実するのpythonコードからクライアント画面に文字列を表示させる場合は、server.sendメソッドを使います。
しかし、、server.sendを使った表示コードはPC側での単体実行で、表示するコードとして使えません。
こそこで、れまでPC側で使っていたprint表示を変更せずに使う場合、次のようにソース先頭でprint関数をserver.sendに置き換えると良いでしょう。
import server
print = server.send # printをクライアントへ出力する機能に置き換える

print(Hello World");
上記のような赤の2行を追加すれば、print関数でクライアントへ表示させることができるようになります。
但し、この置き換えたprintの引数は一つだけで、複数の文字列出力には対応していないことに注意して使う必要があります。

複数の情報を1つのprintで表示させる場合は、フォーマット文字列で一つの引数になるようにまとめると良いでしょう。
以下はフォーマット文字の利用例で、ESP32の使用状況や空状態の表示と使えるメモリ容量を表示させるソースとその実行例の一部です。 ファイルは df.py の名前で、次の内容で作ります。
import server
print = server.send # printをクライアントへ出力する機能に置き換える
import os
import gc

stat = os.statvfs('/')
block_size = stat[0]  # 1ブロックのサイズ(バイト単位)
total_blocks = stat[2]  # f_blocks(総ブロック数)
free_blocks = stat[3] # 空きブロック数
print(f"block_size: {block_size} bytes, free_blocks: {free_blocks}")
free_space=block_size * free_blocks  # 空き容量(バイト単位)
print(f"Free space: {free_space} bytes")
total_size = block_size * total_blocks  # 総容量(バイト)
print(f"Total Storage: {total_size} bytes\n")

print(f"Allocatable memory size:{gc.mem_free()}") # 今どのくらいヒープが空いているかのサイズ(byte)確認

以下は、この実行例です。
D:\esp32ロボット1\ESP32-DevKitC-32D>python client.py
IP (defualt:192.168.220.1)Address>

Connect!
入力:M/F/'quit'?>F
['boot.py', 'client.py', 'df.py', 'filesnd.py', 'info', 'server.py', 'servertest', 'test.py']
送信したいファイル名入力>df.py
Server:'df.py' 692bytes received.
入力:M/F/'quit'?>M
ls -l/cat /get /del /import >import df
Send:import df
import_name:df
block_size: 4096 bytes, free_blocks: 502
Free space: 2056192 bytes
Total Storage: 2097152 bytes

Allocatable memory size:91424
入力:M/F/'quit'?>quit
Send:end_listen_loop
receiveData ended.

D:\esp32ロボット1\ESP32-DevKitC-32D>