新しいものづくりがわかるメディア

RSS


月イチM5工作

月イチM5工作——M5 Din Meterと計量ユニットで販売管理タグを作る

液晶ディスプレイを搭載し、ネットワーク接続の容易さが特徴のマイコンボード、M5Stack。2017年に中国・深圳でリリースされてからラインアップは増え続け、「1週間に一度は新製品がリリースされる」とまで言われる開発速度も大きな魅力です。電子工作やプロトタイピングの選択肢が増える喜びの一方で、全ての新製品に触れ続けるのはなかなか難しいかもしれません。

この連載ではM5Stackの国内販売をサポートするスイッチサイエンス協力のもと、次々とリリースされるM5Stackシリーズの新製品に触れ、その使い心地を確かめながら、特徴を生かした工作を実践していきます。

ロータリーエンコーダー付きの「M5 Din Meter」

今回扱うアイテムは「M5 Din Meter(M5Stamp S3搭載)」。国内では2024年4月1日に発売されました。7月17日時点の販売価格は4136円(税込)です。

M5Stamp S3をベースに、1.14インチ液晶とロータリーエンコーダーが一体化。「機器のパネルに角穴を開けるだけで見栄え良く組込できます」とうたわれている通り、パッケージングのしやすさも特徴です。なお、商品名にも付いているDINとはカーオーディオのサイズ規格のことで、本商品も1/32 DINサイズのパネルメーターと同様の構造になっています。

photo
photo

製品には本体のほかに固定用の治具や両面テープ、LiPo電池パックなどが同梱されており、「すぐにでも実戦投入してほしい」というメーカー側の意図が感じられました。

サンプルプログラムをチェック

それでは早速使っていきましょう。電源をONにするとプリセットのプログラムが起動し、いくつかの機能デモを確認できました。

ロータリーエンコーダーのテスト ロータリーエンコーダーのテスト
明るさの設定 明るさの設定
ブロック崩しゲーム ブロック崩しゲーム

ロータリーエンコーダーのテスト、ディスプレイの明るさ変更、起動時からのRTCタイム計測、Wi-Fiスキャン、秒数指定でのスリープ&ウェイクアップのほか、左右の回転移動と押しボタンで遊べるブロック崩しゲームまで。オレンジ色のエンコーダーを回したり、ポチッと押し込んだりする感覚は心地よく、映像の出力も滑らかです。

これらのユーザーデモはGitHubでソースコードが公開されています。Visual Studio Codeの拡張パッケージであるPlatformIOなどから中身を確認すれば(詳細は前回の記事を参照)、用途に合わせて編集も可能です。実験として、 src > view > view.cppの一部を次のように変更し、ブロック崩しの代わりにポートの状況を確認するプログラムを呼び出してみました。

void _open_app()
    {
        int matching_index = getSelectedOptionIndex();
        if (matching_index == 0)
            _ft->_disp_test();
        else if (matching_index == 1)
            _ft->_disp_set_brightness();
        else if (matching_index == 2)
            _ft->_rtc_test();
        else if (matching_index == 3)
            _ft->_wifi_test();
        else if (matching_index == 4)
            _ft->_encoder_test_user();
        // else if (matching_index == 5)
        //     printf("todo\n");
        else if (matching_index == 5)
        //    _ft->_arkanoid_start(); // ブロック崩しを呼び出さない
              _ft->_io_test(); // ブロック崩しの代わりに io_test を呼び出す
        else if (matching_index == 6)
            _ft->_rtc_wakeup_test_user();
        else if (matching_index == 7)
            _ft->_power_off();
    }
};

view.cppより一部抜粋。

背面のGROVEポートとモジュールを接続した。見慣れない漢字表記に、深圳の風を感じる。 背面のGROVEポートとモジュールを接続した。見慣れない漢字表記に、深圳の風を感じる。

ft_io__test.cpp を実行すると電池残量とGROVEポートの状況が確認され、接続したI2Cデバイスのアドレスが表示されました。

値段を変えられるプライスタグを作る

コンパクトでありながら視認性の高い液晶と、直感的に操作できるロータリーエンコーダーが特徴のM5 Din Meter。お客さんに商品の値段を表示し、必要に応じて価格を変えられるプライスタグのような使い方ができそうです。

ちょうど最近、筆者も自分が運営するメイカースペースでの日々を綴った本を出したばかり。イベント会場などでの利用を見越して、プログラムを作っていきましょう!

商品の状態や売れ行きに応じて価格を変えるイメージ図。 商品の状態や売れ行きに応じて価格を変えるイメージ図。

ここからはArduino IDEを用いてプログラムを制作します。まずは第1回の記事などを参考に、M5Stamp S3にプログラムを書き込む環境を整えましょう。その後、ライブラリマネージャーで「M5 Din Meter」と検索し、M5 Din Meter の専用ライブラリをインストールしてください。

M5 Din Meterライブラリと依存関係にある「M5GFX」と「M5Unified」も合わせてインストールが必要。 M5 Din Meterライブラリと依存関係にある「M5GFX」と「M5Unified」も合わせてインストールが必要。

M5 Din Meterライブラリの使い方はGitHubで確認可能です。

  • DinMeter.Encoder.read() → エンコーダーの値を読み取る
  • DinMeter.BtnA.wasPressed() → ボタンが押されたか判断する
  • DinMeter.BtnA.pressedFor(任意のミリ秒数) → ボタンの長押しを判断する

といった関数は使い勝手が良さそうです。

サンプルプログラムなどを参照して、まずは商品の価格を表示し、ボタンを押してエンコーダーを回すと価格を変えられる仕組みを作ってみました。

通常時は品名と値段が表示されているが…… 通常時は品名と値段が表示されているが……
ボタンを押すと値段を変更できる。 ボタンを押すと値段を変更できる。
#include "M5GFX.h" #include "M5Unified.h" #include "M5DinMeter.h"

M5GFX display;
M5Canvas canvasA(&display);

void setup() {
  auto cfg = M5.config();     
  DinMeter.begin(cfg, true);  // DinMeterの機能を使うために必要
  display.begin();            
  display.setRotation(1);     
  display.fillScreen(TFT_BLACK);
  canvasA.createSprite(display.width(), display.height());
}

int dispMode = 0;  // モード切り替え用のフラグ
long oldPrice = 2530;
float price = 2530;

void loop() {
  DinMeter.update();  // DinMeterの機能を使うために必要

  // 販売価格を変更する
  if (dispMode == 1) {
    long newPrice = DinMeter.Encoder.read();
    if (newPrice != oldPrice) {
      DinMeter.Speaker.tone(8000, 20);
      oldPrice = newPrice;
      price = newPrice;
    }
  }

  // 販売価格を表示するスプライトを作る
  canvasA.fillRect(0, 0, display.width(), display.height(), BLACK);
  canvasA.setTextColor(WHITE);
  canvasA.setTextSize(0.6);
  canvasA.setFont(&fonts::lgfxJapanGothicP_40);
  canvasA.setCursor(0, 40);
  canvasA.println("書籍「京島の十月」");
  canvasA.printf("Price: %.0fyen", price);
  if (dispMode == 1) {
    canvasA.printf(" ←");
  }

  // ボタンを押すとモードが変わる
  if (DinMeter.BtnA.wasPressed()) {
    dispMode = (dispMode + 1) % 2;
    DinMeter.Encoder.write(price); // 現在の価格をエンコーダーの値にする
  }
  
  canvasA.pushSprite(&display, 20, 0);
  delay(10);
}

fabcross_dinMeter01.ino

太字部分がM5 Din Meterに関係するプログラム。冒頭で必要なライブラリを読み込んだのち、エンコーダーの値を読んだり書き込んだり、ボタンが押されたかの判定を行ったりしています。これで売れ行きに合わせて値段を変更する、ダイナミックプライシングができるようになりました。

pohto

しかし、商品の上に直接プライスタグを置くのは見栄えが良くありません。そこでオレンジ色の治具に合わせたパーツを3Dプリントで造形し、トレイに引っ掛けられるようにしました。M3ねじで固定できるため、他の箱やパッケージにも簡単に組み込めそうです。

重さを測って表示する

さらなる追加機能として、在庫の数を管理してみましょう。トレイ全体の重さを測ってから商品1個当たりの重量で割れば、現時点での在庫数が割り出せそうです。

pohto

まずはトレイ全体の重さを図るためのパーツを選定します。M5 Stackシリーズに重量計はいくつか存在しますが、今回は価格も手頃な「M5Stack用計量ユニット 5kgレンジ(HX711)」を利用しました。

ねじを通す穴は中央の4つで良かったのだが、筆者は外側に余計な穴を空けてしまった。 ねじを通す穴は中央の4つで良かったのだが、筆者は外側に余計な穴を空けてしまった。
計量ユニットに同梱されるナットも使い、トレイと固定する。 計量ユニットに同梱されるナットも使い、トレイと固定する。

M5 Din Meterと同じように、計量ユニットにも位置固定を想定した穴が用意されています。トレイの底に穴を開け、短いねじとナットで固定しました。

photo
トレイに置かれた物の分だけ重さが増えて表示される。 トレイに置かれた物の分だけ重さが増えて表示される。

計量ユニットの公式ライブラリを見ると、「.getWeight()」で重さが取得できるようです。サンプルプログラムを少し変えるだけで、簡単に重さを表示できました。より正確性が求められるシチュエーションでは、キャリブレーション用に「.getGapValue()」「.getRawADC()」といった関数も利用できます。

photo
中身が減ると、WeightとStockの値も連動して変化する。 中身が減ると、WeightとStockの値も連動して変化する。

さらに商品1個あたりの重さを設定して、個数を割り出すプログラムを追加してみました。

#include "M5DinMeter.h"
#include "M5GFX.h"
#include "M5Unified.h"
#include "UNIT_SCALES.h"  // 計量ユニットを使うために必要

M5GFX display;
M5Canvas canvasB(&display);
UNIT_SCALES scales;  // 計量ユニットを定義

void setup() {
  auto cfg = M5.config();
  DinMeter.begin(cfg, true);  // DinMeterの機能を使うために必要
  display.begin();
  display.setRotation(1);
  display.fillScreen(TFT_BLACK);
  canvasB.createSprite(display.width(), display.height());

// 計量ユニットとの接続を確認し、成功したらLEDを光らせる
  while (!scales.begin(&Wire, 2, 1, DEVICE_DEFAULT_ADDR)) {  
    DinMeter.Display.print("scales connect error");
    delay(1000);
  }
  scales.setLEDColor(0x001000);
}

float unit = 150; // 商品一個あたりの重さ
int dispMode = 0;  // モード切り替え用のフラグ
long oldPosition = -999;

void loop() {
  DinMeter.update();

  // 商品一個あたりの重さを調整する
  if (dispMode == 1) {
    long newPosition = DinMeter.Encoder.read();
    if (newPosition != oldPosition) {
      DinMeter.Speaker.tone(8000, 20);
      oldPosition = newPosition;
      unit = newPosition;
      if (unit < 1) unit = 1;
    }
  }

  // 重さと個数を表示するスプライトを作る
  float weight = scales.getWeight(); // 現在の重さを計測する
  if (isnan(weight)) {
    weight = 1.0;  // weightがNaNだった場合、1.0を代入
  }
  canvasB.fillRect(0, 0, display.width(), 120, BLACK);
  canvasB.setTextColor(WHITE);
  canvasB.setTextSize(0.6);
  canvasB.setFont(&fonts::lgfxJapanGothic_40);
  canvasB.setCursor(0, 0);
  canvasB.printf("Weight: %.0fg\n", weight); //重さを表示
  canvasB.printf("Stock: %dpcs\n", int(weight / unit)); // 個数を表示
  canvasB.printf("Unit: %.0fg/pcs", unit); //商品一個あたりの重さを表示
  if (dispMode == 1) {
    canvasB.printf(" ←");
  }

  // ボタンを押すとモードが変わる
  if (DinMeter.BtnA.wasPressed()) {
    dispMode = (dispMode + 1) % 2;
    DinMeter.Encoder.write(unit);
  }

  canvasB.pushSprite(&display, 20, 0);
  delay(10);
}

fabcross_dinMeter02.ino

photo

価格表示と同様に、ボタンを押すと商品1個あたりの重さを変更できるようにしているので、扱う商品が変わっても柔軟に対応できます。

モード切り替えも可能なプライスタグができた

最後に、価格表示と重さ表示のプログラムを統合し、長押しでモードが変わる仕組みを実装しました。これで「普段は価格を表示し、必要なときには在庫を確認する」という使い方もできそうです。プログラムの詳細が気になる方は、こちらのリンクから確認してみてください。

photo

今回は最低限の装飾になりましたが、既存の規格や治具としっかり合わせれば、より精度高く利用できるはず。コンパクトながら操作性の高いM5 Din Meter、そして手軽に使える計量ユニットを組み合わせて、色々なシステムを作れそうですね。

それでは、また次回!

取材協力:スイッチサイエンス

おすすめ記事

 

コメント

ニュース

編集部のおすすめ

連載・シリーズ

注目のキーワード

もっと見る