Repository navigation
Home
ScpiLite is a lightweight SCPI (Standard Commands for Programmable Instruments) parser and command handler for the Raspberry Pi Pico. It provides a simple interface for implementing instrument like serial communication over UART, allowing devices to respond to SCPI commands with abbreviated command matching and built-in error handling.
- UART based SCPI command parsing
- Flexible command registration system
- SCPI abbreviation matching (commands can be shortened)
- Built in commands:
*IDN?andSYSTem:ERRor? - Error queue management with automatic error reporting
- Callback based command handling
- Low overhead circular error buffer
The main class that handles UART initialisation, command registration, parsing, and dispatch.
| Constant | Value | Description |
|---|---|---|
TOKEN_LEN |
16 | Maximum length of a command token or parameter |
MAX_PARAMS |
4 | Maximum number of parameters per command |
MAX_CMDS |
16 | Maximum number of registered commands |
MAX_ERRS |
16 | Size of the circular error queue |
using Handler = bool (*)(ScpiLite*, bool is_query, const char params[][TOKEN_LEN],
uint8_t param_count, char* resp, size_t resp_len, void* user_ctx);Command handlers receive:
-
ScpiLite*: Pointer to the ScpiLite instance -
bool is_query: True if the command ended with? -
const char params[][]: Array of parameter strings -
uint8_t param_count: Number of parameters provided -
char* resp: Response buffer to write to -
size_t resp_len: Size of response buffer -
void* user_ctx: User context provided during registration
Handlers return true if a response should be sent, false otherwise.
init_uart(uart_inst_t* uart, uint32_t baudrate, uint8_t tx_pin = 0, uint8_t rx_pin = 1)
Initializes the UART instance with specified baud rate and pins.
scpi.init_uart(uart0, 115200, 0, 1);set_idn(const char* idn)
Sets the identification string returned by *IDN?. Format is typically Manufacturer,Description,SerialNumber,FirmwareVersion.
scpi.set_idn("MyCompany,TestDevice,SN12345,v1.0");register_command(const char* name, Handler h, void* user_ctx)
Registers a command with the SCPI parser. Command names use full SCPI format with uppercase letters indicating mandatory parts.
// MEASure:VOLTage means MEAS:VOLT are mandatory
// Users can send: MEAS:VOLT, MEAS:VOLTAGE, MEASure:VOLT, MEASure:VOLTAGE, etc.
scpi.register_command("MEASure:VOLTage", voltage_handler, nullptr);add_error(int code, const char* msg)
Adds an error to the queue (max 16 errors). Uses SCPI error code convention (negative codes). When the queue is full, the last error becomes -350 "Queue overflow".
scpi.add_error(-222, "Data out of range");void poll()
Must be called regularly in the main loop. Reads characters from UART, buffers complete lines, and dispatches to command handlers.
while (true)
{
scpi.poll();
sleep_ms(1);
}void write_raw(const char* str)
Writes a raw string directly to UART.
scpi.write_raw("Hello\n");Returns the identification string set via set_idn().
*IDN?
< MyCompany,TestDevice,SN12345,v1.0
Pops and returns the next error from the queue. Returns error code and message. If the queue is empty, returns 0,"No error".
Short form: SYS:ERR?
SYS:ERR?
< -222,"Data out of range"
Commands use a specific format to support abbreviation:
- Uppercase letters are mandatory minimum abbreviation
- Lowercase letters are optional and can be omitted
Example: MEASure:VOLTage
- Mandatory:
MEAS:VOLT(4 chars + colon + 4 chars) - Valid inputs:
MEAS:VOLT,MEASure:VOLT,MEAS:VOLTAGE,MEASure:VOLTAGE, etc. - Invalid:
MEA:VOLT(too short),MEASURE:VOLT(extra characters not in definition)
COMMAND[:SUBCOMMAND] [param1, param2, ...]?
- Commands are case insensitive
- Parameters are comma-separated
- Query commands (ending with
?) cannot have parameters - Colons separate command hierarchy levels
Common SCPI error codes:
| Code | Meaning |
|---|---|
| -102 | Illegal parameter value |
| -103 | Input overflow |
| -104 | Syntax error |
| -113 | Undefined command |
| -222 | Data out of range |
| -350 | Queue overflow |
#include "ScpiLite.hpp"
ScpiLite scpi;
// Command handler
bool handle_led(ScpiLite* s, bool is_query, const char params[][16], uint8_t param_count, char* resp, size_t resp_len, void* user_ctx)
{
if (is_query)
{
// LED? - query current state
snprintf(resp, resp_len, "%d", led_state);
return true;
} else if (param_count == 1)
{
// LED 1 - set LED on
led_state = atoi(params[0]);
return false; // no response needed
}
return false;
}
int main()
{
scpi.init_uart(uart0, 115200);
scpi.set_idn("Lab,LED Controller,SN001,1.0");
scpi.register_command("LED", handle_led, nullptr);
while (true)
{
scpi.poll();
}
}- Maximum 16 registered commands
- Maximum 4 parameters per command
- Each token (command/parameter) limited to 15 characters
- Input buffer 127 characters max
- Error queue holds maximum 16 errors
- Response buffer 255 characters max
- Designed for Raspberry Pi Pico (uses pico/stdlib.h)
- UART setup uses 8 data bits, 1 stop bit, no parity
- Hardware flow control is disabled
- FIFO is enabled for better throughput
- The parser consumes complete lines (terminated by
\n), carriage returns are ignored - All SCPI command matching is case-insensitive