技術共有

Bluetooth BLE 開発に Delphi を使用する際の問題

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

コンセプト

Delphi での BLE 開発の場合は、TBlueToothLe をインターフェイスにドラッグし、このコントロールを使用して BLE を開発します(Bluetooth ブレスレットの接続など)。

Delphi に付属するデモには BLEScanner プログラムが含まれており、開発の開始点として使用できます。

質問

上記のプログラムが Windows で実行されている場合、デバイスのスキャン後、デバイス上でマウスをクリックすると、インターフェイスがフリーズし、プログラムが応答しなくなることがあります。タスク管理領域を見ると、確かにプログラムが応答せず、クラッシュしています。

原因分析

マウスをクリックして、選択したデバイスのサービスをスキャンします。その後、デバイスのサービスが検出されると、TBluetoothLE の OnServicesDiscovered イベントがトリガーされます。このイベントでは、デバイスの複数のサービスがループで読み取られ、その後、特定のサービスについてそのキャラクター名がループで読み取られます。ループが文字を読み取る箇所でクラッシュが発生します。

なぜクラッシュしたのか、わかりません。しかし、解決策を見つけました。コードは次のとおりです。

  1. procedure TForm6.BluetoothLE1ServicesDiscovered(const Sender: TObject; const AServiceList: TBluetoothGattServiceList);
  2. var
  3. ServiceIndex: Integer;
  4. Service: TBluetoothGattService;
  5. CharacteristicIndex: Integer;
  6. Characteristic: TBluetoothGattCharacteristic;
  7. begin
  8. //以下代码如果不包到 TTask.Run 里面(原本的代码没有),在 WINDOWS 底下,执行到 for
  9. //CharacteristicIndex := 0 to Service.Characteristics.Count 会界面冻结,而且单步跟踪也停止
  10. //了,没有往下执行。
  11. TTask.Run(
  12. procedure
  13. var
  14. ServiceIndex: Integer;
  15. CharacteristicIndex: Integer;
  16. begin
  17. if AServiceList.Count > 0 then
  18. begin
  19. for ServiceIndex := 0 to AServiceList.Count - 1 do
  20. begin
  21. Service := AServiceList[ServiceIndex];
  22. TThread.Synchronize(nil,
  23. procedure
  24. begin
  25. Listbox2.Items.Add((ServiceIndex + 1).ToString + ' - ' + Service.UUIDName + ' - ' + Service.UUID.ToString);
  26. end
  27. );
  28. //以下代码会导致死机,如果断点跟踪,直接就是停在 for 这一行,不会继续往下执行。
  29. for CharacteristicIndex := 0 to Service.Characteristics.Count - 1 do
  30. begin
  31. Characteristic := Service.Characteristics[CharacteristicIndex];
  32. TThread.Synchronize(nil,
  33. procedure
  34. begin
  35. Listbox2.Items.Add(' - ' + Characteristic.UUIDName + ' - ' + Characteristic.UUID.ToString);
  36. end
  37. );
  38. end;
  39. end;
  40. end
  41. else
  42. TThread.Synchronize(nil,
  43. procedure
  44. begin
  45. Listbox2.Items.Add('- Access not allowed or no service available');
  46. end
  47. );
  48. end
  49. );
  50. //Listbox1.Enabled := True;
  51. end;

コードの説明

上記のコードでは、TTask.Run が私によって追加されました。 TThread.Synchronize も私が追加しました。 TTask.Run と TThread.Synchronize を削除すると、残りのコードが Delphi に付属するデモの元のコードになります。

まず、元のコードを TTask.Run に置きます。これは、これらのコードを実行のためにスレッドに入れることを意味します。最初に OnServicesDiscovered イベントを発生させたスレッドを実行させる代わりに。考えられる理由: OnServicesDiscovered をトリガーしたスレッドは、時間のかかるタスクをあまりにも多く実行できません。

Listbox2.Items.Add コードなど、データをインターフェイス コントロールに書き込む必要がある場合、コードはスレッドで実行されます。そこで、TThread.Synchronize を追加します。

私の開発環境

環境が異なると現象も異なる場合があります。したがって、私の開発環境は次のとおりです。

Delphi 11 コミュニティ エディション。

Windows 11 ホームエディション;

コンパイルして実行する対象プログラムはWin32版です。

このデモが Android で上記の問題を起こすかどうかはテストされていません。ただし、Android では TTask.Run を追加した方がよいと思います。

デモ プログラムの場所

ここで説明するデモは、Delphi をインストールした後、デフォルトでインストールされている場合、デモ プログラムは次のとおりです。

C:UsersPublicDocumentsEmbarcaderoStudio22.0SamplesObject PascalMulti-Device Samplesデバイス センサーとサービスBluetoothBLEScanner

結論は

Delphi を使用してブレスレット APP を作成するなど、BLE プログラムを開発することは問題ありません。ただし、TBluetoothLE コントロールの多くのイベントでは、あまり多くのコードを実行しないことが最善であることに注意してください。複雑なビジネス ロジックがある場合は、イベント メソッドで、対応するスレッドを開始するだけで別のスレッドで実行するのが最善です。

Delphi の場合、新しく追加された TTask.Run を使用すると、スレッド クラスを作成する必要があった以前よりもコードの記述方法がはるかに簡単になります。