Pico PR

【質問回答】RaspberryPi Pico MicroPythonでMTOF171000C0を動かす方法

記事内に商品プロモーションを含む場合があります

はじめに

RaspberryPicoで、ToF測距センサ「MTOF171000C0」 を動かす方法について質問をいただきました。

MTOF171000C0は価格も安く、3.3VなのでPicoと直結することもできます。結線済みの配線も付属しているので、初心者や工作が苦手な方にもおすすめのセンサです。

良いセンサではあるのですが、残念ながら、販売元の秋月電子にはArduinoのコードのみしか公開されておらず、質問者様はPicoでの使用を断念しているとのことでした。

そこで今回は「MTOF171000C0」 をMicroPythonで動かす方法について解説したいと思います。

こんな人におすすめ
  • 安い費用の測距センサーで遊んでみたい。
  • 安い費用でI2C通信を試してみたい。
  • 手を動かしながら、MicroPythonを覚えたい。
  • 人についていくロボットカーを作りたい。

環境

この記事は以下の環境で作成しています。

環境 バージョン 備考
開発用PCのOS Windows11 Windows10でもOKです
言語 MicroPython
開発環境 Thonny 3.3.13
センサ MTOF171000C0 秋月電子 商品コード(M-14538) 
ボード RaspberryPi Pico

※ Picoのセットアップ方法については、こちらの記事をご覧ください。

ToFセンサ MTOF171000C0

税込み780円で購入できる赤外線レーザを使った測距センサです。
安価ですが、流行りのToF(Time of Flight)で距離を計測する方式を採用しており、Pico と同じ3.3Vで駆動します。配線付きのコネクタも付属しているので、半田付けなしで簡単にPicoと接続することができます。

詳細は秋月電子の商品ページをご覧ください。

※ 画像は秋月電子の商品ページより引用

※ 価格は記事作成時のものです。

PicoとMTOF171000C0の接続

以下のように、Picoと MTOF171000C0を接続します。
センサの4番RXDは、「モジュール選択ピン」も兼ねており、I2C通信をする前に「Low」にする必要があります。

RaspberryPi Pico とMTOF171000C0の結線を説明する画像

ピンとコネクタ番号の対応

接続したPicoのピン番号と、センサのコネクタの番号は以下の通りです。

RaspberryPi Pico MTOF171000C0
1番ピン:I2C0 SDA6番:SDA
2番ピン:I2C0 SCL5番:SCL
3番ピン:GND2番:GND
4番ピン:GPIO4番:RXD
—–3番:TXD 使用しません※
36番ピン:3V3 (OUT)1番:VDD

※ センサ側ピンアサインはメーカのアプリケーションノート(P2)に記載。
※ RxDを「モジュール切り替えピン」として使います。TxDは使用しません。

モジュール選択ピンの役割について

メーカ資料に説明はありませんが、このピンは複数のセンサを一つのI2Cのラインにつなげる際に使用するものと推測しています。MTOF171000C0はデバイスのアドレスが1種類のみのため、複数繋げた場合に相手を指定することできません。
そのため、センサを複数つなげた場合は、このピンで通信相手のセンサを指定して(通信したいセンサのみをLowにする)使うものかと思います。

動作は未確認ですが、センサが一つの場合は、このピンをGNDと接続してLowに固定してしまうのも良いかもしれません。(解説ではGPIOを使ってLowに設定します)。

実行結果

後述するコードで距離を測った結果です。固定なしの簡易的な方法で100mm先の白い紙を計測しています。10mm程度のずれは位置を変えても残ったので、センサ個体の製造時のばらつきによるものと思われます。

Thonnyで記事上のコードを実行したところの画像

全体コード

MTOF171000C0で距離を測るコードは以下の通りです。詳細は後述の「コードのポイント」で解説します。

from machine import Pin, I2C
import time

# MTOF171000C0のアドレス(ModuleID)です。
ADDRESS = 0x52

# I2Cのピンの設定です
i2c = I2C(0, scl=Pin(1), sda=Pin(0), freq=100000)

# GPIOのピンの設定です
# センサの「モジュール選択ピン(センサのRxD)」操作に使います
pin4 = machine.Pin(4, machine.Pin.OUT)

# Picoの処理待ちです。
time.sleep_ms(1000)
    
# センサにデータ送信を伝えるため、選択ピンをLowにします
pin4.value(0)

# センサの処理待ちで、5msec待ちます。
time.sleep_ms(5)
    
for i in range(1000):
    
    # データ取得要求を送信します(0xD3)
    # コマンドは配列として作成します。
    cmd = bytearray(1)
    cmd[0] =0xD3
    #i2c.writeto(ADDRESS, cmd)

    # NG 以下の指定では異常な値が返されます
    # i2c.writeto(ADDRESS, b'0xD3')

    # データを受信します
    data = i2c.readfrom(ADDRESS, 2 )

    # 受信データを距離に変換します
    distance = 0
    distance = data[0] << 8
    distance = distance | data[ 1]

    # 距離を表示します
    print(str(distance) + " [mm]")  

    # ループ間隔の調整です    
    time.sleep_ms(1000)

# デバイスに通信終了を知らせるために、
# モジュール選択ピンをHighにします
pin4.value(1)
    
print("done")

コードのポイント

I2Cとモジュール選択ピンの設定

Pico側のI2C・GPIOに使うピンの設定をします。
I2Cの周波数はメーカの資料の記載(100kビット/秒 = 100kHz )に合わせて設定しています。

# I2Cのピンの設定です
i2c = I2C(0, scl=Pin(1), sda=Pin(0), freq=100000)

# GPIOのピンの設定です
# センサの「モジュール選択ピン(センサのRxD)」操作に使います
pin4 = machine.Pin(4, machine.Pin.OUT)

コマンドの送信には配列を使う

MTOF171000C0から距離を要求するのコマンドは「0xD3」のみですが、writeto関数で送信する場合、配列で指定する必要があります。

以下のNGの記載のように、コマンドを配列にせずに指定してもエラーにはなりませんが、受信データが異常な値となるので注意してください。

    # データ取得要求を送信します(0xD3)
    # コマンドは配列として作成します。
    cmd = bytearray(1)
    cmd[0] =0xD3
    #i2c.writeto(ADDRESS, cmd)

    # NG 以下の指定では異常な値が返されます
    # i2c.writeto(ADDRESS, b'0xD3')

受信データは8bitずらして結合する

センサからの距離データは8bit(1byte)のデータで、2つ送られてきます。
データの抜き出し等は不要で、一つ目のデータを8bit分左にずらし、二つ目のデータと結合することで距離データ「mm」が作成できます。

    # 受信データを距離に変換します
    distance = 0
    distance = data[0] << 8
    distance = distance | data[ 1]

計測範囲外だと「8888」が返される。

計測対象が、測定可能な範囲にない場合は、「8888」が距離としてセンサから送られてきます。距離が「8888」となる場は、計測の対象物とセンサとの距離やセンサの向きを変えてみてください。

測距センサを使ったロボットカー

コメントにて、Picoと測距(距離)センサを使ったロボットカーの事例も紹介いただきました。今回のセンサと同じ I2C接続の「vl53l0x」という距離センサを使って製作されており、ソースはAdafruitのライブラリとサンプルのほぼコピーでOKとのことです。

まとめ

RaspberryPicoで、ToF測距センサ「MTOF171000C0」 を動かす方法について解説しました。みなさんの電子工作のお役に少しでも立てればうれしいです。

お知らせ

MicroPythonのプログラミングガイドブックが遂に発売!

「MicroPython」の本が遂にでました。

この一冊で、MicroPythonの言語仕様から、プログラミングの仕方まで”ガッツリ”学べます!。

内容は、普段別言語で開発している人や、これからマイコンを始める(工学系の)学生を対象としているので「初心者向け」ではありません。しかし、「自前のライブラリの作成」が目標なので、これ一冊で「ガッツリ」とMicroPythonを学ぶことができます。

全ての内容はここでは紹介しきれないので、詳細は以下のAmazonページをご覧ください。目次だけでも”ガッツリ”なのが確認できると思います。

Pico/Pico W関連のおすすめ本

RaspberryPi Pico / Pico W関連のおすすめ本を独断と偏見で3つ選んでみました。Picoやるならとりあえずこれ買っとけ的な本や、電子工作全般で使える本などを厳選しています。

質問・要望 大歓迎です

「こんな解説記事作って」「こんなことがしたいけど、〇〇で困ってる」など、コメント欄で教えてください。 質問・要望に、中の人ができる限り対応します。

使えたよ・設定できたよの一言コメントも大歓迎。気軽に足跡を残してみてください。記事を紹介したい方はブログ、SNSにバシバシ貼ってもらってOKです。

ABOUT ME
えす
現役のソフトウェアエンジニアです。 C++ C# Python を使ってます。10年ちょい設計/開発部門にいましたが、今はQAエンジニアっぽいことをしています。

POSTED COMMENT

  1. 球石期人 より:

    はじめまして、球石期人と申します。
    Raspberry Pi Pico&Micro Physonの初心者です。

    個々の簡単なタスクは理解できました。

    人感センサが動作したら1秒間ブザーを鳴らす。なり続けると五月蠅いので19秒間はセンサー検知を無視する。
    スピーカーをパルス駆動してブザー音を作る。

    知りたいのはイベントドリブンプログラムの作り方です。

    たとえば、人感センサーが動作したらスピーカーをパルス駆動して1秒間ブザーを鳴らす。
    なり続けると五月蠅いので19秒間センサー検知を無視する。
    この動作を圧電ブザーなら出来るのですが、これでは芸が無いので、PicoのGPIOをパスル駆動して
    ブザー音を鳴らしたいのですが、この動作につなげていくプログラムわかりません。
    あと、時間の経過に従ってイベントを繋げていくプログラムを知りたいです。

    プログラムの流れ、使う関数、などヒントになるものでも何で良いですので、ご教示頂ければしあわせです。

    やろうとしているのは、下記のような人感センサーを使ったビデオの自動録画です。
    (いままでは、これに似た制御をCとRによる時定数とTRとリレーで組んでいました)

    1.人感センサーに反応があるとGPIO16がHIになると、GPIO17に出力を1秒間HIする(TRとソレノイドでビデオの電源を入れる)
    2.ビデオ電源を入れると録画が終わるまでの間はGPIO16の状態を無視する
    4.ビデオの起動を待って1のGPIO16がHIから10秒後にGPIO19を1秒間HIにする(TRとソレノイドで録画ボタンを押す)
    5.録画ボタンが押されている1秒間、動作確認の為にGPIO20に接続されたスピーカーを駆動してブザー音を鳴らす。
    6.録音ボタンが押されると同時にGPOI27に接続されたCDSで周囲の明るさから昼か夜かを判断する。
    7.明るさが夜なら次に録画ボタンが押されて録画が終了するまでGPIO21の出力をHIにする(LED照明を点灯する)
    8.GPIO19がHIになって10秒経過したら、再びGPIO19を1秒間HIにして録画を停止させる(TRとソレノイドで録画ボタンを押す)
    9.録画ボタンが押されている1秒間、動作確認の為にGPIO20に接続されたスピーカーをPWM1KHZ、ディユーイ比50で駆動してブザー音を鳴らす。
    10.GPIO16人感センサーに反応がある場合はGPIO19を1秒間HIににして再び録画を始める。
    11.10秒経ちふたたび録画終了したタイミングでGPIO16人感センサーに反応がなければ、そのまま10秒待つ(メモリ書き込み中の待機)
    12.GPIO17に出力を1秒間HIする(TRとソレノイドでビデオの電源を切る)
    13.人感センサーの反応を待つ

    • えす より:

      球石期人さん、こんにちは。コメントありがとうございます。
      返信が遅くなり申し訳ないです。ビデオの自動録画を電子回路で作れるなんて凄いですね。

      イベントドリブンのプログラムについては、GPIOの入力を保存するためのフラグ(Bool型の変数)・ループ処理・時間待ちのためのSleep関数・時間計測のためのカウンタ等をを組み合わせていけば、実現できるかと思います。
      1~13については実物がないので作成まではいきませんが、疑似コードや大枠程度なら作成できるかもしれません。
      もう少々お時間をいただければと思います。

  2. 球石期人 より:

    えす様 ご検討ありがとうございます。「溺れる者は藁をもつかむ」と申しますが、藁のような物でも良いので投げて頂ければ助かります。

  3. 球石期人 より:

    えす様

    入出力を個々のDef(関数)にまとめてパーツ化して
    それらをwhileとif文で編んで目的の動作をさせる。
    丁寧な解説を付けて頂き、理解しやすいです。

    これ!いろいろな本を読んでも何処にも書いて無かったんですよ。
    まずはデッドコピーで動作させてみます。
    理解すると色々な応用が出来そうでワクワクしています。
    壁の向こうの世界が見えてきました。
    時間が掛かりそうですがコツコツ勉強させてもらいます。

    本当にありがとうございます。

    さいごに、可能であれば、あと一つアドバイスください。
    Picoのアイドル電流は電源電圧5Vで20mA位でした。
    何日も長期間バッテリーで働かすとなると結構な負担になります。
    調べるとI/Oだけ生かし他の部分を止めて電流をセーブする
    スリープモードが有るようです。
    「センサーの入力(検知)が5分以上無ければスリープする」
    という節電モードを加えることは出来ないでしょうか?

    あらためて、ありがとうございました  球石器人

    • えす より:

      球石期人様、こんにちは。
      えすです。

      つたない説明でしたが、お役に立ててうれしいです。
      壁の突破方法がわかると、「あんなこともできるかも」と、一気に楽しくなりますよね。

      節電モードについては、「deepsleep」の機能かと思います。
      簡単に調べてみましたが、MicroPythonの環境では、「deepsleepへの移行」はできそうですが、
      「GPIOを使って通常状態に復帰する」という機能がまだ実装されていないようです。
      (内部のタイマ(RTC)を使ってX秒後に復帰する方法はありそうですが、今回の自動録画にはちょっと合わないかと思います)

      代替え案として、復帰に「RUN」のピン(リセットピン)を使って、ラッチングリレー(相当)を追加すれば、
      人感センサをトリガーとした、節電モードが作れるかもしれません。

      〇 回路
       ・人感センサのラインを、RUN(30番目)に分岐させて接続
       ・分岐ラインの途中にラッチングリレーを追加(Normal Open)
       ・新規にGPIO2ポートを、ラッチングリレーのセット/リセットコイルに接続

      〇プログラム
       ・無限ループに入る前にリセットコイルをHighにして、人感センサ – RUNピン間を切断
       ・最初の無限ループにカウンタ追加
       ・人感センサ未検知の際にカウントアップ
       ・カウンタが300回になったら以下を実施。
        ・・ セットコイルをHighにして、人感センサ – RUNピン間をつなぐ
        ・・ Deepsleep(節電モード)に移行( machine.deepsleep() と記載)

       ● 節電モード中に人感センサを検知すると、Picoがリセットされて、プログラムが頭から再開
        ※ ビデオが録画されるには、人感センサが再度or一定時間以上Highになる必要があります。

      電気の知識に詳しくないので、回路内容は非現実的かもしれませんし、
      deepsleepも簡単にプログラムが停止したことの確認のみなのでご注意ください。

      記事化は時間がかかりそうなので、一旦文字だけの説明で申し訳ないですが、
      お役にたてればうれしいです。

      なお、話はそれますが、Pico単体でプログラムを実行する際は、「main.py」という名称にしないと、
      電源接続時にプログラムが自動起動しないのでご注意ください。(後で記事に追加しておきます)

  4. キーピング より:

    はじめまして、キーピングと申します。
    Raspberry Pi Pico&Micro Physonの初心者です。えすさんのMTOF171000C0を動かす方法を見て勉強しているものです。で、回路を組みプログラムをまる写しして(すいません)実行を駆けたところエラーが出ました。本等を読み色々試しましたが解決できないので教えて頂きたいと思いメールしました。
    内容がline 40, in
    unsupported types for __or__: ‘int’, ‘bytes’です。ライン40が
    distance = distance | dataです。
    すいませんがよろしくお願いします。

    • えす より:

      キーピングさん、こんにちは

      えすです。

      すみません。
      コード表示ツールの不具合(?)で、dataの ”[1] ” が表示されていませんでした。

      (作成画面では data[1]となっているのですが、
      見ていただいている画面では[1]が消えてしまう)

      回避のため、data[1 ]として記事内のコードを修正しました。
      お手数ですが、修正したコードでもう一度試してもらってもよいでしょうか?

      プログラムの丸写しの件は、まったく問題ありません。
      保証や責任は負えませんが、ガンガン使ってもらえればうれしいです。

  5. キーピング より:

    えす様
    早速の返信ありがとうございます。
    これで確認してみます。
    また、分からない所があったらご教授願います。

  6. たろ より:

    61166 [mm]と出てしまうのは私だけでしょうか…

    • えす より:

      たろさん

      こんにちわ、えすです。
      コメントありがとうございます。

      計測範囲外の時に61166 [mm]と表示される感じでしょうか?
      近距離は測れる、ずっと同じ値、など詳細情報を教えてもらえると助かります。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です