For car enthusiasts and DIY mechanics, accessing real-time data from your vehicle can open up a world of diagnostics and performance insights. By leveraging the power of a bluetooth OBD2 adapter, an Android device running the Torque Pro OBDII app, and the versatile Node-RED platform, you can create a custom dashboard to monitor your car’s vitals in real time.
This guide will walk you through setting up a system to stream data from your car’s OBDII port, via Bluetooth, to Node-RED, a flow-based programming tool. This setup allows you to visualize, log, and even react to your vehicle’s data in countless ways.
Setting Up Torque Pro for Data Streaming via Bluetooth OBD2
First, ensure you have a compatible bluetooth OBD2 adapter plugged into your vehicle’s OBDII port and paired with your Android device. The Torque Pro OBDII app is your primary interface for collecting data. To stream this data to Node-RED, you need to configure Torque Pro’s data logging and upload settings:
- Access Settings: Open Torque Pro and navigate to the “Settings” menu.
- Data Logging & Upload: Look for the “Data Logging and Upload” section.
- Select What to Log: Choose “Select what to log” and ensure all the PIDs (Parameter IDs) you want to monitor in Node-RED are checked. These PIDs represent different sensors and readings from your car’s engine and systems.
- Enable Web Server Upload: Check the “Upload to web server” option. This tells Torque Pro to send data via HTTP.
- Webserver URL Configuration: In the “Webserver URL” field, enter the address of your Node-RED server, followed by the designated path for receiving Torque data. This will typically look like
http://your.nodered.server:1880/torque/
. Replaceyour.nodered.server:1880
with the actual IP address or hostname and port of your Node-RED instance.
Ensure your Android device and Node-RED server are on the same network for seamless communication.
Node-RED Flow for Receiving Bluetooth OBD2 Data from Torque Pro
Now, let’s set up the Node-RED flow to receive and process the data streamed from Torque Pro. Import the following flow JSON into your Node-RED workspace:
[ { "id": "2ce0055.8743efa", "type": "tab", "label": "Torque", "disabled": false, "info": "" }, { "id": "67436221.b54c0c", "type": "http in", "z": "2ce0055.8743efa", "name": "", "url": "/torque", "method": "get", "upload": true, "swaggerDoc": "", "x": 221, "y": 143, "wires": [ [ "f98f22cc.807ba", "557d7766.022168", "33d571b3.db242e", "69b56cf.77bc694", "683933fa.d76f1c", "ab2ff853.e8b808", "c005b8b1.f06b98", "96abaa53.c70988", "ee5bc723.594bf8", "dfdc4763.7a8fd8", "cee9fa09.98b6d8", "f2d748c5.d92da8", "936bc33f.bcd27", "97ab65f2.1a0728" ] ] }, { "id": "f98f22cc.807ba", "type": "http response", "z": "2ce0055.8743efa", "name": "", "statusCode": "", "headers": { "OK!": "" }, "x": 1290, "y": 140, "wires": [] }, { "id": "557d7766.022168", "type": "change", "z": "2ce0055.8743efa", "name": "Barometer", "rules": [ { "t": "move", "p": "payload.kff1270", "pt": "msg", "to": "payload", "tot": "msg" }, { "t": "set", "p": "topic", "pt": "msg", "to": "torque\phone\barometer", "tot": "str" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 731, "y": 403, "wires": [ [ "536ae3f0.1980dc", "5ef6a21e.75ce4c" ] ] }, { "id": "3b30fefa.3f9012", "type": "ui_gauge", "z": "2ce0055.8743efa", "name": "", "group": "9b558fbc.438aa", "order": 1, "width": "6", "height": "6", "gtype": "gage", "title": "RPM", "label": "RPM", "format": "{{value}}", "min": 0, "max": "7000", "colors": [ "#00b500", "#e6e600", "#ca3838" ], "seg1": "", "seg2": "", "x": 1061, "y": 443, "wires": [] }, { "id": "33d571b3.db242e", "type": "change", "z": "2ce0055.8743efa", "name": "RPM", "rules": [ { "t": "move", "p": "payload.kc", "pt": "msg", "to": "payload", "tot": "msg" }, { "t": "set", "p": "topic", "pt": "msg", "to": "torque\vehicle\rpm", "tot": "str" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 721, "y": 443, "wires": [ [ "3b30fefa.3f9012", "5ef6a21e.75ce4c" ] ] }, { "id": "69b56cf.77bc694", "type": "change", "z": "2ce0055.8743efa", "name": "Mass Air Flow Rate", "rules": [ { "t": "move", "p": "payload.k10", "pt": "msg", "to": "payload", "tot": "msg" }, { "t": "set", "p": "topic", "pt": "msg", "to": "torque\vehicle\mafrate", "tot": "str" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 761, "y": 483, "wires": [ [ "7ee21162.28cdb", "5ef6a21e.75ce4c" ] ] }, { "id": "683933fa.d76f1c", "type": "change", "z": "2ce0055.8743efa", "name": "Throttle Position", "rules": [ { "t": "move", "p": "payload.k11", "pt": "msg", "to": "payload", "tot": "msg" }, { "t": "set", "p": "topic", "pt": "msg", "to": "torque\vehicle\throttle", "tot": "str" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 751, "y": 523, "wires": [ [ "3765c6b.fb70f3a", "5ef6a21e.75ce4c" ] ] }, { "id": "ab2ff853.e8b808", "type": "change", "z": "2ce0055.8743efa", "name": "Coolant Temp", "rules": [ { "t": "move", "p": "payload.k5", "pt": "msg", "to": "payload", "tot": "msg" }, { "t": "set", "p": "topic", "pt": "msg", "to": "torque\vehicle\coolant", "tot": "str" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 741, "y": 563, "wires": [ [ "258b6f62.e82ab" ] ] }, { "id": "c005b8b1.f06b98", "type": "change", "z": "2ce0055.8743efa", "name": "Volage", "rules": [ { "t": "move", "p": "payload.kff1238", "pt": "msg", "to": "payload", "tot": "msg" }, { "t": "set", "p": "topic", "pt": "msg", "to": "torque\obd\voltage", "tot": "str" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 721, "y": 603, "wires": [ [ "5b8bdb33.d96134", "5ef6a21e.75ce4c" ] ] }, { "id": "96abaa53.c70988", "type": "change", "z": "2ce0055.8743efa", "name": "Vacuum", "rules": [ { "t": "move", "p": "payload.kff1202", "pt": "msg", "to": "payload", "tot": "msg" }, { "t": "set", "p": "topic", "pt": "msg", "to": "torque\vehicle\vacuum", "tot": "str" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 731, "y": 643, "wires": [ [ "913a6311.540cd", "5ef6a21e.75ce4c" ] ] }, { "id": "3765c6b.fb70f3a", "type": "ui_gauge", "z": "2ce0055.8743efa", "name": "", "group": "9b558fbc.438aa", "order": 2, "width": "3", "height": "3", "gtype": "donut", "title": "Throttle Position", "label": "%", "format": "{{value}}", "min": 0, "max": "100", "colors": [ "#00b500", "#e6e600", "#ca3838" ], "seg1": "", "seg2": "", "x": 1091, "y": 523, "wires": [] }, { "id": "ee5bc723.594bf8", "type": "change", "z": "2ce0055.8743efa", "name": "Speed", "rules": [ { "t": "move", "p": "payload.kd", "pt": "msg", "to": "payload", "tot": "msg" }, { "t": "set", "p": "topic", "pt": "msg", "to": "torque\vehicle\speed", "tot": "str" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 721, "y": 683, "wires": [ [ "c3863c64.b8c2d", "5ef6a21e.75ce4c" ] ] }, { "id": "c3863c64.b8c2d", "type": "ui_gauge", "z": "2ce0055.8743efa", "name": "", "group": "ea9cdc42.4bbc8", "order": 0, "width": 0, "height": 0, "gtype": "gage", "title": "Speed", "label": "MPH", "format": "{{value}}", "min": 0, "max": "100", "colors": [ "#00b500", "#e6e600", "#ca3838" ], "seg1": "", "seg2": "", "x": 1061, "y": 683, "wires": [] }, { "id": "7ee21162.28cdb", "type": "ui_gauge", "z": "2ce0055.8743efa", "name": "", "group": "9b558fbc.438aa", "order": 3, "width": "2", "height": "3", "gtype": "wave", "title": "Mass Air Flow", "label": "g/s", "format": "{{value}}", "min": 0, "max": "100", "colors": [ "#00b500", "#e6e600", "#ca3838" ], "seg1": "", "seg2": "", "x": 1091, "y": 483, "wires": [] }, { "id": "536ae3f0.1980dc", "type": "ui_gauge", "z": "2ce0055.8743efa", "name": "", "group": "26a930d9.ff8c", "order": 0, "width": "3", "height": "3", "gtype": "gage", "title": "Barometer", "label": "units", "format": "{{value}}", "min": "800", "max": "1100", "colors": [ "#00b500", "#e6e600", "#ca3838" ], "seg1": "", "seg2": "", "x": 1071, "y": 403, "wires": [] }, { "id": "dfdc4763.7a8fd8", "type": "change", "z": "2ce0055.8743efa", "name": "Speed GPS", "rules": [ { "t": "move", "p": "payload.kff1001", "pt": "msg", "to": "payload", "tot": "msg" }, { "t": "set", "p": "topic", "pt": "msg", "to": "torque\phone\speed", "tot": "str" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 741, "y": 723, "wires": [ [ "535662ad.f1628c", "5ef6a21e.75ce4c" ] ] }, { "id": "535662ad.f1628c", "type": "ui_gauge", "z": "2ce0055.8743efa", "name": "", "group": "26a930d9.ff8c", "order": 0, "width": "3", "height": "3", "gtype": "gage", "title": "Speed GPS", "label": "MPH", "format": "{{value}}", "min": 0, "max": "100", "colors": [ "#00b500", "#e6e600", "#ca3838" ], "seg1": "", "seg2": "", "x": 1081, "y": 723, "wires": [] }, { "id": "5b8bdb33.d96134", "type": "ui_gauge", "z": "2ce0055.8743efa", "name": "", "group": "ea9cdc42.4bbc8", "order": 0, "width": "3", "height": "3", "gtype": "gage", "title": "Voltage", "label": "V", "format": "{{value}}", "min": "9", "max": "15", "colors": [ "#ff0000", "#008000", "#ff0000" ], "seg1": "13", "seg2": "14.3", "x": 1071, "y": 603, "wires": [] }, { "id": "b0123228.4a063", "type": "ui_gauge", "z": "2ce0055.8743efa", "name": "", "group": "9b558fbc.438aa", "order": 5, "width": "3", "height": "3", "gtype": "gage", "title": "Coolant Temp", "label": "F", "format": "{{value}}", "min": 0, "max": "250", "colors": [ "#00b500", "#e6e600", "#ca3838" ], "seg1": "", "seg2": "", "x": 1081, "y": 563, "wires": [] }, { "id": "913a6311.540cd", "type": "ui_gauge", "z": "2ce0055.8743efa", "name": "", "group": "9b558fbc.438aa", "order": 6, "width": "3", "height": "3", "gtype": "donut", "title": "Vacuum", "label": "", "format": "{{value}}", "min": "-20", "max": "0", "colors": [ "#00b500", "#e6e600", "#ca3838" ], "seg1": "", "seg2": "", "x": 1071, "y": 643, "wires": [] }, { "id": "cee9fa09.98b6d8", "type": "json", "z": "2ce0055.8743efa", "name": "", "pretty": false, "x": 751, "y": 198, "wires": [ [ "3b9be5d8.84e04a" ] ] }, { "id": "f2d748c5.d92da8", "type": "function", "z": "2ce0055.8743efa", "name": "Parse Date", "func": "var date = parseInt(msg.payload.time);nnmsg.payload = new Date(date* 1).toLocaleString([], { hour12: true}).slice(0, 19).replace('T', ' ');nreturn msg;", "outputs": 1, "noerr": 0, "x": 730, "y": 780, "wires": [ [ "88bfc628.5727b8" ] ] }, { "id": "88bfc628.5727b8", "type": "ui_text", "z": "2ce0055.8743efa", "group": "26a930d9.ff8c", "order": 1, "width": 0, "height": 0, "name": "Last Update", "label": "Updated", "format": "{{msg.payload}}", "layout": "row-spread", "x": 1070, "y": 780, "wires": [] }, { "id": "258b6f62.e82ab", "type": "function", "z": "2ce0055.8743efa", "name": "CtoF", "func": "var tempc = msg.payload;n tempf = tempc * 9/5 + 32;n tempf = Math.round(tempf * 10) / 10;n msg.payload = tempf;n return msg;", "outputs": 1, "noerr": 0, "x": 910, "y": 560, "wires": [ [ "b0123228.4a063", "5ef6a21e.75ce4c" ] ] }, { "id": "936bc33f.bcd27", "type": "change", "z": "2ce0055.8743efa", "name": "IntakeTemp", "rules": [ { "t": "move", "p": "payload.kf", "pt": "msg", "to": "payload", "tot": "msg" }, { "t": "set", "p": "topic", "pt": "msg", "to": "torque\vehicle\intake", "tot": "str" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 730, "y": 840, "wires": [ [ "a6732e91.735a9" ] ] }, { "id": "ee354102.4a8d8", "type": "ui_gauge", "z": "2ce0055.8743efa", "name": "", "group": "9b558fbc.438aa", "order": 4, "width": "3", "height": "3", "gtype": "gage", "title": "Intake Temp", "label": "F", "format": "{{value}}", "min": 0, "max": "250", "colors": [ "#00b500", "#e6e600", "#ca3838" ], "seg1": "", "seg2": "", "x": 1070, "y": 840, "wires": [] }, { "id": "a6732e91.735a9", "type": "function", "z": "2ce0055.8743efa", "name": "CtoF", "func": "var tempc = msg.payload;n tempf = tempc * 9/5 + 32;n tempf = Math.round(tempf * 10) / 10;n msg.payload = tempf;n return msg;", "outputs": 1, "noerr": 0, "x": 899, "y": 837, "wires": [ [ "ee354102.4a8d8", "5ef6a21e.75ce4c" ] ] }, { "id": "3b9be5d8.84e04a", "type": "change", "z": "2ce0055.84e04a", "name": "", "rules": [ { "t": "set", "p": "topic", "pt": "msg", "to": "torque\raw", "tot": "str" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 1034, "y": 202, "wires": [ [ "5ef6a21e.75ce4c" ] ] }, { "id": "5ef6a21e.75ce4c", "type": "mqtt out", "z": "2ce0055.8743efa", "name": "", "topic": "", "qos": "0", "retain": "true", "broker": "fee1fac2.261a88", "x": 1292, "y": 193, "wires": [] }, { "id": "97ab65f2.1a0728", "type": "function", "z": "2ce0055.8743efa", "name": "copy to json", "func": "msg.payload.json = JSON.stringify(msg.payload);nreturn msg;", "outputs": 1, "noerr": 0, "x": 770, "y": 100, "wires": [ [ "72347975.0e2688" ] ] }, { "id": "72347975.0e2688", "type": "function", "z": "2ce0055.8743efa", "name": "Create query in topic", "func": "var out = "INSERT INTO torque_json (timestamp,json)"nvar date = Number(msg.payload.time);nout = out + "VALUES ('" + new Date(date* 1).toISOString().slice(0, 19).replace('T', ' ') + "','" nout = out + String(msg.payload.json) + "');"n nmsg.topic=out;nnreturn msg;", "outputs": 1, "noerr": 0, "x": 960, "y": 100, "wires": [ [ "dba0040d.49d798" ] ] }, { "id": "dba0040d.49d798", "type": "mysql", "z": "2ce0055.8743efa", "mydb": "4cd2c8.ee368d38", "name": "", "x": 1300, "y": 100, "wires": [ [] ] }, { "id": "9b558fbc.438aa", "type": "ui_group", "z": "", "name": "Motor", "tab": "ac99e377.fdb83", "order": 1, "disp": true, "width": "9" }, { "id": "ea9cdc42.4bbc8", "type": "ui_group", "z": "", "name": "Van", "tab": "ac99e377.fdb83", "order": 2, "disp": true, "width": "6" }, { "id": "26a930d9.ff8c", "type": "ui_group", "z": "", "name": "Phone", "tab": "ac99e377.fdb83", "order": 3, "disp": true, "width": "6" }, { "id": "fee1fac2.261a88", "type": "mqtt-broker", "z": "", "broker": "mqtt.YOUR.SERVER", "port": "1883", "clientid": "nodered", "usetls": false, "compatmode": true, "keepalive": "60", "cleansession": true, "willTopic": "", "willQos": "0", "willPayload": "", "birthTopic": "", "birthQos": "0", "birthPayload": "" }, { "id": "4cd2c8.ee368d38", "type": "MySQLdatabase", "z": "", "host": "drobo5n.YOUR.SERVER", "port": "3306", "db": "openhab", "tz": "CST" }, { "id": "ac99e377.fdb83", "type": "ui_tab", "z": "", "name": "Torque", "icon": "fa-car", "order": 2 } ]
This flow is designed to:
- HTTP Input Node (
http in
): Listens for incoming HTTP GET requests at the/torque
endpoint. This is where Torque Pro will send data. - HTTP Response Node (
http response
): Immediately responds to Torque Pro with an “OK!” message. This is crucial as Torque Pro expects this confirmation to ensure data is being received. - Change Nodes (
change
): Each “change” node is responsible for extracting specific data points from the incoming payload (data from Torque Pro). For example, the “RPM” change node extracts the value associated withpayload.kc
(RPM PID in Torque Pro) and sets thetopic
for further processing. - UI Gauge Nodes (
ui_gauge
): These nodes create visual gauges in the Node-RED dashboard to display the extracted data in real-time. You can customize these gauges to represent various parameters like RPM, speed, throttle position, and more. - Function Nodes (
function
): Nodes like “CtoF” (Celsius to Fahrenheit) and “Parse Date” perform data transformations as needed. - MQTT Output Node (
mqtt out
): Optionally, the flow includes an MQTT output node, allowing you to publish the processed data to an MQTT broker for use in other applications or systems like home automation platforms (e.g., openHAB as mentioned in the original flow). - MySQL Node (
mysql
): Similarly optional, a MySQL node is included to demonstrate how you could log the raw JSON data to a database for historical analysis.
This flow provides a basic framework. You can customize it further to add more gauges, logging functionalities, alerts, or integrations with other systems.
Real-Time Vehicle Monitoring and Beyond
By combining a bluetooth OBD2 adapter, Android Torque Pro OBDII, and Node-RED, you create a powerful and flexible system for vehicle data monitoring. This setup enables you to:
- Create Custom Dashboards: Visualize the exact car parameters you are interested in with real-time updates.
- Log Data for Analysis: Record vehicle data for later review, performance analysis, or troubleshooting.
- Integrate with Home Automation: Use vehicle data to trigger actions in your smart home system based on driving conditions or car status.
- Gain Deeper Vehicle Insights: Understand your car’s performance and health in detail, going beyond basic dashboard indicators.
Start exploring the possibilities of bluetooth OBD2, Android Torque Pro, and Node-RED to unlock the hidden data within your vehicle and create innovative automotive projects.