Windows11 24H2にUPDATEできなくなった件2

前記事ではFUJITSU Q556/R でWindows11にて新エディションにupdateができなくなり、その原因を報告しました。 Win11対応のCPUが搭載できるQ558とのそれができないQ556を購入間違えていたのが根本原因でした。

運よくQ558/VのCPU/ストレージ無しのドンガラを安価に入手できました。再セットアップ工数と購入費のトレードオフ面では疑問ですが、業務上の買い物失敗は取返したいところです。双方の基板を比較してみます。

Q558ではRS232Cがオプションになったみたいです。今回は残念ながらRS232C無でした。

Q558/Vドンガラは動作不明のため、まず確認用に比較的安価なCPU: core i3 8100T を入手します。

Q558/Vドンガラには、PC4-2666 4GB が差さっててました。本機は core i5 8700Tモデルだった模様です。CPUと通信速度が合うQ556/Rで使っていたPC4-2400 に差替えます。

OSアップデート

Q556/Rで使っていたストレージを差し電源ON。OSライセンス的にはQ556/Rに元々もあるので問題ないハズです。CPUと基板が変わったので少し時間がかかりますが起動しました。

そこからWindows Updateでは24H2のアップデート項目は表示されなかったため、USBメディアでインストールを開始。当然ですがCPUチェックは通過します。

下記のとおりWindows11 24H2 にアップデートできました。

一応マシンとしては別になったので、アプリの動作確認をします。

MS Office 2003はライセンス承認も求めてきます。承認は通過(未だできますね) こちらはパッケージライセンスなので問題無しです。

Q556/Rに入っていたMS Office 2021はプレインストールライセンスの模様で更新はできず。再購入が必要です。 MS-Office 2024パッケージ版を新規購入です。

永続ライセンスで購入できるPDFエディタ:wondershare PDFelement は再ログインでOK。ライセンス変換的な行為は不要でした。これが使えなかったら納入や見積書作成ができずタイヘンでした。

以上、これで正しく Windows11 24H2 にアップデートでき、多分この先も心配しなくていいでしょう。

Bluetooth5の2Mbps通信を明示的に指定する

nRF Sniffer for Bluetooth LE のセットアップにて、Android端末とnRF52840との間で、Bluetooth5の2Mbpsのフレームが見受けられませんでした。一つの原因は端末仕様でした(詳細はこちら)

上位プロトコルへの自動切換えは何となく怪しい、1Mbps2Mbps どちらの状態か把握管理したい。そこで2Mbps( BT用語ではPHY_2Mというようです) 弊方では以下のようにしました。

STATE_CONNECTEDイベント検出後、setPreferredPhy() をcallし、nRF528402Mbps を明示的に要請します。

  private final BluetoothGattCallback mGattcallback = new BluetoothGattCallback() {
        // 接続状態変更
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
           switch  (newState ) {
             case BluetoothProfile.STATE_CONNECTED :    // 接続完了
                if ( /* 端末で2Mbpsがサポート済みか?*/ ) {
                     /* 2MBpsを設定 */
                     gatt.setPreferredPhy(
                         BluetoothDevice.PHY_LE_2M_MASK,
                         BluetoothDevice.PHY_LE_2M_MASK,
                         0
                     );
                 }
              }
                ・・・後 略・・・

これで nRF52840 側の BLE_GAP_EVT_PHY_UPDATE_REQUEST イベントを用意してあれば、速度設定が反映されます。なお大前提として端末が 2Mbps サポートしているかの事前CHECKが必要です。

試したところAndroid側の接続完了が先に到達するようですが、到達順は処理負荷等でずれることも考えられます。nRF52840 側にも仕込んでおきます。こちらは2Mbps サポート有なので無条件に設定します。

static void ble_evt_handler(
	ble_evt_t const * p_ble_evt,	// [in] Bluetooth stack event.
	void 			* p_context		// [in] ハンドラ引数(任意)
) {
    uint32_t err_code;

    switch( p_ble_evt->header.evt_id )
    {
	  // 通常接続。	Connected to peer.
      case BLE_GAP_EVT_CONNECTED:
        /* 2MBpsを設定 */
        ble_gap_phys_t const phys =  {
     	     .rx_phys = BLE_GAP_PHY_2MBPS,
     	     .tx_phys = BLE_GAP_PHY_2MBPS,
   	    };
       	err_code = sd_ble_gap_phy_update(/*in*/ p_ble_evt->evt.gap_evt.conn_handle,
	 								 /*in*/ &phys );
       	APP_ERROR_CHECK( err_code,902 );

        break;

念のためアドバタイズ用の初期値も変更しておきました。

    ・・・前 略・・・
    init.config.ble_adv_secondary_phy = BLE_GAP_PHY_2MBPS | BLE_GAP_PHY_1MBPS ;
    // 1Mと2Mを orするようにとどっかに書いてあった。2MBPSだけだと論理エラーが発生。
    init.config.ble_adv_primary_phy   = BLE_GAP_PHY_2MBPS | BLE_GAP_PHY_1MBPS ;
    init.evt_handler = on_adv_evt;	// EVENTハンドラ関数

 	  // -BLEアドバタイズ初期化 -
    err_code = ble_advertising_init( /*out*/&m_advertising, /*in*/&init );
    APP_ERROR_CHECK( err_code,1501 );

Android側も BluetoothGattCallback onPhyUpdate をオーバライドして、Logcatを仕込んで動きを確認できるようにするとベターです。

更新要求のアナライズ結果です。

更新応答のアナライズ結果です。

肝心の転送レートは、あまり変わりません。

PACKET送信部のアナライズ結果は以下の通りでした。まだ課題がありますね。

しかしながらデットタイムの見られないパターンも一部みられました。L2CAP(分割送信)が効き、251byte が 220byte と 41byte に分割されたときでした。デットタイムはいずれにしても課題ですが、もしかしてNordic UART上ではpacket分割しないのが正しいのかもしれません。

Bluetooth5以上でも2Mbpsをサポートしていない場合

nRF Sniffer for Bluetooth LE のセットアップにて、Bluetooth5の2Mbpsのフレームが見受けられませんでした。巷の情報では、Bluetooth5同士の機器間では自動的にBluetooth5通信になると耳にします。

Android端末側で、Bluetooth5に対応しているかは、BluetoothAdapter isLe2MPhySupported メソッド他3つで機能の有無で判別するように、Android Devloper 公式サイトに記事があります。(しかし最近このサイトはhtml芸が多くて見難いですね) まず試してみました。

今回使用している motorola G32 は、Bluetooth5.2 対応ですが、未サポートになってしまいました。何か端末固有のBluetooth設定が無いか見てみましたが特になく。APIレベルは29です。

他の手持ち端末、ハードな使用に耐える8inchタブレット Galaxy Active Tab 3Bluetooth5.0 対応です。こちらはサポートOKとなりました。

これはどういうことなんでしょう? 規格とよく読むと 2Mbps(PHY 2M)機能はオプションのようです。フレーム長(MTU) 251byteはMotoG32でも実現できていて、これがBluetooth5共通の証のようです。これで2Mbps(PHY 2M)の環境が整いました。次記事に続きます。

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にしないとズレます。

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