BluetoothAim.cs 13 KB

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