03 Case study Private
Augur
Bring up a Devantech dS3484 Ethernet relay module with an Xsens GNSS/INS sensor. A headless build-and-flash tool, an NMEA decoder that runs on the module itself, and a declared data feed, so the board knows its own position, heading and tilt and switches a relay from a geofence with no computer in the chain.
01 Problem
The Devantech dS3484 is an Ethernet relay and I/O module that runs small programs written in the vendor's own language, dScript, built and flashed through a desktop IDE. The Xsens MTi-680G is a GNSS/INS sensor that reports position, heading and tilt as NMEA sentences over RS232.
The goal was to make the board decide for itself: decode the sensor on the module, compute where it is, switch a relay from a geofence with no computer or gateway in the chain, and hand the data back out cleanly. The tooling also had to be something another engineer could run from their own laptop.
The project is named for the Roman augur, who marked out a region of the sky, declared in advance what he was watching for, and reported only what appeared inside it. That became the working rule: declare the region first, report what was observed, claim nothing further.
02 What I built
- A headless flasher. It calls the vendor's own compiler and bootloader classes directly, writing exactly the bytes the IDE would, with a power pre-flight check and clear exit codes.
- An NMEA decoder that runs on the module. It hunts across six baud rates and three serial headers, and only declares a lock on a sentence whose checksum verifies. All of it is fixed-point, because dScript has no floats.
- A declared data feed. An XML endpoint with a written contract, audited against the firmware in both directions: declared but not delivered, and computed but not declared.
- A local control panel. A ten-step self-test that reports each link of the chain separately, a satellite view drawn on a 2D canvas so it works offline, a QR code to open the module on a phone, and a CSV logger.
03 Architecture
Three paths. Build and flash runs from the laptop over USB. The sensor feeds the module over RS232, where the decoder and geofence run. The data comes back out over Ethernet to the panel, a phone, or a logger.
04 Key decisions
Decision 1
Drive the vendor's own compiler, not its GUI
- Context
- The module is normally built and flashed by clicking through a desktop IDE, which can't be scripted or handed to someone else reliably.
- Decision
- Link directly against the IDE's compiler and firmware-updater classes and reproduce its compile-and-upload step headlessly, so the bytes written are exactly the bytes the IDE would write.
- Trade-off
- It's coupled to one version of the vendor's toolchain. Screen-scraping a GUI would have been worse.
Decision 2
Decode on the module, not on a host
- Context
- Parsing on a laptop is easy, but then the geofence needs the laptop. The point was a board that acts on its own position.
- Decision
- Run the NMEA decoder as a thread on the module itself, in fixed-point micro-degrees, because the language has no floating point.
- Trade-off
- No floats, no operator precedence, and program flash at 93%. The board now works with nothing else attached.
Decision 3
A geofence box, not a circle
- Context
- A radius check means squaring distances, and squared micro-degrees overflow a 32-bit integer.
- Decision
- Fence with a latitude/longitude box instead, which needs only comparisons and stays exact.
- Trade-off
- A rectangle (about 45 × 55 m at Cyprus's latitude) instead of a circle. It's correct, and it fits.
Decision 4
The data feed is a contract
- Context
- At first the feed was whatever the web page happened to show, which hid two diagnostic fields nobody knew existed.
- Decision
- Write the feed down as a declared contract, generate it, and audit it against the firmware in both directions. The README states "81 of 97 declared" and lists the rest, instead of claiming them.
- Trade-off
- About 5 KB more website on a nearly full module, for an interface someone can integrate against.
05 Hard problems
A silent sensor with four causes
Nothing came through, and the cause turned out to be four separate faults: an unconnected supply wire, CTS flow control holding every byte, the sensor on the second serial header rather than the first, and left-to-right arithmetic in dScript. That last one put the first fix in the middle of nowhere, at latitude −42.6, longitude −272.8. Each was found and fixed in turn, one variable at a time.
A lock on a single dollar sign
The baud-rate hunt declared a lock as soon as it saw a $, the first byte of every NMEA sentence. Line noise contains plenty of those, so it once "locked" at the wrong speed and served 84 lines of garbage. Now only a sentence whose checksum verifies can declare a lock, and the lock drops after a few seconds without one.
A website that silently disappeared
Twice, the build said success, the upload said OK, and the module served nothing. The first time, a stray zero-byte file was quietly breaking the image, found with three controlled one-variable flashes. The second time, any website above about 470 KB reverted on reboot. The flasher now refuses to write anything over that limit, instead of "succeeding".
06 Outcome & what I'd do differently
Augur works end to end on the bench, with 20–30 satellites in view and a fix accurate to a few metres. It's packaged so another engineer can run it from their own laptop. Still open: validating the logger on hardware, a history view, a geofence that survives a reboot, and RTK, which is waiting on hardware.
- Declare the contract on day one. The page-coupled feed hid real fields for weeks.
- Numbers in the docs come from measurement, not from reading the code. The bench disproved two documented claims, and both corrections are written down.
- Watch the headroom. At 93% program flash, every feature has a cost you only see when it stops fitting.