SerialPortExample.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. using SerialPortUtility;
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using Unity.VisualScripting;
  7. using UnityEngine;
  8. public class SerialPortExample : MonoBehaviour
  9. {
  10. public int baudrate = 115200;
  11. public SerialPortUtilityPro serialPortUtility;
  12. public SerialPortUtilityPro.OpenSystem openMode = SerialPortUtilityPro.OpenSystem.PCI;
  13. private static int baseAddress;
  14. private static int offsetAddress;
  15. private Queue<byte> tempDataQueue;
  16. public string PortName = "";
  17. private int coinsAmount = 0;
  18. private const int SlotCount = 1;
  19. bool urlBack = false;
  20. byte dataIndex = 1;
  21. private static Dictionary<string, int> _isInit = new();
  22. private void Awake()
  23. {
  24. if (!_isInit.TryGetValue(gameObject.name, out int instanceID) && instanceID == 0)
  25. {
  26. DontDestroyOnLoad(gameObject);
  27. Init();
  28. }
  29. }
  30. private void LOG(string msg)
  31. {
  32. Debug.Log($"<color=#00FF00>{msg}</color>");
  33. }
  34. private void Init()
  35. {
  36. _isInit[gameObject.name] = GetInstanceID();
  37. serialPortUtility = gameObject.GetComponent<SerialPortUtilityPro>();
  38. serialPortUtility.IsAutoOpen = false;
  39. #if UNITY_ANDROID && !UNITY_EDITOR
  40. serialPortUtility.OpenMethod = openMode;
  41. serialPortUtility.DeviceName = PortName;
  42. serialPortUtility.StopBit = SerialPortUtilityPro.StopBitEnum.OneBit;
  43. serialPortUtility.DataBit = SerialPortUtilityPro.DataBitEnum.EightBit;
  44. serialPortUtility.Open();
  45. //serialPortUtility.ReadCompleteEventObject.AddListener(ReadStreamingBinary);
  46. //byte[] datas = new byte[8] { 0xAA, 0x08, 0x04, 0x01, 0x00, 0x00, 0x3C, 0xBB };
  47. //serialPortUtility.Write(datas);
  48. LOG($"{PortName} 串口打开");
  49. if (PortName.Contains("/dev/ttyS0"))
  50. {
  51. //拉取二维码
  52. RequestUrl(dataIndex);
  53. //拉取后台数据
  54. ReqSettingData();
  55. }
  56. #endif
  57. }
  58. void Update()
  59. {
  60. #region 测试代码
  61. #if UNITY_EDITOR
  62. if (Input.GetMouseButtonDown(0))
  63. {
  64. //TestPayQR();
  65. }
  66. #endif
  67. #endregion
  68. //if (serialPortUtility != null && serialPortUtility.IsOpened())
  69. // {
  70. // Debug.Log(serialPortUtility.GetSerialDebugString);
  71. // Debug.Log("串口接收到数据");
  72. // }
  73. }
  74. void TestPay()
  75. {
  76. if (!PortName.Contains("/dev/ttyS0"))
  77. return;
  78. Debug.LogError("TestPay");
  79. List<byte> data = new List<byte>();
  80. data.Add(0XAA);
  81. data.Add(0X55);//帧头
  82. data.Add(0x04);//帧长度
  83. data.Add(0x01);//数据编号
  84. data.Add(0XA6);//指令
  85. data.Add(0X01);//数据
  86. data.Add(0X00);//校验位
  87. PrintFrame(data.ToArray());
  88. }
  89. void TestPayQR()
  90. {
  91. if (!PortName.Contains("/dev/ttyS0"))
  92. return;
  93. List<byte> data = new List<byte>();
  94. data.Add(0XAA);
  95. data.Add(0X55);//帧头
  96. data.Add(0x05);//帧长度
  97. data.Add(0xDA);//数据编号
  98. data.Add(0XA1);//指令
  99. data.Add(0X02);//数据
  100. data.Add(0X64);//数据
  101. byte temp = 0;
  102. for (int i = 2; i < data.Count; i++)
  103. {
  104. temp ^= data[i];
  105. }
  106. data.Add(temp);//校验位
  107. Debug.LogError($"TestPayQR temp={temp}");
  108. PrintFrame(data.ToArray());
  109. }
  110. void OnUrlPayResponse(byte[] bytes)
  111. {
  112. if (!PortName.Contains("/dev/ttyS0"))
  113. return;
  114. //计算校验值
  115. byte tempResponseCheck = 0;
  116. for (int i = 2; i < bytes.Length - 1; i++)
  117. {
  118. tempResponseCheck ^= bytes[i];
  119. }
  120. byte responseCheck = bytes[bytes.Length - 1];
  121. //回复云上分
  122. List<byte> response = new List<byte>();
  123. response.Add(0XAA);
  124. response.Add(0X55);//帧头
  125. response.Add(0X04);//帧长
  126. var dataIndex = bytes[3];
  127. response.Add(dataIndex);//数据编号
  128. response.Add(0XA1);//数据编号
  129. var data = responseCheck == tempResponseCheck ? 0X01 : 0x00;
  130. response.Add((byte)data);//数据
  131. var endIdx = bytes[3];
  132. byte temp = 0;
  133. for (int i = 2; i < response.Count; i++)
  134. {
  135. temp ^= response[i];
  136. }
  137. response.Add(temp);//校验位
  138. Debug.LogError($"responseCheck={responseCheck} tempResponseCheck={tempResponseCheck} data ={data} dataIndex={dataIndex} temp={temp}");
  139. serialPortUtility.Write(response.ToArray());
  140. if (data == 0X01)
  141. StandaloneAPI.InsertCoint(bytes[6]);
  142. }
  143. /// <summary>
  144. /// 请求后台数据
  145. /// </summary>
  146. public void ReqSettingData()
  147. {
  148. //数据包开始------->(1个字节)->AA
  149. //命令------------->(1个字节)->08(取值范围->00到FF)
  150. //数据长度--------->(1个字节)->05(取值范围->00到FF)
  151. //校验------------->(1个字节)->0D(取值范围->00到FF)(校验 = 命令 ^ 数据长度)
  152. //数据包结束------->(1个字节)->55
  153. //例如:AA 08 05 0D 55
  154. List<byte> request = new List<byte>();
  155. request.Add(0xAA);
  156. request.Add(0x08);//命令
  157. request.Add(0x05);//数据长度
  158. byte temp = (byte)(request[1] ^ request[2]);
  159. request.Add(temp);//校验 (校验 = 命令 ^ 数据长度)
  160. request.Add(0x55);//结束
  161. serialPortUtility.Write(request.ToArray());
  162. LOG($"请求后台数据!");
  163. }
  164. /// <summary>
  165. /// 后台数据消息回复
  166. /// </summary>
  167. /// <param name="bytes"></param>
  168. private void OnSettingDataBack(byte[] bytes) //AA 08 14 03 00 64 00 64 00 00 00 01 00 00 00 01 01 01 1F 55
  169. {
  170. //数据包开始-------------------------- > (1个字节)->AA
  171. //命令--------------------------------->(1个字节)->08(取值范围->00到FF)
  172. //数据长度---------------------------- > (1个字节)->14(取值范围->00到FF)
  173. //多少币玩一局游戏(1 - 10)------------ > (1个字节)->03(取值范围->00到FF)
  174. var coinPreGame = bytes[3];
  175. UserSettings.ins.PerRoundCoin = coinPreGame;
  176. //一局游戏玩多长时间(1 - 1200)------->(2个字节)->00 64(取值范围->00 00到FF FF)
  177. var time1 = bytes[4];
  178. var time2 = bytes[5];
  179. int time = (time1 << 8) | time2;
  180. UserSettings.ins.PerRoundSeconds = time;
  181. //多少分兑换一张彩票(1 - 10000)------ > (2个字节)->00 64(取值范围->00 00到FF FF)
  182. var changeTicket1 = bytes[6];
  183. var changeTicket2 = bytes[7];
  184. int changeTicket = (changeTicket1 << 8) | changeTicket2;
  185. UserSettings.ins.ChangeTicket = changeTicket;
  186. //累计投币数(0 - 4294967295)-------- > (4个字节)->00 00 00 01(取值范围->00 00 00 00到FF FF FF FF)
  187. var totalCoinNum1 = bytes[8];
  188. var totalCoinNum2 = bytes[9];
  189. var totalCoinNum3 = bytes[10];
  190. var totalCoinNum4 = bytes[11];
  191. UserSettings.ins.TotalCoinsNum = (totalCoinNum1 << 24) | (totalCoinNum2 << 16) | (totalCoinNum3 << 8) | totalCoinNum4;
  192. //累计出彩票数(0 - 4294967295)------ > (4个字节)->00 00 00 01(取值范围->00 00 00 00到FF FF FF FF)
  193. var totalTicketNum1 = bytes[12];
  194. var totalTicketNum2 = bytes[13];
  195. var totalTicketNum3 = bytes[14];
  196. var totalTicketNum4 = bytes[15];
  197. UserSettings.ins.TotalTicketNum = (totalTicketNum1 << 24) | (totalTicketNum2 << 16) | (totalTicketNum3 << 8) | totalTicketNum4;
  198. //是否需要投币(0 - 1)----------------->(1个字节)->01(取值范围->00到FF)
  199. UserSettings.ins.NeedCoin = bytes[16];
  200. //是否出彩票(0 - 1)------------------->(1个字节)->01(取值范围->00到FF)
  201. UserSettings.ins.GiveTicket = bytes[17];
  202. //校验-------------------------------- > (1个字节)->1F(取值范围->00到FF)(校验 = 命令 ^ 数据长度 ^ ......^是否出彩票)
  203. byte temp = 0;
  204. for (int i = 1; i < bytes.Length - 2; i++)
  205. {
  206. temp ^= bytes[i];
  207. }
  208. //数据包结束------------------------->(1个字节)->55
  209. LOG($"收到后台数据--->coinPreGame:{coinPreGame} time={time} 校验结果={bytes[18] == temp} temp={temp} bytes[18]={bytes[18]} !");
  210. }
  211. /// <summary>
  212. /// 请求保存后台数据
  213. /// </summary>
  214. public void ReqSavingSettingData() //AA 07 14 03 00 64 00 65 00 00 00 01 00 00 00 02 01 01 12 55
  215. {
  216. List<byte> request = new List<byte>();
  217. //数据包开始-------------------------- > (1个字节)->AA
  218. request.Add(0xAA);
  219. //命令--------------------------------->(1个字节)->07(取值范围->00到FF)
  220. request.Add(0x07);
  221. //数据长度---------------------------- > (1个字节)->14(取值范围->00到FF)
  222. request.Add(0x14);
  223. //多少币玩一局游戏(1 - 10)------------ > (1个字节)->03(取值范围->00到FF)
  224. request.Add((byte)UserSettings.ins.PerRoundCoin);
  225. //一局游戏玩多长时间(1 - 1200)------->(2个字节)->00 64(取值范围->00 00到FF FF)
  226. int time = UserSettings.ins.PerRoundSeconds;
  227. request.Add((byte)(time >> 8));
  228. request.Add((byte)(time));
  229. //多少分兑换一张彩票(1 - 10000)------ > (2个字节)->00 64(取值范围->00 00到FF FF)
  230. int changeTicket = UserSettings.ins.ChangeTicket;
  231. request.Add((byte)(changeTicket >> 8));
  232. request.Add((byte)(changeTicket));
  233. //累计投币数(0 - 4294967295)-------- > (4个字节)->00 00 00 01(取值范围->00 00 00 00到FF FF FF FF)
  234. int totalCoins = UserSettings.ins.TotalCoinsNum;
  235. request.Add((byte)(totalCoins >> 24));
  236. request.Add((byte)(totalCoins >> 16));
  237. request.Add((byte)(totalCoins >> 8));
  238. request.Add((byte)(totalCoins));
  239. //累计出彩票数(0 - 4294967295)------ > (4个字节)->00 00 00 01(取值范围->00 00 00 00到FF FF FF FF)
  240. int totalTicketNum = UserSettings.ins.TotalTicketNum;
  241. request.Add((byte)(totalTicketNum >> 24));
  242. request.Add((byte)(totalTicketNum >> 16));
  243. request.Add((byte)(totalTicketNum >> 8));
  244. request.Add((byte)(totalTicketNum));
  245. //是否需要投币(0 - 1)----------------->(1个字节)->01(取值范围->00到FF)
  246. request.Add((byte)UserSettings.ins.NeedCoin);
  247. //是否出彩票(0 - 1)------------------->(1个字节)->01(取值范围->00到FF)
  248. request.Add((byte)UserSettings.ins.GiveTicket);
  249. //校验-------------------------------- > (1个字节)->12(取值范围->00到FF)(校验 = 命令 ^ 数据长度 ^ ......^是否出彩票)
  250. byte temp = 0;
  251. for (int i = 2; i < request.Count; i++)
  252. {
  253. temp ^= request[i];
  254. }
  255. request.Add(temp);
  256. //数据包结束------------------------->(1个字节)->55
  257. request.Add(0x55);//结束
  258. serialPortUtility.Write(request.ToArray());
  259. LOG($"请求保存后台数据!");
  260. }
  261. /// <summary>
  262. /// 保存后台数据回复
  263. /// </summary>
  264. private void OnSaveSettingDataBack(byte[] bytes)//AA 07 05 02 55
  265. {
  266. //--> 回复(保存后台数据)的命令
  267. //数据包开始------->(1个字节)->AA
  268. //命令------------->(1个字节)->07(取值范围->00到FF)
  269. //数据长度--------->(1个字节)->05(取值范围->00到FF)
  270. //校验------------->(1个字节)->02(取值范围->00到FF)(校验 = 命令 ^ 数据长度)
  271. //数据包结束------->(1个字节)->55
  272. LOG($"保存后台数据回复!");
  273. }
  274. /// <summary>
  275. /// 串口读取二进制流数据
  276. /// </summary>
  277. /// <param name="data"></param>
  278. public void ReadStreamingBinary(object data)
  279. {
  280. var bin = data as byte[];
  281. PrintFrame(bin);
  282. }
  283. public void PrintFrame(byte[] bytes)
  284. {
  285. string logStr = "";
  286. for (int i = 0; i < bytes.Length; i++)
  287. {
  288. logStr += $"i={bytes[i]}-";
  289. }
  290. Debug.Log($"{PortName} 接收到的串口命令:{logStr}");
  291. if (bytes[0] == 0xAA)
  292. {
  293. if (bytes[1] == 0x55)//旧
  294. {
  295. if (!PortName.Contains("/dev/ttyS8"))
  296. return;
  297. if (bytes[4] == 0xA6)//投币
  298. {
  299. Debug.Log("旧版投币数量:" + bytes[4]);
  300. StandaloneAPI.InsertCoint(bytes[5]);
  301. }
  302. else if (bytes[4] == 0xAA) //支付盒子链接请求返回
  303. OnUrlResponse(bytes);
  304. else if (bytes[4] == 0xA1) //支付盒子支付返回(云上分)
  305. OnUrlPayResponse(bytes);
  306. }
  307. else
  308. {
  309. if (!PortName.Contains("/dev/ttyS0"))
  310. return;
  311. //0x04:为按键是否按下的命令
  312. if (bytes[1] == 0x04)//退出
  313. {
  314. Debug.Log("确认按键退出游戏命令");
  315. if (bytes[3] == 0x01)
  316. {
  317. Debug.Log("确认按下按键");
  318. AimHandler.ins.ExitIntoEvent();
  319. SendMessage(bytes, 3);
  320. return;
  321. }
  322. else if (bytes[3] == 0x00)
  323. {
  324. Debug.Log("松开按键");
  325. SendMessage(bytes, 3);
  326. return;
  327. }
  328. }
  329. else if (bytes[1] == 0x01)//投币
  330. {
  331. Debug.Log("确认投币命令");
  332. StandaloneAPI.InsertCoint(bytes[3]);
  333. SendMessage(bytes, 3);
  334. }
  335. else if (bytes[1] == 0x0F)//进入设置 AA 0F 05 0A 55
  336. OpenSetUp();
  337. else if(bytes[1] == 0x07)//回复保存后台数据 AA 07 05 02 55
  338. OnSaveSettingDataBack(bytes);
  339. else if (bytes[1] == 0x08)//回复读后台数据 AA 08 14 03 00 64 00 64 00 00 00 01 00 00 00 01 01 01 1F 55
  340. OnSettingDataBack(bytes);
  341. }
  342. }
  343. }
  344. /// <summary>
  345. /// 打开设置
  346. /// </summary>
  347. private void OpenSetUp()
  348. {
  349. AudioMgr.ins.PlayBtn();
  350. GameObject settingsViewObj = ViewManager2.getGameObjectAndShowView(ViewManager2.Path_SettingsView);
  351. settingsViewObj.GetComponent<SmartBow.SettingsView>().ShowBoxSound(true);
  352. }
  353. /// <summary>
  354. /// 请求二维码链接
  355. /// </summary>
  356. /// <param name="dataIndex">数据编号 从1开始</param>
  357. private void RequestUrl(byte dataIndex)
  358. {
  359. LOG($"请求后台支付URL");
  360. byte[] datas = new byte[7] { 0xAA, 0x55, 0x04, dataIndex, 0xAA, 0x01, 0xAE };
  361. datas[6] = GetXor(datas);
  362. serialPortUtility.Write(datas);
  363. Invoke("CheckURLBack", 5f);
  364. #if UNITY_EDITOR
  365. TestBackUrl();
  366. #endif
  367. }
  368. private void CheckURLBack()
  369. {
  370. if (!urlBack && dataIndex <= 3)
  371. {
  372. Debug.LogWarning("没有拉取到支付url");
  373. RequestUrl(++dataIndex);
  374. }
  375. }
  376. /// <summary>
  377. /// 测试URL回包
  378. /// </summary>
  379. private void TestBackUrl()
  380. {
  381. var url = $"http://pay.sy1999.com:8901?productNumber=SY4G4365";
  382. var urlData = System.Text.UTF8Encoding.Default.GetBytes(url);
  383. List<byte> data = new List<byte>();
  384. data.Add(0XAA); data.Add(0X55);//帧头
  385. data.Add((byte)(urlData.Length + 3));//帧长度(数据编号到校验位的所有数据长度)
  386. data.Add(0X01);//数据编号
  387. data.Add(0XAA);//指令
  388. data.AddRange(urlData);//数据
  389. data.Add(0X00);//校验位
  390. Debug.Log($"Test: url={url} length={urlData.Length}");
  391. PrintFrame(data.ToArray());
  392. }
  393. /// <summary>
  394. /// URL请求返回
  395. /// </summary>
  396. /// <param name="data"></param>
  397. void OnUrlResponse(byte[] bytes)
  398. {
  399. urlBack = true;
  400. int frameLength = bytes[2];
  401. frameLength = frameLength - 3;
  402. List<byte> datas = new List<byte>();
  403. var startPos = 5;
  404. for (int i = startPos; i < frameLength + startPos; i++)
  405. {
  406. datas.Add(bytes[i]);
  407. }
  408. byte[] bs = datas.ToArray();
  409. var url = System.Text.Encoding.Default.GetString(bs);
  410. StandaloneAPI.url = url;
  411. var homeView = FindObjectOfType<HomeView>();
  412. if (homeView != null)
  413. homeView.FlushUrlQR();
  414. }
  415. void SendMessage(byte[] bytes,int index)
  416. {
  417. List<byte> s = new List<byte>(bytes);
  418. s.RemoveAt(index);
  419. bytes = s.ToArray();
  420. serialPortUtility.Write(bytes);
  421. }
  422. byte GetXor(byte[] btyes)
  423. {
  424. byte temp = 0;
  425. for (int i = 2; i < btyes.Length; i++)
  426. {
  427. temp ^= btyes[i];
  428. }
  429. return temp;
  430. }
  431. private void OnDestroy()
  432. {
  433. if (_isInit.TryGetValue(gameObject.name, out var instanceID) && instanceID == GetInstanceID())
  434. {
  435. LOG($"{PortName} 串口程序被销毁");
  436. serialPortUtility.Close();
  437. _isInit.Remove(gameObject.name);
  438. }
  439. }
  440. }