Reading Equipment Constants From the Host: S2F29 for the Namelist, S2F13 for the Values
You asked S2F13 for one ECID and S2F14 came back with two values. Real S2F29 and S2F13 bytes, and the length check host code must run.
The MES screen shows a target pressure of 65. The equipment screen shows 7.5. The host code asked S2F13 for ECID 301 and nothing else, then took the first value out of the S2F14 that came back. That first value belonged to ECID 300.
This is the quietest bug in equipment constant reads. There are no ECIDs in an S2F14 body. Only values. Which value belongs to which constant is knowable only from the order of the request the host sent, and nothing inside the message lets you confirm the equipment honoured that order. If you are writing this code with no tool to test against, you need to see the structure as bytes once.
Every frame below went over a socket to the SECS/GEM simulator's EQ1 passive listener (127.0.0.1:5501, SessionID 11, no fault modes set). The host side is a Python socket client that encodes and decodes the SECS-II items itself.
An empty list means "send me everything"
The S2F13 body is a list of ECIDs. With zero elements, SEMI E5 reads it as send every equipment constant you have. So a host that passes an empty array by accident gets a full dump rather than an error. That is where this starts.
TX S2F13 W 00 00 00 0C 00 0B 82 0D 00 00 00 00 05 04 01 00
| Bytes | Field | Value |
|---|---|---|
00 00 00 0C | Length | 12 — a 10-byte header plus a 2-byte body |
00 0B | SessionID | 11 |
82 | Header byte 2 | W-bit plus Stream 2 |
0D | Header byte 3 | Function 13 |
00 00 00 00 05 04 | PType, SType, SystemBytes | 0, 0, 0x0504 |
01 00 | Body | L,0 — an empty list |
01 is the list item header: the top six bits are format code 0 (L), the bottom two say one length byte follows. So 00 means zero elements. In a list the length counts elements, not bytes — the item header byte taken apart covers only that.
The answer:
RX S2F14 00 00 00 53 00 0B 02 0E 00 00 00 00 05 04
01 02
01 02 41 17 45 43 33 30 30 5F 54 61 72 67 65 74 54 65 6D 70 65 72 61 74 75 72 65
81 08 40 50 40 00 00 00 00 00
01 02 41 14 45 43 33 30 31 5F 54 61 72 67 65 74 50 72 65 73 73 75 72 65
81 08 40 1E 00 00 00 00 00 00
02 0E is Stream 2 Function 14 with no W-bit, and the SystemBytes are the request's own 00 00 05 04. 41 17 is a 23-byte ASCII item — EC300_TargetTemperature. 81 08 is an 8-byte F8 item, and 40 50 40 00 00 00 00 00 is IEEE 754 double for 65. The second entry is EC301_TargetPressure at 40 1E 00 00 00 00 00 00 = 7.5.
The simulator is not following E5's shape here. E5's S2F14 is a list of ECVs in order; this implementation returns L,2 {A ECNAME, F8 ECV} pairs. That is the more convenient answer for a host parser, which is exactly what makes it dangerous — code that grows used to names arriving alongside the values breaks on its first line against a real tool that sends ECVs only. Do not shape your parser around this.
S2F29 is what makes a bare value interpretable
Which is why S2F13 is not the first message. Send S2F29, the Equipment Constant Namelist Request, with an empty list and the equipment describes everything it owns.
TX S2F29 W 00 00 00 0C 00 0B 82 1D 00 00 00 00 05 03 01 00
RX S2F30 00 00 00 8B 00 0B 02 1E 00 00 00 00 05 03
01 02
01 06 B1 04 00 00 01 2C
41 17 45 43 33 30 30 5F 54 61 72 67 65 74 54 65 6D 70 65 72 61 74 75 72 65
81 08 00 00 00 00 00 00 00 00
81 08 40 60 40 00 00 00 00 00
81 08 40 50 40 00 00 00 00 00
41 00
01 06 B1 04 00 00 01 2D
41 14 45 43 33 30 31 5F 54 61 72 67 65 74 50 72 65 73 73 75 72 65
81 08 00 00 00 00 00 00 00 00
81 08 40 2E 00 00 00 00 00 00
81 08 40 1E 00 00 00 00 00 00
41 00
Each entry is an L,6 holding, in E5's order, ECID, ECNAME, ECMIN, ECMAX, ECDEF and UNITS. B1 04 00 00 01 2C is a 4-byte U4 item, value 300. ECMIN is 0, ECMAX decodes from 40 60 40 00 … to 130, ECDEF is 65. The trailing 41 00 is a zero-length ASCII item: UNITS is the empty string. Treat 41 00 as "no item present" and the parser slips out of alignment right there.
ECMAX landing at exactly twice the current value, and ECDEF equalling it, is the simulator inventing numbers because its model carries no min, max or default. That is not equipment data, so read nothing into the values themselves. What is worth reading is the position and the format.
E5 leaves those slots loosely typed on purpose. ECID may arrive as U1/U2/U4/U8, the signed I equivalents, or A, and ECMIN/ECMAX/ECDEF may be any numeric type, A, B or BOOLEAN. Only ECNAME and UNITS are always A. A host that hardcodes U4 ECIDs runs fine here and dies at connect time on the day it meets a tool that numbers its constants as strings.
Request and reply are bound by position alone
Now ask for one ECID. B1 04 00 00 01 2C is U4 300, in a list of one element.
TX S2F13 W 00 00 00 12 00 0B 82 0D 00 00 00 00 05 05 01 01 B1 04 00 00 01 2C
RX S2F14 00 00 00 53 00 0B 02 0E 00 00 00 00 05 05 01 02 ... (byte for byte the earlier dump)
The length is 00 00 00 53 — 83 bytes, not one byte different from the reply to the empty list. One element requested, two returned. Try an ECID that does not exist: 00 00 03 E7 is 999, a number this equipment does not have.
TX S2F13 W 00 00 00 12 00 0B 82 0D 00 00 00 00 05 06 01 01 B1 04 00 00 03 E7
RX S2F14 00 00 00 53 00 0B 02 0E 00 00 00 00 05 06 01 02 ... (identical again)
No rejection, no empty item, no Stream 9. The same 83 bytes. This listener never reads the S2F13 body at all and returns every constant it holds — that is the simulator's implementation, not a statement about equipment in general. What a real tool does with an unknown ECID varies by vendor. E5's convention in reply lists like this one is a zero-length item in the offending slot, and I have not verified how that comes out on a real tool.
The host-side conclusion is the same either way. Compare the reply list length against the request list length. One asked, two returned, is not data — it is an error. And even when the lengths match, a zero-length item means "no value", not zero. Skip those two checks and the host files ECID 300's value under ECID 301 and logs nothing at all. That is how the 65 in the first paragraph got there.
Two different reasons an S2F0 comes back
S2F13 reads, S2F15 writes. Two apart, so a typo lands on a plausible function number, and since both are valid functions inside Stream 2 nothing catches it at the framing layer. Send this equipment an S2F15:
TX S2F15 W 00 00 00 1E 00 0B 82 0F 00 00 00 00 05 07
01 01 01 02 B1 04 00 00 01 2C 81 08 40 51 80 00 00 00 00 00
RX S2F0 00 00 00 0A 00 0B 02 00 00 00 00 00 05 07
The body is L,1 {L,2 {U4 300, F8 70}} — set ECID 300 to 70. The answer is a bodyless S2F0: header byte 3 is 00, function 0. In E5, SxF0 aborts the transaction, and here it means this listener does not implement S2F15. There is no S2F16 and no EAC. What a write acknowledgement and its EAC codes look like on a real tool, and why EAC 0 does not mean "applied", is a separate article on equipment constant change control.
There is a second route to the same S2F0. Open a fresh session, Select, and go straight to S2F13:
TX Select.req 00 00 00 0A 00 0B 00 00 00 01 00 00 06 01
RX Select.rsp 00 00 00 0A 00 0B 00 00 00 02 00 00 06 01
TX S2F13 W 00 00 00 0C 00 0B 82 0D 00 00 00 00 06 02 01 00
RX S2F0 00 00 00 0A 00 0B 02 00 00 00 00 00 06 02
Same S2F0 — this time not because the function is unimplemented but because this listener refuses primary messages until S1F13/S1F14 has been through, the window E30 names NOT COMMUNICATING. Answering it with an S2F0 rather than an S9F message is the simulator's choice, and I have not verified what a real tool sends in that window. Get S1F13/S1F14 through first and the identical bytes are answered normally; proving the E30 startup handshake in one command walks that order.
TX S1F13 W 00 00 00 0C 00 0B 81 0D 00 00 00 00 06 03 01 00
RX S1F14 00 00 00 37 00 0B 01 0E 00 00 00 00 06 03 01 02 21 01 00 ...
TX S2F13 W 00 00 00 0C 00 0B 82 0D 00 00 00 00 06 04 01 00
RX S2F14 00 00 00 53 00 0B 02 0E 00 00 00 00 06 04 01 02 ...
One reply code covering both "no such message" and "not right now" is bad news on the host side. Looking at the message alone you cannot tell whether the fix is a retry or a code change. So log an S2F0 together with its SystemBytes and the comm state at that moment, or the two stay indistinguishable.
What to actually put in the host code
- Pull the namelist first, and keep ECIDs that are not in it out of S2F13 entirely.
- If the reply list length does not equal the request list length, stop parsing and raise. This is the cheapest check and it catches the most.
- A zero-length item is "no value", not zero.
41 00and81 00both qualify. - Do not hardcode the format codes of ECID and ECV — validate against what the namelist reported. Code that assumes U4 dies on ASCII ECIDs.
- Cache the namelist, but cache it with the equipment software revision. When the string S1F14 returns —
SECSGEM-1.4.1here — changes, throw the cache away. A firmware update that quietly moves ECMIN or ECMAX leaves the host's range check rejecting values the tool would accept, or worse, passing values the tool will clamp.
The cache item is last for a reason. The first four checks fire on day one of deployment; the fifth fires six months later, the morning after a PM. I have met several hosts that read the namelist once, wrote it into a config file, and never read it again — and the route by which anyone discovered that the file disagreed with the tool was always scrap.
Every frame above really went over a socket. The same bytes are in the equipment-side RX log, and the raw export sits in content/demos/s2f13-s2f14-reading-equipment-constants-host-side.json. To push the same bytes yourself, point a socket at the SECS/GEM simulator.