nRF52840 Dongle + Winshark でBTアナライザをSetupする

これまでBLEのアナライズは市販機器とBT接続するアプリを作成する場合、なかなか通信仕様書に未記載のタイミングやシーケンスもありとても有効でした。。これまで WinShark Adafruit ADA-2269 nRF51822搭載 Bluefruit LE Sniffer (下図)を WinShark で用していました。

PCに差せばすぐ使えるので導入しやすいです。しかしBLE4.0までの対応です。今回開発ネタの BLE5.4nRF52840と、BLE5.2のAndorid端末を使います。Adafruit で試したところやはりアナライズに引っ掛からないようです。そこで nRF52840 Dongle をアナライザ化できるとのことで導入しました。その覚書です。

入手期間は1weekくらい。日本に在庫を持っているようです。在庫元はDigkeyでした。静電ビニールに型番が大きく印刷されたシールが貼られていてメーカの自身ありげな感じです。

PCに差すと赤いLEDがふんわり1秒程度で点滅します。これはBootloadrモードです。出荷時にBootloadrが書込んあるので、高価なデバッガインターフェイス無くてもプログラムの書込みが出来るようになっているわけです。

nRF Connect Desktop をインストール済みPCなら、以下のように仮装COMポートの割り当てを確認できます。


導入KIT: nRF Sniffer for Bluetooth LE をダウンロードします。登録等は不要です。なるほど Adafruit のプログラムも元々Nordic製だったのですね。

具体的に何を使って書込むのか、WEBページ、PDFドキュメントには明確に書いてありません。nRF Connect Desktop から、J-linkコマンド版から、nrfprogから、nrfutil からと複数の情報があり悩みます。同Dongleの場合は nRF Connect DesktopProgramer でOKです。 Bootloaderの書込みプロトコルは?と深読みしてしまいがちですが、Programer が同Donglebootloader に対応しています。少し古いバージョンでも大丈夫なようです。

尚、一度書込むとDongleの通常起動時はアナライザプログラムが起動します。WinSharkからのリクエストがないとLEDは何も点滅しないため、失敗したかと焦ります。bootloader は残っているので、起動したまま下図のRESETボタンを押す再びbootloader モードに移行します。USBに差しながら押すとかはナシです。(Zigbeeがそうだったような) 取説には「boot PINとGPIOを接続する」等の記載がみられましたが本操作だけていいようです。


WinSharkの基本セットアップ

導入KITのフォルダー内容をWinSharkコピーします。

Profileをインポートします。

基本コレでOKなハズですが、WinSharkDongleを認識しません。


Dongleを認識しない原因は、プログラム書込み後、Dongleの仮想COMがWindows標準用に代わってしたことによるものでした。以下のように修正します。これでDongle黄緑LED1が約0.5secで点滅を開始しました。(周波数ホッピングでフィルタ時にデータが得られない時も消灯するようです)

しかしWinSharkのインターフェイス一覧に、計測用LAN非接続の Windows8 32bit では表示され、Win10/Win11 64bitでは表示されない事象が発生です。WinShark編集->設定->Protcol->Bluetooth->BT LE LLをみると、 32bit版ではBLE5.0まで、64bit版ではBLE5.4までの対応となっているようで、32bit版では目的が達成できません。


導入KITPythonを使っているますが、そのインストール有無判定にWhereコマンドを使っています。以下12行目です。( py python もない場合スルーなのもいただけないですが… )

Windows10以降は、Microsoft Store 版のpython があり、py python をたたくとMicrosoft Store に飛ぶランチャーが仕込まれています。このため nrf_sniffer_ble.py がスルーされていました。びっくりです。紛らわしいので削除します。この後、正しく本家Pythonも入れて、環境変数のPATHを切ります。


しかしまだうまくいきません。nrf_sniffer_ble.py の処理結果を見たいので、 nrf_sniffer_ble.bat に手を入れます。リダイレクトでエラー出力を、Users\xxx\Appdata\Temp に記録し、エラーがあったらNotepadで開きます。こうすると何か問題が生じたらスルーせずに気づけます。

@echo off

set MYLOG=%TEMP%\nrf_sniffer_ble_bat.log
del /f %MYLOG%

rem Path to this batch file
set NRF_SNIFFER_BLE_PATH=%~dp0
rem Remove the "\" from the end of the path
set NRF_SNIFFER_BLE_PATH=%NRF_SNIFFER_BLE_PATH:~0,-1%

rem Activate virtualenv if present
if exist "%NRF_SNIFFER_BLE_PATH%\env\Scripts\activate.bat" call "%NRF_SNIFFER_BLE_PATH%\env\Scripts\activate.bat" 2> %MYLOG%

rem Find out if the launcher is installed and available
where py > NUL 2>&1 
if %ERRORLEVEL% EQU 0 (
    py -3 "%NRF_SNIFFER_BLE_PATH%\nrf_sniffer_ble_win.py" %* 2>> %MYLOG%
) else (
    python "%NRF_SNIFFER_BLE_PATH%\nrf_sniffer_ble_win.py" %* 2>> %MYLOG%
)

if %ERRORLEVEL% NEQ 0 (
notepad %MYLOG%
)

以下エラーがでていました。option機能を追加インストールします。

警告(UNIXでは出ないのでしょうか?)も一応直して、Windows専用にリネームしときます。カッコ内は ¥¥ しないといけないようです。

これでDongle黄緑LED1が点滅を開始しましたが、WinSharkのメイン画面にインターフェイス名: nRF Sniffer for Bluetooth LE が現れません。


WinSharkの設定見直し4

仕方ないので nrf_sniffer_ble_win.py にトレースを仕込みます。 logging は仕込まれていましたが、logfileに落とすようになっていないので、132行目辺りに以下のコードを挿入、既存 logging インスタンスはlogger に置換。ログファイルはパスを渡すと面倒なのて固定です。

logger = logging.getLogger("nrf_sniffer_ble_win")
logger.setLevel(logging.INFO)
fl_handler = logging.FileHandler(filename="c:\\windows\\temp\\nrf_sniffer_ble_win_py.log")
logger.addHandler(fl_handler)

しかしいろいろいじっているうちにインターフェイス名が現れるようになりました。

ログしてみると RF52840とAndorid端末:MotoG32でためした約2Kbの244byte分割送信が見受けられました。

ようやくアナライズ環境が準備できましたが、新たなギモンが生じました。別記事に引き続きます。

GPLライセンスに従い修正したスクリプトは、本体にupdateするのは恐れ多いので個別にGitHubにおきました。

https://github.com/MOTOPLUS4APPLICATIONS/nrf_sniffer_for_bluetooth_le

AndroidでWindowsのGridVew的な表示を行うには

Androidで、WindowsのGridVew的な表示(HTMLでいうTable)を行う方法が、他記事にあまり見られなかったのでレポートします。他に良い方法があるのかもしれません …

  • TableLayoutTableRow、TextView を使う。
  • データ部はプログラムコードにて生成する。
  • ヘッダ部とデータ部で列幅は合せてくれない。
  • ヘッダ部の列幅の取得はうまくいかないため、データ部列幅は現物合せするしかない。
  • スクロールするには、ヘッダ用とデータ部用で TableLayout を分け、データ部用をScrollViewに載せる。
  • 格子線の描画はうまく行かなかった。res/drawable/*.xml に android:width=”1dp” を記述しても Table全体の外枠しか描画されず。


図にすると以下のように構成します。

xmlの記述例は、

		<!-- データ部。高さはmatch_parentとする -->
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingLeft="16dp"
        android:paddingRight="16dp"
        android:orientation="vertical" >

		    <!-- 表題 -->
        <TextView
            android:id="@+id/editTextTextPersonName"
            android:layout_width="match_parent"
            android:layout_height="32dp"
            android:layout_marginTop="1dp"
            android:gravity="center"
            android:text="CAN Frames View"
            android:textSize="20sp"
            android:visibility="visible"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="1.0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

 		    <!-- ヘッダ部。高さを固定にする -->
        <TableLayout
            android:id="@+id/tblCANFramesHead"
            android:layout_width="match_parent"
            android:layout_height="35dp"
            android:layout_marginStart="1dp"
            android:layout_marginEnd="1dp"
            android:layout_marginBottom="1dp"
            android:gravity="top|left"
            android:padding="5dip"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent">

        	<TableRow android:id="@+id/viewHeader">
            	<TextView
                	android:layout_weight="2"
                	android:gravity="center_horizontal|center_vertical"
                	android:padding="1dip"
                	android:text="ID" />
            	<TextView
                	android:layout_weight="4"
                	android:gravity="center_horizontal|center_vertical"
                	android:padding="1dip"
                	android:text="TIME" />
                	
             ・・・中略・・・
             
        	</TableRow>
        </TableLayout>

  		  <!-- データ部。高さはmatch_parentとする -->
        <ScrollView
            android:id="@+id/srcView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginStart="8dp"
            android:layout_marginLeft="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginRight="8dp"
            android:layout_marginBottom="76dp"
            android:scrollbars="vertical"
            app:layout_constraintBottom_toTopOf="@+id/tblCANFrames"
            app:layout_constraintEnd_toEndOf="@+id/tblCANFrames"
            app:layout_constraintHorizontal_bias="1.0">

            <TableLayout
                android:id="@+id/tblCANFrames"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginStart="1dp"
                android:layout_marginEnd="1dp"
                android:layout_marginBottom="1dp"
                android:gravity="top|left"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent">

                <!-- ダミーData部 無いとコケる -->
                <TableRow android:id="@+id/viewData" >
                    <TextView
                        android:layout_weight="2"
                        android:gravity="center_horizontal|center_vertical"
                        android:padding="1dip" />
                    <TextView
                        android:layout_weight="4"
                        android:gravity="center_horizontal|center_vertical"
                        android:padding="1dip" />
                        
                    ・・・中略・・・
                    
                  </TableRow>
            </TableLayout>
        </ScrollView>
    </LinearLayout>


セル一つづつ生成していきます。ダミーデータ行は最後に削除します。以下コードです。

       TableLayout frameTbl = this.findViewById(R.id.tblCANFrames);

        for (int i = 1; i <= 64 ; i++) {
            TableRow tblRow = new TableRow(this);

            // CAN-ID
            TextView viewId = new TextView(this);
            viewId.setText("000");
            tblRow.addView(viewId, 0);

            ・・・中略・・・

            // DATA
            for (int j = 0, k = 3; j < CAN_DATA_BYTE_NUM; j ++, k ++) {
                TextView viewDat = new TextView(this);
                viewDat.setText("00");
                tblRow.addView(viewDat, k);
            }

            frameTbl.addView(tblRow, i );     // 行追加、Heightき変更不要
        }

        // ダミーデータを削除。
        // ※removeAllするとコケる。TableRow無しのTableLayoutをつくるとコケる。
        frameTbl.removeViewAt(0);


列幅は setPadding で以下のように調整します。

       // CAN-ID
       TextView viewId = new TextView(this);
       viewId.setText("000");
       viewId.setPadding(5,0,5,0); // <- 列幅調整
       tblRow.addView(viewId, 0);

Wordwrapが効いて複数行になってしまう列は、行数を強制します。

      // 受信TIME
      TextView viewTim = new TextView(this);
      viewTim.setText("00000000");
      viewTim.setLines(1);    // <- 1行に強制する
      viewTim.setBackgroundColor( bgclr[i%2] );
      tblRow.addView(viewTim, 1);

格子線描画はうまく行かないため、代わりに見易くするため1行毎に色替えします。

     // 一行を見やすくするため交互に色替え。
     int[] bgclr = new int[2];
     bgclr[0] = Color.rgb(0xFF,0xFF, 0x88);
     bgclr[1] = Color.rgb(0x88,0xFF, 0xFF);
        
     for (int i = 1; i <= CAN_FRAME_MAX_NUM ; i++) {
          TableRow tblRow = new TableRow(this);
          // CAN-ID
          TextView viewId = new TextView(this);
          viewId.setText("000");
          viewId.setPadding(5,0,5,0);
          viewId.setBackgroundColor( bgclr[i%2] ); // <-- 1行毎に色替え
          tblRow.addView(viewId, 0);
          
          ・・・後略・・・


レイアウトのみの実行結果です。


データ表示の実行結果です。 他記事で取り上げたNordic Bluetooth基板から、Bluetoothでテストデータを受信し、1sec周期でリアルタイム表示しています。端末はMotorola G32。予想よりスムーズです。以後課題としては等長fontにしないとズレます。

表示コードは初期と同じなので省略です。

Eclipseの設定/操作方法メモ【随時更新】

汎用/Open SourceのIDE Eclipseの設定/操作方法の覚え書きです。

以外ですが、Ctrl + “+”Ctrl + “-” が効きます。Ctrl + マウスホイール は効きません。

検索ヒストリ

検索ヒストリがなかなか便利。ただEclipceを再起動するとリセットされてしまいます。

こちらの別記事にまとめました。

宣言先等を次々に素早く参照できるのはGoodですが、元がどこだか分からなくなりがちです。その場合の戻るは以下です。

フォルダリンク

Project Folder以外にソースがあるとき、直下に置かなくてもリンクを作ればOK。検索の対象になります。パッケージが全部必要ないとき、検索範囲を絞るのにも有効です。

Eclipce では #if #ifdef でBuild対象外になっているスコープがグレーアウトされます。Eclipce でBuildせず解析のみに使用する場合、ちょっと邪魔です。Project設定に以下のように追加します。

https://sourceforge.net/projects/copyaspath/ 右clickメニューから開いているfileのパスをclipboradにコピー
http://vrapper.sourceforge.net/update-site/stableVim、詳しくはこちらに記載しています。

.jar のpluginをインストールするには、eclipse\plugins にコピーして、クリーン起動(-clean)

pluginをインストール後のクリーン起動は、コマンドラインからたたかなくても以下にショートカットメニューが作られています。

以下に MS932 を設定し、Eclipceを再起動します。バージョンによっては、その他にチェックを入れ、MS932 を手入力します。

以下のように設定をexportします。図例は 2019版 です。

以下のよう移行先でimportします。図例は 2024版 です。

以上、随時更新中です。

Vimの設定メモ【随時更新】

最強最速のエディタviのUNIX上以外用であるVim設定の覚書です。しかし vi を使っていると「懐かしい!」という方がまれにいらっしゃいます。

基本設定

お勧め基本設定は以下のとおりです。vim74\gvimrc を以下のように編集します。

set tabstop=4
" オートインデントな
set noai
set nu
set shiftwidth=1
set nosr
set vb	" ビープ音な
"Explore表示の設定(:Exでファイルリストが表示できる)
let g:explDetailedList=1 "詳細表
let g:explDateFormat="%Y.%m.%d-%H:%M:%S" "Date形
"make 関
set makeprg=nmake.exe
set makeef=.\\make.out

fileと同じディレクトリに、中間file ~、.un~ を作らない

お勧め基本設定は以下のとおりです。vim74\gvimrc を以下のように編集します。

set directory=$LOCALAPPDATA\temp
set backupdir=$LOCALAPPDATA\temp
set undodir=$LOCALAPPDATA\temp

モトローラSレコードの表示

  • こちらから srec.vim をD/Lし、vim74\vim74\syntax にCopy。
  • こちらから srec.vim をD/Lし、vim74\vim74\ftplugin にCopy。
  • vim74\vim74\filetype.vim に以下のエントリに、”*.mot” を追加。
" Moterola S record
au BufNewFile,BufRead *.s19,*.s28,*.s37,*.mot	setf srec
  • アドレスの少し色が薄いのでvim74\vim74\syntax\srec.vim を少し直す。左から3語句目がvim74\vim74\colors\選択しているColorマップ.vim 中の色付け部位名に対応しています。
  HiLink S1Addr		label
  HiLink S2Addr		label
  HiLink S3Addr		label

以下表示結果です。

日本語文字コードの確認と変換

文字コード確認は、

変換は、set fenc=shitf_jis もしくは set fenc=utf-8 。でも部分的に混在しているとうまくいかなかったような、、、

よく分からなくなるモトローラSレコードフォーマットまとめ

組込ソフトの開発現場では、モトローラSレコードを直接見たり、少し加工したりすることもしばしばです。その都度、,ネットをみたり、昔作ったツールのコードを見たり手間なので自分用に必要なことだけをまとめておきます。

レコードの種類

レコードレイアウトはより、まずレコードの種類で悩みます。

種類項目名説明
S0コメントROM書き用なら削除してよい。
コンパイラが吐いたものなら削除していい。
生産情報ょ埋込むユーザさんもいるのでたくさんある場合は注意。
S116bitアドレスデータS2、S3と混在もOK。並びは基本アドレスの昇順。
S224bitアドレスデータS1、S3と混在もOK。並びは基本アドレスの昇順。
S332bitアドレスデータS1、S2と混在もOK。並びは基本アドレスの昇順。
S4リサーブ
S516bitアドレスデータレコード数ほとんど見たことがない。
ROM読書き時は無視してもよい。
S624bitアドレスデータレコード数ほとんど見たことがない。
ROM読書き時は無視してもよい。
S7S3セグメントの末尾に付与される。ここのデータ数値は諸説あるようだが、ROM読書きでは使用しない。
チェックサムではない。
次セグメントのアドレスoffsetは、Intel Hexの方である。
S8S2セグメントの末尾に付与される。ここのデータ数値は諸説あるようだが、ROM読書きでは使用しない。
チェックサムではない。
次セグメントのアドレスoffsetは、Intel Hexの方である。
S9S1セグメントの末尾に付与される。ここのデータ数値は諸説あるようだが、ROM読書きでは使用しない。
チェックサムではない。
次セグメントのアドレスoffsetは、Intel Hexの方である。
その他全体のチェックサム行はない

レコードのフォーマット

まず vim の .mot 設定をSetupしましょう。詳しくはこちら。

S1レコード:

byte位置 1基点桁数内容
12レコードタイプ
32レコード長、16進、
データ長 ÷2 + 8
48アドレス、16進
13桁以降2~512データ、16進、データ長は最大255
後ろから2チェックサム,
0xFF – アドレス以降の2桁毎のバイナリ値の総和を256で割った余り

S2レコード:

byte位置 1基点桁数内容
12レコードタイプ
32レコード長、16進、
データ長÷2 + 12
412アドレス、16進
17桁以降2~512データ、16進、データ長は最大255
後ろから2チェックサム、上に同じ

S3レコード:

byte位置 1基点桁数内容
12レコードタイプ
32レコード長、16進、
データ長桁数÷2 + 16
416アドレス、16進
21桁以降2~512データ、16進、データ長は最大255
後ろから2チェックサム、上に同じ

読込み時は、

  • コンパイラによって、マイコンのアドレス範囲によって、S1、S2、S3 がごちゃ混ぜになる場合がある。(コンパイラ次第ではレコード種を指定すれば回避化可能)
  • コンパイラによって、レコードデータ長が統一されない場合がある。
  • コンパイラによって、ブランクが空く場合がある。(コンパイラ次第では出力アドレス範囲を指定すれば回避可能)

自ら生成する場合は、

  • 利用先システムよって、レコードデータ長が統一されないと異常動作する場合がある。
  • 利用先システムよって、ブランクあると異常動作する場合がある。
  • 利用先システムよって、データフラッシュやRAM領域など、アドレスが 0xFxxx xxxx で書かれたデータを読むと落ちる場合がある。(絶対アドレスとしてメモリ確保されてしまうと当然足りない…)

弊方では、モトローラSレコード、Intel Hex fileのROMデータ加工アプリケーションとAPIライブラリも開発しております。自社で作るのは面倒、時間がない、土台となるコードがないなどありましたら、こちらより気軽にご相談ください。