Mifareカードリーダー

非接触カードで出席管理とか入室管理をするための素材です。日本はFericaですが世界的にはMifareが主流だし、Fericaカードより安いから用途によるけれどもセキュア性が重要でなければMifareで良いんじゃないかと思う。

<hardware>

いつものスイッチサイエンスで購入、送料安いしすぐ届くから便利だと思う。

https://www.switch-science.com/products/8301

<sample code>

https://github.com/m5stack/M5Stack/tree/master/examples/Unit/RFID_RC522

のコードをそのままM5Stackで動かしてみる。環境はVScode + PlatformIOです。

<ソースコードのコメントから>

The card reader and the tags communicate using a 13.56MHz electromagnetic field. The protocol is defined in ISO/IEC 14443-3 Identification cards. eg Mifare or NTAG203(古い規格)

まあMifareだけでFericaは読めないから、どのカードが何の規格採用というのは理解出来てないけれどもセキュアなカードはFerica使っているらしいから、以下の試験結果も納得できます。

キャッシュカード、運転免許証、マイナカード、ETCカード、PASMO、クレカ複数でやってみてUIDが認識されたのはクレカが2枚だけ。

唯一認識されたクレカの読み取り情報見ると7-BYTE UIDのようです。認識された2枚のUIDは当然別物になってます。従って一番単純にはUIDだけで識別ができます、それほどセキュアではないけれども。

 

受信部にはコイルが入っていて、電磁的な結合でエネルギー供給して読み取りや書き込みができます。Mifareカード買ってアプリ作ってみよう。

 

admin

 

 

心拍センサーでパルスオキシメーターを作る

心拍センサーというのが市販されていて、それを使うとパルスオキシメーターが作れるので使ってみました。

https://qiita.com/ghibi/items/aa9ae016dbbe9138a505

にやりたいイメージ通りの例があるので、ほぼそのままを使っています。ただしVScodeで使おうとすると関数を先に定義しないと呼び出せないので、入れ替えはしてあります。関数宣言でもいいですが、これはArduino IDEとVScodeの差分です。

また、デフォルトの設定ではうまく測定値が返ってこないので、

  pox.setIRLedCurrent(MAX30100_LED_CURR_4_4MA);
  //Register a callback for the beat detection
  pox.setOnBeatDetectedCallback(onBeatDetected);
pox.setIRLedCurrent(MAX30100_LED_CURR_4_4MA); 行を追加して、IR ledの電流値を調整しています。画面左上のHeart Pulseマークを見ながら指を当てて測定するのですが、測定値の安定には数秒かかります。
P.S. (2023/2/9)
ライブラリのURL追記
admin

M5Stackでの表示ちらつきの防止方法、

普通はM5Stackで表示内容の書き換えするときに、一度表示内容を消去する必要がありますが、それが表示がちらつく原因です。

ちらつき防止のためには、画面表示内容をフルで別のバッファーに用意しておいて、それを押し込めば表示消去時間が存在しないことになるのでちらつきの防止ができます。

以下のサンプルから、

https://craft-gogo.com/m5stack-sprite/

をそのまま実装してみましたが、確かにちらつきは見えなくなります。

画面書き換えの処理時間を見るために、前後で時間を取得して処理時間を見てみましたが、画面の押し込み(pushSprite())の処理時間は実は普通に画面を書き込む時間と変わらず、大体40msぐらい掛かっています。要は表示領域を一度クリアしないからちらつきを感じることなくなっているわけで、決してpushSprite()にして画面描画時間が高速化される訳ではありません。

 

admin

 

M5Stack内蔵のADCを使ってみる

ネット記事によると精度はそれほどでは無いようですが、実際に使ってみました。

M5StackのAD変換できるピンは35/36になりますが、35ピンに可変電源を1/2.5分割して供給。

測定精度は数%の誤差はあるようですが、用途によっては使えるでしょう。たとえば倒立振子のモーター駆動用電池の電圧をチェックするような目的には。

ノイズは結構盛大にありますから、何回か測定して平均値を取るようにしたほうがいい。

アナログメーター風の表示にはArduinoIDEのサンプルプログラム(TFT_Meter_liniear)を改造して使用。

 

admin

倒立振子(その後)

モーターの駆動にはNiH単三4本使っていますが、電池電圧が低下するとトルクも変わってくるのでPIDパラメータもその変化に対応して変えないと安定状態を保てなくなるという当然の結果。

現実的な対応としては、

① 電池電圧を安定化する

少なくともDD-CON必要だし、電圧ドロップ考えると電池電圧足りないだろうからハード作り直しレベル

② 電池電圧の変化に応じてモーターへの最低印加電圧を可変する

float MOTOR_POWER_MIN = 100;
float MOTOR_POWER_MAX = 250;

ここでMOTOR_POWER_MINを変えてやる、今はモーターが回り始めるぐらいの電圧になるように調整していますが、当然電圧低下するとこの値を変えてやらないとダメなのは自明。

理屈ではM5StackのADコンバーター使って可変することはできそうだね、やるやらないは別にして。

 

admin

M5Stackでオンメモリのイメージデータを表示する

SDカードを使うのは、特に書き込み中のデータ保証ができないのがM5Stack、メモリはそこそこ余裕があるのでメモリ上にイメージファイルを展開してそれを表示する関数、

M5.Lcd.drawJpg(const uint8_t *jpg_data, size_t jpg_len, uint16_t x, uint16_t y)
を使うとオンメモリでの画像表示ができます。M5Stackはjpg形式のデータしか表示できないようですが、フリーのpng形式イラスト素材を持ってきてPhotoScape Xでjpgに変換して、以下のリンクのPythonスクリプトで配列データに変換します。
変換した配列データをソースコードとして扱いコンパイルすると画像データを含むバイナリが作成できます。
こんなデータを使いました。
ソースコードは、
M5Stack上の表示は、
こんな感じで表示できます。
admin

倒立振子(PI制御でハンチングなくした)

P制御だけだと、どうしてもハンチング(オーバーシュート)が発生してしまいますが、I制御を追加することでそれを抑止できます。

D係数は外乱対応とのことですが、調整方法を理解していないので後日触りますが、とりあえずの目標の静止状態は作ることができました。

スピーカーから盛大にノイズが出てますが、これはスピーカー制御端子(25)をモーター制御に使っているから。逆にこういうノイズが出ているときには静止状態であると言えます。

設計データは以下のリンクへ、

https://github.com/chateight/reversed_pendulum

パラメータ(PID)調整はリコンパイルでは時間の無駄なので、Processingを使って動作中に書き換えできるようにしています。

admin

倒立振子(P制御のみ有効化)

チューニングで問題だったのは、M5Stackの画面にログ用に表示させている数値の表示がやたらに遅いこと。ジャイロ・加速度・pitch/role/yawを画面表示させているとloop()の処理時間がおよそ50msも掛かっていました。これではフィードバックが間に合わないので、ログ表示を削除すると10ms以下に収まってます。

P値も大きすぎるとリンギングは盛大になるので、適当なところで止めてみたのが以下の動画です。まあ、振動はしてるし、外乱にも対応できない状態ですが。

 

admin

 

倒立振子パラメータ調整の準備まで完了

倒立振子の製作を始めてますが、PID制御のパラメータ設定を簡単に行えるような準備も整ったのであとはコード全体を書いて調整に取り掛かるだけ。

PIDパラメータは調整のおおまかな方針は決まっているようですが、最後の微調整はカットアンドトライになるので、いちいちソースコンパイル、ダウンロードでは効率が悪いので、ダイナミック(on the fly)でパラメータ書き換えの手段が必須になります。

やり方はM5Stack側はUDP受信で、送信側はProcessingを使って更新します。

<Processingのコード>

/*
set PID parametor to the m5stack using UDP

*/
import hypermedia.net.*;
import controlP5.*;

UDP udp;
ControlP5 cp5;

final String IP = "192.168.1.15";
final int PORT = 3002;
String msg = "10, 3, 1";  // P, I, D

void setup() {
  size(200, 200);

  cp5 = new ControlP5(this);
  udp = new UDP( this, 3002 );

  ControlFont cf = new ControlFont(createFont("Serif",20));

  cp5.addButton("UDP_Msg")
    .setFont(cf)
    .setLabel("send")
    .setPosition(50,50)
    .setSize(100,100);
}

void draw() {
  background(200);
}

void UDP_Msg(){
  udp.send(msg, IP, PORT);
}

SENDボタンでUDP送信、パラメータの書き換えはソースコードで行いますがコンパイル時間は見えないので、実質瞬時に対応できます。

<M5StackのUDP受信処理抜き出し>

M5StackのRegExp処理はいまいちよく分からなかったので、受信テキストは文字列分割で対応。カンマ区切りの3個のパラメータだけなので文字分割でも対応できますが、その分コードは美しくない。このコードをloop()処理中に記述して、ダイナミックにPIDパラメータ変更を実現します。

UDP通信とパラメータ格納の該当部分だけ抜き出したコードです。


#define N 1024

// PID variables
float P_val = 50;
float I_val = 2;
float D_val = 10;

WiFiUDP udp;

	  char packetBuffer[N];
	  int packetSize = udp.parsePacket();

	  if (packetSize){
		  int len = udp.read(packetBuffer, packetSize);

      String pid_data = String(packetBuffer);
      //Serial.println(pid_data);
      int index = pid_data.indexOf(",");
      P_val = (pid_data.substring(0, index)).toFloat();     // P
      pid_data = pid_data.substring(index + 1, pid_data.length());
      index = pid_data.indexOf(",");
      I_val = (pid_data.substring(0, index)).toFloat();     // I
      pid_data = pid_data.substring(index + 1, pid_data.length());
      D_val = pid_data.toFloat();                           // D

 

admin

M5StackでDCモーター制御

M5StackでのPWM制御はArduinoとは違うようなので記録。

① PWMのセットアップ(ledcSetup())を行う

② PWMの制御ピンを論理チャネルとコンバイン(ledcAttachPin())する

③ PWM制御信号を出力(ledcWrite())する

DCモーターの回転方向とブレーキを決める信号は通常のdigitalWrite()で実行。

/#include <m5stack.h>

const uint32_t PWM_Hz = 2000; // PWM freq.
const uint8_t PWM_level = 8;  // PWM resolution 8bit(1~256)
const uint8_t PWM_CH = 1;     // PWM channel

int pwm_a = 2;
int fw_a = 16;
int rv_a = 17;
int pwm_b = 26;
int fw_b = 5;
int rv_b = 25;

void setup() {
  M5.begin();
  M5.Power.begin();

  pinMode(pwm_b, OUTPUT);
  pinMode(fw_b, OUTPUT);
  pinMode(rv_b, OUTPUT);

  // set PWM_CH & resolution
  ledcSetup(PWM_CH, PWM_Hz, PWM_level);
  // combine PWM control pin to PWM_CH
  ledcAttachPin(pwm_b, PWM_CH);
}

void loop() {
  M5.update();

  if (M5.BtnA.wasPressed())
  {
    // PWM duty 0.25(64/256)
    ledcWrite(PWM_CH,128);
    digitalWrite(fw_b, LOW);
    digitalWrite(rv_b, HIGH);
  }
  else if (M5.BtnC.wasPressed())
  {
    // stop
    ledcWrite(PWM_CH,0);
  }

  delay(10); 
}

DIOのピンアサインは以下の通りです。M5StackはArduinoと違ってDIOの本数が限定させるので、他の信号と重複するのは致し方なし。

 

 

admin