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:
- 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"