2025年1月24日金曜日

XIAO nRF52840で加速度センサデータを飛ばす(3)

加速度センサの計測結果をBLEで飛ばすっていう試み。XIAO nRF52840と秋月AE-ADXL367を使って。

 

で、nRF52840で飛ばしたデータをWindowsで受信する方法について考えてみる。
こちらのサイト(https://coskxlabsite.stars.ne.jp/html/for_students/iot/XIAOESP32S3/ESP32S3BLEServer_WinPC.html)に掲載のBLE_serial_terminalを使ってログ採って後でデータを解析するってんで十分なんだけど、データをすぐ見たい、とか、本当にリアルタイムでデータを収集しているのを見たいとか、いろんなことを要求されることあるよね、、、で、なんでWindowsなのかというと、AndroidでBLE受信とかはまぁまぁやってる人が多いので、まぁなんていうか、後で考えようって。で、まぁやってみるわけだよ、そすっと、
dotnet new console
で、Program.csに
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.Advertisement;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
って入れようもんなら、
ってな感じで、怒られちゃう( •̥ࡇ•̥ )で、こっから年をまたいだ格闘があったわけで(すまソ、ちょっとだらだらしてただけ)、で結局、 csprojファイルのTargetFrameworkを変えちゃえばいいっていう結論に達した、、、Visual Studio Community Edition入れてUWPアプリとして作るってのが正攻法っぽいけど、Visual Studio Community EditionのLicense Termsがいやだから使いたくないんよね、、、エンタープライズではほぼ教育以外の目的では使ってはいけないように読み取れる。つまりエンタープライズでは実用アプリをVisual Studio Community Editionで作ってはいけない<--これは明確に書いてある。こちら
こういうのがプログラミングの裾野の拡大を妨げていると思う。ちと考えろやB111 6A7E5 <(`^´)>
で、日曜技術者活動は完全に個人の趣味だけど、誠に勝手ながらエンタープライズの社員によるトレースをすら想定しています(’-’)なので、VS Community Editionは使わない。
本筋に戻ると、
    <targetframework>net8.0</targetframework>

    <targetframework>net6.0-windows10.0.22000.0</targetframework>
ってする。、、、様々な検討の結果がこれよ┐(´∀`)┌
んでもって、こんな感じにする。

study2.csproj
  1. <Project Sdk="Microsoft.NET.Sdk">
  2.  
  3.   <PropertyGroup>
  4.     <OutputType>Exe</OutputType>
  5.     <TargetFramework>net6.0-windows10.0.22000.0</TargetFramework>
  6.     <ImplicitUsings>enable</ImplicitUsings>
  7.     <Nullable>enable</Nullable>
  8.   </PropertyGroup>
  9.  
  10. </Project>

Program.cs
  1. //using System;
  2. //using System.Collections.Generic;
  3. //using System.Collections.ObjectModel;
  4. //using System.Linq;
  5. //using System.Threading.Tasks;
  6. using Windows.Devices.Bluetooth;
  7. using Windows.Devices.Bluetooth.Advertisement;
  8. using Windows.Devices.Bluetooth.GenericAttributeProfile;
  9.  
  10. namespace BLEScanner{
  11.     class Program{
  12.         static Dictionary<ulong, (string Name,string LocalName, string Uuid)> BleDevices = new Dictionary<ulong, (string Name, string LocalName,string Uuid)>();
  13.         static async Task Main(string[] args){
  14.             var watcher = new BluetoothLEAdvertisementWatcher();
  15.             watcher.Received += OnAdvertisementReceived;
  16.             watcher.ScanningMode = BluetoothLEScanningMode.Active;
  17.             watcher.Start();
  18.             //Console.WriteLine("Scanning for BLE devices... Press Enter to exit.");
  19.             //Console.ReadLine();
  20.             Console.WriteLine("Scanning for BLE devices in 30sec...");
  21.             await Task.Delay(30000);
  22.             watcher.Stop();
  23.  
  24.             foreach(var bledev in BleDevices){
  25.                 Console.WriteLine($"{bledev.Key} : {bledev.Value}");
  26.             }
  27.         }
  28.         private static async void OnAdvertisementReceived(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args){
  29.             var device = await BluetoothLEDevice.FromBluetoothAddressAsync(args.BluetoothAddress);
  30.  
  31.             if (device != null){
  32.                 string deviceName = string.IsNullOrEmpty(device.Name) ? "Unknown" : device.Name;
  33.                 string deviceLocalName = string.IsNullOrEmpty(device.Name) ? "Unknown" : args.Advertisement.LocalName;
  34.                 string deviceUuid = await GetDeviceUuid(device);
  35.                 BleDevices.TryAdd(device.BluetoothAddress,(deviceName,deviceLocalName, deviceUuid));
  36.                 //Console.WriteLine($"Found device: {deviceName} -{deviceLocalName}- ({device.BluetoothAddress}) with UUID: {deviceUuid}");
  37.             }
  38.         }
  39.         private static async Task<string> GetDeviceUuid(BluetoothLEDevice device){
  40.             var services = await device.GetGattServicesAsync();
  41.             if (services.Status == GattCommunicationStatus.Success){
  42.                 var service = services.Services.FirstOrDefault();
  43.                 return service?.Uuid.ToString() ?? "UUID not found";
  44.             }
  45.             return "UUID not found";
  46.         }
  47.     }
  48. }
BLEをScanするのはもうあっちこっちに似たようなのがあって、どれを参考にしたのかわからなくなった。で、見つかったデバイスをリスト化するにあたってはDictionaryにBLE Address/Name/service uuidを追加していくってして、同じのがあったらはじかれるのを利用して重複を回避している。まぁ1つのデバイスで複数のservice uuidを持っていることもあるんだけど、そういうのは後で考える、、、
Bad programmers worry about the code. Good programmers worry about data structures and their relationships.(Linus Torvalds)
↑日曜技術者はプログラマーではないのでごめんなさい
んでもって、実行するとこうなる
たぶんうまくいっている。
そんでもって、じゃぁnRF52840が飛ばしているデータを取ろうじゃないか、、、ServiceUUIDとCharacteristicUUIDを指定してとるってなら簡単なんだけど、最終的には固定値で指定するとしても、こいつらをリストアップするのもできんといかんやん?で、ムズイていうよりクソメンドクサイ。
で、結局こんな感じ
  1. //using System;
  2. //using System.Collections.Generic;
  3. //using System.Collections.ObjectModel;
  4. //using System.Linq;
  5. //using System.Threading.Tasks;
  6.  
  7. using Windows.Devices.Bluetooth;
  8. using Windows.Devices.Bluetooth.Advertisement;
  9. using Windows.Devices.Bluetooth.GenericAttributeProfile;
  10.  
  11. namespace BLEScanner{
  12.     class Program{
  13.         static Dictionary<ulong, (ulong Address, string Name,string LocalName)> BleDevices = new Dictionary<ulong, (ulong Address, string Name, string LocalName)>();
  14.  
  15.         static async Task Main(string[] args){
  16.             string xiao_nrf52840_name="XIAO nRF52840";
  17.             ulong xiao_nrf52840_device_address=0;
  18.             var watcher = new BluetoothLEAdvertisementWatcher();
  19.             watcher.Received += OnAdvertisementReceived;
  20.             watcher.ScanningMode = BluetoothLEScanningMode.Active;
  21.  
  22.             watcher.Start();
  23.             //Console.WriteLine("Scanning for BLE devices... Press Enter to exit.");
  24.             //Console.ReadLine();
  25.             Console.WriteLine("Scanning for BLE devices in 30sec...");
  26.             await Task.Delay(30000);
  27.             watcher.Stop();
  28.   
  29.             foreach(var bledev in BleDevices){
  30.                 Console.WriteLine($"{bledev.Key} : {bledev.Value}");
  31.                 if(bledev.Value.Name.Contains(xiao_nrf52840_name)){
  32.                     xiao_nrf52840_device_address=bledev.Key;
  33.                 }
  34.             }
  35.             if(xiao_nrf52840_device_address>0){
  36.                 Console.WriteLine($"{xiao_nrf52840_name} was found.");
  37.                 Console.WriteLine($"Address : {xiao_nrf52840_device_address}");
  38.                 var xiao_nrf52840_device = await BluetoothLEDevice.FromBluetoothAddressAsync(xiao_nrf52840_device_address);
  39.                 var xiao_nrf52840_device_services=await xiao_nrf52840_device.GetGattServicesAsync();
  40.                 foreach(var serv in xiao_nrf52840_device_services.Services){
  41.                     Console.WriteLine($" service uuid: {serv.Uuid}");
  42.                     var xiao_nrf52840_device_characteristics=await serv.GetCharacteristicsAsync();
  43.                     foreach(var chr in xiao_nrf52840_device_characteristics.Characteristics){
  44.                         Console.WriteLine($" characterisitics uuid: {chr.Uuid}");
  45.                     }
  46.                 }
  47.                 var xiao_nrf52840_device_service=await xiao_nrf52840_device.GetGattServicesForUuidAsync(new Guid("6e400001-b5a3-f393-e0a9-e50e24dcca9e"));
  48.                 var xiao_nrf52840_device_char_uartrx=await xiao_nrf52840_device_service.Services[0].GetCharacteristicsForUuidAsync(new Guid("6e400003-b5a3-f393-e0a9-e50e24dcca9e"));
  49.                 var rx_characteristics=xiao_nrf52840_device_char_uartrx.Characteristics[0];
  50.                 rx_characteristics.ValueChanged += OnDataReceived;
  51.                 await rx_characteristics.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);
  52.                 while (true){
  53.                     //
  54.                 }
  55.             }
  56.         }
  57.         static void OnDataReceived(GattCharacteristic sender, GattValueChangedEventArgs eventArgs) {
  58.             //Console.WriteLine(sender);
  59.             //Console.WriteLine(eventArgs);
  60.             byte[] data = new byte[eventArgs.CharacteristicValue.Length];
  61.             Windows.Storage.Streams.DataReader.FromBuffer(eventArgs.CharacteristicValue).ReadBytes(data);
  62.             var encoding = System.Text.Encoding.GetEncoding("UTF-8");
  63.             var text = encoding.GetString(data);
  64.             Console.WriteLine(text.TrimEnd());
  65.         }
  66.         private static async void OnAdvertisementReceived(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args){
  67.             var device = await BluetoothLEDevice.FromBluetoothAddressAsync(args.BluetoothAddress);
  68.             if (device != null){
  69.                 string deviceName = string.IsNullOrEmpty(device.Name) ? "Unknown" : device.Name;
  70.                 string deviceLocalName = string.IsNullOrEmpty(device.Name) ? "Unknown" : args.Advertisement.LocalName;
  71.                 BleDevices.TryAdd(device.BluetoothAddress,(device.BluetoothAddress,deviceName,deviceLocalName));
  72.                 //Console.WriteLine($"Found device: {deviceName} -{deviceLocalName}- ({device.BluetoothAddress}) with UUID: {deviceUuid}");
  73.             }
  74.         }
  75.     }
  76. }
BLEアドレスからServicesをとってひとつひとつのServiceでCharacteristicsをとって要素を表示している。この結果を制御に使ってるわけじゃないけど、こういうことができるってことを知っていると何かイイコトがあるかもしれない。
で、dotnet runするとこんな感じ
まぁうまくいったっしょ。
 
あぁ~だいぶさぼっちゃった( ˙꒳˙)
おっさんになると壁にぶつかってダウンしたときに起き上がるのに時間がかかっちゃう。pythonではbleakってライブラリ使って簡単にできるんだけどな、、、が、最近C#好きなんよね、、、なんとなく。

0 件のコメント:

コメントを投稿