ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
gpib.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 gpib.h
6 * @brief GPIB-over-LAN (Prologix-style) controller command codec (PC_ENABLE_GPIB) - a zero-heap
7 * codec for the Prologix-compatible `++` command set that drives a bench of legacy IEEE-488
8 * (GPIB) instruments through a Prologix GPIB-Ethernet / GPIB-USB adapter (raw socket on TCP
9 * 1234). The bridge into pre-LAN test gear that will never speak SCPI-over-TCP directly.
10 *
11 * The device is the host: it sends `++` commands (a line starting with an unescaped `++`) to
12 * configure/control the adapter, and data lines (anything else) that the adapter forwards over GPIB
13 * to the addressed instrument. This codec builds the commands (@ref pc_gpib_command and the typed
14 * @ref pc_gpib_addr / @ref pc_gpib_read / @ref pc_gpib_spoll / @ref pc_gpib_eos helpers), builds
15 * an escaped data line (@ref pc_gpib_build_data - a leading ESC before a CR / LF / ESC / `+` byte in
16 * the payload, then an unescaped newline terminator), classifies a line (@ref pc_gpib_is_command),
17 * and parses the responses (@ref pc_gpib_parse_decimal for the serial-poll status byte / SRQ /
18 * address, @ref pc_gpib_parse_version). Pure codec, host-tested; the socket / serial link is the
19 * application's.
20 *
21 * Reference: Prologix GPIB-ETHERNET / GPIB-USB Controller manuals (prologix.biz).
22 *
23 * @author Douglas Quigg (dstroy0)
24 * @date 2026
25 */
26
27#ifndef PROTOCORE_GPIB_H
28#define PROTOCORE_GPIB_H
29
30#include "protocore_config.h"
31
32#if PC_ENABLE_GPIB
33
34#include <stddef.h>
35#include <stdint.h>
36
37/** @brief The Prologix GPIB-Ethernet raw-socket TCP port. */
38#define PC_GPIB_PORT 1234
39/** @brief The Prologix NetFinder UDP discovery port. */
40#define PC_GPIB_DISCOVERY_PORT 3040
41
42/** @brief GPIB terminator appended to data sent to the instrument (`++eos`). */
43enum class GpibEos : uint8_t
44{
45 CRLF = 0, ///< CR + LF
46 CR = 1, ///< CR
47 LF = 2, ///< LF
48 PC_NONE = 3, ///< none (use for binary payloads)
49};
50
51/** @brief Read-termination mode (`++read`). */
52enum class GpibRead : uint8_t
53{
54 UNTIL_TIMEOUT, ///< `++read` - read until the inter-character timeout only (NOT until EOI)
55 UNTIL_EOI, ///< `++read eoi` - read until EOI or timeout
56 UNTIL_CHAR, ///< `++read <char>` - read until the given character or timeout
57};
58
59/**
60 * @brief Build a generic `++` command line: `"++"` + @p cmd + `'\n'` (e.g. `pc_gpib_command(...,
61 * "mode 1")` -> `"++mode 1\n"`, `"eoi 1"`, `"clr"`, `"ver"`, `"read_tmo_ms 500"`).
62 * @return characters written (excluding the NUL), or 0 on overflow / bad input.
63 */
64size_t pc_gpib_command(char *buf, size_t cap, const char *cmd);
65
66/**
67 * @brief Build `++addr <pad>[ <sad>]` - set the instrument GPIB address.
68 * @param pad primary address (0-30).
69 * @param sad optional secondary address (96-126); pass < 0 for none.
70 * @return characters written (excluding NUL), or 0 on overflow / bad @p pad.
71 */
72size_t pc_gpib_addr(char *buf, size_t cap, uint8_t pad, int sad);
73
74/**
75 * @brief Build a `++read` command in one of its three forms.
76 * @param ch the read-until character (decimal), used only when @p mode is @ref GpibRead::UNTIL_CHAR.
77 * @return characters written (excluding NUL), or 0 on overflow.
78 */
79size_t pc_gpib_read(char *buf, size_t cap, GpibRead mode, uint8_t ch);
80
81/**
82 * @brief Build `++spoll` (serial poll). With @p pad < 0, polls the currently-addressed instrument;
83 * otherwise `++spoll <pad>[ <sad>]`. The response is the status byte as a decimal string.
84 * @return characters written (excluding NUL), or 0 on overflow.
85 */
86size_t pc_gpib_spoll(char *buf, size_t cap, int pad, int sad);
87
88/** @brief Build `++eos <n>` - the GPIB terminator appended to instrument data. */
89size_t pc_gpib_eos(char *buf, size_t cap, GpibEos eos);
90
91/**
92 * @brief Build an escaped data line to send to the addressed instrument: each CR (13) / LF (10) /
93 * ESC (27) / `+` (43) byte in @p src is preceded by an ESC (27), then an unescaped `'\n'`
94 * line terminator is appended. (Data received FROM instruments is never escaped.)
95 * @return total bytes written (NOT NUL-terminated - the payload may be binary), or 0 on overflow.
96 */
97size_t pc_gpib_build_data(uint8_t *buf, size_t cap, const uint8_t *src, size_t len);
98
99/** @brief True if @p line is a controller command (starts with an unescaped `++`), else it is data. */
100bool pc_gpib_is_command(const char *line, size_t len);
101
102/**
103 * @brief Parse a decimal integer response (trims surrounding spaces / CR / LF) - the `++spoll`
104 * status byte, the `++srq` 0/1, or a `++addr` primary address.
105 * @return true on a clean decimal; false otherwise.
106 */
107bool pc_gpib_parse_decimal(const char *s, size_t len, uint32_t *out);
108
109/**
110 * @brief Parse a `++addr` query response: a primary address, optionally a space + secondary.
111 * @param pad receives the primary address.
112 * @param sad receives the secondary address, or -1 if none was present.
113 * @return true on a clean response; false otherwise.
114 */
115bool pc_gpib_parse_addr(const char *s, size_t len, uint8_t *pad, int *sad);
116
117/**
118 * @brief Parse a `++ver` response: locate the version token after `"version "`. @p ver points INTO
119 * @p s (trailing CR/LF trimmed from @p ver_len).
120 * @return true if a version token was found; false otherwise.
121 */
122bool pc_gpib_parse_version(const char *s, size_t len, const char **ver, size_t *ver_len);
123
124#endif // PC_ENABLE_GPIB
125
126#endif // PROTOCORE_GPIB_H
User-facing configuration for ProtoCore.