Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
010258d
draft
p14c31355 Aug 28, 2025
166efa6
cargo add crc32fast
p14c31355 Aug 28, 2025
d4f0948
Define logger argument
p14c31355 Aug 28, 2025
818fee5
Compile errors fixed by cline
p14c31355 Aug 28, 2025
d100fb8
#102 PR review respond
p14c31355 Aug 28, 2025
10329e2
add USAGE_EXPLORE.md
p14c31355 Aug 28, 2025
c8ddde1
Merge branch 'feature/#101' into docs/README-EXPLORE
p14c31355 Aug 28, 2025
054a2ad
Update docs/USAGE_EXPLORE.md
p14c31355 Aug 28, 2025
322ddbf
Update docs/USAGE_EXPLORE.md
p14c31355 Aug 28, 2025
e394c51
Update docs/USAGE_EXPLORE.md
p14c31355 Aug 28, 2025
752e44d
Merge pull request #103 from p14c31355/docs/README-EXPLORE
p14c31355 Aug 28, 2025
0af52d1
#102 PR review respond
p14c31355 Aug 28, 2025
b159eba
cargo fmt
p14c31355 Aug 28, 2025
7e3ce03
For clippy friendly
p14c31355 Aug 28, 2025
d1c6a32
Update docs/USAGE_EXPLORE.md
p14c31355 Aug 28, 2025
80231d3
#102 PR review respond
p14c31355 Aug 28, 2025
1eb1ffd
#102 PR review respond
p14c31355 Aug 28, 2025
adff02c
#102 PR review respond
p14c31355 Aug 28, 2025
5a3116c
Update src/explorer.rs
p14c31355 Aug 28, 2025
32b9192
Update src/explorer.rs
p14c31355 Aug 28, 2025
8f5c31a
Update src/explorer.rs
p14c31355 Aug 28, 2025
d7c890a
Delete cfc32fast
p14c31355 Aug 28, 2025
43a4aa8
Error mapping & Logging level
p14c31355 Aug 28, 2025
f90977b
Serial log rewrote
p14c31355 Aug 28, 2025
a106359
#102 PR review respond
p14c31355 Aug 28, 2025
ed8d3a2
#102 PR review respond
p14c31355 Aug 28, 2025
d4df14e
Update src/scanner.rs
p14c31355 Aug 28, 2025
a7834a2
edit rustdoc
p14c31355 Aug 28, 2025
81c893a
Merge branch 'feature/#101' of https://github.com/p14c31355/dvcdbg in…
p14c31355 Aug 28, 2025
3100083
Update docs/USAGE_EXPLORE.md
p14c31355 Aug 29, 2025
911b846
Update docs/USAGE_EXPLORE.md
p14c31355 Aug 29, 2025
968bb7c
flush
p14c31355 Aug 29, 2025
11ae41d
cargo fmt
p14c31355 Aug 29, 2025
da662b3
send chunl
p14c31355 Aug 29, 2025
f8412bf
Add pruned exploration method
p14c31355 Aug 29, 2025
2f1ab03
compile errors fixed by cline
p14c31355 Aug 29, 2025
7bd2b8a
use heapless
p14c31355 Aug 29, 2025
69da2c0
Use writeln macro
p14c31355 Aug 29, 2025
3d2d02c
run_pruned_explorer include scan_i2c()
p14c31355 Aug 29, 2025
a63187e
#102 PR review respond
p14c31355 Aug 29, 2025
51ddcd5
#102 PR review respond
p14c31355 Aug 29, 2025
10ef551
Update src/explorer.rs
p14c31355 Aug 29, 2025
b4de3a9
#102 PR review respond
p14c31355 Aug 29, 2025
ed28743
Merge branch 'feature/#101' of https://github.com/p14c31355/dvcdbg in…
p14c31355 Aug 29, 2025
e96b3ec
#102 PR review respond
p14c31355 Aug 29, 2025
52d60ef
#102 PR review respond
p14c31355 Aug 29, 2025
a56a602
#102 PR review respond
p14c31355 Aug 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
240 changes: 240 additions & 0 deletions docs/USAGE_EXPLORE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
# I2C Command Sequence Explorer

This module provides an algorithm and supporting utilities for discovering
valid sequences of I2C commands when device dependencies are unknown or
partially specified. It is designed for embedded bring-up scenarios, where
experimenting with permutations of initialization commands can reveal the
correct sequence for a new or undocumented device.

## Overview
- [`Explorer`] manages a dependency graph of commands and produces only
valid permutations that satisfy the declared constraints.
- [`PermutationIter`] generates these permutations using an iterative,
stack-safe backtracking algorithm (no recursion).
- [`CmdExecutor`] abstracts the execution of a command on the I2C bus.
- [`Logger`] provides pluggable logging backends (serial console, null logger, etc.).

## Key Features
1. **Separation of Concerns**: The permutation engine (`PermutationIter`) is
isolated from bus execution logic (`explore`).
2. **Iterator-based API**: The [`Explorer::permutations`] method yields an
iterator, making the algorithm testable and composable.
3. **Generic Capacity**: The const generic `N` defines the maximum command
capacity at compile time, enabling efficient use on resource-constrained
microcontrollers.
4. **Flexible Logging**: The [`Logger`] trait supports both formatted and
lightweight logging without allocating large buffers.
5. **Robust Error Handling**: [`ExplorerError`] reports dependency cycles,
buffer exhaustion, and runtime execution issues explicitly.

## Typical Use Case
- Bring-up of a new I2C peripheral when the required initialization sequence
is undocumented or incomplete.
- Automated discovery of valid command orderings under dependency constraints.
- Filtering of device addresses that respond consistently to a tested sequence.

## Example
```ignore
use dvcdbg::prelude::*;
use heapless::Vec;
// Example executor for a specific I2C implementation.
struct MyExecutor;
impl<I2C: crate::compat::I2cCompat> CmdExecutor<I2C> for MyExecutor {
fn exec(
&mut self,
i2c: &mut I2C,
addr: u8,
cmd: &[u8]
) -> Result<(), crate::explorer::ExecutorError> {
// Simplified example: prepend 0x00 control byte.
if cmd.len() != 1 {
return Err(crate::explorer::ExecutorError::ExecFailed);
}
let buf = [0x00, cmd[0]];
i2c.write(addr, &buf)
.map_err(|e| crate::explorer::ExecutorError::I2cError(e.to_compat(Some(addr))))
}
}

// Dummy logger that ignores messages.
struct NullLogger;
impl Logger for NullLogger {
fn log_info(&mut self, _msg: &str) {}
fn log_warning(&mut self, _msg: &str) {}
fn log_error(&mut self, _msg: &str) {}
fn log_info_fmt<F>(&mut self, _fmt: F)
where F: FnOnce(&mut heapless::String<{ crate::explorer::LOG_BUFFER_CAPACITY }>) -> Result<(), core::fmt::Error> {}
fn log_error_fmt<F>(&mut self, _fmt: F)
where F: FnOnce(&mut heapless::String<{ crate::explorer::LOG_BUFFER_CAPACITY }>) -> Result<(), core::fmt::Error> {}
}

// Define candidate commands with dependencies.
const CAPACITY: usize = 32;
let cmds = &[
CmdNode { bytes: &[0x01], deps: &[] },
CmdNode { bytes: &[0x02], deps: &[0] }, // depends on first command
CmdNode { bytes: &[0x03], deps: &[0] },
];

let explorer = Explorer::<CAPACITY> { sequence: cmds };
let mut executor = MyExecutor;
let mut logger = NullLogger;
let mut i2c = /* platform-specific I2C impl */;
let result = explorer.explore(&mut i2c, &mut executor, &mut logger);
if let Err(e) = result {
logger.log_error(&format!("Exploration failed: {:?}", e));
}
```

### `CmdNode`
Represents a single I2C command in the dependency graph.

- `bytes` - The I2C command bytes to be sent.
- `deps` - The indices of the commands that must precede this command.

The dependency is now on the index of the dependent command in the sequence.

```rust
#[derive(Copy, Clone)]
pub struct CmdNode {
pub bytes: &'static [u8],
pub deps: &'static [usize],
}
```


Returns a stack-safe iterator for all valid command permutations (topological sorts).

This function first performs cycle detection using a modified Kahn's algorithm.
If a cycle is detected, it returns an `ExplorerError::DependencyCycle`.
Otherwise, it initializes and returns a `PermutationIter` to generate all valid permutations.

Comment on lines +36 to +111

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The documentation in this file appears to be out of sync with the recent code changes, which could confuse users. For example:

  • The CmdExecutor and Logger examples are missing the new const generic parameters for buffer capacity.
  • The documentation describes Explorer::permutations, but this method has been removed and its logic is now in PermutationIter::new.

Please review the entire document and update it to reflect the current API.

Explores valid sequences, attempting to execute them on an I2C bus.

This function iterates through all valid command permutations generated by `PermutationIter`,
and for each permutation, attempts to execute it on all active I2C addresses.
It filters out addresses that fail to respond to any command in a sequence.

# Parameters
- `i2c`: An I2C implementation used to test candidate sequences against device addresses.
- `executor`: The object responsible for executing a single command on the bus.
- `logger`: The object responsible for logging progress and results.

# Returns
- `Ok(ExploreResult)` containing the list of found addresses and the number of permutations tested.
- `Err(ExplorerError)` if an error occurs during permutation generation or I2C execution.

Generates a single valid topological sort of the command sequence.
This is useful when only one valid ordering is needed, and avoids
the computational cost of generating all permutations.

Returns `Ok(Vec<&'a [u8], N>)` containing one valid command sequence,
or `Err(ExplorerError)` if a cycle is detected or buffer overflows.

Attempts to extend the current partial permutation by adding a new command.

It iterates through all available (not yet used) commands and checks if their
dependencies are satisfied (i.e., their in-degree is 0). If a valid command
is found, it's added to the current permutation, its `used` status is updated,
and the in-degrees of its dependent nodes are decremented.

Returns `true` if a command was successfully added, `false` otherwise.

Backtracks to the previous decision point in the permutation search.

This method undoes the last choice made: it removes the last added command
from the current permutation, unmarks it as used, and increments the
in-degrees of its dependent nodes (reversing the decrement).
It then updates the `loop_start_indices` to ensure the next search
at the parent level continues from the next sibling.

Returns `true` if backtracking can continue (i.e., there are more options
at a previous level), or `false` if the root was reached and no more
permutations can be generated.

Runs the I2C explorer with a given initial sequence and logs the results.

This function first performs an I2C scan with the provided `init_sequence` to identify
responsive commands. Then, it uses the `explorer` to find valid command sequences
for discovered devices, applying a `prefix` to each command.

# Type Parameters
- `I2C`: The I2C interface type that implements `crate::compat::I2cCompat`.
- `S`: The serial interface type used for logging, implementing `core::fmt::Write`.
- `N`: A const generic for the maximum number of commands.
- `BUF_CAP`: A const generic for the command buffer capacity.

# Parameters
- `explorer`: An instance of `Explorer` containing the command nodes and their dependencies.
- `i2c`: The I2C bus instance.
- `serial`: The serial writer for logging.
- `init_sequence`: The initial sequence of bytes to test for device responsiveness.
- `prefix`: A byte to prepend to every command sent during exploration.
- `log_level`: The desired logging level.

# Example
```ignore
use dvcdbg::prelude::*;
use arduino_hal::I2c;
use arduino_hal::hal::port::Port;
use arduino_hal::pac::TWI;
use heapless::Vec;
use core::fmt::Write;

# struct MyI2c; // Dummy I2c implementation
# impl dvcdbg::compat::I2cCompat for MyI2c {
# type Error = dvcdbg::error::ErrorKind;
# fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { Ok(()) }
# fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { Ok(()) }
# fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { Ok(()) }
# }
# struct MySerial; // Dummy Serial implementation
# impl core::fmt::Write for MySerial {
# fn write_str(&mut self, s: &str) -> core::fmt::Result { Ok(()) }
# }

let mut i2c = /* your I2C instance */;
let mut serial = /* your serial instance */;
let init_sequence = [0u8; 16]; // Example initial sequence
const EXPLORER_CAP: usize = 32;
const BUF_CAP: usize = 128;
let explorer = Explorer::<EXPLORER_CAP> { sequence: &[] }; // Dummy explorer

run_explorer::<_, _, EXPLORER_CAP, BUF_CAP>(
&explorer,
&mut i2c,
&mut serial,
&init_sequence,
0x00, // Example prefix
LogLevel::Verbose,
).unwrap();
# Ok::<(), dvcdbg::explorer::ExplorerError>(())
# }
```

Runs the I2C explorer to find and execute a single valid command sequence.

This function first obtains one topological sort of the commands from the explorer.
Then, it attempts to execute this single sequence on a specified I2C address.
This is useful for device initialization where only one valid sequence is needed,
avoiding the high computational cost of exploring all permutations.

# Type Parameters
- `I2C`: The I2C interface type that implements `crate::compat::I2cCompat`.
- `S`: The serial interface type used for logging, implementing `core::fmt::Write`.
- `N`: A const generic for the maximum number of commands.
- `BUF_CAP`: A const generic for the command buffer capacity.

### Parameters

- `explorer`: An instance of `Explorer` containing the command nodes and their dependencies.
- `i2c`: The I2C bus instance.
- `serial`: The serial writer for logging.
- `target_addr`: The specific I2C address to execute the sequence on.
- `prefix`: A byte to prepend to every command sent during execution.
- `log_level`: The desired logging level.

### Returns

Returns `Ok(())` if the sequence was successfully executed,
or `Err(ExplorerError)` if an error occurred (e.g., cycle detected, execution failed).
Comment on lines +1 to +240

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The documentation in this file is a great addition, but it seems to be out of sync with the latest refactoring. Several examples and descriptions refer to old APIs that have been changed or removed. For instance:

  • The CmdExecutor and Logger examples don't reflect that these traits are now generic over a buffer capacity (const BUF_CAP: usize and const B: usize).
  • The Explorer::explore method call example is outdated.
  • The file documents private methods of PermutationIter (try_extend, backtrack), which might not be necessary for a usage guide.
  • The documentation for Explorer::permutations is still present, but this method has been replaced by PermutationIter::new.

Please review and update the entire document to match the new API.

2 changes: 1 addition & 1 deletion src/compat/err_compat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ where
E: Debug,
{
fn to_compat(&self, _addr: Option<u8>) -> ErrorKind {
ErrorKind::Unknown
ErrorKind::I2c(I2cError::Nack)
}
}

Expand Down
Loading