いっぺーちゃんの いろいろやってみよ~

micropython on ESP32 でモジュールをインストールする

micropythonにはPyPiサイトに用意されたモジュールをインストールするためのモジュール upip が用意されています。

import upip
upip.install("パッケージ名")

でインストールできます。
ちなみに、upipのソースはmicropython-esp32/esp32/modules/upip.pyにあり、micropython本体に組み込まれています。

しかし、サーバアドレスが固定されているため、自分で作ったモジュールをインストールするには使用できません。
また、PyPiサイトのモジュール管理形式を使用しているため、URLを自分のサーバに変えるだけではうまくいきません。

 

そこで、upipもどきなモジュール upipm (例によって「もどき」の「m」を追加)を作ってみました。
BaseURL で指定されたURLからget_file()の第1パラメータで指定したファイルを第2パラメータで指定したディレクトリに保存します。
ダウンロード元はHTTPサーバでなければなりません(Espruinoで作ったmnpmと同じです)。

import sys
import gc
import uos as os

gc.collect()

debug = False

# Base URL
BaseURL = "http://192.168.78.80/py_modules/"# 自分の環境に合わせて修正してくさだい

class NotFoundError(Exception):
    pass

import ussl
import usocket
warn_ussl = True
def url_open(url):
    global warn_ussl

    if debug:
        print(url)

    proto, _, host, urlpath = url.split('/', 3)
    if debug:
        print("proto:%s, host:%s, urlpath:%s" % (proto, host, urlpath))

    if proto == "https:":
        port = 443
    else:
        port = 80

    try:
        ai = usocket.getaddrinfo(host, port)
    except OSError as e:
        fatal("Unable to resolve %s (no Internet?)" % host, e)

    if debug:
        print("Address infos:", ai)
    addr = ai[0][4]

    s = usocket.socket(ai[0][0])
    try:
        if debug:
            print("Connect address:", addr)
        s.connect(addr)

        if proto == "https:":
            s = ussl.wrap_socket(s)
            if warn_ussl:
                print("Warning: %s SSL certificate is not validated" % host)
                warn_ussl = False

        # MicroPython rawsocket module supports file interface directly
        s.write("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n" % (urlpath, host))
        l = s.readline()
        if debug:
            print(l)
        protover, status, msg = l.split(None, 2)
        if status != b"200":
            if status == b"404" or status == b"301":
                raise NotFoundError("File not found")
            raise ValueError(status)
        while 1:
            l = s.readline()
            if debug:
                print(l)
            if not l:
                raise ValueError("Unexpected EOF in HTTP headers")
            if l == b'\r\n':
                break
    except Exception as e:
        s.close()
        raise e

    return s


def fatal(msg, exc=None):
    print("Error:", msg)
    if exc and debug:
        raise exc
    sys.exit(1)


def get_file(name, distdir=None):
    if distdir is None:
        distdir = sys.path[1]
    if distdir[-1] != "/":
        distdir += "/"

    f = url_open(BaseURL + name)
    print("==== START ====")
    with open(distdir + name, "wb") as outf:
        while True:
            l = f.readline()
            if not l:
                break
            outf.write(l, len(l))
            # print(len(l))
            print(l)
    print("==== DONE ====")

def help():
    print("""\
upipm - Simple PyPI package manager **MODOKI** for MicroPython
Usage: 
import upipm; upipm.get_file(name, [<path>])

If <path> is not given, packages will be installed into sys.path[1].
""")
    print("Current value of sys.path[1]:", sys.path[1])

if __name__ == "__main__":
    def main():
        get_file("upipm.py")

if __name__ == "__main__":
    main()

 

BaseURLは自分の環境に合わせて変更しておいてください。
また、オンボードストレージに/libディレクトリが存在しない場合は、以下のように実行してディレクトリを作成しておいてください。

import uos
uos.chdir("/")
uos.listdir()
     ==> ['boot.py']
uos.mkdir("/lib")
uos.listdir()
     ==> ['boot.py', 'lib']

 

このプログラムのファイルをupipm.pyという名前でBaseURLの場所に格納しておき、プログラム自体をコンソールからコピペして実行すると、upipm.pyがインストールされます。

以下のように実行すると正常にインストールできたか確認できます。

import uos
uos.listdir("/lib")
     ==> ['upipm.py']

 

インストール後は、以下のように実行すると任意のファイルをインストールできます。
モジュール単位でのインストールではなく、ファイル単位なので、ファイル拡張子(.py)も忘れずに指定してください。
デフォルトのインストール先はsys.path[1](ESP32の場合は/lib)です。

import upipm
upipm.get_file("ファイル名")

 

また、第2パラメータにはインストール先を指定することも可能です。
たとえば、こんな感じ。

upipm.get_file("boot.py", "/")