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/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で作るのが順当。

2023/08/21(月)Arduino Nano Everyの挙動

自分用のメモ。
Arduino Nano Everyになってから、UNO R3と違ってタイマ/カウンタが盛りだくさんになった。
その挙動を調べていて使ってるところ使ってないところなどの確認。

とりあえず起動時のTCA0周りを一覧
TCA0.SINGLE.INTCTRL = 0b0
TCA0.SINGLE.CTRLA = 0b1011
TCA0.SINGLE.CTRLB = 0b11
TCA0.SINGLE.CTRLC = 0b0
TCA0.SINGLE.CTRLD = 0b0
TCA0.SINGLE.CTRLECLR = 0b0
TCA0.SINGLE.CTRLESET = 0b0
TCA0.SINGLE.CTRLFCLR = 0b0
TCA0.SINGLE.CTRLFSET = 0b0
TCA0.SINGLE.EVCTRL = 0b0
TCA0.SINGLE.INTCTRL = 0b0
TCA0.SINGLE.PER = 255
TCA0.SINGLE.CMP0 = 128
TCA0.SINGLE.CMP1 = 128
TCA0.SINGLE.CMP2 = 128
TCA0.SINGLE.CTRLCなのですが、起動時は0b0だがtone()実行中は0b111になっています。
比較一致(Compare) CMP2OV CMP1OV CMP0OV 出力ですね。なぜ全部?

あと、この記事がすごく役立ちました。
@CoTechWorks さん
Arduino Nano Everyのタイマー割り込み機能について

この記事を参考に進めています。

2022/08/20(土)Nordic SoftDevice S132 v17.1.0にSerializeFWでアクセス

NordicのnRF52832を積んだマイコンは各社から出てますが、大体がそのチップ自体のプログラミングをして動かしてると思う。
NordicはSerialization FWというのを提供しており、これはSoftDeviceと一緒に書き込むことでそのチップはBLEモジュールとして扱い(Connectivity Chipと言う)ホストMCU側(Application Chipと言う)からUARTを通してすべて操作できるというものです。

となると、UART通信仕様が必要になりますし通信フォーマットが知りたいのですが、なんとこの情報がまともに掲載されていません。v11.0.0のバージョンに無くはないのですが
https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v11.0.0%2Fble_serialization_s130_functions.ble_serialization_s130_functions
命令バイトとなるopcodeがv17系と違っていてバージョン違いで平気で仕様を変えてきます。(なんなんですかこれ…。)
調べているなかで本家のコミュニティのDevZoneに同じ悩みの人がいたのですが、誰も知らないみたいなまともな回答がなくもうなんだか…。(なんなんですかこれ…。2回目)

SDKの中を覗いてみると
nRF5_SDK_17.1.0_ddde560\components\serialization
というのがあって、どうやらMCU側はこのライブラリを使って「よろしく動かしましょう。通信プロトコルやパケットフォーマットは気にしなくてよいです。」ということが言いたそうな空気を感じました。

でも自分は、生の通信を見ながらある程度の動きを把握したいので、解析を頑張りました。
  • TX_RAW_PACKET = [TX_HEADER][TX_PAYLOAD] の構成
[TX_HEADER]は2byteで、パケットの長さを示す[0x03, 0x00]の場合 0x0003で3byte続く
[TX_PAYLOAD]の1byte目は、0x00がMCU→BLEで0x01がMCU←BLEの方向、UARTの場合TX,RXで分かれているので各デバイスからは固定で送出されますね
2byte目はcommand code(opcode)、これがバージョンによって変わる。変わるなよ。例えばv17でsd_ble_version_get = 0x65ですが、v11ではsd_ble_version_get = 0x66です。
3byte目以降はopecodeによります。
*** sd_ble_version_get
--> 03 00 00 65 00 
<-- 06 00 01 65 10 00 00 00 

上記を送るとこんな感じで帰ってきます。10 00 00 00が返信内容でBLEからは4byteが標準的に返ってきます。あとは命令によって様々。
まずは基本はこんな通信みたいです。

肝心のv17.1.0のopcodeですが
\nRF5_SDK_17.1.0_ddde560\components\serialization\common\ser_dbg_sd_str.c
の中にありました。どうやらこれが一覧っぽいです。
S132 v17.1.0

static const char * sd_functions[] = {
    /* 0x60 offset */
    "SD_BLE_ENABLE",                           /*0x60*/
    "SD_BLE_EVT_GET",                          /*0x61*/
    "SD_BLE_UUID_VS_ADD",                      /*0x62*/
    "SD_BLE_UUID_DECODE",                      /*0x63*/
    "SD_BLE_UUID_ENCODE",                      /*0x64*/
    "SD_BLE_VERSION_GET",                      /*0x65*/
    "SD_BLE_USER_MEM_REPLY",                   /*0x66*/
    "SD_BLE_OPT_SET",                          /*0x67*/
    "SD_BLE_OPT_GET",                          /*0x68*/
    "SD_BLE_CFG_SET",                          /*0x69*/
    "SD_BLE_UUID_VS_REMOVE",                   /*0x6A*/
    "SD_UNKNOWN",                              /*0x6B*/
...
抜粋。

全然まとまらない。もう自分用備忘録ということで。

あと、UARTは、速度1Mですが、みんな115200に変更して書き込んでいる模様。
それと、パリティありって、Odd/Evenどっちなのか不明確でしたが、Evenのようです。RTS/CTSも標準で必要。
Arduino風に書くならSERIAL_8E1 | SERIAL_RTSCTSです。

【訂正・追記】2022/8/21

2021/10/01(金)VSCodeでArduino開発するときのメモリ使用量の確認

VSCodeにArduino拡張を入れて開発するとき、本家IDEと違って、以下のようなメモリ使用量がでない。
"C:\\Program Files (x86)\\Arduino\\hardware\\tools\\avr/bin/avr-size" -A "R:\\/test.ino.elf"
最大32256バイトのフラッシュメモリのうち、スケッチが20776バイト(64%)を使っています。
最大2048バイトのRAMのうち、グローバル変数が896バイト(43%)を使っていて、ローカル変数で1152バイト使うことができます。
そこで、VSCodeのtask.jsonを書いて同等の情報出力をファイルに出すようにした。手順は以下の通り。
  1. Ctrl+Shift+Pでコマンドパレットを開く
  2. コマンドパレットに"task"と打ったら「タスクの構成を開く」とあるのでそれを選択
  3. 次に「テンプレートからtask.jsonを生成」とあるのでそれを選択
  4. そして「Others」を選択するとtask.jsonファイルが生成される
以下の例に従って記述を変更する。

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "メモリ状況",
            "type": "shell",
            "presentation": {"echo": true},
            "command": "C:\\Program Files (x86)\\Arduino\\hardware\\tools\\avr\\bin\\avr-size.exe",
            "args": [">", "memory.txt", "-C", "--mcu=atmega328p", "R:\\test.ino.elf"]
        }
    ]
}

なお、ドライブレターやパス、ターゲットelfファイル名などすべてハードコーディングしているので適宜変更が必要。まずは動くことを重視した。task.jsonを保存したらメニューからターミナル->タスクの実行を選択すると「メモリ状況」という項目が増えているのでそれを選択して「タスクの出力をスキャンせずに続行」で実行する。
するとmemory.txtにFlashとSRAMの使用量が出力される。この処理はコンパイル直後に行う。