Adjust I2C tests and update UART script

- Moved I2C tests from i2c1 to i2c2 to work with the EVB harness.
- Moved the device from 0x2e to 0x42 to work with the Zephyr reference.
- Updated the UART script to automatically detokenize UART output from
  pigweed when requested, as well as allow it to operate on systems without
  a full pigweed tree.
diff --git a/.gitignore b/.gitignore
index 61dddb1..c1d1ee0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -133,6 +133,3 @@
 
 # Build scripts
 build.rs.bk
-
-# Cargo build output
-target/
diff --git a/services/i2c/server/src/main.rs b/services/i2c/server/src/main.rs
index ceac19f..3a757d6 100644
--- a/services/i2c/server/src/main.rs
+++ b/services/i2c/server/src/main.rs
@@ -109,6 +109,7 @@
     response: &mut [u8],
     backend: &mut AspeedI2cBackend,
 ) -> usize {
+    pw_log::info!("I2C server dispatch");
     // Parse header
     let Some(header) = I2cRequestHeader::from_bytes(request) else {
         return encode_error(response, ResponseCode::ServerError);
@@ -125,6 +126,7 @@
         // Write: header.write_len bytes from payload → device
         // ------------------------------------------------------------------
         I2cOp::Write => {
+            pw_log::info!("I2C dispatch write");
             let wlen = header.write_len as usize;
             if payload.len() < wlen {
                 return encode_error(response, ResponseCode::BufferTooSmall);
@@ -139,6 +141,7 @@
         // Read: header.read_len bytes from device → response payload
         // ------------------------------------------------------------------
         I2cOp::Read => {
+            pw_log::info!("I2C dispatch read");
             let rlen = header.read_len as usize;
             let avail = response.len().saturating_sub(I2cResponseHeader::SIZE);
             if rlen > avail {
@@ -156,6 +159,7 @@
         // WriteRead: write then read with repeated START
         // ------------------------------------------------------------------
         I2cOp::WriteRead => {
+            pw_log::info!("I2C dispatch writeread");
             let wlen = header.write_len as usize;
             let rlen = header.read_len as usize;
             if payload.len() < wlen {
@@ -178,6 +182,9 @@
         // Probe: write 0 bytes — ACK means device present
         // ------------------------------------------------------------------
         I2cOp::Probe => {
+            pw_log::info!("I2C dispatch probe");
+
+
             match backend.write(header.bus, header.address, &[]) {
                 Ok(()) => encode_success(response, 0),
                 Err(code) => encode_error(response, code),
@@ -188,6 +195,7 @@
         // RecoverBus: attempt to unstick SDA via clock pulses
         // ------------------------------------------------------------------
         I2cOp::RecoverBus => {
+            pw_log::info!("I2C dispatch recover bus");
             match backend.recover_bus(header.bus) {
                 Ok(()) => encode_success(response, 0),
                 Err(code) => encode_error(response, code),
diff --git a/target/ast1060-evb/harness/uart_test_exec.py b/target/ast1060-evb/harness/uart_test_exec.py
index 76f8cd0..fb759e9 100644
--- a/target/ast1060-evb/harness/uart_test_exec.py
+++ b/target/ast1060-evb/harness/uart_test_exec.py
@@ -17,6 +17,35 @@
 from pathlib import Path
 from typing import Optional, Tuple
 
+def _find_pw_tokenizer() -> bool:
+    """Attempt to locate and add pw_tokenizer to sys.path.
+
+    Checks PW_TOK_ROOT first, then falls back to the Bazel output base.
+    Returns True if pw_tokenizer was found and added to sys.path.
+    """
+    pw_tok_root = os.environ.get("PW_TOK_ROOT")
+    if pw_tok_root:
+        sys.path.insert(0, os.path.join(pw_tok_root, "pw_tokenizer", "py"))
+        return True
+
+    try:
+        output_base = subprocess.check_output(
+            ["bazel", "info", "output_base"], text=True, stderr=subprocess.DEVNULL
+        ).strip()
+        candidate = os.path.join(output_base, "external", "pigweed+", "pw_tokenizer", "py")
+        if os.path.isdir(candidate):
+            sys.path.insert(0, candidate)
+            return True
+    except (subprocess.CalledProcessError, FileNotFoundError):
+        pass
+
+    return False
+
+_PW_TOKENIZER_AVAILABLE = _find_pw_tokenizer()
+
+if _PW_TOKENIZER_AVAILABLE:
+    from pw_tokenizer import Detokenizer
+
 try:
     import serial
 except ImportError:
@@ -36,12 +65,22 @@
         self.serial_port: Optional[serial.Serial] = None
         self.log_file = getattr(args, "log_file", None) or f"uart-test-{os.getpid()}.log"
         self.log_file_handle = None
+        elf = getattr(args, "elf", None)
+        self.detokenizer = Detokenizer(elf) if (elf and _PW_TOKENIZER_AVAILABLE) else None
 
     def log(self, message: str):
         """Print message unless in quiet mode."""
         if not self.args.quiet:
             print(message, flush=True)
 
+    def print_uart_data(self, data: str):
+        """Print UART data, with detokenized output on the following line in green."""
+        print(data, end="", flush=True)
+        if self.detokenizer:
+            detokenized = self.detokenizer.detokenize_text(data)
+            if detokenized != data:
+                print(f"\033[32m{detokenized}\033[0m", end="", flush=True)
+
     def run_command(self, cmd: list, check: bool = True) -> Tuple[int, str, str]:
         """Run command and return (returncode, stdout, stderr)."""
         try:
@@ -221,7 +260,7 @@
             if data:
                 buffer += data
                 if not self.args.quiet:
-                    print(data, end="", flush=True)
+                    self.print_uart_data(data)
 
                 # Look for 'U' character
                 if "U" in buffer:
@@ -304,7 +343,7 @@
             if data:
                 buffer += data
                 if not self.args.quiet:
-                    print(data, end="", flush=True)
+                    self.print_uart_data(data)
 
                 lines = buffer.split("\n")
                 for line in lines:
@@ -387,6 +426,7 @@
         "uart_device", nargs="?", help="UART device path (e.g., /dev/ttyUSB0)"
     )
     parser.add_argument("firmware", nargs="?", help="Firmware binary file path")
+    parser.add_argument("--elf", help="ELF file for pw_tokenizer detokenization")
 
     # GPIO control
     parser.add_argument(
@@ -460,6 +500,20 @@
 
     args = parser.parse_args()
 
+    # Validate pw_tokenizer / --elf argument consistency
+    if os.environ.get("PW_TOK_ROOT") and not args.elf:
+        print(
+            "Error: PW_TOK_ROOT is set but --elf was not provided. "
+            "Detokenization requires an ELF file."
+        )
+        sys.exit(1)
+    if args.elf and not _PW_TOKENIZER_AVAILABLE:
+        print(
+            "Error: --elf was provided but pw_tokenizer could not be located. "
+            "Set PW_TOK_ROOT to the Pigweed root or ensure Bazel has fetched it."
+        )
+        sys.exit(1)
+
     # Validate arguments
     if args.upload_only:
         if not args.uart_device or not args.firmware:
diff --git a/target/ast1060-evb/i2c/i2c_client_test.rs b/target/ast1060-evb/i2c/i2c_client_test.rs
index 8986bbb..06ace72 100644
--- a/target/ast1060-evb/i2c/i2c_client_test.rs
+++ b/target/ast1060-evb/i2c/i2c_client_test.rs
@@ -40,10 +40,10 @@
 // ============================================================================
 
 /// I2C bus for master tests (I2C1 — connected to ADT7490 on the EVB)
-const I2C_BUS: BusIndex = BusIndex::BUS_1;
+const I2C_BUS: BusIndex = BusIndex::BUS_2;
 
 /// ADT7490 temperature sensor 7-bit address (on-board)
-const ADT7490_ADDR: u8 = 0x2E;
+const ADT7490_ADDR: u8 = 0x42;
 
 /// ADT7490 register addresses and their expected power-on-reset defaults.
 /// From ADT7490 datasheet — these are read-only default values.
@@ -226,7 +226,7 @@
 
     pw_log::info!("========================================");
     pw_log::info!("I2C Hardware Tests (IPC → ADT7490)");
-    pw_log::info!("Bus: I2C1  Addr: 0x2E");
+    pw_log::info!("Bus: I2C2  Addr: 0x42");
     pw_log::info!("========================================");
 
     test_probe_adt7490(&mut client, &mut results);