| name | shiny-obd |
| description | Generate code using Shiny.Obd, an OBD-II vehicle communication library for .NET with command-object pattern, adapter auto-detection, and BLE + WiFi (TCP) + serial (USB/UART) transports |
| auto_invoke | true |
| triggers | ["obd","obd-ii","obd2","vehicle diagnostics","elm327","obdlink","IObdCommand","IObdConnection","IObdTransport","IObdDeviceScanner","ObdDiscoveredDevice","BleObdDeviceScanner","SerialObdTransport","SerialObdConfiguration","SerialObdDeviceScanner","SerialPortEnumerator","SerialPortInfo","AddShinyObdSerial","serial transport","usb obd","ttyUSB","WifiObdTransport","WifiObdConfiguration","WifiObdDeviceScanner","WifiObdEndpoint","AddShinyObdWifi","wifi transport","wifi obd","elm327 wifi","obdlink mx wifi","ObdCommand","ObdConnection","[Truncated]"] |
Shiny.Obd Skill
You are an expert in Shiny.Obd, a .NET library for communicating with vehicles through OBD-II adapters. It uses a command-object pattern with generic return types, pluggable transports (BLE, WiFi, serial), and adapter auto-detection for ELM327 and OBDLink (STN) adapters.
When to Use This Skill
Invoke this skill when the user wants to:
- Read vehicle data (speed, RPM, coolant temp, VIN, etc.) through OBD-II
- Create custom OBD commands with typed return values
- Connect to an OBD-II adapter over Bluetooth LE, WiFi or serial (USB/UART)
- Scan for / discover available OBD adapters
- Configure ELM327 or OBDLink adapter initialization
- Implement a custom transport (Android USB Host, J2534, a replay harness) for OBD communication
- Send raw AT commands to an OBD adapter
- Handle OBD response parsing and error handling
- Build a MAUI app with OBD integration
Library Overview
- Repository: https://github.com/shinyorg/obd
- Namespaces:
Shiny.Obd, Shiny.Obd.Ble, Shiny.Obd.Wifi, Shiny.Obd.Serial, Shiny.Obd.Commands, Shiny.Obd.Emulator
- NuGet:
Shiny.Obd (core), Shiny.Obd.Ble (BLE), Shiny.Obd.Wifi (WiFi/TCP), Shiny.Obd.Serial (USB/UART), Shiny.Obd.Emulator (+ .Ble) — be an adapter instead of reading one
- Targets:
net10.0 throughout
Core Types
IObdCommand — Command interface
Every OBD command implements this. T is the parsed result type.
public interface IObdCommand<T>
{
string RawCommand { get; }
T Parse(byte[] data);
}
ObdCommand — Base class for standard Mode/PID commands
Validates mode+PID response header, strips it, and delegates to ParseData.
public abstract class ObdCommand<T> : IObdCommand<T>
{
protected ObdCommand(byte mode, byte pid);
public byte Mode { get; }
public byte Pid { get; }
public virtual string RawCommand { get; }
protected abstract T ParseData(byte[] data);
}
IObdConnection — Connection interface
public interface IObdConnection : IAsyncDisposable
{
bool IsConnected { get; }
Task Connect(CancellationToken ct = default);
Task Disconnect();
Task<T> Execute<T>(IObdCommand<T> command, CancellationToken ct = default);
Task<string> SendRaw(string command, CancellationToken ct = default);
}
IObdTransport — Transport abstraction
public interface IObdTransport : IAsyncDisposable
{
bool IsConnected { get; }
Task Connect(CancellationToken ct = default);
Task Disconnect();
Task<string> Send(string command, CancellationToken ct = default);
}
Three implementations ship: BleObdTransport (Shiny.Obd.Ble), WifiObdTransport
(Shiny.Obd.Wifi) and SerialObdTransport (Shiny.Obd.Serial). All three also implement
IDisposable so a container can tear them down on its synchronous path.
IObdDeviceScanner — Device discovery
public interface IObdDeviceScanner
{
Task Scan(Action<ObdDiscoveredDevice> onDeviceFound, CancellationToken ct = default);
}
Cancel the token to stop scanning. Each discovered device invokes the callback.
ObdDiscoveredDevice — Discovered adapter
public class ObdDiscoveredDevice
{
public string Name { get; }
public string Id { get; }
public object NativeDevice { get; }
}
ObdConnection — ELM327 protocol handler
Two constructors:
ObdConnection(IObdTransport transport) — auto-detects adapter via ATI
ObdConnection(IObdTransport transport, IObdAdapterProfile profile) — uses explicit profile, skips detection
Properties:
DetectedAdapter — ObdAdapterInfo? with RawIdentifier (string) and Type (ObdAdapterType enum: Unknown, Elm327, ObdLink). Null when explicit profile used.
Handles:
- ELM327 hex response parsing (single-line and multi-frame CAN)
- Multi-frame framing: discards the leading byte-count line and the
N: frame index, then concatenates frames in order
- Spaced and unspaced hex alike (
0: 49 02 01 and 0:490201)
- Error detection: "NO DATA", "UNABLE TO CONNECT", "BUS INIT: ...ERROR", "?"
- Strips "SEARCHING..." and "BUS INIT" prefixes
IObdAdapterProfile — Adapter initialization
public interface IObdAdapterProfile
{
string Name { get; }
Task Initialize(IObdConnection connection, CancellationToken ct = default);
}
Built-in profiles:
Elm327AdapterProfile — ATZ, ATE0, ATL0, ATS1, ATH0, ATSP0
ObdLinkAdapterProfile — extends Elm327 with STFAC, ATCAF1
Standard Commands (StandardCommands static class)
| Property | Type | Command | Return | Parse Formula |
|---|
VehicleSpeed | VehicleSpeedCommand | 010D | int (km/h) | A |
EngineRpm | EngineRpmCommand | 010C | int (RPM) | ((A*256)+B)/4 |
CoolantTemperature | CoolantTemperatureCommand | 0105 | int (°C) | A-40 |
ThrottlePosition | ThrottlePositionCommand | 0111 | double (%) | (A*100)/255 |
FuelLevel | FuelLevelCommand | 012F | double (%) | (A*100)/255 |
CalculatedEngineLoad | CalculatedEngineLoadCommand | 0104 | double (%) | (A*100)/255 |
IntakeAirTemperature | IntakeAirTemperatureCommand | 010F | int (°C) | A-40 |
RuntimeSinceStart | RuntimeSinceStartCommand | 011F | TimeSpan | (A*256)+B seconds |
Vin | VinCommand | 0902 | string | skip count byte, ASCII decode |
Odometer | OdometerCommand | 01A6 | double (km) | ((A<<24)|(B<<16)|(C<<8)|D)/10 |
DistanceSinceCodesCleared | DistanceSinceCodesClearedCommand | 0131 | int (km) | (A*256)+B |
ControlModuleVoltage | ControlModuleVoltageCommand | 0142 | double (V) | ((A*256)+B)/1000 |
MassAirFlow | MassAirFlowCommand | 0110 | double (g/s) | ((A*256)+B)/100 |
EngineFuelRate | EngineFuelRateCommand | 015E | double (L/h) | ((A*256)+B)/20 |
EngineOilTemperature | EngineOilTemperatureCommand | 015C | int (°C) | A-40 |
FuelType | FuelTypeCommand | 0151 | byte | A (J1979 code) |
HybridBatteryLife | HybridBatteryLifeCommand | 015B | double (%) | (A*100)/255 |
MonitorStatus | MonitorStatusCommand | 0101 | MonitorStatus | see readiness section below |
MonitorStatusThisDriveCycle | MonitorStatusThisDriveCycleCommand | 0141 | MonitorStatus | same layout; byte A reserved |
FuelSystemStatus | FuelSystemStatusCommand | 0103 | FuelSystemStatus | enumerated loop state, A and optional B |
IntakeManifoldPressure | IntakeManifoldPressureCommand | 010B | int (kPa) | A |
BarometricPressure | BarometricPressureCommand | 0133 | int (kPa) | A |
TimingAdvance | TimingAdvanceCommand | 010E | double (° BTDC) | (A/2)-64 |
AmbientAirTemperature | AmbientAirTemperatureCommand | 0146 | int (°C) | A-40 |
RelativeAcceleratorPedalPosition | RelativeAcceleratorPedalPositionCommand | 015A | double (%) | (A*100)/255 |
CommandedThrottleActuator | CommandedThrottleActuatorCommand | 014C | double (%) | (A*100)/255 |
DistanceWithMilOn | DistanceWithMilOnCommand | 0121 | int (km) | (A*256)+B |
TimeRunWithMilOn | TimeRunWithMilOnCommand | 014D | TimeSpan | (A*256)+B minutes |
TimeSinceCodesCleared | TimeSinceCodesClearedCommand | 014E | TimeSpan | (A*256)+B minutes |
CalibrationId | CalibrationIdCommand | 0904 | IReadOnlyList<string> | 16-byte ASCII blocks, null-padded |
CommandedAirFuelRatio | CommandedAirFuelRatioCommand | 0144 | double (lambda) | 2/65536*((A*256)+B) |
CommandedEgr | CommandedEgrCommand | 012C | double (%) | (A*100)/255 |
EgrError | EgrErrorCommand | 012D | double (%) | (A*100/128)-100 |
CommandedEvaporativePurge | CommandedEvaporativePurgeCommand | 012E | double (%) | (A*100)/255 |
EvapVaporPressure | EvapVaporPressureCommand | 0132 | double (Pa) | signed ((A*256)+B)/4 |
AbsoluteEvapVaporPressure | AbsoluteEvapVaporPressureCommand | 0153 | double (kPa) | ((A*256)+B)/200 |
EvapVaporPressureWideRange | EvapVaporPressureWideRangeCommand | 0154 | double (Pa) | signed (A*256)+B |
DriverDemandTorque | DriverDemandTorqueCommand | 0161 | int (%) | A-125 |
ActualEngineTorque | ActualEngineTorqueCommand | 0162 | int (%) | A-125 |
ReferenceTorque | ReferenceTorqueCommand | 0163 | int (N·m) | (A*256)+B |
EnginePercentTorqueData | EnginePercentTorqueDataCommand | 0164 | EnginePercentTorqueData | five points, each X-125 |
FuelPressure | FuelPressureCommand | 010A | int (kPa) | A*3 |
FuelRailPressure | FuelRailPressureCommand | 0122 | double (kPa) | 0.079*((A*256)+B) |
FuelRailGaugePressure | FuelRailGaugePressureCommand | 0123 | int (kPa) | 10*((A*256)+B) |
FuelRailAbsolutePressure | FuelRailAbsolutePressureCommand | 0159 | int (kPa) | 10*((A*256)+B) |
EthanolFuelPercent | EthanolFuelPercentCommand | 0152 | double (%) | (A*100)/255 |
AbsoluteLoadValue | AbsoluteLoadValueCommand | 0143 | double (%) | ((A*256)+B)*100/255 — not capped at 100 |
WarmUpsSinceCodesCleared | WarmUpsSinceCodesClearedCommand | 0130 | int (count) | A |
RelativeThrottlePosition | RelativeThrottlePositionCommand | 0145 | double (%) | (A*100)/255 |
FuelInjectionTiming | FuelInjectionTimingCommand | 015D | double (°) | (((A*256)+B)/128)-210 |
EngineRunTime | EngineRunTimeCommand | 017F | EngineRunTime | support byte, then 3 x 4-byte second counters |
ObdStandards | ObdStandardsCommand | 011C | byte | raw J1979 code; name via ObdStandards.Describe |
CalibrationVerificationNumber | CalibrationVerificationNumberCommand | 0906 | IReadOnlyList<string> | 4-byte blocks as hex strings |
EcuName | EcuNameCommand | 090A | string | 20-byte ASCII, null-padded |
⚠️ PIDs 4D/4E are minutes; RuntimeSinceStart (011F) is seconds. Do not copy one
formula onto the other.
⚠️ Do not confuse the four position PIDs. ThrottlePosition (0111) is absolute throttle plate