SkillAgentSearch skills...

ObdMetrics

OBD Metrics is a Java OBD2 framework that is intended to simplify communication with OBD2 adapters like ELM327 clones. The goal of the implementation is to provide a set of useful functions that can be a foundation for future OBD2 related applications

Install / Use

/learn @tzebrowski/ObdMetrics
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

OBD Metrics

CI codecov Maven Central

About

OBD Metrics stands out as a well-architected and adaptable framework for Java developers interested in vehicle diagnostics and telemetry. Its emphasis on configurability and dynamic data processing makes it suitable for a wide range of applications, from simple data logging to complex diagnostic tools.

Alt text

Table of Contents

Supported Use-Cases & Adapters

Supported use-cases:

  • Collecting vehicle telemetry data (Metrics)
  • Reading Vehicle Metadata (e.g., VIN)
  • Reading and clearing Diagnostic Trouble Codes (DTC)

Supported adapters and protocols:

  • ELM327-based adapters: Fully compatible with the ELM327 AT command set.
  • STNxxxx-based adapters: Utilizes the advanced ST command set available in the STNxxxx device family (more info at scantool.net).

Ecosystem and Tooling

  • MyGiulia: A dedicated Android app for Alfa Romeo owners to visualize their vehicle's telemetry data.
  • JeepAA: An Android application dedicated to Jeep vehicles for visualizing vehicle telemetry data.
  • CanSniffer: A lightweight application used to scan the CAN Bus for further integration with SavvyCAN.
  • OBD Metrics Demo: A demonstration project showcasing the usage of the ObdMetrics library.

Key Features

Configurable PID Definitions

PIDs (Parameter IDs) are defined externally using JSON schemas, allowing for easy customization and support for various vehicle manufacturers without altering the core code.</br> As a result of this design decision, PIDs are not required to be an integral part of the framework and can instead be provided by an external party.</br> Within single resource file PIDs are divided into distinct groups, following categories are available:

  • capabilities - Supported PIDs category
  • dtcRead - Diagnostic trouble code category
  • dtcClear - Diagnostic trouble code category
  • metadata - Metadata PIDs category. PIDs which are read just once during session with the Adapter
  • livedata - Livedata PIDs category. PIDs which are read frequently during session with the Adapter
  • routine - Routines PIDs category. PIDs which are executed on demand and might alter vehicle component behavior, e.g: Turn dashboard illumination on.

During single session the framework is able to work with multiple resource files which might be specific for different automotive manufacturers.</br> Generic list of PIDs can be found here

Configuration might looks like the one below example.

{	
	"capabilities": [
		{
			"id": "21000",
			"mode": "01",
			"pid": "00",
			"description": "Supported PIDs 00",
			"codecClass": "org.obd.metrics.command.SupportedPIDsCodec"
		}
	],
	"dtcRead": [
		{
			
			"id": "27000",
			"mode": "19",
			"pid": "020D",
			"description": "DTC Read",
			"successCode": "5902CF",
			"codecClass": "org.obd.metrics.command.dtc.DiagnosticTroubleCodeCodec"
		}
	],
	
	"dtcClear": [
		{
			"id": "37000",
			"mode": "14",
			"pid": "FFFFFF",
			"description": "DTC Clear",
			"successCode": "54",
			"codecClass": "org.obd.metrics.command.dtc.DiagnosticTroubleCodeClearCodec"
		}
	],
	"metadata": [
		{
			
			"id": "17001",
			"mode": "22",
			"pid": "F190",
			"description": "Vehicle Identification Number"
			"codecClass": "org.obd.metrics.command.meta.HexCodec"
		},
	],
	"livedata": [
		{
			"priority": 0,
			"id": "7001",
			"mode": "22",
			"pid": "195A",
			"length": 2,
			"description": "Boost\nPressure",
			"min": 0,
			"max": 2800,
			"units": "mbar",
			"formula": "(A*256+B)"
		},
	],
	
	"routine": [
		{
			"id": "10002",
			"mode": "2F",
			"pid": "55720308",
			"description":"Turn dashboard illumination on",
			"overrides" : {
				"canMode": "INSTRUMENT_PANEL"
			}
		}
	],	
}

Sniffer mode

The library supports sniffer mode and can grab data from the Can Bus and save in the format readable by SavvyCAN.</br> Example usage: CanSniffer

Dynamic Formula Evaluation

The library supports JavaScript-based formulas to compute PID values from raw data, reducing the need for custom Java decoders. The formula can include additional JavaScript functions like Math.floor .

Example for Measured Boost Pressure PID

{ 
	"pid": "195A",
	"length": 2,
	"formula": "(A*256+B) | 0",
}

Given that 62195A09AA hex data is received from the ECU for above PID, FW implementation converts it (splitting by two characters) into decimal numbers identified by two parameters A and B (PID length here is equal 2). Received data 62195A 09AA is later passed to the formula as follows:

  • A = 09 = 9
  • B = AA = 170

Finally this results as 9 * 256 + 170 = 2474. The value 2474 is what FW emits for later processing.

Signed Hexadecimal Handling

It can interpret signed hexadecimal numbers, which is essential for accurately processing certain sensor data

By default framework interprets all hex as unsigned numbers. In order to process negative numbers, property signed=true must be set true within the PID definition. This property tells framework to decoded hex value using special rules. Moreover, calculation formula must contains dedicated statement: if (typeof X === 'undefined')... to handle negative number which might be received under X parameter, see example bellow:

Definition

{
	"description": "Measured Intake\nValve Crossing",
	"signed": true,
	"formula": "if (typeof X === 'undefined') X =(A*256+B); parseFloat((X * 0.0078125).toFixed(3))"
},

External Parameters in Formulas

Formulas can incorporate external parameters, enabling dynamic calculations based on factors like fuel tank size.

Framework allows to pass external parameters into PID formula. Through this, evaluation formula can be modified dynamically based on external factors.

In this example unit_tank_size is passed as the external parameter.

{
	"priority": 3,
	"id": "7040",
	"mode": "22",
	"pid": "1001",
	"length": 1,
	"description": "Fuel Level\n(Liters)",
	"min": "0",
	"max": "100",
	"units": "L",
	"formula": "parseFloat(((A*0.3921568)/100 * unit_tank_size).toFixed(1))"	
},
final Adjustments optional = Adjustments.builder()
		.formuilaExternalParams(FormulaExternalParams.builder().param("unit_tank_size",tankSize).build())
		.build();

Querying multiple ECUs within the same communication session

The framework is able to speak with multiple ECU with the same communication session. Once sessions established ObdMetrics queries different modules like TCU, ECU without additional overhead. Moreover FW it's able to work either with CAN 11 bit or CAN 29 bit headers.


final Pids pids = Pids
        .builder()
        .resource(contextClassLoader.getResource("mode01.json"))
        .resource(contextClassLoader).getResource("alfa.json")).build();
        .build();

final Init init = Init.builder()
        .delay(1000)
        .header(Header.builder().mode("22").header("DA10F1").build())
        .header(Header.builder().mode("01").header("DB33F1").build())
        .protocol(Protocol.CAN_29)
        .sequence(DefaultCommandGroup.INIT).build();

final Workflow workflow = Workflow
        .pids(pids)
workflow.start(BluetoothConnection.openConnection(), query, init, optional);
CAN headers overrides

The framework allows to override CAN headers just for specific PID's, and adjust it at runtime.

{
	"priority": 0,
	"id": "7029",
	"mode": "22",
	"pid": "051A",
	"length": 1,
	"description": "Gear Engaged",
	"min": "-1",
	"max": "10",
	"units": "",
	"type": "INT",
	"formula": "x=A; if (x==221) {x=0 } else if (x==238) {x=-1} else { x=A/17} x",
	"overrides" : {
		"canMode": "555",
		"batchEnabled": false
	}
},

final Init init = Init.builder()
  .header(Header.builder().mode("22").header("DA10F1").build())
  .header(Header.builder().mode("01").header("DB33F1").build())
   //overrides CAN mode
  .header(Header.builder().mode("555").header("DA18F1").build()) 
  .protocol(Protocol.CAN_29)
  .build();

Testability

As part of the solution there is available dedicated module name ObdMetricsTest which exposes set of interfaces like: CodecTest which allows to write clean PIDs tests with the focus on business aspects of its development.


interface MultiJet_2_2_Test extends CodecTest {

	final String RESOURCE_NAME = "giulia_2_2_multijet.json";

	@Override
	default String getPidFile() {
		return RESOURCE_NAME;
	}
}

public class AirTempMafTest implements MultiJet_2_2_Test {

	@ParameterizedTest
	@CsvSource(value = { 
			"62193F0000=-40",
			"62193F1100=47",
	}, delimiter = '=')
	public void test(String input, Integer expected) {
		assertEquals(input, expected);
	}
}

Custom codecs

The framework provides couple of ways of decoding ECU messages. One and the

Related Skills

View on GitHub
GitHub Stars26
CategoryDevelopment
Updated2d ago
Forks2

Languages

Java

Security Score

95/100

Audited on Mar 22, 2026

No findings