UMEHOSHI ITA TOP PAGE    COMPUTER SHIEN LAB

「UMEHOSHI ITA」にESP32-WROOM-32E-N16を、「UMEHOSHI ITA」へ取り付ける例を示しています。
(「UMEHOSHI ITA」は一部の端子だけをCN11とCN12を介して引き出し、そこに別途の自作基板を取り付けて、少しだけ利用することを想定しています。)

ESP32-WROOM-32E-N16を付けての実験

ESP32をプログラム書き込みの「UART Download Mode」にするためには起動時にIO0端子をLOWにするため切り替えが必要です。
([UMEHOSHI ITA]において、ESP32のIO0端子はどこにも繋がっていません。DTR/RTS制御は対応していません)
そのためには、 「IO0」を引き出して空いているCN12-5とジャンパーで配線し、ここに繋がるスイッチを付けたボードを別途に作成する必要があります。
そのため[UMEHOSHI ITA]基板の裏で「IO0:(U19-25)に繋がっているT6スルーホール」からジャンパー線で引き出して、
コネクタCN12-5と接続させるため、次のように半田付けしました。


上記は半田面です。
下記がレーザー測距センサー「VL53L1X」と9軸センサー「BNO055」とと別途のSW3(黄色)を付けた拡張ボードと、 その回路図 兼 配置図です。

両面スルーホール2.54mm×2mmピッチ基板に
CN11とCN12の2mmピッチソケットを付けています。
このCN11とCN12の繋がりの関係を以下に表で示します。(この部分的な回路図)
CN12ESP32-WROOM-32D端子名PIC32MX端子No:名-CN11ESP32-WROOM-32D端子名PIC32MX端子No:名
CN12-12: 3V313: VDD CN11-1011: IO26
CN12-235:TXD0 出力12: RX (RA4) 入力 CN11-910: IO25
CN12-334:RXD0 入力11: TX (RB4) 出力 CN11-837: IO23
CN12-46: IO34 CN11-736: IO22(SCL)(RTS)
CN12-5ジャンパー配線でSW3へ   CN11-6JMP10-OPEN-U19-23: IO1518: RTS (RB9) 出力
CN12-67: IO35 CN11-533:IO21 (SDA)
CN12-78: IO32 CN11-4 5: SENSOR_VN
CN12-89: IO33 CN11-3 4: SENSER_VP
CN12-912: IO27 CN11-2JUM11-OPEN-16: IO1317: CTS (RB8) 入力
CN12-10GND19: Vss CN11-131: IO19(CTS)
このCN11とCN12のI2C端子(SCLとSDA)で、
「VL53L1X使用 レーザー測距センサーモジュール(ToF)モジュール」と、
「BNO055使用 9軸センサーフュージョンモジュール」をESP32と繋げています。
下記がUMEHOSHI ITAに、上記拡張ボードを取り付けたイメージです。(センサー情報の基準も示しています。)

また、ESP32のIO0端子とSW3(黄)を繋げて、
起動時にSW3(黄)を押すことで、ESP32にファームウェアを書き込むダウンロードモードにすることを可能にしています。

MicroPythonファームウェアの書き込み
MicroPython公式サイトのESP32専用ダウンロードページから、
Releases版(ESP32_GENERIC-20260406-v1.28.0.bin)を取得してesptoolツールでインストールしました。
esptoolツールを使ったMicroPythonファームウェア書き込みの方法は、 このリンクページを参照してください。
(pipでesptoolやmpremoteをインストールして使っています。)

以下は、 ESP32の36PinのIO22(SCL)と、33PinのIO21(SDA)を使って、
「I2C」インターフェイスが正しく動作しているかを確認するMicro Pythonのプログラム(scanI2C.py)を示します。
from machine import Pin, I2C

i2c = I2C(0, scl=Pin(22), sda=Pin(21), freq=100000)
print("--- I2C Bus Scan Start ---")
# I2Cバス上のデバイスをスキャン
devices = i2c.scan()

if len(devices) == 0:
    print("No I2C devices were found.")
else:
    for device in devices:
        print(f"device address: {hex(device)}")
    print("That is all.")
上記をmpremoteを使って実行する例を下記に示します。
PCとUMEHOSHI ITAをUSBケーブルで接続し、 SW1(赤)リセットと、 SW2(白)を押して、
SW1(赤)だけ離した後に3秒待ってLEDが消えた後に、残りスイッチSW2(白)を離します。
これで、UMEHOSHI ITA をスルーモード(USB CDCのブリッジモード)になり、次のように動作できます。
R:\drone>mpremote connect COM3 run scanI2C.py
--- I2C Bus Scan Start ---
device address: 0x28
device address: 0x29
That is all.

R:\drone>
0x29番地のアドレスを持つデバイスが、VL53L1X使用の距離センサーで、
0x28番地のアドレスを持つデバイスが、BNO055使用の9軸センサーです。


MicroPythonで、距離センサーと9軸センサーを使う。

下記で使うVL53L1X使用の距離センサーのコードは、 このリンク先で示すvl53l1x_uld.pyを参考にしています。
またBNO055使用の距離センサーのコードは、 このリンク先で示すBNO055_uld.pyを参考にしています。

以上を参考に、この2つのセンサーのドライバーコード(sensor.py)を次のように作りました。
import time
from machine import I2C, Pin

debug_flag=False # デバック用debug_print関数の出力を行うかの有無指定

def debug_print(*args, **kwargs):
    if debug_flag : print(*args, **kwargs)

# ============ VL53L1Xの距離センサー関連の各種定義 =================================
class VL53L1X:
    DEFAULT_ADDRESS = 0x29 #  I2C ADDRESS
    IDENTIFICATION__MODEL_ID = 0x010F # センサーモデル情報の取得レジスタ
    IDENTIFICATION__MODULE_TYPE = 0x0110 # センサータイプ情報の取得レジスタ
    IDENTIFICATION__REVISION_ID = 0x0111 # センサーリビジョン情報の取得レジスタ
    IDENTIFICATION__MODULE_ID = 0x0112 # センサーモジュール情報の取得レジスタ

    FIRMWARE__SYSTEM_STATUS = 0x00E5 # Boot 判定レジスタ(non-zero = booted)  
    VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND = 0x0008 # VHV(Vertical High Voltage)

    # ---GPIO / interrupt---------------------------------------------------------
    GPIO_HV_MUX__CTRL = 0x0030 # GPIO1ピン(データ準備完了を知らせる割り込み通知ピン)の出力設定用
    GPIO__TIO_HV_STATUS = 0x0031 # データ準備完了などを調べるレジスタ
    SYSTEM__INTERRUPT_CLEAR = 0x0086 # センサーに次の測定を始めさせるためのレジスタ

    # ---- set_distance_modeメソッドの変更で、同時に設定するレジスタ番号--------------
    PHASECAL_CONFIG__TIMEOUT_MACROP = 0x004B # 位相キャリブレーション・タイムアウト

    RANGE_CONFIG__TIMEOUT_MACROP_A_HI = 0x005E # レーザー光が戻りを待つ最大時間(タイムアウト時間)の上位バイト
    RANGE_CONFIG__TIMEOUT_MACROP_B_HI = 0x0061 # レーザー光が戻りを待つ最大時間(タイムアウト時間)の下位バイト

    RANGE_CONFIG__VCSEL_PERIOD_A = 0x0060 #フェーズAの照射するレーザー光のパルスの周期設定用レジスタ
    RANGE_CONFIG__VCSEL_PERIOD_B = 0x0063 #フェーズBの照射するレーザー光のパルスの周期設定用レジスタ

    RANGE_CONFIG__VALID_PHASE_HIGH = 0x0069 # 反射して戻ってきたレーザー光の位相の受け入れ閾値

    SD_CONFIG__WOI_SD0 = 0x0078 # 反射して戻ってきた光の待ち受け時間(ウインドウ)の幅を調整するためのレジスタ
    SD_CONFIG__INITIAL_PHASE_SD0 = 0x007A # 反射して戻ってきた光の待ち受ける開始タイミング調整するためのレジスタ

    ROI_CONFIG__USER_ROI_CENTRE_SPAD = 0x007F # 測定視野の中心となる受光ダイオードを指定するレジスタ
    ROI_CONFIG__USER_ROI_REQUESTED_GLOBAL_XY_SIZE = 0x0080 # 測定視野のためのダイオードの利用幅を指定

    SYSTEM__SEQUENCE_CONFIG = 0x0081 # 測定の中で、「どの内部処理(ステップ)を実行するか」の組み合わせを決める設定
    SYSTEM__MODE_START = 0x0087 # 測定スタートを制御

    # ---Result--測定結果-------------------------------------------------------
    RESULT__RANGE_STATUS = 0x0089 # 直前に行われた距離測定が正しく成功したか、またはどのようなエラーが発生したか
    RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0 = 0x008C # SPAD(光子アバランシェフォトダイオード)の使用個数
    RESULT__AMBIENT_COUNT_RATE_MCPS_SD = 0x0090 # センサーが測定した周囲の環境光(ノイズとなる光)の強さ
    RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0 = 0x0096 # 『距離(ミリメートル単位)』」
    RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0 = 0x0098 # 邪魔な反射を除いた最強の光の強さ

    #-- Distance modes 短距離、中距離、長距離の3つの距離モードがあるが、ここでは2つを指定できる指定にした
    DISTANCE_MODE_SHORT = 1 # 短距離向け 約1.3 m
    DISTANCE_MODE_LONG = 2 # 長距離向け 最大約4 m

    # ULD default configuration------------------------------------------
    DEFAULT_CONFIGURATION = bytes([
    0x00,  # 0x2D: VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND (VHVキャリブレーション タイムアウト制御)
    0x00,  # 0x2E: VHV_CONFIG__COUNT_THRESH (VHVカウント閾値)
    0x00,  # 0x2F: ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS (クロストーク補正オフセット [MSB])
    0x01,  # 0x30: ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS (クロストーク補正オフセット [LSB])
    0x02,  # 0x31: ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS (クロストークX方向勾配 [MSB])
    0x00,  # 0x32: ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS (クロストークX方向勾配 [LSB])
    0x02,  # 0x33: ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS (クロストークY方向勾配 [MSB])
    0x08,  # 0x34: ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS (クロストークY方向勾配 [LSB])
    0x00,  # 0x35: ALGO__PAUSE_SOLID_ARRAY_CHECK (ソリッドアレイ検証の一時停止フラグ)
    0x08,  # 0x36: ALGO__PART_TO_PART_RANGE_OFFSET_MM (個体差距離補正オフセット(mm) [MSB])
    0x10,  # 0x37: ALGO__PART_TO_PART_RANGE_OFFSET_MM (個体差距離補正オフセット(mm) [LSB])
    0x01,  # 0x38: MM_CONFIG__INNER_OFFSET_MM (内部測定領域オフセット(mm) [MSB])
    0x01,  # 0x39: MM_CONFIG__INNER_OFFSET_MM (内部測定領域オフセット(mm) [LSB])
    0x00,  # 0x3A: MM_CONFIG__OUTER_OFFSET_MM (外部測定領域オフセット(mm) [MSB])
    0x00,  # 0x3B: MM_CONFIG__OUTER_OFFSET_MM (外部測定領域オフセット(mm) [LSB])
    0x00,  # 0x3C: DSS_CONFIG__TARGET_TOTAL_RATE_MCPS (DSS目標トータルリターンレート(Mcps) [MSB])
    0x00,  # 0x3D: DSS_CONFIG__TARGET_TOTAL_RATE_MCPS (DSS目標トータルリターンレート(Mcps) [LSB])
    0xFF,  # 0x3E: DEBUG__CTRL (内部デバッグ制御)
    0x00,  # 0x3F: TEST_MODE__CTRL (テストモード制御)
    0x0F,  # 0x40: CLK__CONFIG (内部メインクロック設定)
    0x00,  # 0x41: SYSTEM__START (システムトリガー/開始制御)
    0x00,  # 0x42: SYSTEM__HISTORY_CTRL (履歴バッファ制御)
    0x00,  # 0x43: SYSTEM__STREAM_COUNT_CTRL (ストリームカウント制御)
    0x00,  # 0x44: SYSTEM__SEQUENCE_CONFIG (測定シーケンス有効化設定)
    0x00,  # 0x45: SYSTEM__GROUPED_PARAMETER_HOLD_0 (パラメータ一括適用保持 0)
    0x20,  # 0x46: VHV_CONFIG__INIT (VHV初期化制御)
    0x0B,  # 0x47: VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND (VHVループ制限値)
    0x00,  # 0x48: MYCRO_TUNE (内部アナログチューニング 1)
    0x00,  # 0x49: MYCRO_TUNE (内部アナログチューニング 2)
    0x02,  # 0x4A: GLOBAL_CONFIG__SPAD_ENABLES_REF_0 (参照SPAD有効化マップ 0)
    0x0A,  # 0x4B: GLOBAL_CONFIG__SPAD_ENABLES_REF_1 (参照SPAD有効化マップ 1)
    0x21,  # 0x4C: GLOBAL_CONFIG__SPAD_ENABLES_REF_2 (参照SPAD有効化マップ 2)
    0x00,  # 0x4D: GLOBAL_CONFIG__SPAD_ENABLES_REF_3 (参照SPAD有効化マップ 3)
    0x00,  # 0x4E: GLOBAL_CONFIG__SPAD_ENABLES_REF_4 (参照SPAD有効化マップ 4)
    0x05,  # 0x4F: GLOBAL_CONFIG__SPAD_ENABLES_REF_5 (参照SPAD有効化マップ 5)
    0x00,  # 0x50: GLOBAL_CONFIG__REF_EN_START_SELECT (参照SPAD開始選択)
    0x00,  # 0x51: REF_SPAD_MAN__NUM_REQUESTED_REF_SPADS (要求参照SPAD数)
    0x00,  # 0x52: REF_SPAD_MAN__REF_LOCATION (参照SPAD配置場所)
    0x00,  # 0x53: ALGO__CONSISTENCY_CHECK__REFERENCE_SIGNAL_NON_KIN_INTRINSIC (参照信号整合性チェック)
    0xC8,  # 0x54: ALGO__CONSISTENCY_CHECK__EVENT_CONSISTENCY (イベント整合性閾値 0)
    0x00,  # 0x55: ALGO__CONSISTENCY_CHECK__EVENT_CONSISTENCY (イベント整合性閾値 1)
    0x00,  # 0x56: ALGO__CONSISTENCY_CHECK__EVENT_CONSISTENCY (イベント整合性閾値 2)
    0x38,  # 0x57: ALGO__CONSISTENCY_CHECK__EVENT_CONSISTENCY (イベント整合性閾値 3)
    0xFF,  # 0x58: ALGO__RANGE_IGNORE_THRESHOLD_MCPS (無視するノイズ距離閾値 [MSB])
    0x01,  # 0x59: ALGO__RANGE_IGNORE_THRESHOLD_MCPS (無視するノイズ距離閾値 [LSB])
    0x00,  # 0x5A: ALGO__RANGE_MIN_CLIP (最小クリッピング範囲)
    0x08,  # 0x5B: ALGO__CONSISTENCY_CHECK__TOTAL_RATE_PER_SPAD_MCPS (SPADあたりレート整合性 [MSB])
    0x00,  # 0x5C: ALGO__CONSISTENCY_CHECK__TOTAL_RATE_PER_SPAD_MCPS (SPADあたりレート整合性 [LSB])
    0x00,  # 0x5D: SD_CONFIG__WOI_CONFIG (受光タイミングウィンドウWOI設定 0)
    0x01,  # 0x5E: SD_CONFIG__WOI_CONFIG (受光タイミングウィンドウWOI設定 1)
    0xCC,  # 0x5F: SD_CONFIG__INITIAL_PHASE_CONFIG (初期位相設定 0)
    0x0F,  # 0x60: SD_CONFIG__INITIAL_PHASE_CONFIG (初期位相設定 1)
    0x01,  # 0x61: SYSTEM__GROUPED_PARAMETER_HOLD_0 (パラメータ更新の保留設定)
    0xF1,  # 0x62: SYSTEM__THRESH_HIGH (距離割り込み上限閾値 [MSB])
    0x0D,  # 0x63: SYSTEM__THRESH_HIGH (距離割り込み上限閾値 [LSB])
    0x01,  # 0x64: SYSTEM__THRESH_LOW (距離割り込み下限閾値 [MSB])
    0x68,  # 0x65: SYSTEM__THRESH_LOW (距離割り込み下限閾値 [LSB])
    0x00,  # 0x66: SYSTEM__ENABLE_CONFIG_0 (システム機能有効化フラグ 0)
    0x80,  # 0x67: SYSTEM__ENABLE_CONFIG_1 (システム機能有効化フラグ 1)
    0x08,  # 0x68: SYSTEM__SEQUENCE_CONFIG (シーケンサ動作制御)
    0xB8,  # 0x69: SYSTEM__HISTOGRAM_BIN (ヒストグラムBin初期構成)
    0x00,  # 0x6A: ROI_CONFIG__USER_ROI_CENTRE_SPAD (ユーザー設定ROIの中心SPAD番号)
    0x00,  # 0x6B: ROI_CONFIG__USER_ROI_REQUESTED_GLOBAL_XY_SIZE (ユーザー設定ROIのXYサイズ)
    0x00,  # 0x6C: SYSTEM__CONFIG_VALID_HEADER (設定ヘッダー有効検証)
    0x00,  # 0x6D: SYSTEM__SEQUENCE_CONFIG (内部シーケンス追加制御)
    0x0F,  # 0x6E: SYSTEM__GROUPED_PARAMETER_HOLD_1 (一括パラメータ保持 1)
    0x89,  # 0x6F: SYSTEM__GROUPED_PARAMETER_HOLD (一括パラメータ保持トリガー)
    0x00,  # 0x70: SD_CONFIG__QUANTIFIER_CONFIG (シグナル検出クオンティファイア設定)
    0x00,  # 0x71: ROI_CONFIG__ROI_CONTROL (関心領域ROI動的制御)
    0x00,  # 0x72: FIRMWARE__SYSTEM_STATUS (ファームウェアステータス初期化)
    0x00,  # 0x73: INTERRUPT_CONFIG (割り込み信号出力設定)
    0x00,  # 0x74: INTERRUPT_CLEAR (割り込みクリアフラグ)
    0x00,  # 0x75: INTERRUPT_MODE (割り込み検出モード)
    0x00,  # 0x76: GPIO_HV_PAD__CTRL (GPIO高電圧パッド制御)
    0x01,  # 0x77: GPIO_HV_MUX__CTRL (GPIOマルチプレクサ機能選択)
    0x0F,  # 0x78: GPIO__TIO_HV_STATUS (GPIOピン入出力ステータス)
    0x0D,  # 0x79: SYSTEM__INTERRUPT_CLEAR (システム割り込み解除)
    0x0E,  # 0x7A: SYSTEM__MODE_START (測距モード開始/レンジング有効化)
    0x0E,  # 0x7B: PAD_I2C_HV__CONFIG (I2C通信パッド設定)
    0x00,  # 0x7C: ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS (トリミング用補正値 0x7C)
    0x00,  # 0x7D: ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS (トリミング用補正値 0x7D)
    0x02,  # 0x7E: ALGO__PAUSE_SOLID_ARRAY_CHECK (アレイ制御内部トリミング)
    0xC7,  # 0x7F: ALGO__PART_TO_PART_RANGE_OFFSET_MM (内部距離トリミングレジスタ)
    0xFF,  # 0x80: ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS (内部補正トリミングレジスタ)
    0x9B,  # 0x81: GLOBAL_CONFIG__SPAD_ENABLES_REF (内部SPAD調整トリミング)
    0x00,  # 0x82: SD_CONFIG (シグナル検出内部パラメータ 0x82)
    0x00,  # 0x83: SD_CONFIG (シグナル検出内部パラメータ 0x83)
    0x00,  # 0x84: SD_CONFIG (シグナル検出内部パラメータ 0x84)
    0x01,  # 0x85: SD_CONFIG (シグナル検出内部パラメータ 0x85)
    0x00,  # 0x86: SYSTEM__START (システム起動最終確定フラグ)
    0x00,  # 0x87: SYSTEM__GROUPED_PARAMETER_HOLD (一括設定バッファクリア)
    ])

    # ------Timing budget tables 「各処理に割り当てらる制限時間(許容時間)」 
    # MM(Macro Period)タイムアウト:レーザーを本格的に照射する前に、周囲の環境光(ノイズ)の強さを測定するフェーズの制限時間
    # Range(測距)タイムアウト:実際にレーザーを飛ばして、戻ってくるのを待つフェーズの制限時間
    TIMING_SHORT = { # 1回の測定にかける時間:(MM:Macro Period, Range:測距)
        15:  (0x001D, 0x0027), # 1S のMMとRangeの制限時間
        20:  (0x0051, 0x006E),
        33:  (0x00D6, 0x006E),
        50:  (0x01AE, 0x01E8),
        100: (0x02E1, 0x0388),
        200: (0x03E1, 0x0496),
        500: (0x0591, 0x05C1),
    }

    TIMING_LONG = {
        20:  (0x001E, 0x0022),
        33:  (0x0060, 0x006E),
        50:  (0x00AD, 0x00C6),
        100: (0x01CC, 0x01EA),
        200: (0x02D9, 0x02F8),
        500: (0x048F, 0x04A4),
    }

    # ---------------------------------コンストラクタ---------
    def __init__(self, i2c, address=DEFAULT_ADDRESS):
        self.i2c = i2c
        self.address = address
        self.distance_mode = self.DISTANCE_MODE_LONG
        self.timing_budget_ms = 50

    # ------- I2Cでの8bit レジスタ書き込み ------
    def _write8(self, reg, value):
        buf = bytes([
            (reg >> 8) & 0xFF,
            reg & 0xFF,
            value & 0xFF
        ])
        self.i2c.writeto(self.address, buf)
        #debug_print("W8  %04X <- %02X" % (reg, value))

    # ------- I2Cでの16bit レジスタ書き込み ------
    def _write16(self, reg, value):
        buf = bytes([
            (reg >> 8) & 0xFF,
            reg & 0xFF,
            (value >> 8) & 0xFF,
            value & 0xFF
        ])
        self.i2c.writeto(self.address, buf)
        #debug_print("W16 %04X <- %04X" % (reg, value))

    # ------- I2Cでの32bit レジスタ書き込み ------
    def _write32(self, reg, value):
        buf = bytes([
            (reg >> 8) & 0xFF,
            reg & 0xFF,
            (value >> 24) & 0xFF,
            (value >> 16) & 0xFF,
            (value >> 8) & 0xFF,
            value & 0xFF
        ])
        self.i2c.writeto(self.address, buf)
        #debug_print("W32 %04X <- %08X" % (reg, value))

    # ------- I2Cでの8bit レジスタ読み込み ------
    def _read8(self, reg):
        self.i2c.writeto(
            self.address,
            bytes([
                (reg >> 8) & 0xFF,
                reg & 0xFF
            ])
        )
        value = self.i2c.readfrom(self.address, 1)[0]
        #debug_print("R8  %04X -> %02X" % (reg, value))
        return value

    # ------- I2Cでの16bit レジスタ読み込み ------
    def _read16(self, reg):
        self.i2c.writeto(
            self.address,
            bytes([ (reg >> 8) & 0xFF,  reg & 0xFF ])
        )
        data = self.i2c.readfrom(self.address, 2)
        value = (data[0] << 8) | data[1]
        #debug_print("R16 %04X -> %04X" % (reg, value))
        return value

    # ------- I2Cでの32bit レジスタ読み込み ------
    def _read32(self, reg):
        self.i2c.writeto(
            self.address,
            bytes([ (reg >> 8) & 0xFF, reg & 0xFF  ])
        )
        data = self.i2c.readfrom(self.address, 4)
        value = ( (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]  )
        #debug_print("R32 %04X -> %08X" % (reg, value))
        return value

    # --- 各種 チップ情報取得メソッド ----------------------------------
    def get_model_id(self):
        return self._read8(self.IDENTIFICATION__MODEL_ID)

    def get_module_type(self):
        return self._read8(self.IDENTIFICATION__MODULE_TYPE)

    def get_revision_id(self):
        return self._read8(self.IDENTIFICATION__REVISION_ID)

    def get_module_id(self):
        return self._read16(self.IDENTIFICATION__MODULE_ID)

    def check_device(self):
        model = self.get_model_id()
        if model != 0xEA:
            raise RuntimeError(
                "VL53L1X model ID error: 0x%02X" % model
            )
        return True

    # Bootが終わっているかの判定-------------------------------------
    def boot_state(self):
        return self._read8( self.FIRMWARE__SYSTEM_STATUS ) != 0

    # Bootが終わっるまで、待つ-------------------------------------
    def wait_booted(self, timeout_ms=1000):
        start = time.ticks_ms()

        while not self.boot_state():
            if time.ticks_diff(
                time.ticks_ms(),
                start
            ) > timeout_ms:
                raise RuntimeError(
                    "VL53L1X boot timeout"
                )

            time.sleep_ms(5)

     # これはUltra Lite Driverとしてセンサー初期化---------
    def sensor_init(self):
        self.check_device()
        for offset in range(len(self.DEFAULT_CONFIGURATION)):
            reg = 0x2D + offset
            value = self.DEFAULT_CONFIGURATION[offset]
            self._write8(reg, value)

        self.start_measurement()
        self.wait_data_ready( timeout_ms=1000 )
        self.clear_interrupt()
        self.stop_measurement()

        self._write8( self.VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND, 0x09 ) # 自動電圧調整(VHV)の設定
        self._write8( 0x000B,0x00 ) # VHV(VHVキャリブレーション)最初に1回だけ自動調整を行わせる

    # ----最大測定距離のモード指定 mode 1 = Short, 2 = Long ------------
    def set_distance_mode(self, mode):
        if mode not in ( self.DISTANCE_MODE_SHORT, self.DISTANCE_MODE_LONG  ):
            raise ValueError( "distance mode must be 1 or 2")

        old_budget = self.timing_budget_ms # Save current timing budget.

        if mode == self.DISTANCE_MODE_SHORT:
            self._write8( self.PHASECAL_CONFIG__TIMEOUT_MACROP, 0x14 ) # 位相キャリブレーション・タイムアウト
            
            self._write8( self.RANGE_CONFIG__VCSEL_PERIOD_A, 0x07 )# VCSEL period A           
            self._write8( self.RANGE_CONFIG__VCSEL_PERIOD_B, 0x05)# VCSEL period B
          
            self._write8( self.RANGE_CONFIG__VALID_PHASE_HIGH, 0x38 )# Valid phase high

            self._write16( self.SD_CONFIG__WOI_SD0, 0x0705 ) # 戻ってきた光を受け取る受け時間の幅
            self._write16( self.SD_CONFIG__INITIAL_PHASE_SD0, 0x0606 ) # 戻ってきた光の受光のタイミング

        else:
            self._write8( self.PHASECAL_CONFIG__TIMEOUT_MACROP, 0x0A ) # 位相キャリブレーション・タイムアウト

            self._write8( self.RANGE_CONFIG__VCSEL_PERIOD_A, 0x0F )# VCSEL period A
            self._write8( self.RANGE_CONFIG__VCSEL_PERIOD_B, 0x0D )# VCSEL period B
            
            self._write8( self.RANGE_CONFIG__VALID_PHASE_HIGH, 0xB8 )# Valid phase high

            self._write16( self.SD_CONFIG__WOI_SD0, 0x0F0D ) # 戻ってきた光を受け取る受け時間の幅
            self._write16( self.SD_CONFIG__INITIAL_PHASE_SD0, 0x0E0E ) # 戻ってきた光の受光のタイミング

        self.distance_mode = mode
        if old_budget is not None: # 距離モードを変更した後、以前のタイミングバジェットを再適用
            self.set_timing_budget(old_budget)

    # ---Timing budget STSW-IMG009 supported timing budgets--測定時間の設定------------
    def set_timing_budget(self, budget_ms):
        if self.distance_mode == self.DISTANCE_MODE_SHORT:
            table = self.TIMING_SHORT # Short:  15, 20, 33, 50, 100, 200, 500 ms
        else:
            table = self.TIMING_LONG # Long: 20, 33, 50, 100, 200, 500 ms

        if budget_ms not in table:
            raise ValueError(
                "timing budget %d ms is not valid for "
                "distance mode %d"
                % ( budget_ms,  self.distance_mode)
            )

        timeout_a, timeout_b = table[budget_ms]
        self._write16( self.RANGE_CONFIG__TIMEOUT_MACROP_A_HI, timeout_a )
        self._write16( self.RANGE_CONFIG__TIMEOUT_MACROP_B_HI, timeout_b )
        self.timing_budget_ms = budget_ms

    # -- 現在VL53L1Xに設定されている測定時間(Timing Budget)が何msなのかを、レジスタ値から読み出して返すメソッド
    def get_timing_budget(self):
        a = self._read16( self.RANGE_CONFIG__TIMEOUT_MACROP_A_HI )
        if self.distance_mode == self.DISTANCE_MODE_SHORT:
            values = {
                0x001D: 15,
                0x0051: 20,
                0x00D6: 33,
                0x01AE: 50,
                0x02E1: 100,
                0x03E1: 200,
                0x0591: 500,
            }
        else:
            values = {
                0x001E: 20,
                0x0060: 33,
                0x00AD: 50,
                0x01CC: 100,
                0x02D9: 200,
                0x048F: 500,
            }
        return values.get(a, 0)

    # -------Measurement:測定のスタート・ストップ制御----------------
    def start_measurement(self): # 測定のスタート
        self._write8( self.SYSTEM__MODE_START, 0x40 )

    def stop_measurement(self): # 測定のストップ
        self._write8( self.SYSTEM__MODE_START, 0x00 )

    # -Interrup「polarity:極性:LOWで知らせるか、HIGHで知らせるか」を取得と設定---
    def get_interrupt_polarity(self):
        value = self._read8( self.GPIO_HV_MUX__CTRL )       
        return 0 if (value & 0x10) else 1 #

    def set_interrupt_polarity(self, polarity): # 判定論理の極性を設定
        value = self._read8( self.GPIO_HV_MUX__CTRL )
        value &= 0xEF
        value |= ( 0x00 if (polarity & 1) else 0x10 )
        self._write8( self.GPIO_HV_MUX__CTRL, value )

    def data_ready(self): # 測定結果が得られる状態になったら Trueを返す
        polarity = self.get_interrupt_polarity()
        status = self._read8( self.GPIO__TIO_HV_STATUS )
        return (status & 0x01) == polarity

    # 測定結果が得られる状態になるまで待つ(タイムアウトの引数あり)
    def wait_data_ready(self, timeout_ms=1000):
        start = time.ticks_ms() # マイコンの電源をオン0として、そこから数えた現在のミリ秒
        while not self.data_ready():
            if time.ticks_diff( time.ticks_ms(), start ) > timeout_ms:
                raise RuntimeError( "VL53L1X data-ready timeout")
            time.sleep_ms(1)

    # ---------- データを受け取ったから、割り込み信号を消して次の測定する合図(クリア処理)-------
    def clear_interrupt(self):
        self._write8( self.SYSTEM__INTERRUPT_CLEAR, 0x01 )

    # ---- 測距測定データを取得して返す --------------------------------------------------------
    def get_ranging_measurement_data(self):
        status = self._read8(self.RESULT__RANGE_STATUS) # 測定の成功・失敗の状態
        distance_mm = self._read16(
            self.RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0 # 距離(mm)
        )
        ambient = self._read16( self.RESULT__AMBIENT_COUNT_RATE_MCPS_SD ) # 環境光
        signal = self._read16(
            self.RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0
        )
        spads = self._read16( self.RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0 )/256 # SPAD使用数
        return {
            "status": status, # 測定の成功・失敗の状態
            "distance_mm": distance_mm,# 距離(mm)
            "ambient": ambient,# 環境光
            "signal": signal, # MCPS: Million Counts Per Second(1秒間に何百万回カウントされたか)
            "spads": spads, # 強すぎる光に対する自動的に調整されたSingle-Photon Avalanche Dioの使用数
        }

    # ---- One-shot convenience function -----------------------------------
    def read(self):
        self.wait_data_ready()
        result = self.get_ranging_measurement_data()
        self.clear_interrupt()
        return result

    # -------Continuous measurement generator------------
    def measurements(self):
        while True:
            self.wait_data_ready()
            result = ( self.get_ranging_measurement_data())
            self.clear_interrupt()
            yield result

    # -----Initialize everything ---全て初期化-------------------------------
    def init( self, distance_mode=DISTANCE_MODE_LONG, timing_budget_ms=50 ):
        self.wait_booted() # Wait until the embedded firmware is booted.
        self.sensor_init() # STSW-IMG009 SensorInit().
        self.set_distance_mode( distance_mode )  # Configure distance mode.
        self.set_timing_budget( timing_budget_ms ) # Configure timing budget.
        self.start_measurement() # Start continuous ranging.

i2c = I2C( 0, scl=Pin(22), sda=Pin(21), freq=400000)
debug_print("I2C devices:", [hex(x) for x in i2c.scan()])

# ========================= BNO005関連の各種定義 =====================================================
FILENAME = "bno_offsets.bin" # キャブリレーションの補正データを記憶するファイル
calibration_flag = False; # これがTrueであればキャブリレーションを行う。
ADDR_BNO005 = 0x28 # I2CにおけるBNO005デバイスのアドレス

# レジスタアドレス定義
REG_OPR_MODE = 0x3D #動作モードを設定するレジスタ
REG_EUL_DATA = 0x1A # オイラー角データ開始レジスタ (Heading, Roll, Pitch)
REG_CALIB_STAT = 0x35  # キャリブレーションステータスレジスタ
REG_OFFSET_START = 0x55  # オフセットデータ開始アドレス (22バイト)

# 動作モード定義
MODE_CONFIG = 0x00 # CONFIGMODE:
MODE_NDOF = 0x0C  # 9軸フュージョンモード「設定・キャリブレーション・自己診断を行うためのシステム待機モード」

# BNO状態変更(キャブリレーションやフュージョンモードへの切り替え用)
def set_mode(mode):
    i2c.writeto_mem(ADDR_BNO005, REG_OPR_MODE, bytes([mode]))
    time.sleep_ms(200)

# キャブリレーション情報取得
def read_calibration():
    cal_data = i2c.readfrom_mem(ADDR_BNO005, REG_CALIB_STAT, 1)[0]
    return (
        (cal_data >> 6) & 0x03, # sys:System(システム全体)
        (cal_data >> 4) & 0x03, # gyro:Gyroscope(ジャイロ) 
        (cal_data >> 2) & 0x03, # accel:Accelerometer(加速度)
        cal_data & 0x03,        # mag:Magnetometer(磁気)
    )

# キャブリレーションしてファイル保存
def save_offsets_by_calibration():
    set_mode(MODE_NDOF) # キャブリレーションはNDOFで行う
    for n in range(1000):
        sys, gyro, accel, mag = read_calibration()
        debug_print(f"Calib -> SYS:{sys} GYR:{gyro} ACC:{accel} MAG:{mag}")
        time.sleep(0.1)
        if sys == 3 and gyro == 3 and accel == 3 and mag == 3:
            break# 初回全コンポーネント較正完了(すべて3)のタイミング
    else: return False # 校正失敗(途中の場合を含む) 
    #オフセット(22バイト)を取得してファイルに保存
    offsets = i2c.readfrom_mem(ADDR_BNO005, REG_OFFSET_START, 22)
    with open(FILENAME, "wb") as f: f.write(offsets)
    return False # 校正成功して、オフセットデータ(レジスタ 0x55?0x6A の22バイト) を保存

# ファイルからキャブリレーションの補正データを読み出してBNO055に書き込む
def load_offsets():
    try:
        with open(FILENAME, "rb") as f:
            offsets = f.read()
        if len(offsets) == 22:
            set_mode(MODE_CONFIG)
            i2c.writeto_mem(ADDR_BNO005, REG_OFFSET_START, offsets)
            debug_print("The existing calibration offset has been loaded and applied.")
            return True # 既存のキャリブレーションオフセットが読み込まれ、適用されました。
        #
    except OSError: # 保存されたオフセットファイルが見つかりません。新規作成が必要です。
        debug_print("The saved offset file could not be found. A new one needs to be created.")
    return False

# レジスタ定義(オイラー角)
EULER_H_LSB = 0x1A  # Heading (Yaw)
EULER_R_LSB = 0x1C  # Roll
EULER_P_LSB = 0x1E  # Pitch

def read16(reg): # 各オイラー角の取得用関数
    data = i2c.readfrom_mem(ADDR_BNO005, reg, 2)
    value= data[0] | (data[1] << 8)# データはリトルエンディアン形式(下位→上位の順)
    if value >= 0x8000: value -= 0x10000 # 符号ありとして使う
    return value

# yaw roll pitch 取得
def read_euler():
    # 16bit raw → degree に変換(1LSB = 1/16度)
    yaw  = read16(EULER_H_LSB) / 16.0  # 方位角(北基準のYAW) heading
    roll = read16(EULER_R_LSB) / 16.0  # ロール角(左右の傾き)
    pitch = read16(EULER_P_LSB) / 16.0 # ピッチ角(前後の傾き)
    return yaw, roll, pitch

# --- 9軸センサー初期化 ---
set_mode(MODE_CONFIG)
calibration_succ = load_offsets()# 保存されたオフセットがあれば適用。
if calibration_flag or not calibration_succ:
    calibration_succ = save_offsets_by_calibration()# キャブリレーションして保存
set_mode(MODE_NDOF)
debug_print(f"calibration_succ:{calibration_succ}")

# --- 距離センサー初期化 ---
tof = VL53L1X( i2c,  address=0x29)

PCとUMEHOSHI ITAをUSBケーブルで接続し、 SW1(赤)リセットと、 SW2(白)を押して、 SW1(赤)だけ離した後に3秒待ってLEDが消えた後に、SW2(白)スイッチを離します。
(この操作で、そしてUMEHOSHI ITAはスルーモード(UART CDCブリッジ)になります。 SW3(黄)の基板は付けなくても動作します。)

この状態で、上記のドライバーコード(sensor.py)を次のmpremoteコマンド操作でESP32へ転送しておきます。
mpremote connect COM3 fs cp sensor.py :sensor.py 
上記で、: 直後のファイル名がマイコン側であることを意味します。
このドライバーコードを利用して、距離と姿勢(Yaw, Roll, Pitch)を表示するテストコードを下記に示します。
import sensor
import time

# ----- Initialize  Long mode  20 ms timing budget ------------------------
sensor.tof.init( distance_mode=sensor.VL53L1X.DISTANCE_MODE_LONG,  timing_budget_ms=20 )

start = time.ticks_ms() 
ss=[]
n=0
while n < 500:
    if sensor.tof.data_ready():
        result = sensor.tof.get_ranging_measurement_data()
        sensor.tof.clear_interrupt()
        distance_mm=result["distance_mm"]
    else: distance_mm = 0
    period=time.ticks_diff( time.ticks_ms(), start )
    if period < 20:
        time.sleep_ms(1)
        continue
    start = time.ticks_ms() 
    n+=1
    yaw, roll, pitch = sensor.read_euler()
    s=f"distance_mm={distance_mm:8.2f}, Yaw={yaw:.2f}, Roll={roll:.2f}, Pitch={pitch:.2f}" f", period={period}"
    ss.append( s )
    print(s)

#for s in ss: print(s)
MicroPythonにて、VL53L1Xによる距離測定の周期は約100ms、 BNO055による9軸センサーの姿勢(Yaw, Roll, Pitch)測定の周期は約20msでした。
そこで、VL53L1Xの測定完了をポーリングし、距離データが未取得の状態であれば、 姿勢情報の読み取りを優先して繰り返す処理を実装してみました。
現在時間を、time.ticks_ms() で取得して、繰り返し周期をおおよそ20msになるような制御をしています。
この実行例を以下に示します。
R:\drone>mpremote connect COM3 run sensor_test.py
distance_mm=    0.00, Yaw=19.00, Roll=0.06, Pitch=4.69, period=20
distance_mm=    0.00, Yaw=19.00, Roll=0.06, Pitch=4.69, period=20
distance_mm=    0.00, Yaw=19.00, Roll=0.06, Pitch=4.69, period=20
distance_mm=    0.00, Yaw=19.00, Roll=0.06, Pitch=4.69, period=20
distance_mm=    0.00, Yaw=19.00, Roll=0.06, Pitch=4.69, period=22
distance_mm=  779.00, Yaw=19.00, Roll=0.06, Pitch=4.69, period=21
distance_mm=    0.00, Yaw=19.00, Roll=0.06, Pitch=4.69, period=20
distance_mm=    0.00, Yaw=19.00, Roll=0.06, Pitch=4.69, period=20
distance_mm=    0.00, Yaw=19.00, Roll=0.06, Pitch=4.69, period=20
distance_mm=    0.00, Yaw=19.00, Roll=0.06, Pitch=4.69, period=20
distance_mm=  783.00, Yaw=19.00, Roll=0.06, Pitch=4.69, period=22
distance_mm=    0.00, Yaw=19.00, Roll=0.06, Pitch=4.69, period=20
・・・・・・・・
R:\drone>