{
  "family": "Sense",
  "name": "TOF",
  "rev": "a",
  "tile_id": 17,
  "json_version": "0.11",
  "updated_at": "2026-05-01T12:41:02.859Z",
  "headline": "time-of-flight distance",
  "description": "The Sense.TOF is a single-zone time-of-flight distance sensor based on the ams-Osram TMF8806, which uses a Class-1 eye-safe 940 nm VCSEL laser emitter with SPAD detection.  It provides 1-mm sensing resolution over a range from 1 centimeter to 5 meters (extendable to 10 meters) with a max output data rate of 30Hz.  The sensor features on-chip histogram processing, a 24-degree field of view, and pre-installed firmware suitable for obstacle detection, proximity sensing, collision avoidance, and autofocus applications.",
  "application_notes": [],
  "package": {
    "pads": 10,
    "type": "T44",
    "size_x": 4000,
    "size_y": 4000,
    "size_z": 0
  },
  "power": [
    {
      "max": 3.3,
      "min": 2.7,
      "type": "system",
      "notes": "",
      "gnd_pad": [
        "1"
      ],
      "function": "",
      "direction": "input",
      "is_required": true,
      "max_current": "",
      "positive_pad": [
        "10"
      ]
    }
  ],
  "components": [
    {
      "url": "https://ams-osram.com/products/sensor-solutions/direct-time-of-flight-sensors-dtof/ams-tmf8806-1d-time-of-flight-sensor",
      "part": "TMF8806",
      "datasheet": "https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/17/ams-Osram-TMF8806.pdf",
      "manufacturer": "ams-Osram"
    }
  ],
  "pads": [
    {
      "pad": "1",
      "geometry": {
        "size_x": 1000,
        "size_y": 400,
        "center_x": -1500,
        "center_y": 1600
      },
      "functions": [
        {
          "note": "",
          "type": "power",
          "function": "GND",
          "direction": "input"
        }
      ]
    },
    {
      "pad": "2",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": -1600,
        "center_y": 800
      },
      "functions": []
    },
    {
      "pad": "3",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": -1600,
        "center_y": 0
      },
      "functions": [
        {
          "note": "internap pull-up resistor enables the sensor by default; connect to GND to disable",
          "type": "digital",
          "function": "EN",
          "direction": "input"
        }
      ]
    },
    {
      "pad": "4",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": -1600,
        "center_y": -800
      },
      "functions": [
        {
          "note": "",
          "type": "digital",
          "function": "I2C.CLK",
          "direction": "bidirectional",
          "interface": "I2C"
        }
      ]
    },
    {
      "pad": "5",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": -1600,
        "center_y": -1600
      },
      "functions": [
        {
          "note": "",
          "type": "digital",
          "function": "I2C.DAT",
          "direction": "bidirectional",
          "interface": "I2C"
        }
      ]
    },
    {
      "pad": "6",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": 1600,
        "center_y": -1600
      },
      "functions": []
    },
    {
      "pad": "7",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": 1600,
        "center_y": -800
      },
      "functions": []
    },
    {
      "pad": "8",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": 1600,
        "center_y": 0
      },
      "functions": []
    },
    {
      "pad": "9",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": 1600,
        "center_y": 800
      },
      "functions": [
        {
          "note": "programmable interrupt output",
          "type": "digital",
          "function": "INT",
          "direction": "output"
        }
      ]
    },
    {
      "pad": "10",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": 1600,
        "center_y": 1600
      },
      "functions": [
        {
          "note": "2.7-3.6V",
          "type": "power",
          "function": "V+",
          "direction": "input"
        }
      ]
    }
  ],
  "interfaces": [
    {
      "name": "I2C",
      "type": "I2C",
      "parameters": {
        "modes": [
          "slave"
        ],
        "addresses": [
          {
            "address": "0x41",
            "is_default": true
          }
        ],
        "address_bits": [
          7
        ],
        "max_clock_speed": "1MHz"
      },
      "pad_assignments": [
        {
          "pad": "4",
          "role": "bus",
          "function": "I2C.CLK"
        },
        {
          "pad": "5",
          "role": "bus",
          "function": "I2C.DAT"
        }
      ]
    }
  ],
  "twin": {
    "score": 1,
    "source": "// Digital twin for Sense.TOF — ams-OSRAM TMF8806 direct time-of-flight ranging\n// sensor. The physical world (object distance, return strength, die temp) is\n// driven by the controls; host calls return the result record exactly as\n// firmware reads it (distance in mm, reliability 0..63, result counter), and the\n// INT pin (pad 9) asserts when a measurement result is ready.\n//\n// Pad note: chip GPIO0 is on tile pad 3 with a 100k on-board pull-up to V+. It\n// is a hardware strap (holds GPIO0 high → selects 1.8-3.3V digital I/O for the\n// 2.7-3.6V rail), NOT an I2C-address strap and NOT a runtime control — so it is\n// not modeled as a driven pad output here.\nimport type { TileSim } from '../tileSim';\n\ninterface State {\n  // ── physical world ──\n  distance_mm: number; // object distance from the sensor\n  reliability: number; // return strength / confidence, 0..63\n  temperature: number; // die temperature °C\n\n  // ── operation ──\n  measuring: number; // ranging active (start/stop)\n  int_enabled: number; // result interrupt enabled\n\n  // ── configuration ──\n  mode: number; // 0 short(~200mm) 1 2500mm 2 5000mm\n  period_ms: number; // repetition period code (0x1E=30ms, 0=single-shot)\n  threshold: number; // detection threshold 0..63\n  kilo_iters: number; // iterations in thousands (SNR/power tradeoff)\n  result_number: number; // monotonic result counter (wraps 256)\n\n  // ── threshold-interrupt window ──\n  thr_int_on: number;\n  thr_low_mm: number;\n  thr_high_mm: number;\n  persistence: number;\n\n  calib_valid: number;\n\n  [field: string]: number;\n}\n\nconst I2C_ADDR = 0x41;\nconst PRESENCE_RELIABILITY_MIN = 32; // driver constant for \"object present\"\n\n// Max range for the active distance mode (datasheet algorithm modes).\nfunction maxRange(mode: number): number {\n  return mode === 0 ? 200 : mode === 2 ? 5000 : 2500;\n}\n// Effective return: beyond max range there's no target (distance 0, reliability 0).\nfunction effDistance(s: State): number {\n  return s.distance_mm <= maxRange(s.mode) ? s.distance_mm : 0;\n}\nfunction effReliability(s: State): number {\n  return s.distance_mm <= maxRange(s.mode) ? s.reliability : 0;\n}\n\n// Modeled signal-quality diagnostics [reference_hits, object_hits, crosstalk].\n// Reference hits are a ~constant internal channel; object hits scale with the\n// return strength (more iterations → more photons); crosstalk is the small,\n// calibrated optical-leakage floor. All inferred (no datasheet-exact values).\nfunction signalQuality(s: State): number[] {\n  const rel = effReliability(s);\n  const iterScale = s.kilo_iters / 900; // relative to the default integration\n  const referenceHits = Math.round(180000 * iterScale);\n  const objectHits = Math.round((rel / 63) * 150000 * iterScale);\n  const crosstalk = 480;\n  return [referenceHits, objectHits, crosstalk];\n}\n\nconst pick = (args: number[], i: number, cur: number) =>\n  args.length > i && Number.isFinite(args[i]) ? args[i] : cur;\n\nconst sim: TileSim<State> = {\n  tile: 'Sense.TOF',\n\n  defaultState: {\n    distance_mm: 1000,\n    reliability: 40,\n    temperature: 25,\n\n    measuring: 1,\n    int_enabled: 1,\n\n    mode: 1, // 2500 mm\n    period_ms: 0x1e, // 30 ms\n    threshold: 6,\n    kilo_iters: 900,\n    result_number: 0,\n\n    thr_int_on: 0,\n    thr_low_mm: 0,\n    thr_high_mm: 0,\n    persistence: 0,\n\n    calib_valid: 0,\n  },\n\n  controls: [\n    {\n      type: 'slider',\n      field: 'distance_mm',\n      label: 'Object distance',\n      min: 0,\n      max: 5000,\n      step: 10,\n      unit: 'mm',\n    },\n    { type: 'slider', field: 'reliability', label: 'Return strength', min: 0, max: 63, step: 1 },\n    {\n      type: 'slider',\n      field: 'temperature',\n      label: 'Temperature',\n      min: -40,\n      max: 85,\n      step: 1,\n      unit: '°C',\n    },\n    { type: 'slider', field: 'measuring', label: 'Measuring', min: 0, max: 1, step: 1 },\n    { type: 'slider', field: 'int_enabled', label: 'Result interrupt', min: 0, max: 1, step: 1 },\n  ],\n\n  hostCalls: {\n    // ── lifecycle ──\n    tile_sense_tof_find: () => ({ scalar: I2C_ADDR }),\n    tile_sense_tof_init: () => ({ scalar: 0, nextState: { mode: 1, period_ms: 0x1e } }),\n    tile_sense_tof_sleep: () => ({ nextState: { measuring: 0 } }),\n    tile_sense_tof_wake: () => ({ nextState: {} }),\n    tile_sense_tof_reset: () => ({ nextState: { result_number: 0 } }),\n    tile_sense_tof_start: () => ({ nextState: { measuring: 1 } }),\n    tile_sense_tof_stop: () => ({ nextState: { measuring: 0 } }),\n\n    // ── results ──\n    tile_sense_tof_get_distance_mm: ({ state }) => ({ scalar: effDistance(state) }),\n    tile_sense_tof_get_result: ({ state }) => ({\n      array: [effDistance(state), 0, effReliability(state), state.temperature, state.result_number],\n    }),\n    tile_sense_tof_get_result_flat: ({ state }) => ({\n      array: [effDistance(state), 0, effReliability(state), state.temperature, state.result_number],\n    }),\n    tile_sense_tof_measure_single: ({ state }) => ({\n      scalar: 1,\n      nextState: { result_number: (state.result_number + 1) & 0xff },\n    }),\n    tile_sense_tof_measure_single_flat: ({ state }) => ({\n      array: [\n        effDistance(state),\n        0,\n        effReliability(state),\n        state.temperature,\n        (state.result_number + 1) & 0xff,\n      ],\n    }),\n    tile_sense_tof_result_ready: ({ state }) => ({ scalar: state.measuring }),\n    tile_sense_tof_read_distance_with_confidence: ({ state }) => ({\n      array: [effDistance(state), Math.round((effReliability(state) * 100) / 63)],\n    }),\n    tile_sense_tof_is_object_within: ({ state, args }) => ({\n      scalar:\n        effDistance(state) > 0 &&\n        effDistance(state) <= pick(args, 0, maxRange(state.mode)) &&\n        effReliability(state) >= PRESENCE_RELIABILITY_MIN\n          ? 1\n          : 0,\n    }),\n    tile_sense_tof_wait_for_object: ({ state, args }) => ({\n      scalar:\n        effDistance(state) > 0 &&\n        effDistance(state) <= pick(args, 0, maxRange(state.mode)) &&\n        effReliability(state) >= PRESENCE_RELIABILITY_MIN\n          ? 1\n          : 0,\n    }),\n    tile_sense_tof_get_sys_clock_ticks: ({ state }) => ({\n      scalar: (state.result_number * 4700) >>> 0,\n    }),\n\n    // ── configuration ──\n    tile_sense_tof_set_distance_mode: ({ state, args }) => ({\n      nextState: { mode: pick(args, 0, state.mode) },\n    }),\n    tile_sense_tof_set_period: ({ state, args }) => ({\n      nextState: { period_ms: pick(args, 0, state.period_ms) },\n    }),\n    tile_sense_tof_set_kilo_iters: ({ state, args }) => ({\n      nextState: { kilo_iters: pick(args, 0, state.kilo_iters) },\n    }),\n    tile_sense_tof_set_threshold: ({ state, args }) => ({\n      nextState: { threshold: pick(args, 0, state.threshold) & 0x3f },\n    }),\n    tile_sense_tof_get_signal_quality: ({ state }) => ({ array: signalQuality(state) }),\n    tile_sense_tof_get_signal_quality_flat: ({ state }) => ({ array: signalQuality(state) }),\n    tile_sense_tof_set_threshold_interrupt: ({ args }) => {\n      const persistence = pick(args, 0, 0);\n      const low = pick(args, 1, 0);\n      const high = pick(args, 2, 0);\n      return {\n        scalar: low <= high ? 1 : 0,\n        nextState: {\n          persistence,\n          thr_low_mm: low,\n          thr_high_mm: high,\n          thr_int_on: low <= high ? 1 : 0,\n        },\n      };\n    },\n    tile_sense_tof_get_threshold_interrupt: ({ state }) => ({\n      array: [state.persistence, state.thr_low_mm, state.thr_high_mm],\n    }),\n\n    // ── calibration / state ──\n    tile_sense_tof_factory_calibrate: () => ({ scalar: 1, nextState: { calib_valid: 1 } }),\n    tile_sense_tof_set_calibration: () => ({ nextState: { calib_valid: 1 } }),\n    tile_sense_tof_get_calibration: () => ({ array: new Array(14).fill(0) }),\n    tile_sense_tof_save_state: () => ({ nextState: {} }),\n    tile_sense_tof_restore_state: () => ({ nextState: {} }),\n\n    // ── identity ──\n    tile_sense_tof_get_app_version: () => ({ array: [1, 2, 0] }),\n    tile_sense_tof_get_app_version_flat: () => ({ array: [1, 2, 0] }),\n    tile_sense_tof_get_serial_number: () => ({ array: [0, 0, 0, 0] }),\n    tile_sense_tof_get_serial_number_flat: () => ({ scalar: 0 }),\n\n    // ── histogram (diagnostic) ──\n    tile_sense_tof_read_histogram: () => ({ scalar: 0 }),\n    tile_sense_tof_read_histogram_flat: () => ({ array: [] }),\n  },\n\n  provenance: {\n    // canonical — datasheet result layout / conversions\n    tile_sense_tof_find: 'canonical', // I2C 0x41\n    tile_sense_tof_get_distance_mm: 'canonical', // distance = MSB<<8 | LSB (mm)\n    tile_sense_tof_get_result: 'canonical', // result record layout\n    tile_sense_tof_get_result_flat: 'canonical',\n    tile_sense_tof_measure_single_flat: 'canonical',\n    tile_sense_tof_read_distance_with_confidence: 'canonical', // reliability*100/63\n    tile_sense_tof_get_sys_clock_ticks: 'inferred', // fabricated monotonic ramp, not the real 4.7 MHz counter\n    tile_sense_tof_set_distance_mode: 'canonical', // algo byte / ranges\n    tile_sense_tof_set_period: 'canonical', // repetition period code\n    tile_sense_tof_set_kilo_iters: 'canonical', // cmd_data1/0 kIters\n    tile_sense_tof_set_threshold: 'canonical', // cmd_data3[5:0]\n    tile_sense_tof_set_threshold_interrupt: 'canonical', // WR_ADD_CONFIG format\n    // inferred — behavioral simplifications, not the exact chip bits/values\n    tile_sense_tof_result_ready: 'inferred', // returns \"measuring\", not the INT_STATUS bit\n    tile_sense_tof_get_signal_quality: 'inferred', // modeled hit counts\n    tile_sense_tof_get_signal_quality_flat: 'inferred',\n    // hallucinated — stubbed / opaque\n    tile_sense_tof_read_histogram: 'hallucinated',\n    tile_sense_tof_read_histogram_flat: 'hallucinated',\n    tile_sense_tof_get_serial_number: 'hallucinated',\n    tile_sense_tof_get_serial_number_flat: 'hallucinated',\n    tile_sense_tof_get_calibration: 'hallucinated',\n    tile_sense_tof_set_calibration: 'hallucinated',\n    tile_sense_tof_save_state: 'hallucinated',\n    tile_sense_tof_restore_state: 'hallucinated',\n    power: 'canonical', // TMF8806 Table 4 electrical characteristics\n    // (everything else defaults to \"inferred\")\n  },\n\n  // Each tick is a completed measurement while ranging.\n  deriveState(state) {\n    if (state.measuring === 1) return { result_number: (state.result_number + 1) & 0xff };\n    return {};\n  },\n\n  // INT pin: asserts on result-ready (interrupt enabled). With a threshold\n  // window configured, only fires when the object is inside [low, high].\n  // (Hardware INT is active-low; here 1 = asserted for clarity.)\n  padOutputs(state) {\n    const resultInt = state.measuring === 1 && state.int_enabled === 1;\n    const inWindow =\n      state.thr_int_on === 0 ||\n      (effDistance(state) >= state.thr_low_mm && effDistance(state) <= state.thr_high_mm);\n    return { INT: resultInt && inWindow ? 1 : 0 };\n  },\n\n  // Electrical (TMF8806 datasheet, 2.8 V): standby 85 µA; ranging average\n  // 25.8 mA (2.5 m / 33 ms / 900k iterations). VCSEL peaks ~230 mA internally.\n  power(state) {\n    const ranging = state.measuring === 1;\n    const ua = ranging ? 25800 : 85;\n    return {\n      draw_ua: ua,\n      rails: [\n        {\n          name: 'V+',\n          role: 'supply',\n          v_mv: 2800,\n          i_ua: ua,\n          pads: ['10'],\n          note: ranging ? 'ranging avg (VCSEL peak ~230 mA)' : 'standby',\n        },\n      ],\n    };\n  },\n};\n\nexport default sim;\n",
    "status": "validated",
    "updated_at": "2026-06-22T12:44:21.678Z"
  },
  "config": {
    "interrupt": {
      "kind": "boolean",
      "group": "Sidebands",
      "label": "Use INT line",
      "binding": {
        "pad": "9",
        "kind": "output"
      },
      "default": false,
      "options": [
        {
          "value": false,
          "firmware_contract": [
            {
              "via": "i2c",
              "type": "register",
              "value": "disabled",
              "register": "INT_CONFIG"
            }
          ]
        },
        {
          "value": true,
          "netlist": {
            "expects": [
              {
                "to": {
                  "kind": "matchFunction",
                  "function": "GPIO",
                  "capabilities": [
                    "EXTI"
                  ]
                },
                "tag": "interrupt.attached",
                "from": {
                  "pad": "9",
                  "kind": "tile"
                },
                "role": "interrupt"
              }
            ]
          },
          "firmware_contract": [
            {
              "via": "i2c",
              "type": "register",
              "value": "enabled",
              "register": "INT_CONFIG"
            }
          ]
        }
      ]
    },
    "controlEnable": {
      "kind": "boolean",
      "group": "Sidebands",
      "label": "Core-controlled enable",
      "binding": {
        "pad": "3",
        "kind": "intent"
      },
      "default": false,
      "options": [
        {
          "value": false,
          "firmware_contract": [
            {
              "text": "EN left at its default state; firmware does not power-cycle",
              "type": "note"
            }
          ]
        },
        {
          "value": true,
          "netlist": {
            "expects": [
              {
                "to": {
                  "kind": "matchFunction",
                  "function": "GPIO",
                  "capabilities": [
                    "output"
                  ]
                },
                "tag": "controlEnable.attached",
                "from": {
                  "pad": "3",
                  "kind": "tile"
                },
                "role": "data"
              }
            ]
          },
          "firmware_contract": [
            {
              "text": "Firmware drives EN to power-cycle the sensor as needed",
              "type": "note"
            }
          ]
        }
      ],
      "description": "When true, pad 3 (EN) is wired to a Core GPIO output so firmware can power-cycle the sensor (e.g., for I2C address re-assignment in multi-sensor setups). When false, the chip stays in its reset / always-on state per the catalog default."
    }
  }
}