Support reference FSE23204 / #16460. These tools were built to diagnose a
QuickServer that enters an ERROR state within seconds of an OPW SiteSentinel Nano console being
connected. Everything below is in service of finding out what that console sends that the driver
does not expect.
3.8.0 2026-10-13current
A customer's report already contains every byte the console sent, request by request. Until now,
turning that into a reproduction meant copying one reply's hex into the simulator by hand, and the
simulator could not tell tank 1's request from tank 2's. This release makes the report itself the
simulator's script.
Added — simulator: replay a whole Explorer report
- The simulator's section 4 gains Replay captured responses from a TLS-350 Explorer
report. Load the
tls350-report-….txt by file picker or paste it in.
The REQUESTS section is parsed into one mapping row per distinct request, and the simulator
answers each with the bytes the real console sent — verbatim, with no framing or checksum
added, bypassing fault injection.
- Requests are matched on all six characters, so
i20101,
i20102 and i20103 are separate entries. The old replay keyed on the
four-character command and sent tank 1's reply to every tank.
- Each row has a reply policy: the latest captured reply (default), a specific
captured reply chosen from a list showing sequence number, time and size, cycling through every
captured reply in order, a custom payload in the existing hex or
<SOH> text
form, or off. Cycling is how an intermittent fault that appeared on the fiftieth poll is
reproduced in its original order.
- Requests not in the mapping are generated normally, answered 9999, or met with
silence — selectable. A captured reply of zero bytes (the console timed out) is reproduced
as silence, because that is what the console did.
- Optional: delay each reply by the explorer's captured request-to-reply time instead of the
response-delay field, so the console's pace is reproduced approximately.
- Rows can be added by hand for requests the report never saw, which keeps the earlier one-off
replay use case. The mapping persists in the browser across reloads; Clear mapping
removes it.
- The panel says when a report is missing its middle. The explorer caps the REQUESTS section on a
long session ("showing the most recent 70 and the earliest 130 of 544"), so the loaded set can
be incomplete; the
.pcap from the same session holds every reply.
- The simulator log gains a REPLAY MAPPING section listing the source report,
what was captured, the unmapped and timing settings and every row's policy, and each exchange
names the captured reply it sent, for example
replayed captured #543 (latest) from
tls350-report-2026-08-25-111718.txt.
- Every TX line in the Traffic pane has copy hex and copy text
buttons that put the complete reply on the clipboard. The pane shows only the first 120 bytes
of a long reply, and the downloaded log caps its hex too, so until now the full ASCII of a
tank-inventory reply could not be copied from anywhere.
- Add N alarms in the site table: a count plus category, type and device, so a
client's alarm-count limit can be found by making i101 carry 100, 500 or 5000 identical alarms.
Each alarm adds six characters to the reply (24 + 6×n bytes), and the table says the current
total and reply size. Remove all clears the list. The table draws at most 300 rows; the
reply always carries every alarm. The simulator log collapses identical runs to one line.
Changed
- The single-textarea Replay a captured response box is replaced by the mapping table. A
hand-added row with a custom payload does what it did.
- The explorer is unchanged in behaviour; its version moves with the simulator's so the pair
stays matched.
Testing
- The report parser and reply selection have 24 self-test cases that run in the page on load and
under Node via
scripts/tls350-simulator-selftest.mjs, which also parses the real
FSE23204 report shipped under logs/ and checks its figures (200 records of 544,
344 omitted, chronological order, the loopback echoes).
- The page was driven end to end in headless Edge: load, every policy, every unmapped setting,
timing, persistence, the log section and clearing. 26 checks, all passing.
3.7.0 2026-10-13
A generated configuration can be wrong in two ways that look exactly like success: it can serve
the wrong kind of Modbus register, and its alarm blocks can read "no alarm" on a console that is
not answering at all. Both were seen in the field on FSE23204. This release makes the generator
say so.
Added — holding or input registers
- The Modbus side can now be built on holding registers (4xxxx, function 3) or
input registers (3xxxx, function 4). Holding stays the default, so nothing
generated before this release changes.
- Which one is correct is a property of the system reading the gateway, not of the console, so
it cannot be worked out from a discovery run. It is asked, and the generated file states
which it used.
- A master that wants input registers and meets holding registers is answered "illegal data
address" on every poll. On FSE23204 that accounted for 333 of 851 polls — a gateway
that looked dead but was answering correctly to a question nobody was asking.
Added — the alarm registers no longer fail silently without saying so
- Alarm blocks are passive map descriptors. They are filled in by the System Status poll and
are never polled themselves, so they carry no poll state, so the Modbus server has no error
to report and serves whatever the array already holds. From boot that is zero.
- The effect is that on a console which has never answered, the alarm registers read
"no alarm" rather than faulting, and go on doing so indefinitely. Measured on FSE23204:
486 polls answered
00 00 while the console had been unreachable since boot.
The 5 minute cache age limit does not end it.
- Tank inventory does not behave this way — it is fed by a polled descriptor and returns
a gateway-timeout exception once the console goes quiet. The generated file now explains
that asymmetry rather than leaving it to be discovered.
- The file points at the Veeder driver's own statistics descriptor as the lever for fixing it.
Its exact parameters are left commented and unfilled, because they have to come from the
driver manual for the driver version in use rather than be guessed at here.
Added — the configuration states its own size
- Data arrays, console-side and Modbus-side map descriptor counts, and total registers served.
- A QuickServer refuses to load a configuration that exceeds its data point licence, and on
site that refusal looks like a dead gateway rather than a licence message. The file now says
to check Data_Points_Used against Data_Points_Max after loading.
- These are counts of what the file declares, not FieldServer's own figure, which is the only
authoritative one. The file says that too rather than implying a precision it does not have.
3.6.0 2026-10-12
The person at the console is usually not the person who knows the site. This release stops asking
them to know things: one button reads everything, every option that is not needed moved out of the
way, and the result is complete enough to write a gateway configuration from.
Added — discovery sweeps every address
- One run now asks every report at every device address in range. Tanks and
liquid sensors are swept from 0 to 16, inputs and relays from 0 to 8, and System Status from 0 to
4. Devices are found whether or not anyone knows how many are fitted, and a console that answers
one address correctly and another incorrectly shows up immediately.
- It used to read each report once, then re-read only the devices the first reply happened to
mention. If that first reply was wrong, everything after it was built on the error.
Added — build a gateway configuration from what was found
- A new step eight writes a QuickServer configuration and a Modbus register map
from the discovery results. The tanks and sensors are the ones actually found, the System Status
poll uses an address that answered cleanly, and the console-side character format is the one
discovery actually used rather than a guess.
- Tank alarm blocks are generated 40 registers wide. The driver defines tank alarm types up to 38,
so the 30-register blocks seen in the field silently drop six alarms including the high and low
temperature warnings.
- The two serial ports are assigned rather than typed, so the console and Modbus sides cannot end
up declared on the same port with different character formats.
- The generated configuration carries its own comments explaining why the status address was
chosen, what was found, and what to check before loading it.
Changed — fewer ways to get it wrong
- Step four is one button with nothing to choose. Live monitoring, the single-request sender and
the address comparison moved into an Advanced tools section.
Fixed — the tests themselves
- The simulator group never touched the real receive path. It decided for itself
why each reply had ended, so the receiver's end-marker check, its 64 KB cap, its idle timer and its
cleanup were never run by it: any of them could be deleted with every assertion still green.
Replies are now fed through the real path in chunks, and six new assertions pin the receiver's own
rules. Doing this immediately exposed two real discrepancies the old approach had hidden.
- Three fault modes could not produce the fault they claimed. A flood could not
reach the length cap at any offered speed; a chunked reply took eleven seconds against a five
second timeout, so it always timed out instead of testing reassembly; and a slow reply beat the
timeout by forty milliseconds. The suite also compared the delay against a timeout the tool had
stopped using.
- The simulator could report a fault it had not injected. An unimplemented mode
fell through and was logged as injected on an untouched frame, and the high-bit fault is a silent
no-op at 7 data bits, which is the setting where it matters most. Both now say so.
- Its seeded site described the same sensor as a setup warning through one command and a fuel
alarm through another, which is the exact numbering conflation these tests exist to catch.
- Two commands arriving in one read were handled concurrently, scrambling reply order and the
fault cadence. The unknown-command and replay paths left no trace in the log.
- Three assertions were vacuous. One tested that a two-digit parse was under 100,
which is true of any input; two others passed with the decoder they described deleted entirely.
All three now pin specific decoded values.
Fixed — found in later review rounds
- A reply cut short corrupted the next request on three of the four paths that send
one. Only the discovery sweep settled the line afterwards. Worst in the address
comparison, whose whole purpose is to show that one device address answers differently from
another: a leaked tail made it assert exactly that, from the wrong evidence.
- The address comparison could announce "every address answered correctly" while the
checksum column beside it showed red on every row. It judged on the timestamp alone.
- A liquid sensor, input or relay status that was not a number became a confident red
fault. It raised no anomaly, so it was trusted, counted in the device total and rendered
as a failure that did not exist. A half-shifted frame produces exactly this.
- The raw input and relay status value was never shown, although the decoder's
own comment promises it as the safety net for a polarity the manual and Chipkin's library
disagree about. Both reports now print it.
- Generated configurations put alarm categories under
Data_Type,
where the driver manual requires Veed_Alarm_Category. Under the wrong parameter an
alarm block simply stays at zero on a healthy console.
- A configuration with no tanks and no usable status address emitted section headers with no rows
beneath them, and reported success. It now refuses and says why.
- The "first problem" line named the last serious anomaly of the earliest request rather than the
first, so it reported a downstream checksum failure instead of the truncation that caused it.
- The console clock could be taken from a reply the report had already excluded as corrupted.
- Tank configuration and sensor configuration accepted any record length and any on/off character
without comment.
- A not-a-number or infinite float passed through unflagged and printed as
Infinity
or as a missing value.
- A console that echoes the security code had its "command not understood" reply reported as a
critical framing fault.
- The findings warned about corrupted replies even when the devices they described had been read
cleanly elsewhere in the same sweep.
- A completed sweep kept describing a console after the port was reopened with different
settings.
- The Modbus node ID accepted a fraction. The inventory poll interval had no unit.
- The deliveries table never closed its scroll wrapper, nesting every section after it.
Fixed — found in review of this release
- Sweeping every address could hide a real alarm. An unconfigured device answers
with a frame that is empty but perfectly well formed, so it was trustworthy and newer than the
populated reply from the documented address, and it won. A console with an active alarm reported
All functions normal. The documented address now takes precedence, with any other address
used only as a fallback.
- A reply cut short could corrupt the next request. The gap between requests was
shortened to 120 ms, which is less than the receiver's own idle threshold, so the tail of an
incomplete reply arrived during the following one. The tool now waits for the line to fall quiet
after any reply that did not end on its end marker, rather than guessing a fixed delay.
- A second run reported against the first. Totals counted every transaction the
session had ever made, so a run where nothing answered could still show a reassuring
67 of 134 answered. Each run is now measured on its own, and a run stopped early says so
rather than reporting Finished.
- The configuration generator could state what is fitted from a session that never looked.
It unlocked after any read at all, including a single manual request or a replay run, and would
then assert zero tanks about a console nobody had asked. It now requires a completed discovery
sweep.
- A console with no usable System Status address produced a configuration that polled one
anyway. The fallback was address 0, which is the address most likely to have been the
failing one. The alarm chain is now omitted entirely, with a warning at the top of the generated
file explaining why and confirming the tank readings are unaffected.
- The generated register map quoted float indices beside register addresses. Each
inventory value spans two registers, so an integrator reading offset 2 for volume would have
mapped the product code instead. The map now gives every value its own register pair.
- Liquid sensor blocks could overlap the system alarm block above 50 sensors; the system block is
now placed clear of whatever the sensors used.
- Live monitoring, the gateway replay and the address comparison could leave every control in step
four permanently disabled, including both Connect buttons, if anything threw mid-run. Only a page
reload recovered, which lost the session.
- A saved offline copy carried the previous site's results in its markup. The clearing list missed
the address comparison, the replay statistics, the generator panels, the report and the log.
- The high-bit signature no longer triggers the character-format advice when replies are parsing
normally, since a stray byte in a trailing fragment is not a settings problem.
- The command table is created without a prototype, so a lookup such as
toString
cannot pass the read-only guard's first check.
- Step 8 sat before step 7 in the page order.
Fixed — from the field
- The high-bit check could never fire. It sat after the early return for a
missing start marker, and a reply that is entirely high-bit garbage never contains one. That is
exactly the shape of a character-format mismatch, and exactly what this console returned when read
at the wrong data-bit count. It now runs first.
- Delivery times one character too long no longer cascade. This console writes
most of them as an eleven-character negative number where the protocol fixes the field at ten
digits, which slid the parser out of alignment and produced dozens of false float and field-count
errors. The longer form is now accepted and reported as
SIGNED_DELIVERY_TIME, and the
data behind it reads correctly. Verified against the real 5101-character reply: 49 delivery
records across five tanks, zero bad values.
3.5.0 2026-10-12
Everything in this release comes from FSE23204, where a healthy console looked dead for weeks and
then, once it was talking, turned out to be answering one device address incorrectly. Each change
exists because its absence cost real time.
Added — the settings search is now offered, not buried
- When the evidence points at the character format, the page says so and puts the search
one click away. It fires on total silence, on bytes arriving with bit 7 set, on replies
that never parse, and on framing or parity errors from the port. The result appears under
Read everything and under live monitoring.
- Silence is now named as the normal symptom of a format mismatch, in the result
box, in troubleshooting and in the report. A console set to 7 data bits answers a request sent at
8 data bits with nothing at all, which is indistinguishable from an unplugged cable. On FSE23204
the site ran 9600 7/N/1 and every 8-bit combination returned silence.
- The report records where the site sits relative to the published defaults.
The TLS-350 default is 9600 8/N/1 and the TLS-450 default is 9600 7/O/1. A site matching neither
is called out, because a gateway configured from either default would get silence.
Added — compare device addresses
- A new test in step 4 sends the same request to each device address and lines the
answers up. A console can answer one address correctly and another with a reply that is
structurally valid and carries a correct checksum, yet is wrong in a fixed-width field. From a
single request both look like success.
- When some addresses read cleanly and others do not, the page says so plainly and points out
that a gateway polling one of the bad addresses is fed a malformed reply on every cycle while the
console looks healthy everywhere else.
- The gateway replay can now target a chosen status address. It still defaults to
01, the deployed setting, so the same five minutes can be run against another address to see
whether changing the gateway's configuration would avoid the fault.
Fixed — two anomalies that were ours, not the console's
- A long report cut off by the timeout is no longer reported as a broken frame.
The page now works out how long the reply needed on the wire at the current speed and character
format, and says the timeout was too short. A 3186-byte delivery report needs about three seconds
at 9600 7/N/1 on its own.
- The bytes such a reply leaves behind are blamed on it. They used to arrive
during the next request and be reported as a second, unrelated fault.
- The default reply timeout is now five seconds rather than three. A longer timeout costs time
only when the console is silent, because a reply still ends on its end marker or on an idle gap.
- A malformed reply timestamp now explains the consequence: the protocol fixes the field at ten
characters, so a driver that reads ten and converts them will run into the data behind it. It also
suggests comparing the same request at another address.
Tests
- Eleven new conformance checks, built from the frames the console actually sent, covering the
wire-time arithmetic, the malformed timestamp, the timeout verdict, the leftover-byte attribution
and the settings advice firing on silence while staying quiet on a healthy session.
3.4.0 2026-09-11
Changed — wiring guidance now covers four console families
- Wiring and console setup is now tabbed by console type.
OPW SiteSentinel Nano opens by default, followed by Veeder-Root TLS-350,
Veeder-Root TLS-450 and Other console. The serial protocol is common to all of
them; the connector, the settings screen and the indicators are not, and presenting one
console’s wiring as though it were universal was misleading.
- Veeder-Root TLS-300, TLS-350 and TLS-350R. The port is a 25-pin D-connector on
the bottom left, panel-mount female, wired as DTE. Since a USB adapter is also DTE, a null modem
cable is required and a straight-through cable produces silence. Diagram, pinout for data and
ground, the front panel character format options, and the DIP switch on the CPU board whose
second position enables the RS-232 security code. Sourced from Veeder-Root part 576013-635.
- Veeder-Root TLS-450 and TLS-450PLUS. A 9-pin D-connector, also DTE, also
needing a null modem cable. Full connector pinout, the wider baud range, and a warning that this
console is commonly set to 7 data bits with odd parity rather than the TLS-350’s 8 and none.
Also notes that not every slot and port can be an RS-232 serial port. Sourced from Veeder-Root
part 577013-950.
- Other console. A generic route for any panel speaking this protocol: finding
the port, working out whether a null modem cable is needed, connecting the signal ground, and
three quick checks that narrow a fault down without a capture.
- Both Veeder-Root tabs record that these consoles do not echo received characters, so a reply
that is a copy of the request cannot have come from the console.
- The tab strip is a proper tablist. Arrow keys, Home and End move between tabs, and it wraps to
multiple rows on a phone without the page scrolling sideways.
3.3.0 2026-09-11
Added — wiring and console setup, on the page itself
- New section: Wiring and console setup. Three collapsible references, placed
before step 1 so they are available before anything is connected. Written for the OPW SiteSentinel
Nano and boards that share its layout.
- Wiring the RS-232 connection. A diagram of the console’s RJ45 socket wired
to a DB9 adapter, the pinout, the Cat5 colour mapping for T568B, and a table matching each symptom
this tool reports to its usual cause in the wiring. The RS-232 port is an RJ45 socket on this
console, which surprises people who go looking for a D-sub.
- The ground fault is called out specifically. White / orange is RJ45 pin 1, which
the console uses for RTS. Landing it on the adapter’s ground pin ties the ground reference to
a signal output and produces a link that works, then does not. The ground is white / green on pin 3.
A two-wire link with no ground at all is also covered, since it is a common cause of a connection
that runs for years and then turns intermittent.
- Selecting RS-232 in the console software. The Preferences and Port Settings
sequence for mapping a service to the RS232 port, because correct wiring is not enough on its own
and RS-232 and RS-485 appear as two separate devices.
- Reading the board LEDs. The console has separate transmit and receive lights for
RS-232 and for RS-485. A table reads each combination: whether the console is hearing the request,
hearing it and refusing to answer, or not hearing it at all. This settles in seconds what a capture
takes an afternoon to establish, and it is the only check that can prove replies are not coming
from the console.
- Jumper P4 is documented alongside the LEDs. It switches RS-485 local echo, and with it on every
message comes back as a copy of itself, which this tool reports as a loopback.
- The two relevant troubleshooting entries now point at the new section.
3.2.1 2026-09-01
Added — a looped-back cable is now named as such
- Reflected replies are detected. A reply that is byte-for-byte the request the
tool just sent did not come from a console; it is the adapter’s transmit line fed straight
back into receive — a loopback plug, a cable with TX and RX bridged, or an adapter in echo
mode. Each such reply now raises
REFLECTED_COMMAND (critical) instead of the
misleading pair NO_ETX + MISSING_CHECKSUM_TAG, which pointed at the
console when the fault was in the cable.
- When every reply is a reflection, the tool says the line is looped back in
plain language: in the result of Read everything, in live monitoring, in the settings
search, in the on-screen report, in the downloaded text report, and in the log. The report
summary gains a Reflected replies row and a
LOOPBACK line.
- The settings search scores an echoed request as tier 1 rather than tier 3, so a looped-back
line is no longer reported as “close, the speed is probably right”.
- RS-485 echo is handled. A half-duplex RS-485 adapter passes its own
transmission up as received data, so every reply begins with a copy of the request and the
console’s answer follows. This is noted as
ECHOED_COMMAND (medium), the echo is
removed, and the answer is analysed — and scored in the settings search — as if the
echo had not been there. Previously this tripped a checksum failure on every reply.
- New troubleshooting entry: Every reply is a copy of the request.
- Five new conformance checks cover the reflection and echo detectors.
- Conformance suite: every simulator fault through the analyser. When served over
http, the suite loads the simulator in a hidden frame and pushes each of its nineteen
fault modes, against every command it knows, through the client’s own analyser. Each must
raise the anomaly its help text promises; conformant frames must raise none; and coverage is
checked both ways so a fault added to one side without the other is a failure. From a saved
offline copy the group is reported as skipped, never as passed. The first run of this group caught
a bug in the new echo detector before release: a genuine reply begins with the same bytes as the
request, so a prefix match alone flagged every good reply. An echo is now recognised only when the
console’s own SOH follows the echoed request.
- Simulator: two new fault modes.
loopback sends the request straight
back and nothing else, as a shorted cable would; echo sends the request back and then
the correct reply, as a half-duplex RS-485 adapter does. Both are included in the auto-cycle.
3.2.0 2026-07-30
Five independent reviews before release — customer experience, diagnostic fitness,
regression hunt, safety and deployment, and the usefulness of the output. The most important
change is new: the tool can now reproduce what the gateway actually does, rather than reading the
console once.
Added — the reason for this release
- Reproduce the gateway (step 5). The gateway does not read the console once. Its
deployed configuration polls System Status at address 01 every second and In-Tank Inventory at
addresses 01/02/03 every seven seconds, continuously — and it fails about seventeen seconds
in. Reading each report once, which is all the tool previously did, could easily never provoke
that. This mode issues the same requests, to the same devices, at the same rates, interleaved,
for as long as you let it, and reports when the first problem appeared.
- The report now names the first serious problem of the session, computed before
any truncation. It is the one line most likely to matter.
Fixed — the report could mislead
- "The console reports all functions normal" was shown even when no alarm data had been
received at all. Those are completely different things, and the green reassuring version
was being shown for both.
- Long sessions discarded the beginning, which is the part worth having. The
anomaly, request and trace lists kept only the most recent entries, so the onset of an
intermittent fault — normally the diagnostic moment — was the first thing dropped.
All three now keep both ends and say what was omitted from the middle.
- The report claimed per-byte arrival times were preserved in the capture file. They
never were — the capture timestamps each message, not each byte. Corrected, and
the trace now states plainly that its byte-spacing figures are the only per-byte timing that
exists.
- "Finished" was shown in green even when nothing at all had answered.
- A settings search stopped early reported "no reply on any combination" without mentioning that
most combinations had never been tried.
- Time the tab spent in the background is now measured and reported, because browser throttling
stretches every timing taken during it.
Fixed — read-only hardening
- The allowlist check now also sits in the function that actually writes to the port, not only in
the layer above it. A guard one level up is a guard that can be stepped around.
- The command table and the device limits are frozen, so neither can be extended at runtime.
- The security code is validated before it reaches the wire. It is concatenated
into the request, so a value containing control characters could have injected framing into the
middle of our own command — and a console that resynchronises on an embedded start marker,
behaviour this tool detects in replies, might then have read the remainder as a different
command. Printable characters only, correct length enforced.
- Verified by test: five separate bypass attempts, including calling the internal send function
directly, all blocked with zero bytes transmitted.
Fixed — concurrency
- All four ways of reading the console — read everything, live monitoring, settings search,
and gateway reproduction — now exclude one another. Two running at once would have quietly
destroyed the timing fidelity the reproduction mode exists to provide.
- The settings search now holds the connection controls while it runs; connecting manually
part-way through raced it for the same port.
- Double-clicking Connect could start two overlapping opens, after which the recorded connection
settings might not describe the one the report was taken over.
- Alarm reports now check device numbers like every other decoder does.
Fixed — the conformance tests could not run from disk
- The test page loaded the explorer in a hidden frame and drove it from outside. That works over
http but not from file://, where the browser gives every document its
own unique origin — and the attempt throws rather than returning nothing, so it presented
as a broken tool rather than an unsupported one. The suite now runs inside the explorer itself,
reachable at index.html?selftest, and works from a hosted copy and a saved offline
copy alike. It also now verifies the code in that very file, so a customer's own copy can be
checked by adding ?selftest to whatever they ran.
Added — safety net
- Closing the tab with an un-downloaded session now warns first. Losing it means repeating the
whole on-site procedure.
- A message when the page is blocked from running by a script policy, instead of a blank page
that does nothing.
- The security code appears in both files sent to Chipkin; the page now says so.
3.1.0 2026-07-30
A full pre-release code review and a command-by-command audit against the RS-232 manual. This
release is almost entirely corrections, several of which would have produced confidently wrong
readings rather than visible errors. A new
protocol conformance suite now checks every command on demand.
Fixed — decoding was wrong
- Active and cleared alarm reports (i113, i114) were parsed with the wrong layout
entirely. Both begin with four 20-character station-header strings and use wider records
than the system status report — 18 characters for i113 and 20 for i114, including a sensor
category and a timestamp. They were being read as the 6-character records of i101, which meant
80 characters of site name were parsed as alarm codes and every real record after them was
misaligned. Both now have their own parser.
- System alarm names (category 01) were wrong throughout. The table bore almost
no relation to the published one — type 05 is "Too many tanks", not "Printer out of paper".
Replaced with the manual's table in full.
- Liquid sensor alarm names (category 03) were shifted by two. The i101 alarm
table starts at 02 and is numbered differently from the i301 sensor status values; the two had
been conflated, so every liquid sensor alarm was reported under the wrong name.
- Input alarm names (category 05) were incomplete — "Generator off",
"Generator on" and "Input out alarm" were missing and would have been reported as unknown.
- Input status names (i401) were guesses and all four were wrong. The documented
values are setup data warning, normal, alarm and out alarm.
- Relay status names (i406) were inverted. The manual defines 0001 as Relay
Active and 0002 as Relay Inactive.
- The About Screen report (i907) is a length-prefixed structure, not prose. It
was being run through the version-string parser, which presented raw length bytes as if they
were text. It is now captured verbatim and left for a human to read.
- Tank numbers above 16 were accepted. A TLS-350 has at most 16 tanks, so a
misaligned reply claiming "tank 40" is impossible and should be caught — which is the
entire point of that guard. Sensors, inputs and relays remain bounded at 64.
- Delivery reports (i202) did not check tank numbers at all, unlike inventory and sensor
reports.
Fixed — could produce a wrong report
- Two requests could be in flight at once. Starting live monitoring or a
settings search while "Read everything" was running began a second command stream, and the
protocol allows only one command at a time. The controls now exclude each other, and the request
layer refuses an overlapping send outright rather than relying on every button being disabled
correctly in every error path.
- Reconnecting mid-run is now blocked. It could tear down the port an in-flight
request was using and finish the run on different settings than it started with, producing one
report that silently described two different connections.
- A dropped cable was never noticed. When the serial stream died the tool
stopped reading but still believed it was connected, so a live session would spin indefinitely
inventing timeouts. It now reports the disconnection and offers to reconnect.
- An unparseable clock reply (i501) was echoed into the report as though it were a timestamp,
unfiltered. It is now flagged as malformed and not displayed as a time.
Fixed — capture file
- Messages larger than about 65500 bytes produced a malformed capture. The
length fields in the readable format are 16-bit and silently wrapped, so Wireshark rejected the
packet — losing the evidence in precisely the runaway-console case worth capturing.
Oversized messages are now split across consecutive packets.
Added
- A protocol conformance suite that drives the shipping decoder
with responses built from the manual's documented layout for all fifteen commands, and asserts
every field decodes to the value that was encoded. It is deliberately checked against
deliberately-broken builds to confirm it detects regressions rather than passing vacuously.
- The tool's own self-check result now appears in the environment check, so a build with broken
protocol arithmetic is visible to whoever is standing at the console instead of only in a
downloaded file.
- A warning when the security code is not exactly six characters. A wrong-length code produces a
command the console silently ignores — indistinguishable from a dead cable, and exactly
the sort of thing that sends someone hunting a wiring fault that does not exist.
- The simulator now sends real data for active and cleared alarms, inputs, relays, tank and
sensor configuration, port settings, and a full delivery record. Previously these returned only a
bare timestamp, so the decoders for them were never exercised by testing.
Fixed — simulator
- Stopping the simulator left the fault auto-cycle timer running.
- Neither history was capped, despite the auto-cycle being built for multi-hour runs.
- The read loop lacked the event-loop guard the explorer was given in 2.3.0.
- The count-field faults could silently do nothing while still being reported as applied, which
would make a correctly-behaving explorer look as though it had missed a fault.
3.0.0 2026-07-30
Renamed
- The client is now TLS-350 Explorer. It speaks the Veeder-Root TLS-350 serial
protocol, which is what the OPW SiteSentinel Nano and a range of other consoles implement, so the
name describes what it talks to rather than one vendor's badge.
- The explorer and the simulator now share a single version number and this single change log.
Previously they were versioned separately, which made it impossible to tell from two log files
whether they came from a matching pair.
Fixed
- The port stayed open after an automatic settings search. Pressing
Use these settings and then Connect failed with
InvalidStateError: The port is already open. The search borrows the port for every
combination it tries and did not always hand it back — our own bookkeeping could believe the port
was closed while the browser still held it open. Every connection attempt now closes first,
and the search explicitly releases the port when it finishes.
- Use these settings now connects directly instead of only filling in the boxes and
leaving you to press Connect.
Added
- Save for offline use. A button in the last step downloads this tool as a
single file you can keep on a laptop and open later with no internet connection at all. Useful
for sites where the machine that reaches the console cannot reach the web.
- Links in the header of every page to move between the explorer, the simulator and this change
log.
- A footer on every page showing the version and linking to chipkin.com and this change log.
- Much fuller on-page instructions, including a troubleshooting section covering
the problems customers actually hit: an empty port list, a port that will not open, no reply from
the console, replies that arrive garbled, and downloads being blocked.
2.3.0 2026-07-30
Fixed
- The automatic settings search froze after its first attempt and the Stop
button did nothing.
port.open() can sit unresolved forever when the driver has not
finished releasing the handle from the previous close, and an await that never
settles is a loop that never checks its abort flag. Every serial operation is now time-bounded —
open 5 s, request 6 s, close 5 s — with a settle delay between attempts and each phase logged by
name, so a stall says where it happened instead of the page simply stopping.
- A fast console's reply could be missed. The receiver was installed after the
write completed, so a console answering inside that gap had its reply counted as unsolicited and
the request timed out anyway. The receiver is now in place before anything is sent.
- The page could lock up completely. If the input stream was already closed,
reads returned instantly and the read loop spun on microtasks alone, starving rendering and
input. It now detects that and stops, and yields to the browser on every pass regardless.
Added
- A running status line during the settings search naming the combination being tried, so a slow
attempt is visibly slow rather than looking like a hang.
2.2.0 2026-07-30
Fixed
- Corrupted replies invented tanks and sensors that do not exist. A reply with a
second start marker embedded in it decodes into a mixture of real devices and fragments of the
reply header. Because the site picture merged results by device number, one such reply
permanently added a phantom device that no later good reply could remove — a fault-injection run
produced a "tank 0" whose height was really tank 2's volume, and sensors "0" and "30" conjured
out of a date stamp. The tank, sensor and alarm figures are now built only from replies that
arrived intact. Excluded replies keep their full detail lower down and are counted in the
summary.
- Replies claiming a device numbered outside 1–64 now raise
INVALID_DEVICE_NUMBER.
The protocol reserves 00 for "all devices" in a request; no reply should ever use it.
Changed
- Protocol anomalies and Requests are scrollable with the newest at the top, and anomalies are
grouped by type with counts before the detail.
- The text report is bounded — 200 anomalies, 200 requests, 800 trace messages, newest first —
with true counts always stated. The capture file remains complete and is never truncated.
2.1.0 2026-07-30
Added
- Live monitoring. Polls the console every 1–30 seconds and refreshes the whole
report each cycle, flagging how many problems are new, so changes at the site show up as they
happen.
- Simulator: an auto-cycle that steps through every fault on a timer with clean intervals between
them, which is what an intermittent problem actually looks like.
- Simulator: every tank and sensor starts with different values, and two alarms are active by
default, so it is obvious at a glance whether the explorer mapped each device correctly.
Fixed
- The capture file recorded one packet per byte. At 9600 baud the browser
delivers a byte at a time, so a six-command session became 141 unreadable rows. Bytes are now
grouped into whole messages.
- A deadlock between closing the port and the read loop acquiring its next reader.
Changed
- The capture file offers two formats. The default wraps each message in Ethernet/IP/UDP with
invented addresses so Wireshark's Source and Destination columns name the two ends and Follow
Stream reconstructs the conversation as text; the alternative uses the true RS-232 link type,
where those columns are necessarily blank.
2.0.0 2026-07-30
Added
- Full read-only interrogation across fifteen reports, then each tank and sensor individually.
- A console report covering tanks, deliveries, liquid sensors, alarms, inputs and relays.
- Three depths of automatic settings search: 19, 54 or all 108 combinations.
- Delivery records decode properly instead of being dumped raw.
Changed
- Dropped the JSON download; the text report now carries everything.
1.0.0 2026-07-30
- First release. Built to answer one question before committing to a build approach: will a
browser talk to the console on the site laptop at all? It does — including, unexpectedly, from a
local file with no web server.
- Environment check, port selection, read-only command set, anomaly detection, Wireshark capture,
and a console simulator with sixteen deliberate fault modes.