ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
scpi.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file scpi.h
6 * @brief SCPI / IEEE 488.2 instrument-control codec (PC_ENABLE_SCPI) - a zero-heap codec for the
7 * text command language nearly every modern bench instrument speaks (DMMs, oscilloscopes,
8 * power supplies, function/arbitrary generators, SMUs, spectrum/network analyzers, loads)
9 * over a raw TCP socket on port 5025 (also USBTMC / VXI-11 / HiSLIP / serial).
10 *
11 * The codec is symmetric - it serves both roles:
12 * - **Controller** (device drives an instrument): build command lines with a `:`-hierarchy header
13 * and comma-separated parameters (@ref pc_scpi_build), and parse the instrument's replies -
14 * numeric NR1/NR2/NR3 (@ref pc_scpi_parse_number), boolean (@ref pc_scpi_parse_bool), quoted
15 * string (@ref pc_scpi_parse_string), and the IEEE 488.2 arbitrary block `#<n><len><data>` used
16 * for waveform captures (@ref pc_scpi_parse_block).
17 * - **Instrument** (device answers a controller): the IEEE 488.2 status model - the Status Byte,
18 * Standard Event Status Register + its enable mask, the Service Request Enable mask, and the SCPI
19 * error/event queue (@ref ScpiStatus) - plus a SCPI short/long-form header matcher
20 * (@ref pc_scpi_match) to dispatch an incoming command against a pattern like `"SYSTem:ERRor?"`.
21 *
22 * Common commands (@ref pc_scpi_common) return the IEEE 488.2 mnemonics (`*IDN?`, `*RST`, `*CLS`,
23 * `*ESR?`, `*STB?`, ...). Pure codec, host-tested; the TCP/USB/serial transport is the app's.
24 *
25 * References:
26 * - IEEE Std 488.2-1992 (Codes, Formats, Protocols and Common Commands).
27 * - SCPI Consortium, "Standard Commands for Programmable Instruments", Vol. 1 Syntax & Style (1999).
28 *
29 * @author Douglas Quigg (dstroy0)
30 * @date 2026
31 */
32
33#ifndef PROTOCORE_SCPI_H
34#define PROTOCORE_SCPI_H
35
36#include "protocore_config.h"
37
38#if PC_ENABLE_SCPI
39
40#include <stddef.h>
41#include <stdint.h>
42
43/** @brief The default raw-socket ("SCPI-RAW") TCP port instruments listen on. */
44#define PC_SCPI_PORT 5025
45
46// ── Standard Event Status Register (ESR) bits - read/cleared by *ESR? ──────────────────────────
47#define SCPI_ESR_OPC 0x01 ///< bit 0: Operation Complete
48#define SCPI_ESR_RQC 0x02 ///< bit 1: Request Control
49#define SCPI_ESR_QYE 0x04 ///< bit 2: Query Error
50#define SCPI_ESR_DDE 0x08 ///< bit 3: Device-Dependent Error
51#define SCPI_ESR_EXE 0x10 ///< bit 4: Execution Error
52#define SCPI_ESR_CME 0x20 ///< bit 5: Command Error
53#define SCPI_ESR_URQ 0x40 ///< bit 6: User Request
54#define SCPI_ESR_PON 0x80 ///< bit 7: Power On
55
56// ── Status Byte Register (STB) bits - read by *STB? / serial poll (SCPI model) ─────────────────
57#define SCPI_STB_EAV 0x04 ///< bit 2: Error/event queue not empty
58#define SCPI_STB_QSB 0x08 ///< bit 3: QUEStionable status summary
59#define SCPI_STB_MAV 0x10 ///< bit 4: Message Available (output queue not empty)
60#define SCPI_STB_ESB 0x20 ///< bit 5: Standard Event status Summary (ESR & ESE)
61#define SCPI_STB_MSS 0x40 ///< bit 6: Master Summary Status (RQS in a serial poll)
62#define SCPI_STB_OSB 0x80 ///< bit 7: OPERation status summary
63
64/** @brief The mandatory IEEE 488.2 common commands (`pc_scpi_common` renders each). */
65enum class ScpiCommon : uint8_t
66{
67 SCPI_CLS, ///< "*CLS" clear status
68 SCPI_ESE, ///< "*ESE" set event status enable
69 SCPI_ESE_Q, ///< "*ESE?" query event status enable
70 SCPI_ESR_Q, ///< "*ESR?" query + clear event status register
71 SCPI_IDN_Q, ///< "*IDN?" identification query
72 SCPI_OPC, ///< "*OPC" set operation-complete bit when done
73 SCPI_OPC_Q, ///< "*OPC?" query operation complete (returns 1)
74 SCPI_RST, ///< "*RST" reset
75 SCPI_SRE, ///< "*SRE" set service request enable
76 SCPI_SRE_Q, ///< "*SRE?" query service request enable
77 SCPI_STB_Q, ///< "*STB?" query status byte
78 SCPI_TST_Q, ///< "*TST?" self-test query
79 SCPI_WAI, ///< "*WAI" wait to continue
80};
81
82/** @brief Render an IEEE 488.2 common command mnemonic (a static string, never null). */
83const char *pc_scpi_common(ScpiCommon c);
84
85/**
86 * @brief Build a SCPI command line: `header` + ' ' + comma-joined `args` + '\n'.
87 *
88 * With @p argc 0 the line is just `header` + '\n' (e.g. `"*RST\n"`, `"MEAS:VOLT:DC?\n"`).
89 * @param header a full `:`-hierarchy header or a common command, e.g. `"SOURce:VOLTage"`, `"*IDN?"`.
90 * @param args already-formatted parameter strings (see @ref pc_scpi_fmt_real to format a number).
91 * @return characters written (excluding the NUL terminator), or 0 on overflow / bad input.
92 * @note The line is NUL-terminated so callers may treat @p buf as a C-string.
93 */
94size_t pc_scpi_build(char *buf, size_t cap, const char *header, const char *const *args, size_t argc);
95
96/**
97 * @brief Format a real number as a SCPI parameter (NR2 fixed, or NR3 scientific for large/small
98 * magnitudes), trailing zeros trimmed. Handy for @ref pc_scpi_build args.
99 * @return characters written (excluding NUL), or 0 on overflow.
100 */
101size_t pc_scpi_fmt_real(char *buf, size_t cap, double v);
102
103/**
104 * @brief Parse a SCPI numeric response (NR1 integer / NR2 fixed / NR3 scientific), hand-rolled
105 * (no stdlib). Recognizes the special values `9.9E37` (INFinity), `-9.9E37`, `9.91E37` (NaN).
106 * @return true on a fully-consumed numeric field; false on malformed input.
107 */
108bool pc_scpi_parse_number(const char *s, size_t len, double *out);
109
110/**
111 * @brief Parse a SCPI boolean response: `1`/`0` or `ON`/`OFF` (case-insensitive).
112 * @return true on a recognized boolean; false otherwise.
113 */
114bool pc_scpi_parse_bool(const char *s, size_t len, bool *out);
115
116/**
117 * @brief Parse a SCPI string response: strip the surrounding single or double quotes and collapse a
118 * doubled quote (`""` or `''`) to one. Copies into @p out (NUL-terminated).
119 * @return the unquoted length (excluding NUL), or 0 on a missing/mismatched quote or overflow.
120 */
121size_t pc_scpi_parse_string(const char *s, size_t len, char *out, size_t cap);
122
123/**
124 * @brief Parse an IEEE 488.2 arbitrary block: definite `#<n><n-length-digits><data>` or indefinite
125 * `#0<data>` (runs to the trailing newline). On success @p data points INTO @p buf.
126 * @param data receives a pointer to the first data byte.
127 * @param data_len receives the data byte count.
128 * @param consumed receives the total bytes the block occupied (header + data [+ the indefinite NL]).
129 * @return true on a complete block; false if truncated or malformed.
130 */
131bool pc_scpi_parse_block(const uint8_t *buf, size_t len, const uint8_t **data, size_t *data_len, size_t *consumed);
132
133// ── IEEE 488.2 / SCPI status model (instrument side) ───────────────────────────────────────────
134
135/** @brief One error/event queue entry. @ref msg points at a static or app-owned string. */
136struct ScpiError
137{
138 int16_t number; ///< SCPI error number (negative = standard, positive = device-specific, 0 = no error)
139 const char *msg; ///< the description text (without the surrounding quotes)
140};
141
142/** @brief The IEEE 488.2 status registers + the SCPI error/event queue (one owned block). */
143struct ScpiStatus
144{
145 uint8_t esr; ///< Standard Event Status Register (latched events)
146 uint8_t ese; ///< Standard Event Status Enable mask (ESR -> ESB)
147 uint8_t sre; ///< Service Request Enable mask (STB -> MSS)
148 uint8_t summary; ///< app-set STB summary bits (QSB/MAV/OSB); merged into *STB?
149 ScpiError queue[PC_SCPI_ERR_QUEUE]; ///< the error/event queue (FIFO ring)
150 uint8_t head; ///< ring read index
151 uint8_t count; ///< entries currently queued
152};
153
154/** @brief Clear every register + the queue (power-on state). */
155void pc_scpi_status_init(ScpiStatus *s);
156
157/** @brief Latch one or more ESR event bits (e.g. @ref SCPI_ESR_OPC). */
158void pc_scpi_event(ScpiStatus *s, uint8_t esr_bits);
159
160/**
161 * @brief Enqueue an error/event and latch its ESR class bit (CME/EXE/DDE/QYE from the -1xx/-2xx/
162 * -3xx/-4xx number range). On overflow the tail entry becomes -350 "Queue overflow".
163 * @param msg the description; pass nullptr to use the standard text for a standard number.
164 */
165void pc_scpi_push_error(ScpiStatus *s, int16_t number, const char *msg);
166
167/**
168 * @brief Pop the head error (the `SYSTem:ERRor?` action). When the queue is empty @p out becomes
169 * `0,"No error"`.
170 * @return true if an error was dequeued; false if the queue was empty (out = the no-error entry).
171 */
172bool pc_scpi_pop_error(ScpiStatus *s, ScpiError *out);
173
174/** @brief Compute the Status Byte: EAV (queue), ESB (esr & ese), the app summary bits, and MSS. */
175uint8_t pc_scpi_stb(const ScpiStatus *s);
176
177/** @brief The `*CLS` action: clear the ESR and empty the error/event queue (enables untouched). */
178void pc_scpi_cls(ScpiStatus *s);
179
180/** @brief The standard SCPI message for a standard (negative) error number, or "" if unknown. */
181const char *pc_scpi_std_error(int16_t number);
182
183/**
184 * @brief Match an incoming command header against a SCPI short/long-form pattern.
185 *
186 * Case-insensitive. Each `:`-separated node in @p pattern has an uppercase run (the required short
187 * form) followed by an optional lowercase tail (the long form); the input node must equal EITHER the
188 * short form OR the whole long form (no other truncation). An optional numeric suffix is compared
189 * with an omitted suffix defaulting to 1 (`OUTP` == `OUTPut1`). A trailing `?` in the pattern must
190 * be matched by a `?` in the input. A `*`-prefixed pattern (common command) is matched whole,
191 * case-insensitively. A leading `:` on the input (absolute path) is ignored.
192 *
193 * @return true if @p input (its header, up to the first space) matches @p pattern.
194 */
195bool pc_scpi_match(const char *input, size_t input_len, const char *pattern);
196
197#endif // PC_ENABLE_SCPI
198
199#endif // PROTOCORE_SCPI_H
User-facing configuration for ProtoCore.
#define PC_SCPI_ERR_QUEUE
SCPI error/event queue depth (entries). The SCPI status model requires a queue; when it overflows the...