Extending the protocol#

The server and the device communicate through a binary protocol, designed exclusively for Scrutiny. This protocol is half-duplex and operates in a command-based fashion. Each request has a command ID (7bits) and a subcommand ID (8bits).

In order to allow a user to share that communication channel with Scrutiny without interfering, it is possible to encapsulate data into a dedicated Scrutiny command called UserCommand. This command ID solely activates a user-defined callback in the firmware, passing the subfunction and data to this callback.

It is possible to send a UserCommand through the python SDK. The device’s response will then be relayed directly back to the client.


ScrutinyClient.user_command(subfunction, data=b'')[source]#

Sends a UserCommand request to the device with the given subfunction and data. UserCommand is a request that calls a user defined callback in the device firmware. It allows a developer to take advantage of the scrutiny protocol to communicate non-scrutiny data with its device.

Parameters:
  • subfunction (int) – Subfunction of the request. From 0x0 to 0x7F

  • data (bytes) – The payload to send to the device

Raises:
  • ValueError – Bad parameter value

  • TypeError – Given parameter not of the expected type

  • OperationFailure – If the command completion fails

Return type:

UserCommandResponse


class scrutiny.sdk.UserCommandResponse[source]#

(Immutable struct) Response returned by the device after performing a ScrutinyClient.user_command

subfunction: int#

The subfunction echoed by the device when sending a response

data: bytes#

The data returned by the device


Example#

C++

//main.cpp
#include <cstdint>
#include <iostream>
#include <iomanip>
#include "scrutiny.hpp"

uint32_t get_timestamp_microsec();

uint8_t scrutiny_rx_buffer[64];
uint8_t scrutiny_tx_buffer[128];

// the function below can be invoked remotely by a Scrutiny client through the Python SDK
void my_user_command_callback(
    uint8_t const subfunction,          // ID coming from the SDK
    uint8_t const *request_data,        // Input data coming from the SDK
    uint16_t const request_data_length, // Length of input data
    uint8_t *response_data,             // Output data to send back to the SDK
    uint16_t *response_data_length,     // Length of output data
    uint16_t const response_max_data_length // Maximum size of the output buffer. Dictated by the TX Buffer size.
)
{
    if (subfunction == 1){
        std::cout << "Hello" << std::endl;
    } 
    else if (subfunction == 2) {
        std::cout << " World" << std::endl;
    }
    else if (subfunction == 3) {
        std::cout << "Received: ";
        for (uint32_t i=0; i<request_data_length; i++){
            std::cout << std::hex << static_cast<uint32_t>(request_data[i]);
        }
        std::cout << std::endl;
    }

    if (response_max_data_length >= 3) {  // Prevent overflow
        response_data[0] = 0xAA;
        response_data[1] = 0xBB;
        response_data[2] = 0xCC;
        *response_data_length = 3;
    }
}

int main(void){
    scrutiny::Config config;
    config.set_buffers(
        scrutiny_rx_buffer, sizeof(scrutiny_rx_buffer),     // Receive
        scrutiny_tx_buffer, sizeof(scrutiny_tx_buffer)      // Transmit 
        );

    // ==== User Command callback! ======
    config.set_user_command_callback(my_user_command_callback);
    // ==================================

    scrutiny::MainHandler scrutiny_main;
    scrutiny_main.init(&config);
    
    uint32_t last_timestamp = get_timestamp_microsec();
    while(true){
        uint32_t const timestamp = get_timestamp_microsec();
        // ... 
        // ...
        uint32_t const time_delta = (timestamp-last_timestamp);
        scrutiny_main.process( time_delta*10U );  // Timesteps are multiples of 100ns
        last_timestamp = timestamp;
    }
}

Python

from scrutiny.sdk.client import ScrutinyClient
from binascii import hexlify

client = ScrutinyClient()
with client.connect('localhost', 8765):
    client.user_command(subfunction=1)  # C++ prints "Hello"
    client.user_command(subfunction=2)  # C++ prints " World"
    response = client.user_command(subfunction=3, data=bytes([0x11, 0x22]))  # C++ prints "Received: 1122"

    print(hexlify(response.data))    # Python prints : "AABBCC"