Unity bluetooth OBD-II reader setup not working on K-Line

4 hours ago 3
ARTICLE AD BOX

I got a system in Unity that connects to one of those bluetooth ELM327-based adaptors to get data back, In my case for a speedometer.

On a SEAT Ibiza Mk-4 (2008-2017) it works OK. However when I try this on an Audi A3 8L (1996-2000) with an OBD-II port which runs on a K-Line system, I'm guessing, and not a CAN bus like newer cars. The following happens:

The adaptor start I can start the reading I receive a bit a of data back e.g. speed, RPM, engine. This loads 1 or 2 times and finally hangs with the signal XX XX NO DATA

What's throwing me off, the fact that it works for 2 seconds, then it fails

I've tried initializing it for a K-line setup I've tried a system that waits for the response before sending another one I've tried getting just speed and RPM (as some cars don't have some values like fuel level, throttle position etc.)

But it still does the exact same thing.

My actual system works with a Java AAB library connected to a script. I'm guessing the code is fine as it does work fine on the newer car, but I still want to take a look at what could be the problem for older cars. The reader I'm using is a PDF quality plus adapter

using System; using UnityEngine; using UnityEngine.UI; using UnityEngine.SceneManagement; using System.Collections.Generic; public class OBDIIManage : MonoBehaviour { public static OBDIIManage _instance; private AndroidJavaObject obd; private bool isConnected = false; private bool isReading = false; public float rpm; public float speed; public float throttle; public float engineLoad; public float fuelLevel; public float coolantTemp; public string lastsend; private string pattern = "SRTSRLSRTSRLSRTSRLSRCSRLSRTSRLSRTSRLSRTSRLSRFSRC"; private int patternIndex = 0; private char lastCommand; public Action ontick; public string[] devs; public RectTransform deviceList; public GameObject deviceLObj; public Button refBt; public Image ConnectBox; public Text CBText; public GameObject MMenu; public GameObject MMenu2; public List<Button> MBts; public List<Sprite> Textures; float nextSendTime = 0f; float sendInterval = 0.01f; bool usingKLine = false; void Awake() { if (_instance == null) { _instance = this; DontDestroyOnLoad(gameObject); SceneManager.sceneLoaded += OnSceneLoaded; } else { Destroy(gameObject); } } void Start() { #if UNITY_ANDROID && !UNITY_EDITOR AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); AndroidJavaObject activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"); obd = new AndroidJavaObject( "com.example.obdplugin.BluetoothOBD", activity ); #endif int useK = PlayerPrefs.GetInt("KLINEMODE", 0); ToggleKMode(useK == 1, true); } public string[] GetPairedDevices() { #if UNITY_ANDROID && !UNITY_EDITOR return obd.Call<string[]>("getAvailableDevices"); #else return new string[0]; #endif } public bool Connect(string mac) { #if UNITY_ANDROID && !UNITY_EDITOR isConnected = obd.Call<bool>("connect", mac); return isConnected; #else return false; #endif } public void InitELM(int uK) { usingKLine = (uK == 1); nextSendTime = Time.time; Send("ATZ"); Send("ATE0"); Send("ATL0"); Send("ATS0"); Send("ATH0"); if (usingKLine) { sendInterval = 0.15f; Send("ATAT0"); Send("ATSTFF"); Send("ATSP5"); } else { sendInterval = 0.01f; Send("ATAT2"); Send("ATSP0"); } } public void ToggleKMode(bool tr, bool realconnect) { if(tr) { pattern = "SRSRSRSR"; MBts[0].GetComponent<Image>().sprite = Textures[0]; MBts[1].GetComponent<Image>().sprite = Textures[1]; PlayerPrefs.SetInt("KLINEMODE", 1); if (realconnect) { InitELM(1); } } else { pattern = "SRTSRLSRTSRLSRTSRLSRCSRLSRTSRLSRTSRLSRTSRLSRFSRC"; MBts[1].GetComponent<Image>().sprite = Textures[0]; MBts[0].GetComponent<Image>().sprite = Textures[1]; PlayerPrefs.SetInt("KLINEMODE", 0); if (realconnect) { InitELM(0); } } } void Update() { if (!isReading) return; string res = obd.Call<string>("getAndClearResponse"); if (!string.IsNullOrEmpty(res)) { lastsend = res; ParseByLastCommand(res); ontick?.Invoke(); } if (Time.time >= nextSendTime) { SendNext(); nextSendTime = Time.time + sendInterval; } } void SendNext() { char p = pattern[patternIndex]; patternIndex = (patternIndex + 1) % pattern.Length; lastCommand = p; switch (p) { case 'R': Send("010C"); break; case 'S': Send("010D"); break; case 'T': Send("0111"); break; case 'L': Send("0104"); break; case 'F': Send("012F"); break; case 'C': Send("0105"); break; } } void Send(string cmd) { if (obd != null) obd.Call("sendCommand", cmd); } public void StartReading() { if (!isConnected || isReading) return; isReading = true; } public void StopReading() { isReading = false; } void ParseByLastCommand(string res) { float val; switch (lastCommand) { case 'R': val = ParseRPM(res); if (!float.IsNaN(val)) rpm = val; break; case 'S': val = ParseSpeed(res); if (!float.IsNaN(val)) speed = val; break; case 'T': val = ParseThrottle(res); if (!float.IsNaN(val)) throttle = val; break; case 'L': val = ParseLoad(res); if (!float.IsNaN(val)) engineLoad = val; break; case 'F': val = ParseFuel(res); if (!float.IsNaN(val)) fuelLevel = val; break; case 'C': val = ParseCoolant(res); if (!float.IsNaN(val)) coolantTemp = val; break; } } private string Clean(string res) { res = res.Replace("\r", "") .Replace("\n", "") .Replace(">", "") .Trim(); if (!res.Contains(" ") && res.Length % 2 == 0) { System.Text.StringBuilder sb = new System.Text.StringBuilder(); for (int i = 0; i < res.Length; i += 2) { sb.Append(res.Substring(i, 2)); sb.Append(" "); } res = sb.ToString().Trim(); } return res; } float ParseRPM(string res) { try { string[] p = Clean(res).Split(' '); if (p.Length < 4) return float.NaN; int A = Convert.ToInt32(p[2], 16); int B = Convert.ToInt32(p[3], 16); return ((A * 256) + B) / 4f; } catch { return float.NaN; } } float ParseSpeed(string res) { try { string[] p = Clean(res).Split(' '); if (p.Length < 3) return float.NaN; int A = Convert.ToInt32(p[2], 16); return A; } catch { return float.NaN; } } float ParseThrottle(string res) { try { string[] p = Clean(res).Split(' '); if (p.Length < 3) return float.NaN; int A = Convert.ToInt32(p[2], 16); return (A * 100f) / 255f; } catch { return float.NaN; } } float ParseLoad(string res) { try { string[] p = Clean(res).Split(' '); if (p.Length < 3) return float.NaN; int A = Convert.ToInt32(p[2], 16); return (A * 100f) / 255f; } catch { return float.NaN; } } float ParseFuel(string res) { try { string[] p = Clean(res).Split(' '); if (p.Length < 3) return float.NaN; int A = Convert.ToInt32(p[2], 16); return (A * 100f) / 255f; } catch { return float.NaN; } } float ParseCoolant(string res) { try { string[] p = Clean(res).Split(' '); if (p.Length < 3) return float.NaN; int A = Convert.ToInt32(p[2], 16); return A - 40f; } catch { return float.NaN; } } void OnSceneLoaded(Scene scene, LoadSceneMode mode) { MBts.Clear(); MMenu = GameObject.Find("SettingMenu"); MMenu2 = GameObject.Find("(OBDMENU"); deviceList = GameObject.Find("DVL").GetComponent<RectTransform>(); deviceLObj = GameObject.Find("DVLCL"); refBt = GameObject.Find("rfbt").GetComponent<Button>(); ConnectBox = GameObject.Find("DB").GetComponent<Image>(); CBText = ConnectBox.transform.GetChild(0).GetComponent<Text>(); MBts.Add(GameObject.Find("KLBTN1").GetComponent<Button>()); MBts.Add(GameObject.Find("KLBTN2").GetComponent<Button>()); MBts[0].onClick.AddListener(() => { ToggleKMode(false, isReading); }); MBts[1].onClick.AddListener(() => { ToggleKMode(true, isReading); }); refBt.onClick.AddListener(() => { RefList(); }); MMenu2.SetActive(false); MMenu.SetActive(false); deviceLObj.SetActive(false); RefList(); } public void RefList() { devs = GetPairedDevices(); foreach (Transform fs in deviceList) Destroy(fs); for (int f = 0; f < devs.Length; f++) { GameObject newBtn = Instantiate(deviceLObj, deviceList); newBtn.SetActive(true); newBtn.GetComponent<RectTransform>().anchoredPosition = new Vector2(0f, -10f + (-120f * f)); int splitpos = devs[f].IndexOf('|'); int lbf = f; newBtn.transform.GetChild(0).GetComponent<Text>().text = devs[f].Remove(splitpos); newBtn.GetComponent<Button>().onClick.AddListener(() => { StartCoroutine(AttemptConnect(devs[lbf])); }); } deviceList.sizeDelta = new Vector2(deviceList.sizeDelta.x, 20f + (120f * devs.Length)); } public System.Collections.IEnumerator AttemptConnect(string full) { ConnectBox.color = Color.white; CBText.fontSize = 38; CBText.text = "Connecting..."; string[] split = full.Split('|'); yield return null; bool connct = Connect(split[1]); if (connct) { ConnectBox.color = new Color(0.45f, 1f, 0.5f); CBText.fontSize = 60; CBText.text = split[0]; nextSendTime = Time.time; int useK = PlayerPrefs.GetInt("KLINEMODE", 0); obd.Call<string>("getAndClearResponse"); StartReading(); ToggleKMode(useK == 1, true); } else { ConnectBox.color = Color.red; CBText.text = "Connection Failed"; } } }
Read Entire Article