BluetoothAim.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. using ArduinoBluetoothAPI;
  2. using System;
  3. using UnityEngine;
  4. using System.Collections.Generic;
  5. using UnityEngine.UI;
  6. using DG.Tweening;
  7. using UnityEngine.SceneManagement;
  8. /* 蓝牙瞄准模块 */
  9. public class BluetoothAim : MonoBehaviour
  10. {
  11. readonly string targetDeviceName = "Bbow_20210501";
  12. //旧设备-服务和特征
  13. readonly string targetDeviceService = "0000fff0"; //字母用小写
  14. readonly string targetDeviceCharacteristicWrite = "0000fff2"; //字母用小写
  15. readonly string targetDeviceCharacteristicNotify = "0000fff1"; //字母用小写
  16. //新设备-服务和特征
  17. //readonly string targetDeviceService = "6e400001"; //字母用小写
  18. //readonly string targetDeviceCharacteristicWrite = "6e400002"; //字母用小写
  19. //readonly string targetDeviceCharacteristicNotify = "6e400003"; //字母用小写
  20. BluetoothHelper bluetoothHelper;
  21. BluetoothHelperCharacteristic characteristicWrite;
  22. BluetoothHelperService bluetoothService;
  23. string deviceName = "";
  24. bool canConnect = true;
  25. [SerializeField] Text textUI;
  26. public BluetoothStatusEnum status = BluetoothStatusEnum.Connect;
  27. public bool hasData = false;
  28. public long hasDataTime;
  29. public static bool scanLock = false; //防止同时扫描冲突
  30. public static BluetoothAim ins;
  31. void Start()
  32. {
  33. ins = this;
  34. InitAutoDormancy();
  35. #if UNITY_STANDALONE_WIN
  36. new GameObject("BleUDP").AddComponent<BleUDP>();
  37. #endif
  38. }
  39. void OnDestroy()
  40. {
  41. DisconnectBleHelper();
  42. }
  43. private bool userDoConnect = false;
  44. private bool doConnect = false;
  45. public void DoConnect()
  46. {
  47. if (status == BluetoothStatusEnum.Connect)
  48. {
  49. userDoConnect = true;
  50. doConnect = true;
  51. SetStatus(BluetoothStatusEnum.Connecting);
  52. }
  53. else if (status == BluetoothStatusEnum.ConnectSuccess)
  54. {
  55. userDoConnect = false;
  56. doConnect = false;
  57. OnDisconnect();
  58. #if UNITY_ANDROID
  59. DisconnectBleHelper();
  60. #elif UNITY_STANDALONE_WIN
  61. BleUDP.ins.Disconnect();
  62. #endif
  63. }
  64. }
  65. void OnDisconnect()
  66. {
  67. hasData = false;
  68. canConnect = true;
  69. SetStatus(BluetoothStatusEnum.ConnectFail);
  70. BowCamera.isTouchMode = true;
  71. DestroyWhenDisconenct();
  72. }
  73. void Update()
  74. {
  75. if (userDoConnect && status == BluetoothStatusEnum.Connect)
  76. {
  77. DoConnect();
  78. }
  79. if (doConnect) Connect();
  80. }
  81. void SetStatus(BluetoothStatusEnum statusValue)
  82. {
  83. status = statusValue;
  84. if (status == BluetoothStatusEnum.ConnectFail)
  85. {
  86. Sequence sequence = DOTween.Sequence();
  87. sequence.AppendInterval(2f);
  88. sequence.AppendCallback(delegate ()
  89. {
  90. if (status == BluetoothStatusEnum.ConnectFail)
  91. {
  92. status = BluetoothStatusEnum.Connect;
  93. }
  94. });
  95. sequence.SetUpdate(true);
  96. DeviceReconnectView.Show();
  97. }
  98. }
  99. void DisconnectBleHelper()
  100. {
  101. if (bluetoothHelper != null) bluetoothHelper.Disconnect();
  102. }
  103. void Connect()
  104. {
  105. if (BluetoothShoot.scanLock)
  106. {
  107. return;
  108. }
  109. if (!canConnect)
  110. {
  111. return;
  112. }
  113. doConnect = false;
  114. scanLock = true;
  115. canConnect = false;
  116. SetStatus(BluetoothStatusEnum.Connecting);
  117. #if UNITY_ANDROID
  118. ConnectBleHelper();
  119. #elif UNITY_STANDALONE_WIN
  120. ConnectBleByUDP();
  121. #endif
  122. }
  123. void ConnectBleHelper()
  124. {
  125. try
  126. {
  127. BluetoothHelper.BLE = true;
  128. bluetoothHelper = BluetoothHelper.GetNewInstance();
  129. bluetoothHelper.OnConnected += (BluetoothHelper helper) =>
  130. {
  131. Log("连接成功\n" + helper.getDeviceName());
  132. SetStatus(BluetoothStatusEnum.ConnectSuccess);
  133. BowCamera.isTouchMode = false;
  134. foreach (BluetoothHelperService service in helper.getGattServices())
  135. {
  136. if (service.getName().ToLower().StartsWith(targetDeviceService))
  137. {
  138. bluetoothService = service;
  139. foreach (BluetoothHelperCharacteristic characteristic in service.getCharacteristics())
  140. {
  141. if (characteristic.getName().ToLower().StartsWith(targetDeviceCharacteristicWrite))
  142. {
  143. characteristicWrite = characteristic;
  144. }
  145. else if (characteristic.getName().ToLower().StartsWith(targetDeviceCharacteristicNotify))
  146. {
  147. BluetoothHelperCharacteristic ch = new BluetoothHelperCharacteristic(characteristic.getName());
  148. ch.setService(bluetoothService.getName());
  149. bluetoothHelper.Subscribe(ch);
  150. }
  151. }
  152. }
  153. }
  154. // CallDelay(1, OpenInfrared);
  155. // CallDelay(2, OpenReceiveData);
  156. // CallDelay(3, RequestBattery);
  157. CallDelay(2, () =>
  158. {
  159. if (status != BluetoothStatusEnum.ConnectSuccess) return;
  160. InitWhenConenct();
  161. });
  162. };
  163. bluetoothHelper.OnConnectionFailed += (BluetoothHelper helper) =>
  164. {
  165. Log("连接失败\n" + helper.getDeviceName());
  166. OnDisconnect();
  167. };
  168. bluetoothHelper.OnCharacteristicChanged += (helper, value, characteristic) =>
  169. {
  170. if (!hasData) hasDataTime = JC.CS.Utility.GetTimestamp();
  171. hasData = true;
  172. byte[] bytes = value;
  173. // Log(String.Join(",", bytes));
  174. BluetoothClient.UploadData(0, bytes);
  175. if (AimHandler.ins)
  176. {
  177. AimHandler.ins.OnDataReceived(bytes);
  178. }
  179. };
  180. int scanCount = 0;
  181. bluetoothHelper.OnScanEnded += (BluetoothHelper helper, LinkedList<BluetoothDevice> nearbyDevices) =>
  182. {
  183. scanLock = false;
  184. foreach (BluetoothDevice device in nearbyDevices)
  185. {
  186. if (device.DeviceName == targetDeviceName)
  187. {
  188. deviceName = device.DeviceName;
  189. bluetoothHelper.setDeviceName(deviceName);
  190. bluetoothHelper.Connect();
  191. Log("发现设备\n" + device.DeviceName);
  192. return;
  193. }
  194. }
  195. if (scanCount < 3)
  196. { //如果没扫描到,则重新扫描,达到延迟提示失败的效果
  197. scanCount++;
  198. scanLock = true;
  199. bluetoothHelper.ScanNearbyDevices();
  200. }
  201. else
  202. {
  203. canConnect = true;
  204. Log("没有发现设备");
  205. SetStatus(BluetoothStatusEnum.ConnectFail);
  206. }
  207. };
  208. bluetoothHelper.ScanNearbyDevices();
  209. Log("正在扫描设备");
  210. }
  211. catch (Exception e)
  212. {
  213. Debug.LogError(e.Message);
  214. Debug.LogError(e.StackTrace);
  215. scanLock = false;
  216. canConnect = true;
  217. SetStatus(BluetoothStatusEnum.ConnectFail);
  218. Log("请打开蓝牙");
  219. }
  220. }
  221. void ConnectBleByUDP()
  222. {
  223. try
  224. {
  225. BleUDP.ins.OnConnected = () =>
  226. {
  227. Log("连接成功\n" + deviceName);
  228. SetStatus(BluetoothStatusEnum.ConnectSuccess);
  229. BowCamera.isTouchMode = false;
  230. InitWhenConenct();
  231. };
  232. BleUDP.ins.OnConnectionFailed = () =>
  233. {
  234. Log("连接失败\n" + deviceName);
  235. OnDisconnect();
  236. };
  237. BleUDP.ins.OnCharacteristicChanged = (byte[] value) =>
  238. {
  239. if (!hasData) hasDataTime = JC.CS.Utility.GetTimestamp();
  240. hasData = true;
  241. byte[] bytes = value;
  242. // Log(String.Join(",", bytes));
  243. BluetoothClient.UploadData(0, bytes);
  244. if (AimHandler.ins)
  245. {
  246. AimHandler.ins.OnDataReceived(bytes);
  247. }
  248. };
  249. BleUDP.ins.OnScanEnded = () =>
  250. {
  251. scanLock = false;
  252. deviceName = targetDeviceName;
  253. BleUDP.ins.Connect();
  254. Log("发现设备\n" + deviceName);
  255. };
  256. BleUDP.ins.ScanNearbyDevices();
  257. }
  258. catch (Exception e)
  259. {
  260. Debug.LogError(e.Message);
  261. Debug.LogError(e.StackTrace);
  262. scanLock = false;
  263. canConnect = true;
  264. SetStatus(BluetoothStatusEnum.ConnectFail);
  265. }
  266. }
  267. #region 自动进入/退出休眠状态, 这里做程指令发送队列,为了控制连续发送指令的间隔,避免硬件收不到或处理不过来
  268. class CmdToSend
  269. {
  270. public string[] cmds;
  271. public Action onComplete;
  272. public Func<bool> canDo;
  273. public CmdToSend(string[] cmds, Action onComplete, Func<bool> canDo)
  274. {
  275. this.cmds = cmds;
  276. this.onComplete = onComplete;
  277. this.canDo = canDo;
  278. }
  279. }
  280. Queue<CmdToSend> cmdWaitingList = new Queue<CmdToSend>();
  281. bool isSendCmdLocked = false;
  282. bool canAutoDormancy = false;
  283. bool isStartUp = false;
  284. JC.CS.CountLocker needModularAwake = new JC.CS.CountLocker();
  285. void CheckAndStartUp()
  286. {
  287. if (needModularAwake.IsLocked())
  288. {
  289. StartUp();
  290. }
  291. else
  292. {
  293. Dormancy();
  294. }
  295. }
  296. void InitAutoDormancy()
  297. {
  298. // GlobalEventCenter.ins.onGameSceneLoad += () => {
  299. // needModularAwake.Lock();
  300. // CheckAndStartUp();
  301. // };
  302. // GlobalEventCenter.ins.onGameSceneDestroy += () => {
  303. // needModularAwake.Unlock();
  304. // CheckAndStartUp();
  305. // };
  306. // GlobalEventCenter.ins.onSimulateMouseAwakeChanged += (waked) => {
  307. // if (waked) needModularAwake.Lock();
  308. // else needModularAwake.Unlock();;
  309. // CheckAndStartUp();
  310. // };
  311. // GlobalEventCenter.ins.onDeviceCalibrateViewAwakeChanged += (waked) => {
  312. // if (waked) needModularAwake.Lock();
  313. // else needModularAwake.Unlock();;
  314. // CheckAndStartUp();
  315. // };
  316. //暂时关闭自动休眠,默认是需要模块保持激活
  317. needModularAwake.Lock();
  318. }
  319. void InitWhenConenct()
  320. {
  321. canAutoDormancy = true;
  322. List<string> cmds = new List<string>();
  323. cmds.Add("b"); //确保开启stm32
  324. cmds.Add("b"); //获取初始电量
  325. cmds.Add("1"); //开启发送逻辑
  326. Action onComplete = null;
  327. if (needModularAwake.IsLocked())
  328. {
  329. cmds.Add("w"); //红外灯开启
  330. cmds.Add("3"); //九轴开启
  331. onComplete = () =>
  332. {
  333. isStartUp = true;
  334. };
  335. }
  336. else
  337. {
  338. cmds.Add("s"); //红外灯关闭
  339. cmds.Add("S"); //Stm32关闭
  340. cmds.Add("4"); //九轴关闭
  341. onComplete = () =>
  342. {
  343. isStartUp = false;
  344. };
  345. }
  346. SendCDM(null, onComplete, cmds.ToArray());
  347. }
  348. void DestroyWhenDisconenct()
  349. {
  350. canAutoDormancy = false;
  351. sendCMD_CheckAndDoStop(null);
  352. }
  353. //启动
  354. void StartUp()
  355. {
  356. SendCDM(() =>
  357. {
  358. return !isStartUp;
  359. }, () =>
  360. {
  361. isStartUp = true;
  362. }, "b", "w", "3");
  363. }
  364. //休眠
  365. void Dormancy()
  366. {
  367. SendCDM(() =>
  368. {
  369. return isStartUp;
  370. }, () =>
  371. {
  372. isStartUp = false;
  373. }, "4", "s", "S");
  374. }
  375. void SendCDM(Func<bool> canDo, Action onComplete, params string[] cmds)
  376. {
  377. CmdToSend cmdToSend = new CmdToSend(cmds, onComplete, canDo);
  378. if (isSendCmdLocked)
  379. {
  380. cmdWaitingList.Enqueue(cmdToSend);
  381. return;
  382. }
  383. sendCMD_NotCheck(cmdToSend);
  384. }
  385. void sendCMD_NotCheck(CmdToSend cmdToSend)
  386. {
  387. if (cmdToSend.canDo != null && !cmdToSend.canDo.Invoke())
  388. {
  389. sendCMD_CheckNext();
  390. return;
  391. }
  392. isSendCmdLocked = true;
  393. Sequence sequence = DOTween.Sequence();
  394. sequence.PrependInterval(0.3f);
  395. foreach (var cmd in cmdToSend.cmds)
  396. {
  397. sequence.AppendCallback(() =>
  398. {
  399. bool stopped = sendCMD_CheckAndDoStop(sequence);
  400. if (!stopped) WriteData(cmd);
  401. });
  402. sequence.AppendInterval(0.5f);
  403. }
  404. sequence.AppendCallback(() =>
  405. {
  406. bool stopped = sendCMD_CheckAndDoStop(sequence);
  407. if (!stopped)
  408. {
  409. isSendCmdLocked = false;
  410. cmdToSend.onComplete?.Invoke();
  411. sendCMD_CheckNext();
  412. }
  413. });
  414. sequence.SetUpdate(true);
  415. }
  416. void sendCMD_CheckNext()
  417. {
  418. if (cmdWaitingList.Count <= 0) return;
  419. CmdToSend cmdToSend = cmdWaitingList.Dequeue();
  420. sendCMD_NotCheck(cmdToSend);
  421. }
  422. bool sendCMD_CheckAndDoStop(Sequence sequence)
  423. {
  424. if (canAutoDormancy) return false;
  425. isStartUp = false;
  426. isSendCmdLocked = false;
  427. cmdWaitingList.Clear();
  428. if (sequence != null) sequence.Kill();
  429. return true;
  430. }
  431. #endregion
  432. public void RequestBattery()
  433. {
  434. if (!isStartUp) return;
  435. if (isSendCmdLocked) return;
  436. WriteData("b");
  437. }
  438. public void ReplyInfraredShoot()
  439. {
  440. if (isSendCmdLocked) return;
  441. WriteData("I");
  442. }
  443. void CallDelay(float delayTime, TweenCallback callback)
  444. {
  445. Sequence sequence = DOTween.Sequence();
  446. sequence.PrependInterval(delayTime).AppendCallback(callback);
  447. sequence.SetUpdate(true);
  448. }
  449. public void WriteData(string data)
  450. {
  451. #if UNITY_ANDROID
  452. if (DebugDeviceCMD.ins) DebugDeviceCMD.ins.ShowCMD(data);
  453. BluetoothHelperCharacteristic ch = new BluetoothHelperCharacteristic(characteristicWrite.getName());
  454. ch.setService(bluetoothService.getName());
  455. bluetoothHelper.WriteCharacteristic(ch, data);
  456. #elif UNITY_STANDALONE_WIN
  457. BleUDP.ins.SendMsg(data);
  458. #endif
  459. }
  460. void Log(string text)
  461. {
  462. if (textUI)
  463. {
  464. textUI.text = text;
  465. }
  466. }
  467. }