2024/05/16(木)GP2Y0A21YKを簡単な計算式で使う

定番の測距モジュール シャープのGP2Y0A21YKがある。

秋月電子 - シャープ測距モジュール GP2Y0A21YK
スイッチサイエンス - 赤外線近接センサGP2Y0A21YKと接続ケーブル
共立電子産業 - PSD測距センサ[鉛F] GP2Y0A21YK

マイコン側ではADCでアナログ電圧を取得、それを計算して検出した障害物までの距離を測ることができる。この距離算出の計算が少々面倒かつ、ネット上ですぐに見つかるライブラリはpow()関数を使っていたり少々大げさなコードが目立つ。そこで、データシートのグラフから逆数回帰で解析して近似曲線を求め、それを測距の関数化をすることにした。このセンサーは最近のレーザー式とは違って精度がでないので10mm程度の誤差は許容するような使い方をする前提と思われる。(男性用小便器の人体検知センサーなどに使われてそう)

目標となる関数は、y = A + (B/x)とし、係数A, Bを求めればよい。

まずは、グラフから手作業で読み取った値を書き出し。
x=0.4のとき Y=80
x=0.5のとき Y=70
x=0.6のとき Y=54
x=0.7のとき Y=44
x=0.8のとき Y=37
x=0.9のとき Y=33
x=1.0のとき Y=29
x=1.1のとき Y=26
x=1.2のとき Y=24
x=1.3のとき Y=21
x=1.4のとき Y=20
x=1.5のとき Y=18
x=1.6のとき Y=17
x=1.7のとき Y=15.5
x=1.8のとき Y=14.5
x=1.9のとき Y=14
x=2.0のとき Y=13
x=2.1のとき Y=12
x=2.2のとき Y=11.5
x=2.3のとき Y=11
x=2.4のとき Y=10.5
x=2.5のとき Y=9.5
計算サイトで理論計算すると、 A = -4.82049, B = 34.83012845 となったのでこれを基に、Google関数グラフで係数を微調整していく。
2024-05-16.png


現物合せで、 A = -4.10000, B = 34.4000 となった。
よって算出する関数は、
const double A = -4.1000;
const double B = 34.4000;
dist = A + B / vol;
となる。コード全体は以下の通り、ヘッダーファイルだけで納めてしまっているので.cppはなし。
#ifndef __GP2Y0A_H__
#define __GP2Y0A_H__

//-----------------------------------------
#define DEF_PIN (A3)
#define OPERATING_VOLTAGE (5.0)
#define RESOLUTION (1024.0)
#define BIT_VOLTAGE (OPERATING_VOLTAGE / RESOLUTION)

#define LOW_LIMIT  (0.0)
#define HIGH_LIMIT (2.6)

//-----------------------------------------
class GP2Y0A
{
public:
    GP2Y0A(uint8_t analog_pin = DEF_PIN)
    {
        pin = analog_pin;
        pinMode(pin, INPUT);
    };
    ~GP2Y0A()
    {
        
    };
    double distance()
    {
        int ad = analogRead(pin);
        double vol = ad * BIT_VOLTAGE;
        double dist = 0.0;
        if(LOW_LIMIT < vol && vol < HIGH_LIMIT)
        {
            // 逆数回帰で解析して近似曲線を求める y = A + (B/x)
            // 理論計算で、 A = -4.82049, B = 34.83012845 
            // 現物合せで、 A = -4.10000, B = 34.4000
            const double A = -4.1000;
            const double B = 34.4000;
            dist = A + B / vol;
        }

        return dist;
    };

private:
    uint8_t pin;

};
#endif
また、距離が近すぎると値が化けるので取得したADC値をLOW_LIMITとHIGH_LIMITで範囲を制限している。使うときはグローバル変数で、
GP2Y0A *psd;
しておいて、Arduinoの場合、setup()関数内で
//PSD init.
psd = new GP2Y0A(A3);// example A3 pin
あとは、loop()関数は、
void loop()
{
    static double kyori;
    char buf[16];
    
    //sample of psd sensor
    kyori = psd->distance();
    dtostrf(kyori, 6, 2, buf);
    Serial->println(buf);

    delay(100);
}
とでもすれば、そこそこ良い感じの値が読み取れているのがわかる。

*1

*1 : 追記:PSD方式とTOF方式は違うとご指摘を受けました。伴ってコードにあるTOF表記をPSDへ変更。

2024/04/30(火)CH376でUSBメモリの読み書きをする。

江蘇沁恒股分有限公司 wch.cn のシリアル通信対応のUSBマスストレージ管理ICを使うためにいろいろと実験した記録。
チップ単体では秋月電子でも取り扱いがあって入手性も問題ない。
USBメモリ&SDカード ファイル管理制御IC CH376S
実験用にモジュールキットはAliexpressで入手した。

概要

  • ボーレートは9600で動かせる。9600bps, 8N1
  • SD制御を行わない場合、5V電源の5VIO単一の回路構成にできる。
  • そのためモジュールキットのLDOを除去してバイパス、V3pinはデータシート通りで10nF接続した。
  • USBシリアル側も5VIOで実験できる。秋月のType-C USBシリアルがソレ。
  • D4,5,6ピンをVccに接続することで、電源投入時に9600buadとなるはず。
CH376_TEST_pic.jpg

UART制御時の仕様

UART通信仕様として、
通信キャリブレーションとして常に先頭に 0x57, 0xAB を付加してからコマンドを送信する。
送受信の表記はすべて16進数。
例1
 送信:57 AB 06 01
 57 AB=コマンド接頭語(固定)
 06=CHECK_EXIST
 01=テストバイト引数
 受信:FE(テストバイトの反転バイト)

 送信57 AB 06 55 → 受信AA
 送信57 AB 06 AA → 受信55

例2
 送信:57 AB 01
 57 AB=コマンド接頭語(固定)
 01=GET_IC_VER
 受信=41など、バージョン番号が返る

初期化手順

  • CHECK_EXIST 06 通信確認
    • 送信 57 AB 06 55
    • 受信 AA
  • RESET_ALL 05 リセット
    • 送信 57 AB 05
    • 受信 (なし)
  • CHECK_EXIST 06 通信確認
    • 送信 57 AB 06 AA
    • 受信 55
  • GET_IC_VER 01 チップバージョン
    • 送信 57 AB 01
    • 受信 41(など)
  • SET_USB_MODE 15 USBモードを設定(USBメモリ接続の場合は06)
    • 送信 57 AB 15 06
    • 受信 51(CMD_RET_SUCCESS)
この時USBデバイスが挿さっていると追加で15(USB_INT_CONNECT)を受信
以降、現在の状態でUSBデバイスが挿されると15,抜かれると16(USB_INT_DISCONNECT)が返る
  • DISK_CONNECT 30 DISK接続
    • 送信 57 AB 30
    • 受信 14(USB_INT_SUCCESS)が返れば、ディスク接続完了。
  • DISK_MOUNT 31 DISKマウント
    • 送信 57 AB 31
    • 受信 14(USB_INT_SUCCESS)が返れば、ディスクをマウント完了。
これでディスクアクセスの準備完了

接続されたディスクの容量を確認

  • DISK_CAPACITY 3E DISK容量取得
    • 送信 57 AB 3E
    • 受信 14(USB_INT_SUCCESS)が返れば、でディスク容量を取得完了。内部バッファにデータが格納される。
  • RD_USB_DATA0 27 バッファ読み出し
    • 送信 57 AB 27
    • 受信 04 FF FF EF 00(など) バッファ内容が返る。
      • 04(バッファデータバイト長)、00EFFFFF(ディスクセクタ数)
      • →1セクタ512byte(標準)なので0x00EFFFFF * 512 = 8,053,063,168 bytesとなり8GBのUSBメモリであることがわかる。

読み込みテスト

ルートディレクトに「1234.TXT」があり、内容は「1234567890ABCDEF」であるとする。
  • SET_FILE_NAME 2F 操作ファイル名を設定
    • 送信 57 AB 2F 2F 31 32 33 34 2E 54 58 54 00
      • 2F 31 32 33 34 2E 54 58 54 00 がファイル名(ファイルパス)"/1234.TXT\0"
    • 受信 (なし)
  • FILE_OPEN 32 ファイルオープン
    • 送信 57 AB 32
    • 受信 14(USB_INT_SUCCESS)が返れば成功
  • BYTE_READ 3A ファイルから内部バッファへ読み込み
    • 送信 57 AB 3A 08 00
      • 08 00 (読み出しバイト数、0008(8byte))
    • 受信 1D(USB_INT_DISK_READ)が返れば成功
  • RD_USB_DATA0 27 バッファ内容を取得
    • 送信 57 AB 27
    • 受信 08 31 32 33 34 35 36 37 38(など)が返る。
      • 08(バッファデータ長)、31 32 33 34 35 36 37 38(バッファ内容="12345678")
BYTE_READ, RD_USB_DATA0を繰り返しファイル末端へ到達した場合
  • RD_USB_DATA0 27 バッファ内容を取得
    • 送信 57 AB 27
    • 受信 00
      • 00(バッファデータ長)が返れば、それはファイル末尾まで読み出し完了し、読み込めたバイト数が0(なし)であることを示す。
  • FILE_CLOSE 36 ファイルクローズ
    • 送信 57 AB 36 00
      • 00(ファイルサイズの自動更新をしない)
    • 受信 14(USB_INT_SUCCESS)が返れば成功

書込みテスト

  • SET_FILE_NAME 2F 操作ファイル名を設定
    • 送信 57 AB 2F 2F 57 52 49 54 45 2E 54 58 54 00
      • 2F 57 52 49 54 45 2E 54 58 54 00 がファイル名(ファイルパス)"/WRITE.TXT\0"
  • FILE_CREATE 34 ファイル作成/オープン
    • 送信 57 AB 34
    • 受信 14(USB_INT_SUCCESS)が返れば成功。
      • すでに存在するファイル名の場合、その内容は削除される
  • BYTE_WRITE 3C 書き込みバッファ領域確保
    • 送信 57 AB 3C 05 00
      • 05 00(データバイト長0005(5byte))
    • 受信 1E(USB_INT_DISK_WRITE)が返れば成功
  • WR_REQ_DATA 2D 書き込みデータ送信
    • 送信 57 AB 2D 31 32 33 34 00
      • 31 32 33 34 00 が"1234\0"
    • 受信 05(など)が返る。
      • 05(書き込みできたバイト数05(5byte))
  • BYTE_WR_GO 3D 書き込みを完了させる(?)(Flush?)
    • 送信 57 AB 3D
      • 次の書き込み要求待ちにする(?) これについては詳細不明だが、このコマンドがないと実際にデータが書き込みされない。
  • FILE_CLOSE 36 ファイルクローズ
    • 送信 57 AB 36 01
      • 01(ファイルサイズの自動更新をする)
    • 受信 14(USB_INT_SUCCESS)が返れば成功

2024/01/26(金)テキストエディタをJmEditorからMeryに。

昔話

その昔、BCC DeveloperというBorland C++の無償コンパイラを利用してWindowsアプリが作れる/作りやすいIDEがあった。
その作者が高機能テキストエディタJmEditorというのソフトも公開していてすごくよくできていて、シンプルなのに必要な機能がほとんどそろっていて非常に動作も軽くて自分はずって手放せないでいました。使い始めたのが2003年とかそれくらいで、Widnows2000のころだったのでもう20年は使っています。そしてWindows10でも動いてるんだからMicrosoftもそれはそれですごい。

移行先

とはいっても、印刷機能がなかったりさすがに古くなっているし、作者のサイトもなくなっていてもうずっとアップデートもありません。
それで移行先をずっと探していました。数年前に検討して少し使っているのは gPad というソフトですが、高機能すぎて自分にはちょっと違うなという感じでした。
そして今回見つけたのがMery、JmEditorの雰囲気になんとなく近くてしっくりきました。
窓の杜でも紹介されていますがこちらはバージョンが古く2.x系で作者のサイトに行くと最新の3.x系の入手が可能です。
現在はVer.3.6.5beta、アップデートも随時行われているので今後はこれを使っていきたいと思います。

それにしても、JmEditorを20年も使っていたのか…。

2024/01/17(水)mpg123とmpg321@ラズパイCUI

自分は、ラズパイなどLinuxでもGUIがないとほとんど何もできない&WindowsのようにGUIできるの最高だぜな人。
しかし、最近ラズパイをCUIだけで使い始めててちょっと楽しくなってきた。
それで、ラズパイならつけっぱなしでも気楽だし部屋のBGMに垂れ流し音楽プレイヤーにもしようと思ってCUIプレイヤー探していたときに「なんやねん」って思ったのがmpg123mpg321は別のアプリってこと。いやほんとなんやねんこれ。

最初mpg321をインストールして使ってたけどなんかプチプチ途切れたような再生で音質が悪い。それから結局mplayerという別のアプリを試したらノイズが消えて解決しました。この時点でmpg123の方の存在は知らなくてノイズの原因調べてたらmpg321はmpg123の亜種みたいなソフトでノイズが乗る現象をほかの人も書いてたことを知って、何が言いたいかというと「ねんやねんそれ」っていう文句w

まとめるとLinuxのCUIで有名な(?)プレイヤーを知った3つが以下
  • mpg123
  • mpg321(mpg123の亜種)
  • mplayer(FFmpegがエンジンなのでなんでもござれプレイヤー)
いずれも、
sudo apt-get install ほげほげ
するだけの簡単インストール。

熟成された格ゲー・音ゲーみたいに、初心者に厳しいLinuxと思う…のでそれを少しでも緩和するべく記録に残すことにした。

あ、あとubuntuでuseraddadduserで挙動が違うのは許し難いぞ…。

2024/01/12(金)Arduinoボード 3品種のLDO駆動能力

よく使うArduino3品種の3.3V出力の最大消費電流が分からなかったので調べたメモ。
搭載LDOの最大電流は下記の通り、自作の回路やシールド基板では1割程度余裕を見ておいたほうが良いと思う。
ボード搭載LDO最大電流
Arduino UNO R3LP2985-33DBVR150mA
Arduino Uno R4 Minimaマイコン内蔵LDO100mA
Arduino Nano EveryAP2112K-3.3600mA
Nano Every 結構強力、W5500 Ethernetチップなんかも余裕で動きそう。
がっつり3.3Vが必要な場合はVINから自前でDCDCで作るのが順当。