MainForm.cs 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. using System.Windows.Forms;
  8. using System.Diagnostics;
  9. using Windows.Devices.Bluetooth;
  10. using Windows.Devices.Enumeration;
  11. using Windows.Devices.Bluetooth.Advertisement;
  12. using Windows.Devices.Bluetooth.GenericAttributeProfile;
  13. using Windows.Security.Cryptography;
  14. using Windows.Storage.Streams;
  15. using MSerialization;
  16. using BLEComm;
  17. using System.Runtime.InteropServices.WindowsRuntime;
  18. namespace BLETool
  19. {
  20. public partial class MainForm : Form
  21. {
  22. readonly uint E_BLUETOOTH_ATT_WRITE_NOT_PERMITTED = 0x80650003;
  23. readonly uint E_BLUETOOTH_ATT_INVALID_PDU = 0x80650004;
  24. readonly uint E_ACCESSDENIED = 0x80070005;
  25. readonly uint E_DEVICE_NOT_AVAILABLE = 0x800710df; // HRESULT_FROM_WIN32(ERROR_DEVICE_NOT_AVAILABLE)
  26. //找到的设备
  27. private Dictionary<string, DeviceInformation> devices = new Dictionary<string, DeviceInformation>();
  28. private DeviceWatcher deviceWatcher;
  29. ListViewColumnSorter lvwColumnSorter;
  30. //当前选中的BLE设备
  31. BluetoothLEDevice ble_Device;
  32. //上一个BLE设备
  33. BluetoothLEDevice last_ble_Device;
  34. //当前设备提供的服务组
  35. IReadOnlyList<GattDeviceService> ble_Services = null;
  36. //当前选中的BLE服务
  37. GattDeviceService ble_Service = null;
  38. //当前选中的BLE属性组
  39. IReadOnlyList<GattCharacteristic> ble_Characteristics = null;
  40. //当前选中的BLE属性
  41. GattCharacteristic ble_Characteristic = null;
  42. //上一个BLE属性
  43. GattCharacteristic last_ble_Characteristic = null;
  44. //订阅的Notify或IndicateBLE属性
  45. GattCharacteristic callback_Characteristic = null;
  46. //数据格式
  47. GattPresentationFormat presentationFormat;
  48. //数据结果
  49. IBuffer ble_result;
  50. public MainForm()
  51. {
  52. InitializeComponent();
  53. lvwColumnSorter = new ListViewColumnSorter();
  54. this.lv_device.ListViewItemSorter = lvwColumnSorter;
  55. this.ss_bottom.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.HorizontalStackWithOverflow;
  56. }
  57. //发现新设备
  58. private void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation deviceInfo)
  59. {
  60. try
  61. {
  62. this?.Invoke(new Action(() =>
  63. {
  64. if (sender == deviceWatcher)
  65. {
  66. // Make sure device isn't already present in the list.
  67. if (!devices.ContainsKey(deviceInfo.Id))
  68. {
  69. if (deviceInfo.Name != "" || chb_ShowHidden.Checked == true)
  70. {
  71. string id = deviceInfo.Id;
  72. devices.Add(deviceInfo.Id, deviceInfo);
  73. //添加到列表
  74. ListViewItem lvi = new ListViewItem();
  75. lvi.Text = deviceInfo.Name;
  76. lvi.Name = id;
  77. lvi.SubItems.Add(id);
  78. lvi.SubItems.Add(deviceInfo.Pairing.CanPair.ToString());
  79. lvi.SubItems.Add(deviceInfo.Pairing.IsPaired.ToString());
  80. lvi.SubItems.Add("");
  81. lv_device.Items.Add(lvi);
  82. }
  83. }
  84. }
  85. }));
  86. }
  87. catch { }
  88. }
  89. //设备信息被更新
  90. private void DeviceWatcher_Updated(DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate)
  91. {
  92. try
  93. {
  94. this?.Invoke(new Action(() =>
  95. {
  96. // Protect against race condition if the task runs after the app stopped the deviceWatcher.
  97. if (sender == deviceWatcher)
  98. {
  99. //存在该设备
  100. if (devices.Keys.Contains(deviceInfoUpdate.Id) && devices[deviceInfoUpdate.Id] != null)
  101. {
  102. string id = deviceInfoUpdate.Id;
  103. devices[deviceInfoUpdate.Id].Update(deviceInfoUpdate);
  104. lv_device.Items[id].SubItems[0].Text = devices[deviceInfoUpdate.Id].Name;
  105. lv_device.Items[id].SubItems[2].Text = devices[deviceInfoUpdate.Id].Pairing.CanPair.ToString();
  106. lv_device.Items[id].SubItems[3].Text = devices[deviceInfoUpdate.Id].Pairing.IsPaired.ToString();
  107. }
  108. }
  109. }));
  110. }
  111. catch { }
  112. }
  113. //从设备列表移除设备
  114. private void DeviceWatcher_Removed(DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate)
  115. {
  116. this?.Invoke(new Action(() =>
  117. {
  118. if (sender == deviceWatcher)
  119. {
  120. string id = deviceInfoUpdate.Id;
  121. if (chb_ShowUnconnectableDevice.Checked != true)
  122. {
  123. devices.Remove(deviceInfoUpdate.Id);
  124. lv_device.Items.RemoveByKey(id);
  125. }
  126. }
  127. }));
  128. }
  129. //设备枚举完成
  130. private void DeviceWatcher_EnumerationCompleted(DeviceWatcher sender, object e)
  131. {
  132. this?.Invoke(new Action(() =>
  133. {
  134. log("设备搜索完毕");
  135. }));
  136. }
  137. //终止设备搜索
  138. private void DeviceWatcher_Stopped(DeviceWatcher sender, object e)
  139. {
  140. this?.Invoke(new Action(() =>
  141. {
  142. log("搜索被终止");
  143. }));
  144. }
  145. private void Form1_Load(object sender, EventArgs e)
  146. {
  147. }
  148. private void StartBleDeviceWatcher()
  149. {
  150. //BLE device watch
  151. string[] requestedProperties = { "System.Devices.Aep.DeviceAddress", "System.Devices.Aep.IsConnected", "System.Devices.Aep.Bluetooth.Le.IsConnectable" };
  152. string aqsAllBluetoothLEDevices = "(System.Devices.Aep.ProtocolId:=\"{bb7bb05e-5972-42b5-94fc-76eaa7084d49}\")";
  153. deviceWatcher =
  154. DeviceInformation.CreateWatcher(
  155. aqsAllBluetoothLEDevices,
  156. requestedProperties,
  157. DeviceInformationKind.AssociationEndpoint);
  158. // Register event handlers before starting the watcher.
  159. deviceWatcher.Added += DeviceWatcher_Added;
  160. deviceWatcher.Updated += DeviceWatcher_Updated;
  161. deviceWatcher.Removed += DeviceWatcher_Removed;
  162. deviceWatcher.EnumerationCompleted += DeviceWatcher_EnumerationCompleted;
  163. deviceWatcher.Stopped += DeviceWatcher_Stopped;
  164. deviceWatcher.Start();
  165. }
  166. private void StartBleAdvertisementWatcher()
  167. {
  168. //BLE advertisement watcher
  169. var watcher = new BluetoothLEAdvertisementWatcher();
  170. watcher.Received += Watcher_Received;
  171. watcher.Start();
  172. }
  173. private void Watcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
  174. {
  175. string bleaddr = args.BluetoothAddress.ToString("x");
  176. bleaddr = string.Join(":", System.Text.RegularExpressions.Regex.Split(bleaddr, "(?<=\\G.{2})(?!$)"));
  177. this?.Invoke(new Action(() =>
  178. {
  179. foreach (ListViewItem lvi in lv_device.Items)
  180. {
  181. if (lvi.Name.Contains(bleaddr))
  182. {
  183. Console.WriteLine(args.RawSignalStrengthInDBm.ToString());
  184. lvi.SubItems[4].Text = args.RawSignalStrengthInDBm.ToString();
  185. }
  186. }
  187. if (txt_deviceID.Text.Contains(bleaddr))
  188. {
  189. txt_rssi.Text = args.RawSignalStrengthInDBm.ToString();
  190. }
  191. }));
  192. }
  193. private void StopBleDeviceWatcher()
  194. {
  195. if (deviceWatcher != null)
  196. {
  197. deviceWatcher.Stop();
  198. // Unregister the event handlers.
  199. deviceWatcher.Added -= DeviceWatcher_Added;
  200. deviceWatcher.Updated -= DeviceWatcher_Updated;
  201. deviceWatcher.Removed -= DeviceWatcher_Removed;
  202. deviceWatcher.EnumerationCompleted -= DeviceWatcher_EnumerationCompleted;
  203. deviceWatcher.Stopped -= DeviceWatcher_Stopped;
  204. deviceWatcher = null;
  205. }
  206. }
  207. private void btn_Search_Click(object sender, EventArgs e)
  208. {
  209. //清除设备列表
  210. devices.Clear();
  211. lv_device.Items.Clear();
  212. log("正在搜索设备...");
  213. //启动设备搜索
  214. StartBleDeviceWatcher();
  215. }
  216. private void lv_device_ColumnClick(object sender, ColumnClickEventArgs e)
  217. {
  218. // 检查点击的列是不是现在的排序列.
  219. if (e.Column == lvwColumnSorter.SortColumn)
  220. {
  221. // 重新设置此列的排序方法.
  222. if (lvwColumnSorter.Order == SortOrder.Ascending)
  223. {
  224. lvwColumnSorter.Order = SortOrder.Descending;
  225. }
  226. else
  227. {
  228. lvwColumnSorter.Order = SortOrder.Ascending;
  229. }
  230. }
  231. else
  232. {
  233. // 设置排序列,默认为正向排序
  234. lvwColumnSorter.SortColumn = e.Column;
  235. lvwColumnSorter.Order = SortOrder.Ascending;
  236. }
  237. // 用新的排序方法对ListView排序
  238. this.lv_device.Sort();
  239. }
  240. private void lv_device_SelectedIndexChanged(object sender, EventArgs e)
  241. {
  242. }
  243. private void btn_Stop_Click(object sender, EventArgs e)
  244. {
  245. StopBleDeviceWatcher();
  246. }
  247. private void btn_Refresh_Click(object sender, EventArgs e)
  248. {
  249. StartBleAdvertisementWatcher();
  250. log("正在获取信号强度(RSSI)");
  251. }
  252. private void btn_pair_Click(object sender, EventArgs e)
  253. {
  254. //解除上一个设备的回调
  255. UnhookNotify();
  256. if (last_ble_Device != null)
  257. {
  258. last_ble_Device.ConnectionStatusChanged -= CurrentDevice_ConnectionStatusChanged;
  259. }
  260. GetDeviceServices();
  261. }
  262. private void chb_ShowUnconnectableDevice_CheckedChanged(object sender, EventArgs e)
  263. {
  264. }
  265. private void log(string text)
  266. {
  267. }
  268. private void cmb_service_SelectedIndexChanged(object sender, EventArgs e)
  269. {
  270. cmb_characteristic.Items.Clear();
  271. cmb_characteristic.Text = "";
  272. lab_charcount.Text = "特征[0]";
  273. //getCharacteristic();
  274. }
  275. private async void GetDeviceServices()
  276. {
  277. log("获取服务");
  278. lab_servicecount.Text = "服务[0]";
  279. cmb_service.Text = "";
  280. lab_charcount.Text = "特征[0]";
  281. cmb_characteristic.Items.Clear();
  282. cmb_characteristic.Text = "";
  283. txt_rssi.Text = "";
  284. lab_connection.BackColor = Color.White;
  285. BluetoothLEDevice bledevice = null;
  286. try
  287. {
  288. if (lv_device.Items.Count <= 0 || lv_device.SelectedItems.Count <= 0) return;
  289. lab_connection.Text = "Connecting...";
  290. lab_connection.ForeColor = Color.Black;
  291. string id = lv_device.SelectedItems[0].Name;
  292. string name = lv_device.SelectedItems[0].Text;
  293. txt_devicename.Text = name;
  294. txt_deviceID.Text = id;
  295. if (id != null)
  296. {
  297. log("配对设备 " + id + "(" + name + ")");
  298. bledevice = await BluetoothLEDevice.FromIdAsync(id);
  299. DevicePairingResult result = await bledevice.DeviceInformation.Pairing.PairAsync();
  300. //log(result.Status.ToString());
  301. if (bledevice == null)
  302. {
  303. lab_connection.BackColor = Color.Red;
  304. lab_connection.ForeColor = Color.White;
  305. lab_connection.Text = "Disconnected...";
  306. log("设备连接失败");
  307. }
  308. }
  309. }
  310. catch (Exception ex) when (ex.HResult == E_DEVICE_NOT_AVAILABLE)
  311. {
  312. log("蓝牙设备不可用");
  313. }
  314. if (bledevice != null && bledevice != last_ble_Device)
  315. {
  316. last_ble_Device = ble_Device;
  317. ble_Device = bledevice;
  318. ble_Device.ConnectionStatusChanged -= CurrentDevice_ConnectionStatusChanged;
  319. ble_Device.ConnectionStatusChanged += CurrentDevice_ConnectionStatusChanged;
  320. getService();
  321. }
  322. }
  323. private async void getService()
  324. {
  325. GattDeviceServicesResult result;
  326. try
  327. {
  328. result = await ble_Device.GetGattServicesAsync(BluetoothCacheMode.Uncached);
  329. }
  330. catch
  331. {
  332. return;
  333. }
  334. if (result.Status == GattCommunicationStatus.Success)
  335. {
  336. lab_connection.BackColor = Color.DarkGreen;
  337. lab_connection.Text = "Connected";
  338. lab_connection.ForeColor = Color.White;
  339. var services = result.Services;
  340. log(String.Format("找到 {0} 个服务", services.Count));
  341. lab_servicecount.Text = "服务[" + services.Count + "]";
  342. cmb_service.Items.Clear();
  343. ble_Services = services;
  344. foreach (var service in services)
  345. {
  346. string servicename = BLEInfo.GetServiceName(service);
  347. cmb_service.Items.Add(servicename);
  348. }
  349. if (services.Count > 0)
  350. {
  351. cmb_service.Text = cmb_service.Items[0].ToString();
  352. ble_Service = services[0];
  353. }
  354. }
  355. else
  356. {
  357. log("设备不可访问");
  358. }
  359. }
  360. private async void getCharacteristic()
  361. {
  362. log("获取当前服务特征");
  363. IReadOnlyList<GattCharacteristic> characteristics = null;
  364. if (cmb_service.SelectedIndex >= 0)
  365. try
  366. {
  367. ble_Service = ble_Services[cmb_service.SelectedIndex];
  368. // Ensure we have access to the device.
  369. var accessStatus = await ble_Service.RequestAccessAsync();
  370. if (accessStatus == DeviceAccessStatus.Allowed)
  371. {
  372. // BT_Code: Get all the child characteristics of a service. Use the cache mode to specify uncached characterstics only
  373. // and the new Async functions to get the characteristics of unpaired devices as well.
  374. var result = await ble_Service.GetCharacteristicsAsync(BluetoothCacheMode.Uncached);
  375. if (result.Status == GattCommunicationStatus.Success)
  376. {
  377. characteristics = result.Characteristics;
  378. log(String.Format("找到 {0} 个服务特征", characteristics.Count));
  379. lab_charcount.Text = "特征[" + characteristics.Count + "]";
  380. ble_Characteristics = characteristics;
  381. foreach (var characteristic in characteristics)
  382. {
  383. string characteristicname = BLEInfo.GetCharacteristicName(characteristic);
  384. cmb_characteristic.Items.Add(characteristicname);
  385. }
  386. if (ble_Characteristics.Count > 0)
  387. {
  388. cmb_characteristic.Text = cmb_characteristic.Items[0].ToString();
  389. ble_Characteristic = ble_Characteristics[0];
  390. }
  391. }
  392. else
  393. {
  394. //log(result.Status.ToString());
  395. // On error, act as if there are no characteristics.
  396. characteristics = new List<GattCharacteristic>();
  397. lab_charcount.Text = "特征[0]";
  398. }
  399. }
  400. else
  401. {
  402. // Not granted access
  403. log("该服务访问失败");
  404. // On error, act as if there are no characteristics.
  405. ble_Characteristics = new List<GattCharacteristic>();
  406. lab_charcount.Text = "特征[0]";
  407. }
  408. }
  409. catch (Exception ex)
  410. {
  411. log("该服务特征禁止操作: " + ex.Message);
  412. // On error, act as if there are no characteristics.
  413. characteristics = new List<GattCharacteristic>();
  414. lab_charcount.Text = "特征[0]";
  415. }
  416. }
  417. //控件颜色闪烁
  418. private void BlinkControl(Control control)
  419. {
  420. control.BackColor = Color.Blue;
  421. System.Timers.Timer timer = new System.Timers.Timer();
  422. timer.Interval = 200;
  423. timer.AutoReset = false;
  424. timer.Elapsed += delegate
  425. {
  426. control.BackColor = SystemColors.Control;
  427. };
  428. timer.Start();
  429. }
  430. private void Characteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
  431. {
  432. if (callback_Characteristic == null) return;
  433. // BT_Code: An Indicate or Notify reported that the value has changed.
  434. // Display the new value with a timestamp.
  435. GattCharacteristicProperties properties = callback_Characteristic.CharacteristicProperties;
  436. if (properties.HasFlag(GattCharacteristicProperties.Indicate))
  437. {
  438. log("收到Indicate通知");
  439. BlinkControl(this.lab_Indicate);
  440. }
  441. if (properties.HasFlag(GattCharacteristicProperties.Notify))
  442. {
  443. log("收到Notify通知");
  444. BlinkControl(this.lab_Notify);
  445. }
  446. if (ble_Characteristic == null) return;
  447. this.Invoke(new Action(() =>
  448. {
  449. txt_DecResult.Text = "";
  450. txt_HexResult.Text = "";
  451. txt_UTF8Result.Text = "";
  452. var result = args.CharacteristicValue;
  453. string type = "";
  454. string formattedResult = BLEInfo.FormatValueByPresentation(result, presentationFormat, out type);
  455. txt_UTF8Result.Text = formattedResult;
  456. if (type == "HEX") { rad_hex.Checked = true; rad_dec.Checked = false; rad_utf8.Checked = false; }
  457. if (type == "Decimal") { rad_hex.Checked = false; rad_dec.Checked = true; rad_utf8.Checked = false; }
  458. if (type == "UTF8") { rad_hex.Checked = false; rad_dec.Checked = false; rad_utf8.Checked = true; }
  459. try
  460. {
  461. byte[] data;
  462. CryptographicBuffer.CopyToByteArray(result, out data);
  463. txt_UTF8Result.Text = Encoding.UTF8.GetString(data);
  464. }
  465. catch
  466. {
  467. txt_UTF8Result.Text = "";
  468. }
  469. try
  470. {
  471. byte[] data;
  472. CryptographicBuffer.CopyToByteArray(result, out data);
  473. txt_HexResult.Text = BitConverter.ToString(data, 0).Replace("-", " ").ToUpper();
  474. }
  475. catch
  476. {
  477. txt_HexResult.Text = "";
  478. }
  479. try
  480. {
  481. byte[] data;
  482. CryptographicBuffer.CopyToByteArray(result, out data);
  483. if (data.Length == 1) txt_DecResult.Text = data[0].ToString();
  484. if (data.Length == 2) txt_DecResult.Text = BitConverter.ToInt16(data, 0).ToString();
  485. if (data.Length == 4) txt_DecResult.Text = BitConverter.ToInt32(data, 0).ToString();
  486. if (data.Length == 8) txt_DecResult.Text = BitConverter.ToInt64(data, 0).ToString();
  487. }
  488. catch
  489. {
  490. txt_DecResult.Text = "";
  491. }
  492. }));
  493. }
  494. private void btn_refreshCharacteristic_Click(object sender, EventArgs e)
  495. {
  496. cmb_characteristic.Items.Clear();
  497. cmb_characteristic.Text = "";
  498. lab_charcount.Text = "特征[0]";
  499. getCharacteristic();
  500. }
  501. private void cmb_characteristic_SelectedIndexChanged(object sender, EventArgs e)
  502. {
  503. //GetData();
  504. if (cmb_characteristic.SelectedIndex >= 0)
  505. {
  506. last_ble_Characteristic = ble_Characteristic;
  507. ble_Characteristic = ble_Characteristics[cmb_characteristic.SelectedIndex];
  508. }
  509. }
  510. private async void UnhookNotify()
  511. {
  512. if (callback_Characteristic == null) return;
  513. GattCharacteristicProperties callback_properties = callback_Characteristic.CharacteristicProperties;
  514. //特征支持Nodify或者Indicate属性
  515. if (callback_properties.HasFlag(GattCharacteristicProperties.Notify) || callback_properties.HasFlag(GattCharacteristicProperties.Indicate))
  516. {
  517. //解除上一个属性的Notify和Indicate
  518. try
  519. {
  520. // BT_Code: Must write the CCCD in order for server to send notifications.
  521. // We receive them in the ValueChanged event handler.
  522. // Note that this sample configures either Indicate or Notify, but not both.
  523. var result = await
  524. callback_Characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(
  525. GattClientCharacteristicConfigurationDescriptorValue.None);
  526. if (result == GattCommunicationStatus.Success)
  527. {
  528. callback_Characteristic.ValueChanged -= Characteristic_ValueChanged;
  529. }
  530. callback_Characteristic = null;
  531. }
  532. catch (Exception ex)
  533. {
  534. // This usually happens when a device reports that it support notify, but it actually doesn't.
  535. log(ex.Message);
  536. }
  537. }
  538. }
  539. //为蓝牙的characteristic设置回调使能和回调函数
  540. private async void HookNotify()
  541. {
  542. if (ble_Characteristic == null) return;
  543. GattCharacteristicProperties properties = ble_Characteristic.CharacteristicProperties;
  544. //特征支持Nodify或者Indicate属性
  545. if (properties.HasFlag(GattCharacteristicProperties.Notify) || properties.HasFlag(GattCharacteristicProperties.Indicate))
  546. {
  547. callback_Characteristic = ble_Characteristic;
  548. txt_callback_service.Text = cmb_service.Text;
  549. txt_callback_characteristic.Text = cmb_characteristic.Text;
  550. cb_display_callbackData.Checked = true;
  551. // initialize status
  552. GattCommunicationStatus status = GattCommunicationStatus.Unreachable;
  553. var cccdValue = GattClientCharacteristicConfigurationDescriptorValue.None;
  554. if (properties.HasFlag(GattCharacteristicProperties.Indicate))
  555. {
  556. cccdValue = GattClientCharacteristicConfigurationDescriptorValue.Indicate;
  557. }
  558. else if (properties.HasFlag(GattCharacteristicProperties.Notify))
  559. {
  560. cccdValue = GattClientCharacteristicConfigurationDescriptorValue.Notify;
  561. }
  562. try
  563. {
  564. //设置回调使能
  565. // BT_Code: Must write the CCCD in order for server to send indications.
  566. status = await callback_Characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(cccdValue);
  567. if (status == GattCommunicationStatus.Success)
  568. {
  569. //设置回调函数
  570. ble_Characteristic.ValueChanged += Characteristic_ValueChanged;
  571. log("成功订阅了服务特征变化的通知");
  572. }
  573. else
  574. {
  575. log($"未成功订阅服务特征变化的通知: {status}");
  576. }
  577. }
  578. catch (Exception ex)
  579. {
  580. // This usually happens when a device reports that it support indicate, but it actually doesn't.
  581. log(ex.Message);
  582. }
  583. }
  584. }
  585. //读数characteristic的类型,如果支持read属性则读取值
  586. private async void btn_getData_Click(object sender, EventArgs e)
  587. {
  588. log("正在读取特征的数据类别");
  589. txt_DecResult.Text = "";
  590. txt_HexResult.Text = "";
  591. txt_UTF8Result.Text = "";
  592. if (ble_Characteristic != null)
  593. {
  594. try
  595. {
  596. // Get all the child descriptors of a characteristics.
  597. var result = await ble_Characteristic.GetDescriptorsAsync(BluetoothCacheMode.Uncached);
  598. if (result.Status != GattCommunicationStatus.Success)
  599. {
  600. log("Descriptor read failure: " + result.Status.ToString());
  601. }
  602. // BT_Code: There's no need to access presentation format unless there's at least one.
  603. presentationFormat = null;
  604. if (ble_Characteristic.PresentationFormats.Count > 0)
  605. {
  606. if (ble_Characteristic.PresentationFormats.Count.Equals(1))
  607. {
  608. // Get the presentation format since there's only one way of presenting it
  609. presentationFormat = ble_Characteristic.PresentationFormats[0];
  610. }
  611. else
  612. {
  613. // It's difficult to figure out how to split up a characteristic and encode its different parts properly.
  614. // In this case, we'll just encode the whole thing to a string to make it easy to print out.
  615. }
  616. }
  617. log("特征类别获取成功.");
  618. GattCharacteristicProperties properties = ble_Characteristic.CharacteristicProperties;
  619. btn_Read.Enabled = properties.HasFlag(GattCharacteristicProperties.Read);
  620. btn_Write.Enabled = properties.HasFlag(GattCharacteristicProperties.Write);
  621. lab_Indicate.Enabled = properties.HasFlag(GattCharacteristicProperties.Indicate);
  622. lab_Notify.Enabled = properties.HasFlag(GattCharacteristicProperties.Notify);
  623. log("该特征支持操作属性 {" + properties.ToString() + "}");
  624. //特征支持读属性
  625. if (btn_Read.Enabled == true)
  626. {
  627. txt_DecResult.Enabled = true;
  628. txt_HexResult.Enabled = true;
  629. txt_UTF8Result.Enabled = true;
  630. btn_Read_Click(null, null);
  631. }
  632. else
  633. {
  634. txt_DecResult.Text = "Read function is not supported for the characteristic";
  635. txt_HexResult.Text = "Read function is not supported for the characteristic";
  636. txt_UTF8Result.Text = "Read function is not supported for the characteristic";
  637. txt_DecResult.Enabled = false;
  638. txt_HexResult.Enabled = false;
  639. txt_UTF8Result.Enabled = false;
  640. }
  641. HookNotify();
  642. }
  643. catch (Exception ee)
  644. {
  645. log("设备暂时不可达");
  646. }
  647. }
  648. }
  649. //读取charactistic的值
  650. private async void btn_Read_Click(object sender, EventArgs e)
  651. {
  652. BlinkControl(this.btn_Read);
  653. log("正在读取数据");
  654. if (ble_Characteristic == null) return;
  655. txt_DecResult.Text = "";
  656. txt_HexResult.Text = "";
  657. txt_UTF8Result.Text = "";
  658. // BT_Code: Read the actual value from the device by using Uncached.
  659. GattReadResult result = await ble_Characteristic.ReadValueAsync(BluetoothCacheMode.Uncached);
  660. if (result.Status == GattCommunicationStatus.Success)
  661. {
  662. ble_result = result.Value;
  663. string type = "";
  664. string formattedResult = BLEInfo.FormatValueByPresentation(result.Value, presentationFormat, out type);
  665. txt_UTF8Result.Text = formattedResult;
  666. if (type == "HEX") { rad_hex.Checked = true; rad_dec.Checked = false; rad_utf8.Checked = false; }
  667. if (type == "Decimal") { rad_hex.Checked = false; rad_dec.Checked = true; rad_utf8.Checked = false; }
  668. if (type == "UTF8") { rad_hex.Checked = false; rad_dec.Checked = false; rad_utf8.Checked = true; }
  669. log("数据读取成功");
  670. try
  671. {
  672. byte[] data;
  673. CryptographicBuffer.CopyToByteArray(ble_result, out data);
  674. txt_UTF8Result.Text = Encoding.UTF8.GetString(data);
  675. }
  676. catch
  677. {
  678. txt_UTF8Result.Text = "";
  679. }
  680. try
  681. {
  682. byte[] data;
  683. CryptographicBuffer.CopyToByteArray(ble_result, out data);
  684. txt_HexResult.Text = BitConverter.ToString(data, 0).Replace("-", " ").ToUpper();
  685. }
  686. catch
  687. {
  688. txt_HexResult.Text = "";
  689. }
  690. try
  691. {
  692. byte[] data;
  693. CryptographicBuffer.CopyToByteArray(ble_result, out data);
  694. if (data == null)
  695. {
  696. log("不存在要被读取的数据");
  697. return;
  698. }
  699. if (data.Length == 1) txt_DecResult.Text = data[0].ToString();
  700. if (data.Length == 2) txt_DecResult.Text = BitConverter.ToInt16(data, 0).ToString();
  701. if (data.Length == 4) txt_DecResult.Text = BitConverter.ToInt32(data, 0).ToString();
  702. if (data.Length == 8) txt_DecResult.Text = BitConverter.ToInt64(data, 0).ToString();
  703. }
  704. catch
  705. {
  706. txt_DecResult.Text = "";
  707. }
  708. }
  709. else
  710. {
  711. log("读操作失败,系统错误: " + result.Status.ToString());
  712. }
  713. }
  714. //写入characteristic的值
  715. private async void btn_Write_Click(object sender, EventArgs e)
  716. {
  717. try
  718. {
  719. //存储发送过的指令
  720. bool foundinlist = false;
  721. string txt = txt_write.Text;
  722. foreach (string cmbitem in txt_write.Items)
  723. {
  724. if (cmbitem == txt) foundinlist = true;
  725. }
  726. if (!foundinlist) txt_write.Items.Add(txt);
  727. log("正在写入数据");
  728. if (!String.IsNullOrEmpty(txt_write.Text))
  729. {
  730. bool writeSuccessful = false;
  731. if (rad_writeUTF8.Checked == true)
  732. {
  733. var writeBuffer = CryptographicBuffer.ConvertStringToBinary(txt_write.Text, BinaryStringEncoding.Utf8);
  734. writeSuccessful = await WriteBufferToSelectedCharacteristicAsync(writeBuffer);
  735. }
  736. else if (rad_writeDec.Checked == true)
  737. {
  738. var isValidValue = Int32.TryParse(txt_write.Text, out int readValue);
  739. if (isValidValue)
  740. {
  741. var writer = new DataWriter();
  742. writer.ByteOrder = ByteOrder.LittleEndian;
  743. writer.WriteInt32(readValue);
  744. writeSuccessful = await WriteBufferToSelectedCharacteristicAsync(writer.DetachBuffer());
  745. }
  746. else
  747. {
  748. log("数据类型不是整数");
  749. }
  750. }
  751. else
  752. {
  753. try
  754. {
  755. string s = txt_write.Text.Replace(" ", "");
  756. byte[] buffer = new byte[s.Length / 2];
  757. for (int i = 0; i < s.Length; i += 2)
  758. {
  759. buffer[i / 2] = (byte)Convert.ToByte(s.Substring(i, 2), 16);
  760. }
  761. writeSuccessful = await WriteBufferToSelectedCharacteristicAsync(WindowsRuntimeBufferExtensions.AsBuffer(buffer, 0, buffer.Length));
  762. }
  763. catch { }
  764. }
  765. //if (writeSuccessful) log("Write operation ok"); else log("Write operation failed");
  766. }
  767. else
  768. {
  769. log("数据为空");
  770. }
  771. }
  772. catch
  773. {
  774. log("写入失败");
  775. }
  776. }
  777. //写入值到当前激活的charactistic
  778. private async Task<bool> WriteBufferToSelectedCharacteristicAsync(IBuffer buffer)
  779. {
  780. try
  781. {
  782. if (ble_Characteristic == null) return false;
  783. // BT_Code: Writes the value from the buffer to the characteristic.
  784. var result = await ble_Characteristic.WriteValueWithResultAsync(buffer);
  785. if (result.Status == GattCommunicationStatus.Success)
  786. {
  787. log("写入成功");
  788. return true;
  789. }
  790. else
  791. {
  792. log($"写入失败: {result.Status}");
  793. return false;
  794. }
  795. }
  796. catch (Exception ex) when (ex.HResult == E_BLUETOOTH_ATT_INVALID_PDU)
  797. {
  798. log("系统错误 " + ex.Message);
  799. return false;
  800. }
  801. catch (Exception ex) when (ex.HResult == E_BLUETOOTH_ATT_WRITE_NOT_PERMITTED || ex.HResult == E_ACCESSDENIED)
  802. {
  803. // This usually happens when a device reports that it support writing, but it actually doesn't.
  804. log("系统错误 " + ex.Message);
  805. return false;
  806. }
  807. }
  808. private void lv_device_SizeChanged(object sender, EventArgs e)
  809. {
  810. //设备列表的尺寸发生变更则按比例改变列宽
  811. float factor = (float)this.Width / 874;
  812. lv_device.Columns[0].Width = (int)(200 * factor);
  813. lv_device.Columns[1].Width = (int)(360 * factor);
  814. lv_device.Columns[2].Width = (int)(100 * factor);
  815. lv_device.Columns[3].Width = (int)(100 * factor);
  816. lv_device.Columns[4].Width = (int)(60 * factor);
  817. }
  818. private void lv_device_MouseDoubleClick(object sender, MouseEventArgs e)
  819. {
  820. //尝试配对设备
  821. btn_pair_Click(sender, e);
  822. }
  823. private void Form1_Shown(object sender, EventArgs e)
  824. {
  825. //MSerialization.MSaveControl.Load_All_SupportedControls(this.Controls);
  826. txt_deviceID.Text = "";
  827. txt_devicename.Text = "";
  828. txt_callback_service.Text = "";
  829. txt_callback_characteristic.Text = "";
  830. txt_rssi.Text = "";
  831. lab_connection.ForeColor = Color.Gray;
  832. }
  833. private void Form1_FormClosing(object sender, FormClosingEventArgs e)
  834. {
  835. MSaveControl.Save_All_SupportedControls(this.Controls);
  836. }
  837. /// <summary>
  838. /// 设备连接状态发生变化的回调函数
  839. /// </summary>
  840. /// <param name="sender"></param>
  841. /// <param name="args"></param>
  842. private void CurrentDevice_ConnectionStatusChanged(BluetoothLEDevice sender, object args)
  843. {
  844. if (sender.ConnectionStatus == BluetoothConnectionStatus.Disconnected && ble_Device.DeviceId != null)
  845. {
  846. //log("Device disconnected");
  847. this.Invoke(new Action(() =>
  848. {
  849. lab_connection.BackColor = Color.Red;
  850. lab_connection.ForeColor = Color.White;
  851. lab_connection.Text = "Disconnected...";
  852. }));
  853. }
  854. else
  855. {
  856. //log("Device connected");
  857. this.Invoke(new Action(() =>
  858. {
  859. lab_connection.BackColor = Color.Green;
  860. lab_connection.ForeColor = Color.White;
  861. lab_connection.Text = "Connected";
  862. ble_Device = sender;
  863. }));
  864. }
  865. }
  866. /// <summary>
  867. /// 用户定制功能
  868. /// </summary>
  869. /// <param name="sender"></param>
  870. /// <param name="e"></param>
  871. private void button1_Click(object sender, EventArgs e)
  872. {
  873. txt_write.Text = "steer," + textBox1.Text;
  874. btn_Write_Click(sender, e);
  875. }
  876. private void button2_Click(object sender, EventArgs e)
  877. {
  878. txt_write.Text = "steer," + textBox2.Text;
  879. btn_Write_Click(sender, e);
  880. }
  881. private void button3_Click(object sender, EventArgs e)
  882. {
  883. txt_write.Text = "steer," + textBox3.Text;
  884. btn_Write_Click(sender, e);
  885. }
  886. private void cb_display_callbackData_Click(object sender, EventArgs e)
  887. {
  888. if (cb_display_callbackData.Checked)
  889. {
  890. HookNotify();
  891. }
  892. else
  893. {
  894. UnhookNotify();
  895. }
  896. }
  897. }
  898. }