{
  "product": "STEPFOC",
  "firmware": "V108",
  "commit": "46b4911dd8f7",
  "byte_order": "big-endian",
  "bitrate": 1000000,
  "commands": [
    {
      "key": "IN_DATA_PACK_1",
      "id": 2,
      "name": "Send_data_pack_1",
      "description": "The motion command. Its length decides the control mode the driver enters: 8 bytes command a position with a speed and a current feedforward, 5 bytes a speed with a current feedforward, 2 bytes a current. Send 0 for a feedforward you do not use. A frame of any other length is ignored (the driver flags it as a wrong data length) but still answered.",
      "forms": [
        {
          "direction": "host",
          "length": 8,
          "remote": false,
          "title": "position setpoint with speed and current feedforward (position mode)",
          "fields": [
            {
              "name": "Position setpoint",
              "byte": 0,
              "size": 3,
              "bits": null,
              "width": 24,
              "signed": true,
              "float": false,
              "lsb_first": false,
              "meaning": "encoder ticks"
            },
            {
              "name": "Speed feedforward",
              "byte": 3,
              "size": 3,
              "bits": null,
              "width": 24,
              "signed": true,
              "float": false,
              "lsb_first": false,
              "meaning": "encoder ticks/s"
            },
            {
              "name": "Current feedforward",
              "byte": 6,
              "size": 2,
              "bits": null,
              "width": 16,
              "signed": true,
              "float": false,
              "lsb_first": false,
              "meaning": "mA"
            }
          ]
        },
        {
          "direction": "host",
          "length": 5,
          "remote": false,
          "title": "speed setpoint with current feedforward (velocity mode)",
          "fields": [
            {
              "name": "Speed setpoint",
              "byte": 0,
              "size": 3,
              "bits": null,
              "width": 24,
              "signed": true,
              "float": false,
              "lsb_first": false,
              "meaning": "encoder ticks/s"
            },
            {
              "name": "Current feedforward",
              "byte": 3,
              "size": 2,
              "bits": null,
              "width": 16,
              "signed": true,
              "float": false,
              "lsb_first": false,
              "meaning": "mA"
            }
          ]
        },
        {
          "direction": "host",
          "length": 2,
          "remote": false,
          "title": "current setpoint (current mode)",
          "fields": [
            {
              "name": "Current setpoint",
              "byte": 0,
              "size": 2,
              "bits": null,
              "width": 16,
              "signed": true,
              "float": false,
              "lsb_first": false,
              "meaning": "mA"
            }
          ]
        }
      ]
    },
    {
      "key": "IN_DATA_PACK_PD",
      "id": 4,
      "name": "Send_data_pack_PD",
      "description": "Puts the driver in impedance (PD) mode and sets the position, speed and current feedforward of the PD loop, whose gains are KP and KD (Send_PD_Gains).",
      "forms": [
        {
          "direction": "host",
          "length": 8,
          "remote": false,
          "title": "command",
          "fields": [
            {
              "name": "Position setpoint",
              "byte": 0,
              "size": 3,
              "bits": null,
              "width": 24,
              "signed": true,
              "float": false,
              "lsb_first": false,
              "meaning": "encoder ticks"
            },
            {
              "name": "Speed setpoint",
              "byte": 3,
              "size": 3,
              "bits": null,
              "width": 24,
              "signed": true,
              "float": false,
              "lsb_first": false,
              "meaning": "encoder ticks/s"
            },
            {
              "name": "Current feedforward",
              "byte": 6,
              "size": 2,
              "bits": null,
              "width": 16,
              "signed": true,
              "float": false,
              "lsb_first": false,
              "meaning": "mA"
            }
          ]
        }
      ]
    },
    {
      "key": "IN_HALL_INDEX",
      "id": 31,
      "name": "Send_data_pack_HALL",
      "description": "Homing against a sensor: the driver spins at the given speed until the input on the ADDITIONAL2 pin shows the trigger condition, then latches the position it reached as its position setpoint and holds it. With a level trigger (0 or 1) it holds only while the pin stays at that level: when the pin changes back, the driver spins on again until the level returns. The edge trigger (2) holds after the first edge. Sending the command again while the driver is already in HALL mode does not re-arm it; leave the mode first.",
      "forms": [
        {
          "direction": "host",
          "length": 4,
          "remote": false,
          "title": "command",
          "fields": [
            {
              "name": "Speed",
              "byte": 0,
              "size": 3,
              "bits": null,
              "width": 24,
              "signed": true,
              "float": false,
              "lsb_first": false,
              "meaning": "encoder ticks/s; the sign gives the direction"
            },
            {
              "name": "Trigger",
              "byte": 3,
              "size": 1,
              "bits": null,
              "width": 8,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "0 = trigger when the pin is low, 1 = when it is high, 2 = on any edge"
            }
          ]
        }
      ]
    },
    {
      "key": "OUT_DATA_PACK_1",
      "id": 3,
      "name": "Respond_data_pack_1",
      "description": "The driver's answer to Send_data_pack_1 and Send_data_pack_PD: where the motor is, how fast it turns and the current it draws. The Python library stores them as `position`, `speed` and `current`.",
      "forms": [
        {
          "direction": "driver",
          "length": 8,
          "remote": false,
          "title": "driver feedback",
          "fields": [
            {
              "name": "position",
              "byte": 0,
              "size": 3,
              "bits": null,
              "width": 24,
              "signed": true,
              "float": false,
              "lsb_first": false,
              "meaning": "encoder ticks"
            },
            {
              "name": "speed",
              "byte": 3,
              "size": 3,
              "bits": null,
              "width": 24,
              "signed": true,
              "float": false,
              "lsb_first": false,
              "meaning": "encoder ticks/s"
            },
            {
              "name": "current",
              "byte": 6,
              "size": 2,
              "bits": null,
              "width": 16,
              "signed": true,
              "float": false,
              "lsb_first": false,
              "meaning": "Iq in mA"
            }
          ]
        }
      ]
    },
    {
      "key": "OUT_DATA_HALL",
      "id": 32,
      "name": "Respond_data_hall",
      "description": "The driver's answer to Send_data_pack_HALL: the position and the state of the sensor input. Python attributes: `position`, `HALL_trigger`, `additional_pin2`, `hall_index`.",
      "forms": [
        {
          "direction": "driver",
          "length": 4,
          "remote": false,
          "title": "driver feedback",
          "fields": [
            {
              "name": "position",
              "byte": 0,
              "size": 3,
              "bits": null,
              "width": 24,
              "signed": true,
              "float": false,
              "lsb_first": false,
              "meaning": "encoder ticks"
            },
            {
              "name": "HALL_trigger",
              "byte": 3,
              "size": 1,
              "bits": [
                7
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "1 while the driver waits for the trigger (with a level trigger, also again after the pin left the level), 0 while it holds"
            },
            {
              "name": "additional_pin2",
              "byte": 3,
              "size": 1,
              "bits": [
                6
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "the raw level of the ADDITIONAL2 pin"
            },
            {
              "name": "hall_index",
              "byte": 3,
              "size": 1,
              "bits": [
                5
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "1 once the trigger has been seen (latched)"
            }
          ]
        }
      ]
    },
    {
      "key": "IN_KP_KD",
      "id": 16,
      "name": "Send_PD_Gains",
      "description": "Sets KP and KD, the gains of the impedance (PD) loop. Keep them with Send_Save_config.",
      "forms": [
        {
          "direction": "host",
          "length": 8,
          "remote": false,
          "title": "command",
          "fields": [
            {
              "name": "KP",
              "byte": 0,
              "size": 4,
              "bits": null,
              "width": 32,
              "signed": false,
              "float": true,
              "lsb_first": false,
              "meaning": ""
            },
            {
              "name": "KD",
              "byte": 4,
              "size": 4,
              "bits": null,
              "width": 32,
              "signed": false,
              "float": true,
              "lsb_first": false,
              "meaning": ""
            }
          ]
        }
      ]
    },
    {
      "key": "IN_KIIQ_KPIQ",
      "id": 17,
      "name": "Send_Current_Gains",
      "description": "Sets Kp and Ki of the PI current loops (Iq and Id).",
      "forms": [
        {
          "direction": "host",
          "length": 8,
          "remote": false,
          "title": "command",
          "fields": [
            {
              "name": "Kp_iq",
              "byte": 0,
              "size": 4,
              "bits": null,
              "width": 32,
              "signed": false,
              "float": true,
              "lsb_first": false,
              "meaning": ""
            },
            {
              "name": "Ki_iq",
              "byte": 4,
              "size": 4,
              "bits": null,
              "width": 32,
              "signed": false,
              "float": true,
              "lsb_first": false,
              "meaning": ""
            }
          ]
        }
      ]
    },
    {
      "key": "IN_KPV_KIV",
      "id": 18,
      "name": "Send_Velocity_Gains",
      "description": "Sets Kp and Ki of the PI velocity loop.",
      "forms": [
        {
          "direction": "host",
          "length": 8,
          "remote": false,
          "title": "command",
          "fields": [
            {
              "name": "Kpv",
              "byte": 0,
              "size": 4,
              "bits": null,
              "width": 32,
              "signed": false,
              "float": true,
              "lsb_first": false,
              "meaning": ""
            },
            {
              "name": "Kiv",
              "byte": 4,
              "size": 4,
              "bits": null,
              "width": 32,
              "signed": false,
              "float": true,
              "lsb_first": false,
              "meaning": ""
            }
          ]
        }
      ]
    },
    {
      "key": "IN_KPP",
      "id": 19,
      "name": "Send_Position_Gains",
      "description": "Sets Kp of the P position loop.",
      "forms": [
        {
          "direction": "host",
          "length": 4,
          "remote": false,
          "title": "command",
          "fields": [
            {
              "name": "Kpp",
              "byte": 0,
              "size": 4,
              "bits": null,
              "width": 32,
              "signed": false,
              "float": true,
              "lsb_first": false,
              "meaning": ""
            }
          ]
        }
      ]
    },
    {
      "key": "IN_LIMITS",
      "id": 20,
      "name": "Send_Limits",
      "description": "Sets the speed limit and the current limit the loops may reach. The firmware stores the current limit as sent: unlike `#Ilim` over UART, it is not clamped to the driver's maximum of 2800 mA. Send 2800 mA or less, and no more than the motor's rated current.",
      "forms": [
        {
          "direction": "host",
          "length": 8,
          "remote": false,
          "title": "command",
          "fields": [
            {
              "name": "Speed limit",
              "byte": 0,
              "size": 4,
              "bits": null,
              "width": 32,
              "signed": false,
              "float": true,
              "lsb_first": false,
              "meaning": "encoder ticks/s"
            },
            {
              "name": "Current limit",
              "byte": 4,
              "size": 4,
              "bits": null,
              "width": 32,
              "signed": false,
              "float": true,
              "lsb_first": false,
              "meaning": "mA"
            }
          ]
        }
      ]
    },
    {
      "key": "IN_KT",
      "id": 22,
      "name": "Send_Kt",
      "description": "Sets the motor's torque constant Kt, which the driver uses to estimate torque from current.",
      "forms": [
        {
          "direction": "host",
          "length": 4,
          "remote": false,
          "title": "command",
          "fields": [
            {
              "name": "Kt",
              "byte": 0,
              "size": 4,
              "bits": null,
              "width": 32,
              "signed": false,
              "float": true,
              "lsb_first": false,
              "meaning": "Nm/A"
            }
          ]
        }
      ]
    },
    {
      "key": "IN_VLIM",
      "id": 34,
      "name": "Send_Voltage_Limit",
      "description": "Sets the voltage limit of the current loops (the same value as `#Vlim`), in mV. 0 means use the supply voltage. Added in firmware 105.",
      "forms": [
        {
          "direction": "host",
          "length": 4,
          "remote": false,
          "title": "command",
          "fields": [
            {
              "name": "Voltage limit",
              "byte": 0,
              "size": 4,
              "bits": null,
              "width": 32,
              "signed": true,
              "float": false,
              "lsb_first": false,
              "meaning": "mV; 0 = Vbus"
            }
          ]
        }
      ]
    },
    {
      "key": "OUT_IN_PING",
      "id": 10,
      "name": "Send_Respond_Ping",
      "description": "Asks the driver to answer with an empty frame, to see that it is on the bus.",
      "forms": [
        {
          "direction": "host",
          "length": 0,
          "remote": true,
          "title": "status request (RTR)",
          "fields": []
        },
        {
          "direction": "driver",
          "length": 0,
          "remote": false,
          "title": "reply",
          "fields": []
        }
      ]
    },
    {
      "key": "OUT_IN_TEMPERATURE_CAN",
      "id": 23,
      "name": "Send_Respond_Temperature",
      "description": "Asks for the thermistor temperature. Meaningful only with the thermistor enabled (`#Term 1`).",
      "forms": [
        {
          "direction": "host",
          "length": 0,
          "remote": true,
          "title": "status request (RTR)",
          "fields": []
        },
        {
          "direction": "driver",
          "length": 2,
          "remote": false,
          "title": "reply",
          "fields": [
            {
              "name": "temperature",
              "byte": 0,
              "size": 2,
              "bits": null,
              "width": 16,
              "signed": true,
              "float": false,
              "lsb_first": false,
              "meaning": "\u00b0C"
            }
          ]
        }
      ]
    },
    {
      "key": "OUT_IN_VOLTAGE_CAN",
      "id": 24,
      "name": "Send_Respond_Voltage",
      "description": "Asks for the supply voltage.",
      "forms": [
        {
          "direction": "host",
          "length": 0,
          "remote": true,
          "title": "status request (RTR)",
          "fields": []
        },
        {
          "direction": "driver",
          "length": 2,
          "remote": false,
          "title": "reply",
          "fields": [
            {
              "name": "voltage",
              "byte": 0,
              "size": 2,
              "bits": null,
              "width": 16,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "mV"
            }
          ]
        }
      ]
    },
    {
      "key": "OUT_IN_DEVICE_INFO_CAN",
      "id": 25,
      "name": "Send_Respond_Device_Info",
      "description": "Asks for the driver's identity: hardware version and batch date from the EEPROM, the serial number, and the firmware release (from firmware 105 on this is the build's own version; before, a value from the EEPROM). This firmware does not read the serial number back from the EEPROM at power-up: it reports 1 until `#SERNUM` sets it.",
      "forms": [
        {
          "direction": "host",
          "length": 0,
          "remote": true,
          "title": "status request (RTR)",
          "fields": []
        },
        {
          "direction": "driver",
          "length": 7,
          "remote": false,
          "title": "reply",
          "fields": [
            {
              "name": "HARDWARE_VERSION",
              "byte": 0,
              "size": 1,
              "bits": null,
              "width": 8,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "PCB revision"
            },
            {
              "name": "BATCH_DATE",
              "byte": 1,
              "size": 1,
              "bits": null,
              "width": 8,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "production batch"
            },
            {
              "name": "SOFTWARE_VERSION",
              "byte": 2,
              "size": 1,
              "bits": null,
              "width": 8,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "firmware release, e.g. 108"
            },
            {
              "name": "SERIAL_NUMBER",
              "byte": 3,
              "size": 4,
              "bits": null,
              "width": 32,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "serial number as held in RAM: 1 after a power-up"
            }
          ]
        }
      ]
    },
    {
      "key": "OUT_IN_STATE_OF_ERRORS_CAN",
      "id": 26,
      "name": "Send_Respond_State_of_Errors",
      "description": "Asks for every error flag and the calibrated, activated and watchdog states.",
      "forms": [
        {
          "direction": "host",
          "length": 0,
          "remote": true,
          "title": "status request (RTR)",
          "fields": []
        },
        {
          "direction": "driver",
          "length": 2,
          "remote": false,
          "title": "reply",
          "fields": [
            {
              "name": "error",
              "byte": 0,
              "size": 1,
              "bits": [
                7
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "any error is active"
            },
            {
              "name": "temperature_error",
              "byte": 0,
              "size": 1,
              "bits": [
                6
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "motor over the temperature limit"
            },
            {
              "name": "encoder_error",
              "byte": 0,
              "size": 1,
              "bits": [
                5
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "the encoder reports no magnet (too weak a field)"
            },
            {
              "name": "vbus_error",
              "byte": 0,
              "size": 1,
              "bits": [
                4
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "supply voltage outside the limits"
            },
            {
              "name": "driver_error",
              "byte": 0,
              "size": 1,
              "bits": [
                3
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "reserved; never set by this firmware (STEPFOC does not read a fault output of its driver stage)"
            },
            {
              "name": "velocity_error",
              "byte": 0,
              "size": 1,
              "bits": [
                2
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "speed above 30,000,000 ticks/s, a fixed threshold (not #Vlimit)"
            },
            {
              "name": "current_error",
              "byte": 0,
              "size": 1,
              "bits": [
                1
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "reserved; never set by this firmware"
            },
            {
              "name": "estop_error",
              "byte": 0,
              "size": 1,
              "bits": [
                0
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "Send_ESTOP was received"
            },
            {
              "name": "calibrated",
              "byte": 1,
              "size": 1,
              "bits": [
                7
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "the motor is calibrated"
            },
            {
              "name": "activated",
              "byte": 1,
              "size": 1,
              "bits": [
                6
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "the controller is activated"
            },
            {
              "name": "watchdog_error",
              "byte": 1,
              "size": 1,
              "bits": [
                5
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "the watchdog ran out"
            }
          ]
        }
      ]
    },
    {
      "key": "OUT_IN_IQ",
      "id": 27,
      "name": "Send_Respond_Iq_data",
      "description": "Asks for the measured Iq current.",
      "forms": [
        {
          "direction": "host",
          "length": 0,
          "remote": true,
          "title": "status request (RTR)",
          "fields": []
        },
        {
          "direction": "driver",
          "length": 2,
          "remote": false,
          "title": "reply",
          "fields": [
            {
              "name": "current",
              "byte": 0,
              "size": 2,
              "bits": null,
              "width": 16,
              "signed": true,
              "float": false,
              "lsb_first": false,
              "meaning": "mA"
            }
          ]
        }
      ]
    },
    {
      "key": "OUT_IN_ENCODER",
      "id": 28,
      "name": "Send_Respond_Encoder_data",
      "description": "Asks for the position and the filtered speed, as 32-bit values. Python attributes `position` and `speed`.",
      "forms": [
        {
          "direction": "host",
          "length": 0,
          "remote": true,
          "title": "status request (RTR)",
          "fields": []
        },
        {
          "direction": "driver",
          "length": 8,
          "remote": false,
          "title": "reply",
          "fields": [
            {
              "name": "position",
              "byte": 0,
              "size": 4,
              "bits": null,
              "width": 32,
              "signed": true,
              "float": false,
              "lsb_first": false,
              "meaning": "encoder ticks"
            },
            {
              "name": "speed",
              "byte": 4,
              "size": 4,
              "bits": null,
              "width": 32,
              "signed": true,
              "float": false,
              "lsb_first": false,
              "meaning": "encoder ticks/s"
            }
          ]
        }
      ]
    },
    {
      "key": "OUT_IN_KT",
      "id": 33,
      "name": "Send_Respond_Kt",
      "description": "Asks for the torque constant Kt the driver uses. Added in firmware 105.",
      "forms": [
        {
          "direction": "host",
          "length": 0,
          "remote": true,
          "title": "status request (RTR)",
          "fields": []
        },
        {
          "direction": "driver",
          "length": 4,
          "remote": false,
          "title": "reply",
          "fields": [
            {
              "name": "Kt",
              "byte": 0,
              "size": 4,
              "bits": null,
              "width": 32,
              "signed": false,
              "float": true,
              "lsb_first": false,
              "meaning": "Nm/A"
            }
          ]
        }
      ]
    },
    {
      "key": "OUT_HEARTBEAT",
      "id": 9,
      "name": "Respond_Heartbeat",
      "description": "The frame the driver sends on its own at the heartbeat interval (Send_Heartbeat_Setup); empty. Off by default. Any other frame the driver sends resets the heartbeat timer, so a driver that answers motion commands at a high rate never sends a heartbeat.",
      "forms": [
        {
          "direction": "driver",
          "length": 0,
          "remote": false,
          "title": "driver feedback",
          "fields": []
        }
      ]
    },
    {
      "key": "IN_CAN_ID",
      "id": 11,
      "name": "Send_CAN_ID",
      "description": "Gives the driver a new node ID, 0 to 13 (the firmware accepts up to 15, but the bootloader reserves 14 and 15: a driver on one of those cannot be updated over CAN). Saving the configuration (Send_Save_config) also writes the ID into the bootloader's board ID. The driver answers to the new ID at once; the ID is kept over a power cycle only after Send_Save_config.",
      "forms": [
        {
          "direction": "host",
          "length": 1,
          "remote": false,
          "title": "command",
          "fields": [
            {
              "name": "ID",
              "byte": 0,
              "size": 1,
              "bits": null,
              "width": 8,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "0 to 13; 14 and 15 are accepted but reserved by the bootloader"
            }
          ]
        }
      ]
    },
    {
      "key": "IN_ESTOP",
      "id": 0,
      "name": "Send_ESTOP",
      "description": "Stops the motor and raises the E-stop error, which stays until Send_Clear_Error.",
      "forms": [
        {
          "direction": "host",
          "length": 0,
          "remote": false,
          "title": "command",
          "fields": []
        }
      ]
    },
    {
      "key": "IN_IDLE",
      "id": 12,
      "name": "Send_Idle",
      "description": "Puts the driver in idle mode (the motor coasts or brakes, see Send_Settings).",
      "forms": [
        {
          "direction": "host",
          "length": 0,
          "remote": false,
          "title": "command",
          "fields": []
        }
      ]
    },
    {
      "key": "IN_SAVE_CONFIG",
      "id": 13,
      "name": "Send_Save_config",
      "description": "Writes the current configuration (gains, limits, calibration, CAN ID, heartbeat, watchdog, gripper flag) to the EEPROM, and the CAN ID into the bootloader's board ID. The idle behaviour of Send_Settings and the current loop bandwidth are not kept. It also writes the serial number held in RAM, which is 1 after a power-up unless `#SERNUM` set it, over the stored one.",
      "forms": [
        {
          "direction": "host",
          "length": 0,
          "remote": false,
          "title": "command",
          "fields": []
        }
      ]
    },
    {
      "key": "IN_RESET",
      "id": 14,
      "name": "Send_Reset",
      "description": "Resets the driver. From firmware 105 the reset lands in the bootloader, which waits 2 s for a flashing host and then starts the firmware again; this is how a firmware update begins.",
      "forms": [
        {
          "direction": "host",
          "length": 0,
          "remote": false,
          "title": "command",
          "fields": []
        }
      ]
    },
    {
      "key": "IN_CLEAR_ERROR",
      "id": 1,
      "name": "Send_Clear_Error",
      "description": "Clears the error flags and goes to idle. An error whose cause is still present comes straight back. A calibration error is cleared only by calibrating. A gripper E-stop comes back until the E-stop bit of Send_gripper_data_pack is 0 again.",
      "forms": [
        {
          "direction": "host",
          "length": 0,
          "remote": false,
          "title": "command",
          "fields": []
        }
      ]
    },
    {
      "key": "IN_WATCHDOG_TIMEOUT",
      "id": 15,
      "name": "Send_Watchdog",
      "description": "Sets the watchdog: when the time runs out the driver reports a watchdog error and stops. Every valid motion frame (Send_data_pack_1, Send_data_pack_PD, Send_data_pack_HALL, Send_gripper_data_pack) and every status request the driver answers resets it; the setting frames (gains, limits, Idle, Clear, Save, Settings and Send_Watchdog itself) do not. 0 turns it off (the default). The action byte is stored but not acted on by this firmware: the driver always stops into its idle behaviour. Send_Save_config keeps the watchdog, and a kept watchdog runs from power-up: a host that sends no motion frame or status request within the time finds the driver in watchdog error, and clears it with Send_Clear_Error.",
      "forms": [
        {
          "direction": "host",
          "length": 5,
          "remote": false,
          "title": "command",
          "fields": [
            {
              "name": "Time",
              "byte": 0,
              "size": 4,
              "bits": null,
              "width": 32,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "ms; 0 = off"
            },
            {
              "name": "Action",
              "byte": 4,
              "size": 1,
              "bits": null,
              "width": 8,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "0 idle, 1 hold, 2 brake (stored only)"
            }
          ]
        }
      ]
    },
    {
      "key": "HEARTBEAT_SETUP",
      "id": 30,
      "name": "Send_Heartbeat_Setup",
      "description": "Sets the heartbeat interval in ms; 0 turns the heartbeat off (the default). Kept by Send_Save_config.",
      "forms": [
        {
          "direction": "host",
          "length": 4,
          "remote": false,
          "title": "command",
          "fields": [
            {
              "name": "Interval",
              "byte": 0,
              "size": 4,
              "bits": null,
              "width": 32,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "ms; 0 = off"
            }
          ]
        }
      ]
    },
    {
      "key": "IN_SETTINGS",
      "id": 35,
      "name": "Send_Settings",
      "description": "Sets boolean settings that have no command of their own: a mask byte says which settings the frame touches, a value byte gives their new values; settings outside the mask are left alone. Bit 0 is the idle behaviour: 1 brake (phases shorted), 0 coast. The setting is not kept by Send_Save_config: at power-up the driver brakes. Added in firmware 107. The Python library has no call for it yet.",
      "forms": [
        {
          "direction": "host",
          "length": 2,
          "remote": false,
          "title": "command",
          "fields": [
            {
              "name": "Mask",
              "byte": 0,
              "size": 1,
              "bits": null,
              "width": 8,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "bit 0 = write the idle behaviour"
            },
            {
              "name": "Value",
              "byte": 1,
              "size": 1,
              "bits": null,
              "width": 8,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "bit 0: 1 brake, 0 coast"
            }
          ]
        }
      ]
    },
    {
      "key": "IN_GRIPPER_DATA_PACK",
      "id": 61,
      "name": "Send_gripper_data_pack",
      "description": "The gripper command, for a driver set up as a gripper (`#Gripper 1`, saved). Position, speed and the current limit of the move, plus the activate, action and E-stop bits. A move is carried out only while the gripper is calibrated, activated and the action bit is 1. An empty frame (0 bytes) only asks for the state.",
      "forms": [
        {
          "direction": "host",
          "length": 5,
          "remote": false,
          "title": "command",
          "fields": [
            {
              "name": "Position",
              "byte": 0,
              "size": 1,
              "bits": null,
              "width": 8,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "0 fully open, 255 fully closed"
            },
            {
              "name": "Speed",
              "byte": 1,
              "size": 1,
              "bits": null,
              "width": 8,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "0 slowest, 255 fastest"
            },
            {
              "name": "Current",
              "byte": 2,
              "size": 2,
              "bits": null,
              "width": 16,
              "signed": true,
              "float": false,
              "lsb_first": false,
              "meaning": "current limit of the move in mA; not clamped by the firmware, not even to the driver's 2800 mA; see the gripper manual for the safe range"
            },
            {
              "name": "Activate",
              "byte": 4,
              "size": 1,
              "bits": [
                7
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "1 activate the gripper, 0 deactivate"
            },
            {
              "name": "Action",
              "byte": 4,
              "size": 1,
              "bits": [
                6
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "1 go to the position, 0 idle"
            },
            {
              "name": "E-stop",
              "byte": 4,
              "size": 1,
              "bits": [
                5
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "1 raises the gripper E-stop error, again on every control cycle until a frame with this bit 0 arrives; then Send_Clear_Error"
            },
            {
              "name": "Release direction",
              "byte": 4,
              "size": 1,
              "bits": [
                4
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "read by the firmware but not used"
            }
          ]
        },
        {
          "direction": "host",
          "length": 0,
          "remote": false,
          "title": "status poll",
          "fields": []
        }
      ]
    },
    {
      "key": "IN_GRIPPER_CALIB",
      "id": 62,
      "name": "Send_gripper_calibrate",
      "description": "Starts the gripper calibration (the jaws find their end stops). Needed after every power-up.",
      "forms": [
        {
          "direction": "host",
          "length": 0,
          "remote": false,
          "title": "command",
          "fields": []
        }
      ]
    },
    {
      "key": "OUT_GRIPPER_DATA_PACK",
      "id": 60,
      "name": "Respond_Gripper_data_pack",
      "description": "The driver's answer to Send_gripper_data_pack: position, measured current, and the state bits. Python attributes `gripper_position`, `gripper_current`, `gripper_activated`, `gripper_action_status`, `gripper_object_detection`, `gripper_temperature_error`, `gripper_timeout_error`, `gripper_estop_error`, `gripper_calibrated`.",
      "forms": [
        {
          "direction": "driver",
          "length": 4,
          "remote": false,
          "title": "driver feedback",
          "fields": [
            {
              "name": "gripper_position",
              "byte": 0,
              "size": 1,
              "bits": null,
              "width": 8,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "0 fully open, 255 fully closed"
            },
            {
              "name": "gripper_current",
              "byte": 1,
              "size": 2,
              "bits": null,
              "width": 16,
              "signed": true,
              "float": false,
              "lsb_first": false,
              "meaning": "measured current in mA"
            },
            {
              "name": "gripper_activated",
              "byte": 3,
              "size": 1,
              "bits": [
                7
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "1 activated"
            },
            {
              "name": "gripper_action_status",
              "byte": 3,
              "size": 1,
              "bits": [
                6
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "the action bit of the last Send_gripper_data_pack (1 after #Grippos); the driver never clears it, so it stays 1 after the move ends: the object detection tells when the move has ended"
            },
            {
              "name": "gripper_object_detection",
              "byte": 3,
              "size": 1,
              "bits": [
                5,
                4
              ],
              "width": 2,
              "signed": false,
              "float": false,
              "lsb_first": true,
              "meaning": "bit 5 is the low bit, bit 4 the high bit: 0 moving, 1 object detected while closing, 2 object detected while opening, 3 at the position"
            },
            {
              "name": "gripper_temperature_error",
              "byte": 3,
              "size": 1,
              "bits": [
                3
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "a copy of the thermistor temperature error"
            },
            {
              "name": "gripper_timeout_error",
              "byte": 3,
              "size": 1,
              "bits": [
                2
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "a copy of the CAN watchdog error (Send_Watchdog), not a move timeout"
            },
            {
              "name": "gripper_estop_error",
              "byte": 3,
              "size": 1,
              "bits": [
                1
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "the E-stop bit of Send_gripper_data_pack (or #Gripstop 1) was set"
            },
            {
              "name": "gripper_calibrated",
              "byte": 3,
              "size": 1,
              "bits": [
                0
              ],
              "width": 1,
              "signed": false,
              "float": false,
              "lsb_first": false,
              "meaning": "1 calibrated"
            }
          ]
        }
      ]
    }
  ]
}
