{
  "doc": "PoseLab Core serial packet protocol — AnzdF B-method (SOT)",
  "sot": true,
  "extractedFrom": "A.hw-verify01/src/commPacket.h",
  "mirroredBy": "W04-nextjs-SaaS-Alpha/standalone/seat/_shared/graph-heatmap/serial-module.js",
  "updated": "2026-07-05",
  "transport": {
    "baud": 2000000,
    "byteOrder": "little-endian",
    "fps": 50,
    "framePeriodMs": 20,
    "rxBufBytes": 4096,
    "txBufBytes": 2048
  },
  "sync": {
    "header": ["0xFF", "0xFF"],
    "tail": ["0x00", "0xFE"],
    "validValMax": "0xEF"
  },
  "sensorEncoding": {
    "bytesPerCell": 2,
    "hi": "value / 100",
    "lo": "value % 100",
    "reason": "0xF0+ is reserved as sync words; >>8 raw high byte can collide. /100,%100 keeps hi<=40",
    "noiseFloor": "value < 5 -> 0",
    "rangeAdc": [0, 4095]
  },
  "packet": {
    "lengthFormula": "10 + N*2 + 10 + 2",
    "fields": [
      { "idx": 0,  "name": "sync0",      "size": 1, "type": "u8",    "value": "0xFF" },
      { "idx": 1,  "name": "sync1",      "size": 1, "type": "u8",    "value": "0xFF" },
      { "idx": 2,  "name": "rows",       "size": 1, "type": "u8",    "note": "NUM_REAL_HEIGHT (seat=3). grid boundary rows, not cell count" },
      { "idx": 3,  "name": "cols",       "size": 1, "type": "u8",    "note": "NUM_REAL_WIDTH (seat=14)" },
      { "idx": 4,  "name": "boardId",    "size": 1, "type": "u8",    "note": "fixed 0. dip_decimal is corrupted by right-side pressure, so unused" },
      { "idx": 5,  "name": "rollingIdx", "size": 1, "type": "u8",    "note": "build_count % 200 (0~199)" },
      { "idx": 6,  "name": "timestamp",  "size": 4, "type": "u32le", "note": "millis()" },
      { "idx": 10, "name": "sensorData", "size": "N*2", "type": "u8[]", "note": "N cells x (hi,lo)" },
      { "idx": "10+N*2+0", "name": "cofX",     "size": 2, "type": "s16le", "note": "0.0~1.0 x10000, default 5000 (center) when no active cell" },
      { "idx": "10+N*2+2", "name": "cofY",     "size": 2, "type": "s16le", "note": "0.0~1.0 x10000" },
      { "idx": "10+N*2+4", "name": "maxVal",   "size": 2, "type": "u16le", "note": "raw ADC max" },
      { "idx": "10+N*2+6", "name": "velocity", "size": 2, "type": "s16le", "note": "maxVal - prev_maxVal" },
      { "idx": "10+N*2+8", "name": "avg",      "size": 2, "type": "u16le", "note": "avg of cells above COF_THRESHOLD(5)" },
      { "idx": "end-2", "name": "tail0", "size": 1, "type": "u8", "value": "0x00" },
      { "idx": "end-1", "name": "tail1", "size": 1, "type": "u8", "value": "0xFE" }
    ]
  },
  "anzd": {
    "cofScale": 10000,
    "cofThreshold": 5,
    "cofDefaultCenter": 5000
  },
  "instances": {
    "seat":   { "N": 32,  "rows": 3,  "cols": 14, "packetBytes": 86,  "cellLayout": "10-14-8" },
    "matrix": { "N": 120, "rows": 12, "cols": 10, "packetBytes": 262 }
  },
  "multiBoard": {
    "transport": "RS485 (Serial1)",
    "note": "MainBoard receives Sub packets and relays them. boardId cycles the tx-grant token (MainBoard_Rx1/SubBoard_Rx1)"
  }
}
