diff --git a/.github/skills/README.md b/.github/skills/README.md new file mode 100644 index 0000000000..f2afcd7d6c --- /dev/null +++ b/.github/skills/README.md @@ -0,0 +1,169 @@ +# GitHub Copilot AI Skills for .NET IoT Repository + +This directory contains AI skills that help GitHub Copilot agents perform specialized tasks in the .NET IoT repository. Skills are automatically loaded by Copilot based on context and task requirements. + +## Available Skills + +### 1. [add-device-binding](./add-device-binding/SKILL.md) +**Purpose:** Guide for adding new IoT device bindings following repository conventions. + +**When to use:** +- Adding a new sensor, display, motor, or other hardware device binding +- Creating driver code under `src/devices/` +- Ensuring device follows .NET IoT patterns + +**Key features:** +- Device structure setup +- API design patterns (TryRead* methods, UnitsNet types) +- Resource management (IDisposable) +- Documentation requirements +- Sample application guidelines + +### 2. [debug-build-issues](./debug-build-issues/SKILL.md) +**Purpose:** Diagnose and resolve build, compilation, and SDK issues. + +**When to use:** +- Build fails with SDK resolution errors +- Package restore failures +- Long-running build timeouts +- CI/CD pipeline failures +- Memory issues during build + +**Key features:** +- Azure DevOps feeds troubleshooting +- Common build issues and solutions +- Alternative build approaches +- Expected timing reference +- Log location guidance + +### 3. [fix-api-conventions](./fix-api-conventions/SKILL.md) +**Purpose:** Ensure device bindings follow repository API conventions. + +**When to use:** +- Reviewing device binding code +- Fixing API design issues +- Code review identifies convention violations +- Modernizing older device bindings + +**Key features:** +- API naming conventions +- UnitsNet usage patterns +- Constructor design guidelines +- Resource management patterns +- Error handling best practices + +### 4. [update-device-documentation](./update-device-documentation/SKILL.md) +**Purpose:** Create and maintain comprehensive device documentation. + +**When to use:** +- Adding documentation for new device +- Updating existing device docs +- Adding wiring diagrams +- Improving code examples +- Adding XML documentation + +**Key features:** +- README structure and content +- Wiring information templates +- XML documentation patterns +- Code example guidelines +- Markdown linting compliance + +### 5. [hardware-abstraction-check](./hardware-abstraction-check/SKILL.md) +**Purpose:** Verify proper hardware abstraction and cross-platform compatibility. + +**When to use:** +- Reviewing new device binding code +- Code contains platform-specific APIs +- Ensuring cross-platform compatibility +- Validating no OS dependencies introduced + +**Key features:** +- Anti-pattern detection (P/Invoke, hardcoded paths) +- System.Device.* abstraction usage +- Automated compliance checks +- Cross-platform testing guidance +- Platform-specific code guidelines + +## How Skills Work + +GitHub Copilot agents automatically discover and use these skills when: +1. Task matches skill description +2. Agent needs domain-specific guidance +3. Context indicates skill would be helpful + +Skills are defined using the standard GitHub Copilot Agent Skills format: +- Each skill has its own directory +- `SKILL.md` file contains metadata (YAML frontmatter) and instructions +- Skills can include supporting files (scripts, examples, templates) + +## Skill Format + +Each `SKILL.md` follows this structure: + +```markdown +--- +name: skill-name +description: Brief description of what the skill does +license: MIT +--- + +## Purpose +What the skill helps with + +## When to Use This Skill +Specific scenarios for using the skill + +## Instructions +Detailed step-by-step guidance + +## Examples +Code examples and demonstrations + +## References +Links to related documentation +``` + +## Creating New Skills + +To add a new skill: + +1. Create directory: `.github/skills//` +2. Create `SKILL.md` with proper frontmatter +3. Include clear instructions and examples +4. Add references to relevant documentation +5. Test skill with Copilot agent + +## Skill Development Guidelines + +When creating or updating skills: + +- **Be specific:** Provide concrete, actionable instructions +- **Include examples:** Show code examples and commands +- **Reference documentation:** Link to relevant docs and conventions +- **Keep focused:** Each skill should address one specific domain +- **Test thoroughly:** Ensure instructions work as documented + +## Relationship to Copilot Instructions + +These skills complement the main [copilot-instructions.md](../copilot-instructions.md): + +- **Copilot Instructions:** General guidance loaded for all tasks +- **Skills:** Specialized, task-specific guidance loaded on-demand + +Skills should reference and build upon copilot instructions, not duplicate them. + +## Contributing + +When adding new skills, consider: +- Common issues encountered in the repository +- Repetitive tasks that need guidance +- Complex procedures that benefit from step-by-step instructions +- Platform-specific knowledge (build system, conventions, etc.) + +## References + +- [GitHub Copilot Agent Skills Documentation](https://docs.github.com/en/copilot/concepts/agents/about-agent-skills) +- [.NET IoT Copilot Instructions](../copilot-instructions.md) +- [.NET IoT Contributing Guide](../../Documentation/CONTRIBUTING.md) +- [Device Conventions](../../Documentation/Devices-conventions.md) diff --git a/.github/skills/add-device-binding/SKILL.md b/.github/skills/add-device-binding/SKILL.md new file mode 100644 index 0000000000..994c54d35e --- /dev/null +++ b/.github/skills/add-device-binding/SKILL.md @@ -0,0 +1,250 @@ +--- +name: add-device-binding +description: Guide for adding new IoT device bindings to the repository following .NET IoT conventions +license: MIT +--- + +## Purpose + +This skill helps Copilot agents create new IoT device bindings (sensors, displays, motors, etc.) that follow the repository's established patterns and conventions for System.Device.Gpio and Iot.Device.Bindings. + +## When to Use This Skill + +- Developer requests adding a new device binding for a sensor, display, motor, or other hardware component +- Creating a new driver under `src/devices/` +- Need to ensure the device follows .NET IoT conventions and patterns + +## Instructions + +### 1. Device Structure Setup + +Create the following directory structure under `src/devices/`: + +```text +src/devices// +├── .csproj +├── .cs (main class) +├── samples/ +│ ├── .Samples.csproj +│ └── Program.cs +├── tests/ (if applicable) +│ └── .Tests.csproj +└── README.md +``` + +### 2. Constructor & Dependencies + +- Accept hardware transports from the caller: `I2cDevice`, `SpiDevice`, `GpioController`, pin numbers, bus addresses +- Provide sensible defaults (e.g., common I2C addresses like 0x48) +- **Never hardcode board-specific details** (e.g., GPIO pin numbers, bus IDs, or hardware paths like `/dev/i2c-1`) +- Avoid singletons and global state + +**Example Constructor:** + +```csharp +public Bmp280(I2cDevice i2cDevice, I2cAddress address = I2cAddress.Primary) +{ + _i2cDevice = i2cDevice ?? throw new ArgumentNullException(nameof(i2cDevice)); + _address = address; +} +``` + +### 3. API Design Patterns + +- Use **`TryRead*` methods** that return `bool` and set `out` parameters for sensor readings + + ```csharp + public bool TryReadTemperature(out Temperature temperature) + ``` + + +- Use **UnitsNet** types for physical quantities (Temperature, Pressure, Length, etc.) +- Properties for values that don't change between calls +- Methods for values that may change +- Use enums/flags for registers, modes, scales +- Keep synchronous APIs predictable; only use `async` if genuine I/O concurrency needed + +### 4. Resource Management (CRITICAL) + +- **MUST implement `IDisposable`** +- Dispose hardware resources (I2C/SPI devices, GPIO pins) in the Dispose method +- **MUST provide a way for caller to retain ownership** of transports (e.g., via `shouldDispose` parameter, default true) +- If transport is passed by caller, only dispose it when `shouldDispose` is true or when device creates it +- Design for failure: ensure exceptions leave device in consistent state + +**Example Disposal:** + +```csharp +protected virtual void Dispose(bool disposing) +{ + if (_disposed) + return; + + if (disposing) + { + _i2cDevice?.Dispose(); + _i2cDevice = null; + } + + _disposed = true; +} +``` + +### 5. Error Handling + +- Guard against misuse with `ArgumentException`/`ArgumentNullException` +- For transient hardware issues, throw or return `false` in `Try*` patterns +- **Never return bogus data silently** +- Validate checksums/status bits where appropriate + +### 6. Documentation Requirements + +- XML documentation comments on all public APIs +- **Include units** in comments (°C, Pa, %RH, lux, etc.) +- Document parameter ownership: caller owns the transport unless `shouldDispose` parameter allows device to take ownership +- **README.md** must follow these conventions: + +#### README Title (First Heading) + +- Format: `# DeviceName - Brief functional description` +- Keep title short and descriptive (appears in device listings) +- Use device's official name/model number +- Brief description states what the device does (e.g., "Digital Temperature Sensor", "3 Axis Digital Compass") +- Examples: + - `# BMP180 - barometer, altitude and temperature sensor` + - `# HMC5883L - 3 Axis Digital Compass` + - `# ADS1115 - Analog to Digital Converter` + +#### Documentation Section (Second Heading) + +- **Must be `## Documentation`** (exact name) +- First item should **always link to official datasheet/documentation** +- Format: `[Datasheet](URL)` or `Product [datasheet](URL)` +- Additional items can list supported devices, features, or related docs +- Examples: + + ```markdown + ## Documentation + + - HMC5883L [datasheet](https://example.com/datasheet.pdf) + ``` + +#### Required Sections + +- `## Usage` - Code examples demonstrating typical usage +- Optional: `## Binding Notes` - Implementation-specific details +- Optional: Circuit diagrams, wiring instructions, sensor images + +### 7. Sample Application + +Create a runnable sample in `samples/Program.cs`: + +- Keep it simple and readable +- Demonstrate typical usage patterns +- Show proper disposal (using statements) +- Handle exceptions gracefully +- Don't hardcode board-specific paths + +**Example Sample:** + +```csharp +using System; +using System.Device.I2c; +using Iot.Device.Bmp280; + +using I2cDevice i2cDevice = I2cDevice.Create(new I2cConnectionSettings(1, Bmp280.DefaultI2cAddress)); +using Bmp280 sensor = new(i2cDevice); + +while (true) +{ + if (sensor.TryReadTemperature(out Temperature temp)) + { + Console.WriteLine($"Temperature: {temp.DegreesCelsius:F2} °C"); + } + Thread.Sleep(1000); +} +``` + +### 8. Cross-Platform Considerations + +- Use **System.Device.*** abstractions exclusively +- No P/Invoke or OS-specific syscalls +- No hardcoded paths to `/sys` or `/proc` +- If a feature is platform-only, guard it and fail gracefully + +### 9. Testing + +- Add unit tests for logic that can be isolated from hardware +- Focus on: framing/parsing functions, calculations, protocol logic +- Hardware-dependent tests should be clearly marked +- Ensure samples compile even without hardware + +### 10. Build Validation + +After creating the device: + +```bash +cd src/devices/ +../../../dotnet.sh build + +cd samples +../../../../dotnet.sh build +``` + +## Review Checklist + +Before completing: + +- [ ] Public API follows repo naming/style +- [ ] Members are XML-documented with units (°C, Pa, %RH, lux, etc.) +- [ ] Constructor accepts caller-provided transports +- [ ] Implements `IDisposable`; all hardware resources released +- [ ] Uses enums/consts for registers/addresses +- [ ] Validates timing and checksums +- [ ] `Try*` methods return accurate success/failure +- [ ] Adds sample application +- [ ] Adds device README with wiring info +- [ ] Adds tests for hardware-independent logic +- [ ] Builds successfully without errors +- [ ] No platform-specific code or P/Invoke + +## Examples + +### Good Device Binding Example + +```csharp +public class Dht22 : IDisposable +{ + private GpioController? _controller; + private readonly int _pin; + private readonly bool _shouldDispose; + + public Dht22(int pin, GpioController? controller = null, bool shouldDispose = true) + { + _pin = pin; + _shouldDispose = shouldDispose || controller is null; + _controller = controller ?? new GpioController(); + } + + public bool TryReadTemperature(out Temperature temperature) + { + // Implementation that validates data and checksums + } + + public void Dispose() + { + if (_shouldDispose) + { + _controller?.Dispose(); + } + _controller = null; + } +} +``` + +## References + +- [Device Conventions](../../../Documentation/Devices-conventions.md) +- [Contributing Guidelines](../../../Documentation/CONTRIBUTING.md) +- [Copilot Instructions](../../copilot-instructions.md) +- [UnitsNet Library](https://github.com/angularsen/UnitsNet) diff --git a/.github/skills/debug-build-issues/SKILL.md b/.github/skills/debug-build-issues/SKILL.md new file mode 100644 index 0000000000..a74c65122a --- /dev/null +++ b/.github/skills/debug-build-issues/SKILL.md @@ -0,0 +1,288 @@ +--- +name: debug-build-issues +description: Guide for diagnosing and resolving build, compilation, and SDK issues in the .NET IoT repository +license: MIT +--- + +## Purpose + +This skill helps Copilot agents diagnose and fix build issues in the .NET IoT repository, which has specific requirements around Azure DevOps feeds, .NET SDK versions, and lengthy build times. + +## When to Use This Skill + +- Build fails with SDK resolution errors +- Package restore failures +- Compilation errors after making changes +- Long-running build timeouts +- CI/CD pipeline failures +- Memory issues during build + +## Instructions + +### 1. Understanding Build Requirements + +**CRITICAL Prerequisites:** + +- Azure DevOps feeds access required for full builds +- .NET 8.0.300 SDK (specified in `global.json`) +- Build takes 30-45 minutes normally - **NEVER CANCEL** +- Test runs take 15-30 minutes + +### 2. Common Build Issues and Solutions + +#### SDK Resolution Errors + +**Symptom:** `Could not resolve SDK Microsoft.DotNet.Arcade.Sdk` + +**Diagnosis:** + +```bash +# Check SDK version +./dotnet.sh --version + +# Verify global.json +cat global.json +``` + +**Solutions:** + +1. Azure DevOps feeds are inaccessible (network restriction) + - Document as known limitation + - Try alternative build approaches (see below) +2. SDK version mismatch + - Ensure .NET 8.0.300 SDK is installed + - `./dotnet.sh --version` will auto-download if needed + +#### Package Not Found Errors + +**Symptom:** NuGet package restore failures + +**Diagnosis:** + +```bash +# Check NuGet sources +cat NuGet.config + +# Check network connectivity to Azure DevOps +# (feeds are at Azure DevOps, not public NuGet) +``` + +**Solutions:** + +1. Verify NuGet.config has correct sources +2. Check if Azure DevOps feeds are accessible +3. Try clearing NuGet cache: `dotnet nuget locals all --clear` + +#### Memory Issues During Build + +**Symptom:** Out of memory errors, process killed + +**Solutions:** + +```bash +# Disable parallel builds +./build.sh --restore --build /maxCpuCount:1 + +# Or build smaller subsets +cd src/devices/Bmp280 +../../../dotnet.sh build +``` + +#### Timeout Issues + +**Symptom:** Build appears hung or times out + +**Important:** Builds legitimately take 30-45 minutes + +- Set timeout to **60+ minutes** for full builds +- Set timeout to **45+ minutes** for test runs +- Use `initial_wait` parameter appropriately in bash commands + +### 3. Alternative Build Approaches + +When Azure DevOps feeds are inaccessible: + +#### Build Individual Samples + +```bash +# Samples use public NuGet packages +cd samples/led-blink +../../dotnet.sh build +``` + +#### Test Basic IoT Functionality + +```bash +./dotnet.sh new console -o test-project +cd test-project +./dotnet.sh add package System.Device.Gpio +./dotnet.sh add package Iot.Device.Bindings +./dotnet.sh build && ./dotnet.sh run +``` + +#### Build Individual Device Bindings + +```bash +cd src/devices/ +../../../dotnet.sh build +``` + +### 4. Build Commands by Scenario + +#### Full Repository Build + +```bash +# Use this for complete validation +./build.sh --restore --build --configuration Release +# Expected time: 30-45 minutes +# Timeout setting: 60+ minutes +``` + +#### Build with Packages + +```bash +# For testing distribution +./build.sh --restore --build --pack --configuration Release +``` + +#### Run Tests + +```bash +# All tests +./build.sh --test --configuration Release +# Expected time: 15-30 minutes + +# Specific test project +./dotnet.sh test src/devices/Dhtxx/tests/ +``` + +#### Individual Device Build + +```bash +cd src/devices/Dhtxx +../../../dotnet.sh build +# Expected time: 1-3 minutes +``` + +### 5. Debugging Compilation Errors + +#### After Code Changes + +```bash +# Build the specific project you changed +cd src/devices/ +../../../dotnet.sh build + +# Check for warnings/errors in output +# Fix compiler warnings - they're required by analyzers +``` + +#### Check for Common Issues + +```bash +# Verify .editorconfig compliance +# Build will show analyzer warnings + +# Check for using directive issues +# Ensure proper namespace imports + +# Validate nullability +# Code should be null-safe with proper annotations +``` + +### 6. CI/CD Pipeline Issues + +When GitHub Actions workflows fail: + +```bash +# Check workflow status +gh run list --limit 5 + +# View specific run logs +gh run view --log + +# Common CI issues: +# - Markdown linting failures +# - Build timeouts (increase timeout) +# - Test failures (check if pre-existing) +``` + +#### Markdown Linting + +```bash +npm install -g markdownlint-cli +markdownlint -c .markdownlint.json . +``` + +### 7. Log Locations + +When builds fail, check logs: + +- Build logs: `artifacts/log/{Configuration}/` +- Test results: Published in CI artifacts +- Individual project logs: In project's bin/obj folders + +### 8. Cross-Platform Build Issues + +#### Linux vs Windows Issues + +- Check for platform-specific files: `*.Linux.cs` vs `*.Windows.cs` +- Verify configurations in Visual Studio +- Use `System.Device.*` abstractions, not OS-specific APIs + +### 9. Diagnostic Commands + +```bash +# Check repository status +git --no-pager status +git --no-pager diff + +# Verify .NET installation +./dotnet.sh --version +./dotnet.sh --info + +# Check for stale build artifacts +ls -la artifacts/ + +# Clean build +rm -rf artifacts/ +./build.sh --restore --build --configuration Release + +# Check for platform abstraction violations +grep -r "Environment.OSVersion\|DllImport\|P/Invoke" src/devices// +``` + +### 10. When to Escalate + +Document and inform user when: + +- Azure DevOps feeds are confirmed inaccessible (network policy) +- Build requires infrastructure changes +- Pre-existing test failures unrelated to changes +- Issues require maintainer intervention + +## Troubleshooting Workflow + +1. **Identify the error** - Read error messages carefully +2. **Check prerequisites** - Verify SDK version, network access +3. **Try minimal build** - Build just the affected component +4. **Check for pre-existing issues** - Build before your changes +5. **Clear and rebuild** - Remove artifacts and try clean build +6. **Use alternative approaches** - Try sample builds if main build blocked +7. **Document limitations** - Note if Azure DevOps access is issue + +## Expected Timing Reference + +| Command | Expected Time | Timeout Setting | +|---------|---------------|-----------------| +| `./build.sh --restore --build` | 30-45 minutes | 60+ minutes | +| `./build.sh --test` | 15-30 minutes | 45+ minutes | +| Individual device build | 1-3 minutes | 10 minutes | +| Sample project build | 30-60 seconds | 5 minutes | +| Full CI pipeline | 45-60 minutes | 90+ minutes | + +## References + +- [Copilot Instructions - Build Section](../../copilot-instructions.md#bootstrap-and-build-process) +- [Contributing Guidelines](../../../Documentation/CONTRIBUTING.md) +- [Azure Pipelines Config](../../../azure-pipelines.yml) diff --git a/.github/skills/fix-api-conventions/SKILL.md b/.github/skills/fix-api-conventions/SKILL.md new file mode 100644 index 0000000000..2dfc91c6b6 --- /dev/null +++ b/.github/skills/fix-api-conventions/SKILL.md @@ -0,0 +1,392 @@ +--- +name: fix-api-conventions +description: Guide for ensuring device bindings follow .NET IoT API conventions and design patterns +license: MIT +--- + +## Purpose + +This skill helps Copilot agents review and fix device bindings to ensure they follow the repository's established API conventions, naming patterns, and design principles. + +## When to Use This Skill + +- Reviewing existing device binding code for convention compliance +- Fixing API design issues in device bindings +- Code review identifies convention violations +- Modernizing older device bindings to match current patterns + +## Instructions + +### 1. API Naming Conventions + +#### Methods vs Properties + +- **Methods** (e.g., `ReadTemperature()`, `GetTemperature()`) for values that may change between calls +- **Properties** (e.g., `SomeRegister`) for values that don't change (except via setter) + +#### Naming Patterns + +- Follow .NET naming rules: PascalCase for public members/types, camelCase for locals/fields +- Interfaces start with `I` (e.g., `ITemperatureSensor`) +- Async methods must have `Async` suffix and synchronous equivalent + +#### Read Methods + +✅ **DO:** + +```csharp +public bool TryReadTemperature(out Temperature temperature) +public Temperature ReadTemperature() +public bool TryGetPressure(out Pressure pressure) +``` + +❌ **DON'T:** + +```csharp +public double GetTemp() // Use Temperature type, not double +public double ReadTemperature() // Should return Temperature or use Try pattern +public Temperature? ReadTemperature() // Use Try pattern instead of nullable +``` + +### 2. Value Types and Units + +#### Use UnitsNet Types + +✅ **DO:** + +```csharp +using UnitsNet; + +public bool TryReadTemperature(out Temperature temperature) +public Pressure ReadPressure() +public ElectricPotential ReadVoltage() +``` + +❌ **DON'T:** + +```csharp +public double GetTemperatureCelsius() // Use Temperature type +public float GetPressurePa() // Use Pressure type +``` + +#### When UnitsNet Not Available + +- Use SI units (International System of Units) +- Document units clearly in XML comments + +```csharp +/// +/// Reads humidity in percent (0-100). +/// +/// Relative humidity in %RH +public double ReadHumidity() +``` + +#### Handling Invalid Values + +✅ **DO:** + +```csharp +public bool TryReadTemperature(out Temperature temperature) +{ + // Return false on failure, set valid value on success + if (/* read failed */) + { + temperature = default; + return false; + } + temperature = Temperature.FromDegreesCelsius(value); + return true; +} +``` + +❌ **DON'T:** + +```csharp +public double ReadTemperature() +{ + return double.NaN; // Don't use sentinel values +} +``` + +### 3. Constructor Design + +#### Required Parameters Only + +- Constructor should only require parameters device cannot work without +- Everything else should have default values +- Use Settings class for devices with many parameters + +✅ **DO:** + +```csharp +public Bmp280(I2cDevice i2cDevice, I2cAddress address = I2cAddress.Primary) +public Dht22(int pin, GpioController? controller = null, bool shouldDispose = true) +``` + +❌ **DON'T:** + +```csharp +public Bmp280(I2cDevice i2cDevice, int pollingDelay, bool enableFilter, + Oversampling tempOversampling, Oversampling pressOversampling) +// Too many required parameters - use Settings class +``` + +#### Integer Invalid Values + +- Use `-1` for invalid/unassigned integer values (not `null` or `Nullable`) + +```csharp +public int Pin { get; set; } = -1; // -1 indicates unassigned +``` + +### 4. Resource Management + +#### IDisposable Implementation + +MUST implement IDisposable if device owns hardware resources. + +✅ **DO:** + +```csharp +public class Bmp280 : IDisposable +{ + private I2cDevice? _i2cDevice; + private bool _disposed; + + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + + if (disposing) + { + _i2cDevice?.Dispose(); + _i2cDevice = null; + } + + _disposed = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } +} +``` + +#### Transport Ownership + +- **I2cDevice, SpiDevice, PwmChannel**: 1:1 with hardware, **should be disposed by device** +- **GpioController**: Can be shared, **add optional `shouldDispose` flag** (default true) + +✅ **DO:** + +```csharp +public Dht22(int pin, GpioController? controller = null, bool shouldDispose = true) +{ + _pin = pin; + _shouldDispose = shouldDispose || controller is null; + _controller = controller ?? new GpioController(); +} + +public void Dispose() +{ + if (_shouldDispose) + { + _controller?.Dispose(); + } + _controller = null; +} +``` + +### 5. Register and Address Management + +#### Use Enums for Registers + +✅ **DO:** + +```csharp +private enum Register : byte +{ + ChipId = 0xD0, + Reset = 0xE0, + Status = 0xF3, + Control = 0xF4, + Config = 0xF5, + PressureData = 0xF7, + TemperatureData = 0xFA +} + +// Usage +byte value = Read(Register.ChipId); +``` + +❌ **DON'T:** + +```csharp +private const byte CHIP_ID_REG = 0xD0; // Use enum instead +byte value = Read(0xD0); // Magic number +``` + +### 6. Error Handling + +#### Input Validation + +✅ **DO:** + +```csharp +public Sensor(int pin) +{ + if (pin < 0) + throw new ArgumentOutOfRangeException(nameof(pin), "Pin must be non-negative"); + _pin = pin; +} + +public void SetSamplingRate(int rate) +{ + if (rate < 1 || rate > 100) + throw new ArgumentOutOfRangeException(nameof(rate), "Rate must be 1-100 Hz"); + _samplingRate = rate; +} +``` + +#### Hardware Failures + +✅ **DO:** + +```csharp +public bool TryReadTemperature(out Temperature temperature) +{ + try + { + byte[] data = ReadRegister(Register.TempData); + if (!ValidateChecksum(data)) + { + temperature = default; + return false; + } + temperature = ParseTemperature(data); + return true; + } + catch (IOException) + { + temperature = default; + return false; + } +} +``` + +❌ **DON'T:** + +```csharp +public Temperature ReadTemperature() +{ + try + { + return ParseTemperature(ReadRegister(Register.TempData)); + } + catch + { + return Temperature.FromDegreesCelsius(0); // Bogus data + } +} +``` + +### 7. Visibility and Access Modifiers + +#### Public API Minimalism + +- Only most useful APIs should be public +- Advanced features should be `protected` (allows inheritance) +- Internal implementation details should be `private` or `internal` + +✅ **DO:** + +```csharp +public class Sensor +{ + // Main functionality - public + public bool TryReadTemperature(out Temperature temp) { } + + // Advanced calibration - protected + protected void SetCalibrationValue(int index, double value) { } + + // Internal details - private + private byte[] ReadRawData() { } +} +``` + +### 8. Documentation Requirements + +#### XML Comments + +✅ **DO:** + +```csharp +/// +/// Reads the current temperature from the sensor. +/// +/// The temperature in degrees Celsius +/// True if read successful, false on communication error +/// +/// Reading takes approximately 10ms. Values range from -40°C to +85°C. +/// +public bool TryReadTemperature(out Temperature temperature) +``` + +### 9. Common Anti-Patterns to Fix + +❌ **Console I/O in Library Code** + +```csharp +public void ReadSensor() +{ + var temp = ReadTemperature(); + Console.WriteLine($"Temp: {temp}"); // Move to sample, not library +} +``` + +❌ **Thread-Static Caches** + +```csharp +[ThreadStatic] +private static byte[]? _buffer; // Hidden state, avoid +``` + +❌ **Background Threads Without Clear Ownership** + +```csharp +private Thread? _pollingThread; // Ensure proper disposal and control +``` + +❌ **Board-Specific Hardcoded Paths** + +```csharp +const string GpioPath = "/sys/class/gpio"; // Use abstractions +``` + +## Review Checklist + +Use this when reviewing device binding APIs: + +- [ ] Methods/properties used appropriately +- [ ] UnitsNet types used for physical quantities +- [ ] Try* pattern for operations that may fail +- [ ] No sentinel values (NaN, null) for failures +- [ ] Constructor has only required parameters +- [ ] IDisposable implemented correctly +- [ ] Transport ownership handled properly +- [ ] Enums used for registers/addresses +- [ ] Input validation with appropriate exceptions +- [ ] XML documentation with units +- [ ] Public API is minimal and focused +- [ ] No console I/O in library code +- [ ] No platform-specific code + +## References + +- [Device Conventions Document](../../../Documentation/Devices-conventions.md) +- [Copilot Instructions - API Design](../../copilot-instructions.md#device-binding-design) +- [UnitsNet Documentation](https://github.com/angularsen/UnitsNet) diff --git a/.github/skills/hardware-abstraction-check/SKILL.md b/.github/skills/hardware-abstraction-check/SKILL.md new file mode 100644 index 0000000000..ebcb8c9c5b --- /dev/null +++ b/.github/skills/hardware-abstraction-check/SKILL.md @@ -0,0 +1,460 @@ +--- +name: hardware-abstraction-check +description: Guide for ensuring device bindings maintain proper hardware abstraction and cross-platform compatibility +license: MIT +--- + +## Purpose + +This skill helps Copilot agents verify that device bindings use proper hardware abstractions (System.Device.*) instead of platform-specific code, ensuring compatibility across Linux, Windows, and embedded systems. + +## When to Use This Skill + +- Reviewing new device binding code +- Investigating cross-platform compatibility issues +- Code contains platform-specific APIs or P/Invoke +- Ensuring code works on Raspberry Pi, Windows IoT, and other platforms +- Validating changes don't introduce OS dependencies + +## Instructions + +### 1. Core Principle + +**Device bindings must use System.Device.* abstractions exclusively.** + +The repository provides cross-platform abstractions: + +- `System.Device.Gpio` for GPIO pins +- `System.Device.I2c` for I²C communication +- `System.Device.Spi` for SPI communication +- `System.Device.Pwm` for PWM signals + +These abstractions handle platform differences internally. + +### 2. Anti-Patterns to Detect + +#### ❌ Direct File System Access + +```csharp +// WRONG - hardcoded Linux GPIO paths +const string GpioPath = "/sys/class/gpio"; +File.WriteAllText("/sys/class/gpio/export", "17"); + +// WRONG - platform-specific device paths +var i2cDevice = new I2cUnixDevice("/dev/i2c-1", 0x48); +``` + +✅ **CORRECT - Use abstractions:** + +```csharp +using System.Device.Gpio; +using System.Device.I2c; + +using GpioController controller = new(); +controller.OpenPin(17, PinMode.Output); + +I2cConnectionSettings settings = new(busId: 1, deviceAddress: 0x48); +using I2cDevice device = I2cDevice.Create(settings); +``` + +#### ❌ P/Invoke and DllImport + +```csharp +// WRONG - platform-specific P/Invoke +[DllImport("libc.so.6")] +private static extern int ioctl(int fd, uint request, int value); + +// WRONG - Windows-specific +[DllImport("kernel32.dll")] +private static extern bool DeviceIoControl(...); +``` + +✅ **CORRECT - Device abstractions handle this internally** + +#### ❌ OS Detection and Branching + +```csharp +// WRONG - OS-specific branching +if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) +{ + // Linux GPIO implementation +} +else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) +{ + // Windows GPIO implementation +} + +// WRONG - Environment checks +if (Environment.OSVersion.Platform == PlatformID.Unix) +{ + // Unix-specific code +} +``` + +✅ **CORRECT - Abstractions handle platform differences:** + +```csharp +// Works on all platforms +using GpioController controller = new(); +controller.OpenPin(17, PinMode.Output); +``` + +#### ❌ Process Execution for Hardware Access + +```csharp +// WRONG - shelling out to OS utilities +Process.Start("gpio", "mode 17 out"); +Process.Start("i2cset", "-y 1 0x48 0x00 0xFF"); +``` + +✅ **CORRECT - Use device APIs** + +### 3. Automated Detection Commands + +Run these checks on device binding code: + +```bash +# Check for P/Invoke usage +grep -r "DllImport\|P/Invoke" src/devices// + +# Check for OS detection +grep -r "Environment.OSVersion\|RuntimeInformation\|OSPlatform" src/devices// + +# Check for hardcoded paths +grep -r "/sys/\|/dev/\|/proc/" src/devices// + +# Check for process execution +grep -r "Process.Start\|ProcessStartInfo" src/devices// + +# All of these should return minimal or no results +``` + +### 4. Acceptable Platform-Specific Code + +Some cases may require platform-specific code: + +#### When Platform-Specific Code is Acceptable + +1. **Feature is inherently platform-specific** (e.g., Windows-only hardware) +2. **Properly guarded with clear error messages** +3. **Documented as platform-specific in XML comments** +4. **Fails gracefully on unsupported platforms** + +✅ **Example of acceptable platform-specific code:** + +```csharp +/// +/// Enables hardware feature X. Only supported on Linux. +/// +/// +/// Thrown on non-Linux platforms +/// +public void EnableAdvancedFeature() +{ + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + throw new PlatformNotSupportedException( + "Advanced feature X requires Linux kernel 5.0+"); + } + + // Platform-specific implementation +} +``` + +### 5. Using Device Abstractions Correctly + +#### GPIO Controller Usage + +✅ **DO:** + +```csharp +public class LedDevice : IDisposable +{ + private readonly GpioController _controller; + private readonly int _pin; + private readonly bool _shouldDispose; + + public LedDevice(int pin, GpioController? controller = null, + bool shouldDispose = true) + { + _pin = pin; + _shouldDispose = shouldDispose || controller is null; + _controller = controller ?? new GpioController(); + _controller.OpenPin(_pin, PinMode.Output); + } + + public void Dispose() + { + if (_shouldDispose) + { + _controller?.Dispose(); + } + } +} +``` + +#### I2C Device Usage + +✅ **DO:** + +```csharp +public class I2cSensor : IDisposable +{ + private I2cDevice _i2cDevice; + + public I2cSensor(I2cDevice i2cDevice) + { + _i2cDevice = i2cDevice ?? throw new ArgumentNullException(nameof(i2cDevice)); + } + + public byte ReadRegister(byte register) + { + Span writeBuffer = stackalloc byte[] { register }; + Span readBuffer = stackalloc byte[1]; + _i2cDevice.WriteRead(writeBuffer, readBuffer); + return readBuffer[0]; + } + + public void Dispose() + { + _i2cDevice?.Dispose(); + _i2cDevice = null; + } +} +``` + +#### SPI Device Usage + +✅ **DO:** + +```csharp +public class SpiDisplay : IDisposable +{ + private SpiDevice _spiDevice; + + public SpiDisplay(SpiDevice spiDevice) + { + _spiDevice = spiDevice ?? throw new ArgumentNullException(nameof(spiDevice)); + } + + public void WriteData(ReadOnlySpan data) + { + _spiDevice.Write(data); + } + + public void Dispose() + { + _spiDevice?.Dispose(); + _spiDevice = null; + } +} +``` + +### 6. Sample Code Guidelines + +Sample applications should also follow abstraction principles: + +✅ **DO:** + +```csharp +// Platform-agnostic sample +using System.Device.Gpio; +using System.Device.I2c; +using Iot.Device.Bmp280; + +// Works on any platform with I2C support +I2cConnectionSettings settings = new(busId: 1, deviceAddress: Bmp280.DefaultI2cAddress); +using I2cDevice device = I2cDevice.Create(settings); +using Bmp280 sensor = new(device); + +Console.WriteLine($"Temperature: {sensor.ReadTemperature().DegreesCelsius}°C"); +``` + +❌ **DON'T:** + +```csharp +// Platform-specific sample +#if LINUX + var device = new UnixI2cDevice("/dev/i2c-1", 0x76); +#elif WINDOWS + var device = new WindowsI2cDevice("I2C1", 0x76); +#endif +``` + +### 7. Testing Cross-Platform Compatibility + +#### Build on Multiple Platforms + +```bash +# Test builds on different configurations +dotnet build -c Release + +# If possible, test on: +# - Linux (Raspberry Pi, Ubuntu) +# - Windows (Windows IoT, Desktop) +# - macOS (if GPIO simulator available) +``` + +#### Verify No Platform-Specific References + +```bash +# Check project file for platform-specific packages +cat src/devices//.csproj | grep -i "windows\|linux\|unix" + +# Should only reference System.Device.* packages +``` + +### 8. Common Cross-Platform Issues + +#### Timing Differences + +Some operations may have different timing characteristics across platforms: + +✅ **DO:** + +```csharp +// Use conservative timing that works everywhere +Thread.Sleep(10); // Safe across platforms + +// Or use Stopwatch for precise timing +var sw = Stopwatch.StartNew(); +while (sw.ElapsedMilliseconds < 10) { } +``` + +#### Endianness + +Be aware of byte order when working with multi-byte values: + +✅ **DO:** + +```csharp +// Use BitConverter with explicit endianness +int value = BinaryPrimitives.ReadInt16LittleEndian(buffer); +``` + +### 9. Documentation Requirements + +When code must be platform-specific: + +✅ **DO:** + +```csharp +/// +/// Enables high-speed mode. +/// +/// +/// Platform Support: +/// +/// Linux: Fully supported on kernel 5.0+ +/// Windows IoT: Not supported +/// Windows Desktop: Not supported +/// +/// +/// +/// Thrown on unsupported platforms +/// +public void EnableHighSpeedMode() +``` + +### 10. Review Checklist + +Use this when reviewing device binding for hardware abstraction: + +- [ ] No `DllImport` or P/Invoke declarations +- [ ] No hardcoded paths (`/sys/`, `/dev/`, `/proc/`) +- [ ] No `RuntimeInformation.IsOSPlatform` checks (unless justified) +- [ ] No `Environment.OSVersion` checks +- [ ] No `Process.Start` for hardware access +- [ ] Uses `System.Device.Gpio` for GPIO +- [ ] Uses `System.Device.I2c` for I²C +- [ ] Uses `System.Device.Spi` for SPI +- [ ] Uses `System.Device.Pwm` for PWM +- [ ] Transport objects (I2C/SPI/GPIO) passed from caller +- [ ] Platform-specific features properly documented +- [ ] Platform-specific features fail gracefully +- [ ] Sample code is platform-agnostic +- [ ] Builds successfully without platform-specific errors + +## Common Violations and Fixes + +### Violation 1: Direct GPIO Access + +```csharp +// ❌ WRONG +File.WriteAllText("/sys/class/gpio/export", "17"); +File.WriteAllText("/sys/class/gpio/gpio17/direction", "out"); + +// ✅ CORRECT +using GpioController controller = new(); +controller.OpenPin(17, PinMode.Output); +``` + +### Violation 2: Platform Detection + +```csharp +// ❌ WRONG +if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) +{ + _gpioPath = "/sys/class/gpio"; +} + +// ✅ CORRECT - Let abstractions handle it +using GpioController controller = new(); +``` + +### Violation 3: Process Execution + +```csharp +// ❌ WRONG +Process.Start("i2cget", "-y 1 0x48"); + +// ✅ CORRECT +I2cConnectionSettings settings = new(1, 0x48); +using I2cDevice device = I2cDevice.Create(settings); +byte[] data = new byte[1]; +device.Read(data); +``` + +## Quick Validation Script + +Save as `check-abstraction.sh`: + +```bash +#!/bin/bash +DEVICE_DIR=$1 + +echo "Checking hardware abstraction compliance for $DEVICE_DIR" +echo "" + +echo "Checking for P/Invoke..." +grep -rn "DllImport" "$DEVICE_DIR" || echo " ✓ No P/Invoke found" + +echo "" +echo "Checking for OS detection..." +grep -rn "RuntimeInformation\|OSPlatform\|Environment.OSVersion" "$DEVICE_DIR" || echo " ✓ No OS detection found" + +echo "" +echo "Checking for hardcoded paths..." +grep -rn "/sys/\|/dev/\|/proc/" "$DEVICE_DIR" || echo " ✓ No hardcoded paths found" + +echo "" +echo "Checking for process execution..." +grep -rn "Process.Start" "$DEVICE_DIR" || echo " ✓ No process execution found" + +echo "" +echo "Hardware abstraction check complete!" +``` + +Usage: + +```bash +chmod +x check-abstraction.sh +./check-abstraction.sh src/devices/Bmp280 +``` + +## References + +- [System.Device.Gpio Documentation](https://docs.microsoft.com/dotnet/api/system.device.gpio) +- [System.Device.I2c Documentation](https://docs.microsoft.com/dotnet/api/system.device.i2c) +- [Copilot Instructions - Cross-Platform](../../copilot-instructions.md#cross-platform) +- [Device Conventions](../../../Documentation/Devices-conventions.md) diff --git a/.github/skills/update-device-documentation/SKILL.md b/.github/skills/update-device-documentation/SKILL.md new file mode 100644 index 0000000000..9a2b39ac93 --- /dev/null +++ b/.github/skills/update-device-documentation/SKILL.md @@ -0,0 +1,411 @@ +--- +name: update-device-documentation +description: Guide for creating and maintaining comprehensive documentation for IoT device bindings +license: MIT +--- + +## Purpose + +This skill helps Copilot agents create and maintain high-quality documentation for device bindings, including README files, XML documentation, wiring diagrams, and usage examples. + +## When to Use This Skill + +- Adding documentation for a new device binding +- Updating existing device documentation +- Adding wiring diagrams or connection instructions +- Improving code examples in README files +- Adding XML documentation comments to APIs + +## Instructions + +### 1. Device README Structure + +Each device binding must have a `README.md` in its directory with these sections: + +```markdown +# DeviceName - Brief Description + +Brief overview of what the device does (1-2 sentences). + +## Documentation + +Link to device datasheet/specification. + +## Usage + +Basic code example showing typical usage. + +## Binding Notes + +Important information about this specific binding: +- Default I2C/SPI addresses +- Required configuration +- Known limitations +- Platform-specific considerations + +## References + +Links to: +- Datasheet +- Device manufacturer page +- Related bindings +``` + +### 2. Writing Effective Device Documentation + +#### Device Overview Section + +✅ **DO:** + +```markdown +# BME280 - Combined Temperature, Pressure, and Humidity Sensor + +The BME280 is a combined digital humidity, pressure and temperature sensor +manufactured by Bosch. It provides high precision measurements over I2C or SPI. +``` + +Include: + +- Full device name and manufacturer +- What the device measures/does +- Communication protocol(s) supported + +#### Documentation Section + +✅ **DO:** + +```markdown +## Documentation + +- BME280 [datasheet](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf) +``` + +Always link to official datasheets and specifications. + +### 3. Usage Examples + +#### Code Example Requirements + +- Show complete, runnable code +- Use `using` statements for proper disposal +- Include necessary namespace imports +- Show error handling +- Add comments explaining key steps +- Don't hardcode board-specific values + +✅ **DO:** + +```csharp +## Usage + +**Important**: Make sure you properly setup the I2C pins for your device. + +```csharp +using System; +using System.Device.I2c; +using System.Threading; +using Iot.Device.Bmxx80; +using Iot.Device.Bmxx80.PowerMode; +using UnitsNet; + +// I2C address 0x76 is default, use 0x77 if SDO pin is high +using I2cDevice i2cDevice = I2cDevice.Create(new I2cConnectionSettings(1, Bme280.DefaultI2cAddress)); +using Bme280 bme280 = new(i2cDevice); + +// Set sampling and filtering +bme280.TemperatureSampling = Sampling.Standard; +bme280.PressureSampling = Sampling.Standard; +bme280.HumiditySampling = Sampling.Standard; + +while (true) +{ + // Read sensor values + if (bme280.TryReadTemperature(out Temperature temperature)) + { + Console.WriteLine($"Temperature: {temperature.DegreesCelsius:F2} °C"); + } + + if (bme280.TryReadPressure(out Pressure pressure)) + { + Console.WriteLine($"Pressure: {pressure.Hectopascals:F2} hPa"); + } + + if (bme280.TryReadHumidity(out RelativeHumidity humidity)) + { + Console.WriteLine($"Humidity: {humidity.Percent:F2} %"); + } + + Thread.Sleep(1000); +} +\`\`\` +``` + +### 4. Wiring Information + +#### Wiring Diagram Requirements + +Include wiring information showing: + +- Pin connections between device and board +- Required pull-up/pull-down resistors +- Power supply requirements +- Common connection mistakes to avoid + +✅ **DO:** + +```markdown +## Wiring + +### I2C Connection + +| BME280 Pin | Raspberry Pi Pin | +|------------|------------------| +| VCC | 3.3V (Pin 1) | +| GND | Ground (Pin 6) | +| SCL | GPIO3/SCL (Pin 5)| +| SDA | GPIO2/SDA (Pin 3)| +| SDO | Ground or 3.3V | + +**Note:** +- Pull-up resistors (4.7kΩ) are typically required on SCL and SDA lines +- SDO pin sets I2C address: Low = 0x76, High = 0x77 +- Use 3.3V power only, do not connect to 5V +``` + +### 5. Binding Notes Section + +Document important implementation details: + +✅ **DO:** + +```markdown +## Binding Notes + +### Default Addresses +- Primary I2C address: 0x76 (SDO pin grounded) +- Secondary I2C address: 0x77 (SDO pin to VCC) + +### Measurement Timing +- Single measurement takes approximately 10ms +- Continuous mode updates at configured rate (0.5Hz to 16Hz) + +### Calibration +- Device includes factory calibration data +- Calibration is automatically read and applied by the binding + +### Known Limitations +- SPI mode not yet implemented +- Forced mode not supported, use normal mode instead +``` + +### 6. XML Documentation for APIs + +#### Public API Documentation + +Every public member needs XML documentation: + +✅ **DO:** + +```csharp +/// +/// Reads the current temperature from the BME280 sensor. +/// +/// +/// When this method returns true, contains the measured temperature. +/// When false, contains the default value. +/// +/// +/// True if the temperature was read successfully; false if communication +/// error occurred or data was invalid. +/// +/// +/// Temperature range: -40°C to +85°C with ±1°C accuracy. +/// Reading takes approximately 10ms in standard sampling mode. +/// +public bool TryReadTemperature(out Temperature temperature) +``` + +#### Include Units in Documentation + +Always specify units: + +```csharp +/// +/// Gets or sets the temperature oversampling rate. +/// Higher values provide better accuracy but slower measurements. +/// +/// Oversampling multiplier (1x, 2x, 4x, 8x, or 16x) +public Sampling TemperatureSampling { get; set; } +``` + +### 7. Property and Enum Documentation + +Document ranges and valid values: + +✅ **DO:** + +```csharp +/// +/// Measurement sampling rate options. +/// +public enum Sampling +{ + /// + /// Measurement skipped (0x) + /// + Skipped = 0b000, + + /// + /// Oversampling x1 - fastest, least accurate + /// + UltraLowPower = 0b001, + + /// + /// Oversampling x2 + /// + LowPower = 0b010, + + /// + /// Oversampling x4 (recommended for most applications) + /// + Standard = 0b011 +} +``` + +### 8. Markdown Linting + +Ensure documentation passes markdown linting: + +```bash +# Install markdownlint +npm install -g markdownlint-cli + +# Check device README +markdownlint -c .markdownlint.json src/devices/Bme280/README.md + +# Fix common issues: +# - No trailing spaces +# - Blank line after headers +# - Proper list formatting +# - No bare URLs (use [text](url) format) +``` + +### 9. Common Documentation Patterns + +#### For Sensor Devices + +```markdown +## Specifications + +- Measurement range: -40°C to +85°C +- Accuracy: ±1°C +- Resolution: 0.01°C +- Supply voltage: 1.8V to 3.6V +- Interface: I2C (up to 3.4 MHz) or SPI (up to 10 MHz) +``` + +#### For Display Devices + +```markdown +## Specifications + +- Display size: 128x64 pixels +- Colors: Monochrome (white on black) +- Interface: I2C or SPI +- Typical current: 20mA at 3.3V +``` + +#### For Motor/Actuator Devices + +```markdown +## Specifications + +- Control interface: PWM +- Operating voltage: 5-12V +- Maximum current: 2A per channel +- PWM frequency: 1kHz to 10kHz recommended +``` + +### 10. Documentation Review Checklist + +Before completing documentation: + +- [ ] README.md exists in device directory +- [ ] Device name and description are clear +- [ ] Link to datasheet/specification included +- [ ] Code example is complete and runnable +- [ ] Code example uses proper disposal (`using` statements) +- [ ] Wiring diagram or connection table included +- [ ] Required pull-ups/resistors documented +- [ ] Default I2C/SPI addresses documented +- [ ] Known limitations documented +- [ ] All public APIs have XML documentation +- [ ] Units specified in XML comments (°C, Pa, %RH, etc.) +- [ ] Markdown linting passes +- [ ] No hardcoded board-specific paths in examples +- [ ] Cross-platform considerations mentioned if applicable + +## Examples + +### Complete Example: Simple Sensor + +```markdown +# DHT22 - Temperature and Humidity Sensor + +DHT22 is a low-cost digital temperature and humidity sensor that uses a +one-wire protocol for communication. + +## Documentation + +- DHT22 [datasheet](https://www.sparkfun.com/datasheets/Sensors/Temperature/DHT22.pdf) + +## Usage + +```csharp +using System; +using System.Device.Gpio; +using System.Threading; +using Iot.Device.DHTxx; +using UnitsNet; + +using GpioController controller = new(); +using Dht22 sensor = new(pin: 4, controller); + +while (true) +{ + if (sensor.TryReadTemperature(out Temperature temperature) && + sensor.TryReadHumidity(out RelativeHumidity humidity)) + { + Console.WriteLine($"Temp: {temperature.DegreesCelsius:F1}°C, " + + $"Humidity: {humidity.Percent:F1}%"); + } + else + { + Console.WriteLine("Read failed"); + } + + Thread.Sleep(2000); // DHT22 requires 2 second interval +} +\`\`\` + +## Wiring + +Connect DHT22 to Raspberry Pi: +- Pin 1 (VCC) to 3.3V +- Pin 2 (DATA) to GPIO4 with 4.7kΩ pull-up to 3.3V +- Pin 4 (GND) to Ground + +## Binding Notes + +- Minimum reading interval: 2 seconds +- Pull-up resistor (4.7kΩ) required on data line +- Temperature range: -40°C to +80°C (±0.5°C accuracy) +- Humidity range: 0-100% RH (±2-5% accuracy) +``` + +## References + +- [Device Conventions](../../../Documentation/Devices-conventions.md) +- [Contributing Guidelines](../../../Documentation/CONTRIBUTING.md) +- [Copilot Instructions](../../copilot-instructions.md) +- [Markdown Style Guide](../../../.markdownlint.json) diff --git a/.markdownlint.json b/.markdownlint.json index 9c1a747ba9..1fca35cd4c 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -2,6 +2,7 @@ "MD012": false, "MD013": false, "MD033": false, + "MD041": false, "MD059": false, "MD060": false }