2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

M5UnitUnified について

Last updated at Posted at 2024-12-12

概要

M5 Japan Tour 2024 Autumn 東京 にて発表された M5UnitUnified について当日のスライド では語れなかった事柄や、詳細について書いていきます。

なお改めて申し上げますが、各ユニットの M5UnitUnfied 対応ライブラリは新規に書き起こしされています。
従来の内製ライブラリや、依存している外部ライブラリでは対応していない機能の追加や、バグ修正、挙動のデータシートへの準拠を目標に作成しています。

さらにまだアルファ版であることにご留意ください。
以下の構成や API は今後大幅に変更される可能性があります。

またご意見、ご要望、ご感想他フィードバックやレスポンスに大変飢えておりますので、色々不備もあるとは思いますが、お試しいただければ幸いです。

ユニット対応状況

現時点( 2024/12/13 現在)で公開されているユニットについて見ていきましょう。
同系列なユニットはできる限り同じリポジトリに収める方針です。

公開済み

M5Unit-ENV

こちらは既存のライブラリに M5UnitUnified 関連のファイルが追加された形となっています。
ENV 系列のユニットに関するライブラリです。

M5Unit-METER

新規リポジトリ。
Meter 系列のユニットに関するライブラリです。

M5Unit-HUB

新規リポジトリ。
Hub 系列のユニットに関するライブラリです。

M5Unit-GESTURE

新規リポジトリ。

M5Unit-HEART

新規リポジトリ。

M5Unit-TOF

新規リポジトリ。
ToF 系列のユニットに関するライブラリです。

M5Unit-WEIGHT

新規リポジトリ。
Weight 系列のユニットに関するライブラリです。

近日公開予定

M5Unit-RFID

新規リポジトリ。
RFID 系列のユニットに関するライブラリです。

M5Unit-ANADIG

新規リポジトリ。
ADC,DAC 系列のユニットに関するライブラリです。

基本的な使用の流れ

  • ユニットの所属するライブラリをインストール
    ArduinoIDE ならライブラリマネージャを使用
    PlatformIO なら platformio.ini に記述
; ToF の場合
lib_deps = m5stack/M5Unit-TOF

(依存する M5UnitUnified M5Utility M5HAL も自動でインストールされるはずですが、されない場合は手動でお願いします)

  • M5UnitUnified とユニットのインクルードファイルをインクルードします
    さらに M5Unified と組合せる事で、ソースが共通化できます。
// ToF 場合
#include <M5Unified.h>
#include <M5UnitUnified.h>
#include <M5UnitUnifiedTOF.h>

namespace {
m5::unit::UnitUnified Units; // Unit 管理
m5::unit::UnitToF unit;      // ToF ユニット
}  // namespace

void setup()
{
    M5.begin();

    auto pin_num_sda = M5.getPin(m5::pin_name_t::port_a_sda);
    auto pin_num_scl = M5.getPin(m5::pin_name_t::port_a_scl);
    M5_LOGI("getPin: SDA:%u SCL:%u", pin_num_sda, pin_num_scl);
    Wire.begin(pin_num_sda, pin_num_scl, 400 * 1000U);

    if (!Units.add(unit, Wire) || !Units.begin()) { // 管理クラスに Add して開始!
        M5_LOGE("Failed to begin");
        while (true) {
            m5::utility::delay(10000);
        }
    }
}

void loop()
{
    M5.update();
    Units.update();       // ユニットの更新(定期計測機構があれば計測して蓄積)
    if (unit.updated()) { // 更新されたら
        M5_LOGI("\n>Range:%d\nStatus:%u", unit.range(), unit.range_status()); // データを使用
    }
}

各ユニットのリポジトリの examples も参照してください

Unit クラスで共通している事柄

さて、既存の値取得系ライブラリの使用例では、以下のような loop() の書き方をよく見ます

void loop() {
    if (foo.update()) { // 計測して値を取得 (取れるまでブロックする実装もある)
        printf("%f\n", foo.anyValue()); // 表示
    }
    delay(1000); // 適当な delay または delay なし
}

計測チップのほとんどには定期計測の為の機構があります。
定期計測機構を使用する場合、大抵のユニットではインターバル時の消費電力も少なくなり、バッテリー駆動時の稼働時間を増やせるのではないでしょうか?
M5UnitUnified ではその機構を積極的に使用し、更新関数では計測タイミングでなければ即時リターンする挙動をとります。
delay に関しては必要に応じてユーザー側で行うことができます。

  • delay をしたい場合 (A)
void loop() {
    Units.update();
	// Units.update() では計測タイミングでなければ即リターン
	if(unit.updated()) {
	    printf("%f\n", unit.anyValue());
        m5::utility::delay(unit.interval()); // 値が取れたら次の計測予想タイミングまで
    }
}
  • delay をしたい場合 (B)
void loop() {
    Units.update();
	// Units.update() では計測タイミングでなければ即リターン
	if(unit.updated()) {
	    printf("%f\n", unit.anyValue());
    }
    m5::utility::delay(1); // 最小 delay を常時
}

クラス関数

使用感や、API のできる限りの統一を目指すという事で、各 Unit クラスは共通の基底クラス m5::unit::Component から派生されています。
またデータ蓄積機能を持つクラスの場合は、 m5::unit::PeriodicMeasurementAdapter も基底となります(インターフェイスクラス)
基底にあるクラス関数は派生された Unit クラスにおいて、もちろん使用できます。
各 Unit クラスにある API では補えない処理を独自に行いたい場合など、これらを使用すれば解決するかも知れません。
基底など M5UnitUnified 関連のドキュメントはこちらです。

基本ルール

  • component_config 関数によって共通の設定の取得、設定が行える
  • config 関数によってユニット独自の設定の取得、設定が行える
    定期計測機構がある場合の初期値は、典型的な設定で定期計測が開始される状態
    よって Units.begin() によって定期計測がスタートする
  • readFoo はユニットにアクセスして値を取得する
  • writeFoo はユニットにアクセスして値を書き込む
  • startPeriodicMeasurement(ユニット毎に異なる引数) は定期計測を開始する (機構が存在すれば)
  • stopPeriodicMeasurement() は定期計測を停止する (機構が存在すれば)
  • measureSingleshot(ユニット毎に異なる引数) シングルショット計測による計測値の取得 (機構が存在すれば)
  • 定期計測を持つユニットでは、任意の要素数を環状バッファに蓄積できます
    初期値は 1 なので、常に最新のデータのみにアクセスします
    component_config で要素数を変更可能です

Component の関数(一部)

プロパティ

  • const char* deviceName() const;
    ユニット名称
  • m5::unit::types::uid_t identifier() const;
    一意なユニットクラス ID
  • uint8_t address() const;
    使用している I2C アドレス

定期計測関連

  • bool inPeriodic() const;
    定期計測中か?
  • bool updated() const;
    直近の更新によって値が取得できたか?
  • m5::unit::types::elapsed_time_t updatedMillis() const;
    値が取得できた時の時刻(ms)
  • m5::unit::types::elapsed_time_t interval() const;
    定期計測の間隔(ms)

Read

template によって 8/16 bit レジスタを識別

  • m5::hal::error::error/_t readWithTransaction(uint8_t* data, const size_t len);
    読み込み
  • template ::value && std::is_unsigned::value && sizeof(Reg) <= 2,std::nullptr\_t>::type = nullptr> bool readRegister(const Reg reg, uint8_t* rbuf, const size_t len, const uint32_t delayMillis,const bool stop = true);
    レジスタ読み込み
  • template ::value && std::is_unsigned::value && sizeof(Reg) <= 2, std::nullptr\_t>::type = nullptr> bool readRegister8(const Reg reg, uint8_t& result, const uint32_t delayMillis, const bool stop = true);
    レジスタ 8bit 読み込み
  • template ::value && std::is_unsigned::value && sizeof(Reg) <= 2, std::nullptr\_t>::type = nullptr> bool readRegister16(const Reg reg, uint16_t& result, const uint32_t delayMillis, const bool stop = true);
    レジスタ 16bit 読み込み

Write

template によって 8/16 bit レジスタを識別

  • m5::hal::error::error_t writeWithTransaction(const uint8_t* data, const size_t len, const bool stop = true);
    書き込み
  • template ::value && std::is_unsigned::value && sizeof(Reg) <= 2, std::nullptr\_t>::type = nullptr> m5::hal::error::error_t writeWithTransaction(const Reg reg, const uint8_t* data, const size_t len, const bool stop = true);
    レジスタ書き込み
  • template ::value && std::is_unsigned::value && sizeof(Reg) <= 2, std::nullptr\_t>::type = nullptr> bool writeRegister(const Reg reg, const uint8_t* buf = nullptr, const size_t len = 0U, const bool stop = true);
    レジスタ書き込み
  • template ::value && std::is_unsigned::value && sizeof(Reg) <= 2, std::nullptr\_t>::type = nullptr> bool writeRegister8(const Reg reg, const uint8_t value, const bool stop = true);
    レジスタ 8bit 書き込み
  • template ::value && std::is_unsigned::value && sizeof(Reg) <= 2, std::nullptr\_t>::type = nullptr> bool writeRegister16(const Reg reg, const uint16_t value, const bool stop = true);
    レジスタ 16bit 書き込み
    bool generalCall(const uint8_t* data, const size_t len);
    ジェネラルコール書き込み

PeriodicMeasurementAdapter の関数(一部)

蓄積されたデータに対する操作

  • size_t available() const;
    蓄積されているデータ数
  • bool empty() const;
    蓄積データは空か?
  • bool full() const;
    蓄積データは環状バッファの最大に達しているか?
  • ユニット毎の計測データ型 oldest() const;
    蓄積されている中で最も古いデータ
  • ユニット毎の計測データ型 latest() const;
    蓄積されている中で最も新しいデータ
  • void discard();
    蓄積されている中で最も古いデータを削除
  • void flush();
    全ての蓄積データを削除

蓄積データが複数の場合のアクセスの例

void any_task(void*)
{
    for(;;) {
        Units.update();
        while(unit.available()) { // 蓄積データがある限り
            copy_data_function(unit.oldest().anyValue()); // 最も古い物をどこかへコピー
            unit.discard(); // 最も古い物を棄却 [これがないと永久ループ注意!]
        }
    }
}

計測データ

ユニット毎に独自のデータ型を持ちます。
生のデータと、生データからの値取得の関数を搭載しています。

// ToF の場合
struct Data {
    std::array<uint8_t, 17> raw{};  //!< RAW data
    RangeStatus range_status() const;
    inline bool valid() const
    {
        return raw[0] == 9;
    }
    inline int16_t range() const
    {
        return valid() ? m5::types::big_uint16_t(raw[13], raw[14]).get() : -1;
    }
};

またユニット側には最も古いデータに対してのクイックアクセス API を搭載しています。

// ToF の場合
class Unit UnitVL53L1X {
    // ... 前略
    inline int16_t range() const
    {
        return !empty() ? oldest().range() : -1;
    }
    inline bool valid() const
    {
        return !empty() && oldest().valid();
    }
    inline vl53l1x::RangeStatus range_status() const
    {
        return !empty() ? oldest().range_status() : vl53l1x::RangeStatus::Unknown255;
    }
    // ... 後略
};

その他ユニット毎のドキュメントは各リポジトリの README に GitHub Pages へのリンクがありますのでご参照ください。

今後

ユニット対応を進行しつつ、定期的に更新していく予定です。
M5HAL との統合時期は未定ですので、当分は TwoWire を直接使用する現状の形になると思われます。
GPIO / UART 接続ユニットについては 同様に Serial 等で代替する形で進行する予定です。

ご意見、ご要望、ご感想他フィードバックやレスポンスに大変飢えておりますので、色々不備もあるとは思いますが、お試しいただければ幸いです。

2
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?