Skip to main content

shiny-obd

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

Ir para a instalação

Informações da origem

Repositório
shinyorg/obd
Última atividade na origem
4 de setembro de 2026 às 01:20
Idioma detectado do SKILL.md
inglês
Estrelas
6
Forks
0

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
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<T> — Command interface Every OBD command implements this. `T` is the parsed result type. ```csharp public interface IObdCommand<T> { string RawCommand { get; } T Parse(byte[] data); } ``` ### ObdCommand<T> — Base class for standard Mode/PID commands Validates mode+PID response header, strips it, and delegates to `ParseData`. ```csharp 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; } // "{Mode:X2}{Pid:X2}" protected abstract T ParseData(byte[] data); // data after header } ``` ### IObdConnection — Connection interface ```csharp 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 ```csharp 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 ```csharp 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 ```csharp public class ObdDiscoveredDevice { public string Name { get; } // e.g. "OBDLink MX+" public string Id { get; } // unique identifier (BLE UUID, IP, etc.) public object NativeDevice { get; } // IPeripheral for BLE, IPEndPoint for WiFi, etc. } ``` ### 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. - `Protocol` — `string?`, settable before `Connect`. The ELM protocol number to pin with `ATSP` instead of searching. Ignored when an explicit profile was supplied — pass it to the profile's constructor instead. - `NegotiatedProtocol` — `string?`, the protocol the adapter reports it is on. Refreshed by `Connect` and by `RefreshNegotiatedProtocol()`. Methods: - `RefreshNegotiatedProtocol(CancellationToken)` — re-reads `ATDPN` and updates `NegotiatedProtocol`. **Always pin the protocol on reconnect.** `ATSP0` does not choose a protocol — it defers the choice to the first command that needs the bus, and that command pays the whole ELM search: seconds of it, routinely longer than a command timeout. `ATZ` discards the result, so an unpinned adapter pays it again on every reconnect. Generate this pattern whenever an app reconnects to a remembered adapter: ```csharp var connection = new ObdConnection(transport) { Protocol = savedProtocol }; // null on a first run await connection.Connect(); // ⚠️ Ask AFTER something has needed the bus. ATSP0 has chosen nothing at the end of Connect, so // asking there reports null and the app never learns a number to save. await connection.Execute(new SupportedPidsCommand(0x00)); savedProtocol = await connection.RefreshNegotiatedProtocol(); ``` A stale pin is safe — it is verified with mode 01 during initialization and dropped for a search when nothing answers. 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 ```csharp public interface IObdAdapterProfile { string Name { get; } Task Initialize(IObdConnection connection, CancellationToken ct = default); } ``` Built-in profiles: - `Elm327AdapterProfile(string? protocol = null)` — ATZ, ATE0, ATL0, ATS1, ATH0, then `ATSP{protocol}` or `ATSP0` - `ObdLinkAdapterProfile(string? protocol = null)` — STFAC, then the Elm327 sequence, then ATCAF1 `ATZ` is sent **once** per connect, by the profile. Auto-detection probes with `ATI` alone and does not reset first. `STFAC` restores factory defaults, so it precedes the ELM327 configuration rather than following it. ## 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` |
Ver no GitHub
Este SKILL.md e muito grande, entao o SkillsMP mostra aqui apenas a primeira secao. Ver no GitHub