Motion Master
Loading...
Searching...
No Matches
Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Unreleased

5.8.2 - 2026-08-25

Fixed

  • System identification could fail on one host and work on every other, for the same drive, and stay that way. The last thing it does is write the velocity controller integral limit, and Motion Master refuses a write whose value does not match the data type it holds for that entry, so a wrong data type fails the whole procedure with nothing reaching the drive. The object dictionary it holds that type in is enumerated over the SDO Information service, one object per transfer through ecx_readOE — a function that loops over the subindexes but returns only the last one's working counter, so a subindex whose answer never arrived was invisible. The entry was then filled from a zeroed buffer, or from the previous subindex's answer arriving late in this one's place, which gives the entry another subindex's name and data type. Observed on a Circulo NG, where 0x2011:04 was recorded under subindex 3's name as REAL32, and the value 1650 the drive holds there was read back as 2.31e-42. Each subindex is now asked for separately and its own answer checked, which puts every one of them behind the retry that drains the mailbox and asks again; a subindex whose answer stays lost is named in the log and left out of the dictionary rather than invented. A response that names a different entry than the one asked for is rejected outright rather than accepted as this request's answer, which is how another entry's name and data type arrived in the first place — the SDO Information service answers one request at a time, so such a response is an earlier request's answer arriving late, and both the object and the entry it describes are on the wire and were not being read. The dictionary is also cached to a file keyed on the device's identity and read back on every later scan, never checked against the device again, so one incomplete enumeration used to persist on that host for as long as the file existed. Such an enumeration is no longer written to the cache at all.
  • A value refused for not matching its entry's data type was reported without naming the device, the entry or the type — "Cannot assign an integer value to a non-integer entry!", and the same for floats and strings, where every other branch of the same check names all three. They now read like the rest, for example "Device 1: Cannot assign the integer value (1650) to entry (0x2011:04), whose type is REAL32 (0x0008)!".

5.8.1 - 2026-08-22

Added

  • Encoder configuration and narrow-angle calibration run on the devices running Jasper firmware, hardware IDs 4101 to 4105. Both procedures decided what to write from get_circulo_type(), which recognised only the 8500 to 8699 range, so a Jasper device was refused outright by the configuration procedure and took the unsupported branch in calibration. The encoder class is now answered per encoder rather than per device, because the two encoders of a board are not always alike: on 4101 and 4102 the inner encoder carries a 16-pole-pair ring read at 18 singleturn bits, while the outer one carries the 32-pole-pair ring a Circulo 7 carries. Every 32-pole-pair encoder takes the Circulo 7 register values unchanged. The 16-pole-pair ring is a class no product has carried before, and its values follow from the ring: MPC is 0x04, OUT_MSB is 0x04 on the singleturn path and 0x16 on the multiturn one, the iC-PVL period count PCR is 0x0F, and the exponent that converts a singleturn position to an angle when the iC-PVL is synchronised to the iC-MU is 18. No calibration velocity has been measured for that ring, so it takes the 32-pole-pair figures: a base of 90 rpm, and a 70 rpm floor on the motor shaft below which the operator is warned that the motion range is narrow. Only the base velocity can be overridden, per run, through velocity_base_value_rpm.

Fixed

  • Reading the magnet distance could terminate Motion Master. The amplification limits and the polynomial that converts a measured amplification to a distance in millimetres are both looked up with at(), keyed on an encoder type that a client supplies directly through external_encoder_type whenever the encoder is not on sensor port 1 or 2. A value with no measured curve therefore threw out of the procedure rather than being reported. The lookup is now checked before it is made. On a device running Jasper firmware the check happens up front instead: none of its magnetic rings has been characterised, and a curve fitted to a different ring would report a plausible wrong air gap for the operator to act on, so the request says that no curve has been measured rather than answering with a number.

5.8.0 - withdrawn

Released and then removed the same day. The Linux archive and the Docker image never built, so the version existed only as a Windows archive and could not be installed on every supported platform. Its tag and its GitHub release are deleted, and everything it was meant to carry is in 5.8.1 above.

The cause was a std::map::contains() call, which is C++20, in the magnet distance guard. CMAKE_CXX_STANDARD is declared for the Windows presets only, so a Linux build compiles at whatever standard the compiler defaults to: C++20 on a recent GCC, C++17 on the one in the release container. The same line therefore passed a local build and the Windows job and failed the Linux one. 5.8.1 uses count(), which every standard has.

5.7.0 - 2026-08-20

Added

  • EtherCAT product code 0x501 is recognised as a CiA 402 drive. Until now the device type was resolved from a table of vendor and product code pairs that listed 0x201, 0x301, 0x302 and 0x401, so a device reporting 0x501 in 0x1018:02 was left with an unspecified type and a warning in the log. Type resolution gates everything CiA 402: motion control, device procedures, auto-tuning and the quick stop issued on shutdown all check for a CiA 402 drive first, so such a device was enumerated and its parameters were readable, but it would not move.

Fixed

  • Recording the cogging torque crashed Motion Master on a drive that does not report a position control strategy, instead of reporting that the drive is not tuned. Before the recording starts, the procedure checks that the position controller gains are non-zero, and which of them have to be non-zero depends on the strategy the drive reports in 0x2002 — a simple position loop needs only its own proportional gain, a cascaded one needs the velocity loop's as well. That was the one lookup in the check that did not first ask whether the object exists, where every other lookup around it does, so on a drive whose object dictionary does not define it the check dereferenced nothing and the process died. A Circulo NG is such a drive: the object is absent from the dictionary it declares. The check now names the missing object in the log and reports the gains as unusable, which is what it already did when they could not be read.
  • Startup spent up to eight seconds per device working out that no safety module is fitted. Detecting one reads 0xF050:02, an object belonging to the Modular Device Profile, which only a product that can carry a detachable module implements — among the SOMANET devices, only Circulo Safe Motion. Integrated FSoE safety is a separate thing, so a Circulo NG is a safety product and has none of these objects either. Every other device was being asked a question its dictionary does not define, and each answered at its own cost: a Node aborts the read in about 150 ms, an Integro does not answer at all and spends the full two-second mailbox timeout, four times over with retries — a measured 8.15 seconds of every startup. The object dictionary is read from the device immediately before this, so it now settles the question at no cost: a device whose dictionary has no 0xF050:02 is not asked. The read itself also drops from three retries to one, which bounds the case that remains — a device that does have the object but does not answer — while keeping the retry that matters, since a queued CoE emergency fails whichever transfer follows it and clears on the next one.
  • Interrupting Motion Master did not always stop it. Ctrl-C (or SIGTERM) sets a flag that the main loop is expected to notice and act on, and the shutdown that follows is a cooperative sequence: stop the devices, disable them, wait for every machine to finish. Any step of that can fail to complete, and there was nothing to fall back on when it did — so the process kept cycling and only SIGKILL ended it. Two cases were reported: while waiting for devices to appear, where the branch that ends the loop was unreachable because the initialization it waits on was still in flight; and with devices connected but no longer answering, where a device that never reports itself stopped keeps the shutdown sequence from ever finishing. The shutdown now has a five-second deadline, after which the loop ends with whatever work is still in progress, and a second interrupt terminates the process immediately instead of waiting. Device initialization also gives up when asked to: it used to run its whole sequence to the end regardless — reading each mapped object over SDO, detecting whether a safety module is fitted, transitioning a device that looks like it has no firmware into BOOT, and programming the process image on every device — which on a bus that was answering slowly added several seconds nobody wanted, and could leave a drive sitting in BOOT because a key was pressed at the wrong moment. It now stops between those steps instead. The signal handler also no longer writes through the logger: it took a lock that another thread could be holding at the time, which deadlocked the handler itself, so a request to terminate could go completely unacknowledged.
  • Full auto-tuning failed outright on firmware with a two-digit major version, reporting ‘State evaluation error. invalid literal for int() with base 10: ’' — a message that names nothing leading to the cause. Motion Master sends the drive's firmware version to the standalone-autotuning subprocess, which read it by character slicing rather than by parsing: software_version[0:6] assumes exactly vX.Y.Z, so v10.0.0 became v10.0. and split to an empty last component. A two-digit patch was silently wrong in the same way, with v5.6.10 read as 5.6.1. The bundled standalone-autotuning` moves from v3.0.0 to v3.1.0, which parses the version per component instead. The fix itself was released as v3.0.1; v3.1.0 carries it unchanged and adds builds for platforms Motion Master does not ship on.

5.6.4 - 2026-08-18

Fixed

  • The broadcast read that counts the slaves on the bus had the same exposure as the PDO assign walk fixed in 5.6.3, from a different timeout. ecx_config_init issues it with EC_TIMEOUTSAFE, which was still 20 ms, so a frame the host held back past that read as a bus with fewer slaves on it than it has. It is now level with EC_TIMEOUTRET at 100 ms. Motion Master's own slave-count read already used 100 ms; this is SOEM's.
  • The two register reads the cyclic thread makes to describe a bus whose slave count has just dropped now give up after 2 ms instead of waiting out the full frame timeout. They run on the thread that has a cycle to keep and they run precisely when slaves have stopped answering, so they are the reads least likely to get a reply and the worst place to wait for one — and their answer only goes into a log line. Raising EC_TIMEOUTRET to 100 ms would otherwise have held the cycle for that long per slave. Every read off the cyclic thread keeps the full timeout, where waiting out a late frame is what stops it being counted as lost. mm::comm::soem::getSlaveState takes the timeout as an argument for this, defaulting to the old behaviour.

5.6.3 - 2026-08-18

Fixed

  • A device could fail the transition to SAFE-OPERATIONAL with AL status code 0x001D (invalid output configuration) or 0x001E (invalid input configuration) on one start and come up normally on the next, with the same firmware and cabling. SOEM sizes each SyncManager by walking the device's 0x1C12/0x1C13 PDO assignment over CoE, and that walk never checked the working counter of any read it made: a read that failed left the PDO index at 0, which it took for "no PDO here" and skipped, so the process image came out short by a whole PDO. Captured on the wire from a SOMANET drive, one failed read of 0x1C12:03 dropped 0x1602 from the assignment and SM2 was programmed with 27 bytes where the device's own mapping needs 39 — after which the only party that could notice was the device, and all it can report is that the configuration is invalid, naming neither the PDO nor the read. Every read in the walk now retries before giving up, and a read that still fails aborts the walk instead of returning a total that is short: a SyncManager of zero length is unmistakable where a plausible-looking 27 is not. EC_TIMEOUTRET, the time a frame has to come back before its transfer is abandoned, also goes from 20 ms to 100 ms. On the host this was measured on, frame round trips are about 20 microseconds but a handful per minute land between 20 and 23 ms with nothing at all in between, so every one of those outliers was a lost transfer at the old value. The cyclic process-data exchange is unaffected either way, as it passes its own timeout when receiving.
  • Motion Master now compares the process image SOEM mapped against the PDO mapping it read from the same devices, and reports the two byte counts when they disagree. This is the failure above seen from the other side: the mapping is read with retries during device setup and was already correct in the case captured, so the disagreement pins the fault at the point it happens rather than leaving an AL status code to be traced back to it. It reports rather than repairs — re-running the mapping is not safe, because SOEM appends FMMUs from the last one it used and walks forward past every populated entry without a bound, so a second pass runs off the end of the array.

5.6.2 - 2026-08-18

Fixed

  • A device whose firmware does not list the PDO mapping objects in its CoE object dictionary had its mapping erased at startup, and then could not leave PRE-OPERATIONAL: it failed the transition to SAFE-OPERATIONAL with AL status code 0x001E (invalid input configuration) and the watchdog reported it as not in the OPERATIONAL state. setup_od_entries() adds local placeholder object dictionary entries for 0x1600-0x1603 and 0x1A00-0x1A03 when the device's own list does not report them, so that the mapping stays addressable through the object dictionary API — but it seeded each placeholder with set_value(0), which is not a local assignment: it writes over SDO. Writing 0 to a mapping object's entry count clears that object's mapping, so all eight were emptied, the process image came out zero-length, and the device correctly rejected it. Nothing about the placeholders needs the bus, so they are now filled in locally. Two things hid this. The write bypassed --skip-pdo-mapping entirely, which gates only the separate ui.config.json mapping step, so the documented way to leave a device's mapping alone did not. And setup_od_entries() returns early when a cached object dictionary exists for the device's vendor, product code and revision, so the erasure happened only on the first start after that cache was written or removed — every later start read the cache, skipped the placeholder step, and worked.
  • Object dictionary, file and PDO-mapping transfers failed for reasons that had nothing to do with the transfer, most visibly as a device that reached SAFE-OPERATIONAL on one start and failed with AL status code 0x001E on the next with the same firmware and cabling. A slave puts emergency messages and mailbox errors into its mailbox without being asked, and ecx_mbxreceive recognises one, handles it, and returns a working counter of 0 — so whichever transfer happens to collect it is reported as failed, and the next one succeeds because the mailbox is now clear. Every control-plane path (SDO read/write, SDO Info, FoE read/write) now discards what is waiting in the mailbox and drains SOEM's error list before retrying, and reports the queued reason in its log line. SOEM's error list was previously never drained at all: it is a fixed 64-entry ring that silently overwrites its tail once full, and an entry left behind is inherited by the next transfer to fail. The FoE read and write retries now also treat an emergency, a mailbox error and a packet error as worth another attempt; previously they were treated as the device's final answer, so a file read could fail outright while reporting an error code belonging to a message the device had raised on its own.
  • A mailbox transfer started from Motion Master could collide with another one already in flight to the same device, in which case one thread received the other's reply. A mailbox holds one message at a time, so control-plane traffic has to be serialised, and three separate mutexes each covered part of it: a per-slave array in the application layer, honoured by the SDO and file paths there; a per-instance mutex on mm::comm::soem::Slave; and nothing at all in libs/comm/soem.cc, where the PDO mapping is written and read back, the SMM module is configured and the mapping is verified. Those are now one recursive mutex in libs/comm, taken by every mailbox transfer in both layers. The cyclic process-data exchange deliberately does not take it — it uses no mailbox — so the 1 ms loop is not affected. On a bus with more than one device, a long file transfer (a firmware upload in particular) now delays SDO-polled parameter refreshes for every device rather than only the one being written; parameters served from the process image are unaffected.

Removed

  • g_pdo_mapping_in_progress, a flag the cyclic loop, the parameter refresher and the monitoring loop each checked in order to stand down while the PDO mapping was being rewritten. Nothing ever set it, so none of them ever did, and the four branches guarding on it could not be taken. It is not needed: re-enumeration stops the master before rebuilding the process image, and the mailbox contention it appeared to guard against is now covered by the control-plane mutex.

5.6.1 - 2026-07-30

Fixed

  • The Windows release of 5.6.0 failed to build. standalone_autotuning.h includes <windows.h>, which brings in the original WinSock.h unless WIN32_LEAN_AND_MEAN is defined first, and that cannot coexist with the WinSock2.h that Boost.Asio requires — the compiler stopped with WinSock.h has already been included. Motion Master defined the macro only for apps/motion_master, so it never reached the auto-tuning library target. The header now defines it itself, and the target defines it too for a consumer that reaches <windows.h> by another route first. Linux was unaffected.

5.6.0 - 2026-07-30

Fixed

  • Motion Master hung on shutdown, after logging Stopping the standalone-autotuning daemon process..., in a state no further SIGINT could end. Stopping that process was left to the destructor of its global instance, which runs during static destruction — and by then the internal logger's map, defined after the instance in the same translation unit and therefore destroyed before it, is gone, so the loguru callback registered with a pointer into it locked a mutex that no longer existed. The process is now stopped explicitly at the end of main() while everything it needs is still alive, and the internal-log callback is removed immediately afterwards so that no later destructor can reach it either.
  • Auto-tuning and system identification could take minutes instead of seconds, while standalone-autotuning sat at ~90% CPU and the cyclic loop reported wait intervals of a full 100 ms round-robin timeslice. increaseThreadsPriorityLevels() puts Motion Master's threads on SCHED_RR, and it runs before the autotuning program is spawned, so the child process inherited the real-time policy across fork() — and so did every BLAS worker thread it created, one per core. Those workers synchronize by spin-waiting, so at real-time priority each one held a core for its entire timeslice waiting for the others, while starving the 1 ms process-data exchange it was competing with. The spawned process now resets itself to SCHED_OTHER before executing the program, and on Windows it is created with NORMAL_PRIORITY_CLASS so it cannot inherit the realtime priority class either. Measured on an idle machine, identifying a 96000-sample recording takes about 7 seconds. System identification additionally drops its own worker thread to normal priority once the recording is finished — matching what full auto-tuning already did — so that rendering the measurements and waiting for the model no longer competes with the cyclic loop; the recording itself keeps the real-time priority it needs to stream the chirp at 1 kHz.
  • Full position auto-tuning failed immediately with the error message ‘'highFriction’. The bundled auto-tuning release gained a high-friction tuning rule after v2.3.3, and its full_auto_tune_position_controller reads the highFriction input unconditionally, so the request Motion Master sent — unchanged since before that rule existed — raised a KeyError whose text is the quoted key name. The request now carries highFriction: 0, selecting the standard tuning rule, which is the behaviour every auto-tuning release before v3.0.0 had. The high-friction rule itself is not reachable yet: choosing it needs a way for a client to ask for it, and the highFrictionGain` the function now returns has no field in the protobuf schema to travel back in. Velocity full auto-tuning was unaffected, as is the damping-ratio/settling-time slider tuning, whose corresponding input is optional.
  • The three WebSocket server threads no longer run at real-time priority. They inherited it from the thread that starts them, having no use for it: they serve sockets and serialize protobuf messages — occasionally CPU-heavy work, such as monitoring frames at rate or a bode diagram of tens of thousands of values — which competed with the cyclic process-data exchange at the same priority. They touch no device and share only the two message queues with the machine manager, whose critical sections are a single push or pop, so nothing waits on them. The two threads that do need the real-time policy, the machine manager's cyclic loop and the timer thread that emits the tick every cyclic listener waits on, now claim it themselves instead of relying on having inherited it; the timer thread's priority previously depended on which thread happened to construct the singleton first, and a tick source silently demoted to the default policy is invisible until the cycle starts slipping.

Changed

  • Full auto-tuning produces different gains than before, because the bundled standalone-autotuning moves from v2.3.3 to v3.0.0 and its tuning library changed with it: 172 lines of sncn_full_auto_tuning_library.py were rewritten for the high-friction tuning rule, and slycot — absent from every earlier release's requirements, so the H-infinity norm calculation the library calls six times could never run — is now installed. The new results are markedly more conservative on anything beyond a first-order plant: measured against v2.3.3 with identical inputs, a 4th-order plant's position ki drops 56% and its demanded settling time doubles, and a resonant plant's position kp drops 46% with ki down 73%. A simple first-order plant differs by only 0.2%, which is why this is easy to miss in a quick check. The transport is not responsible: with the same binary on both sides, the slider tuning functions are bit-identical and system identification returns byte-identical bode data.
  • Auto-tuning now talks to standalone-autotuning over its HTTP API instead of the IPC channel that was removed in the auto-tuning release the bundled executable is pinned to (v3.0.0). Motion Master starts the executable with --http on port 63528 of the loopback interface, overridable with the new --autotuning-port flag, and waits for GET /api/health before reporting it ready; a missing or failing executable is now logged plainly at startup instead of surfacing later as a failed auto-tuning request. The IPC transport carried a 4096-byte message ceiling, which is why bulk data used to be exchanged through files on disk: the system identification measurements were written to sys_id.csv for the autotuning process to read back, and the bode diagram data was returned as a path to a CSV file in a temporary directory. Both now travel in the request and response bodies, so nothing is exchanged through the filesystem and neither process needs access to the other's files — which also means auto-tuning no longer breaks when the two run with different filesystem views. sys_id.csv is still written, purely to keep the recording around for support. Clients are unaffected: the bode data is rendered back into the bode_file_content CSV that the system identification status and the forward-call response have always carried, byte-identical to the file the executable used to write.

5.5.11 - 2026-07-28

Fixed

  • EtherCAT slaves were never re-initialized after a power brown-out, leaving every device reported as having no firmware installed until Motion Master was restarted or the drives were physically power-cycled. When the supply sags (e.g. a motor pulling heavy torque) the slaves briefly lose power and immediately return; the cyclic recovery in SoemMaster::execute() only escalated to a full re-enumeration when the slave count reached zero, but a brown-out-and-return keeps the count above zero, so Motion Master logged Slave count did not reach zero after retries and did nothing else. The returned slaves stayed stuck in a bad AL state with their SyncManager/FMMU/PDO configuration lost (all identity SDO reads, e.g. 0x1018:02, returned wkc=0) and no DEINITIALIZING/INITIALIZING lifecycle event was emitted, so clients never cleared and reloaded. execute() now sets the new g_reinitialization_required flag when a slave-count drop recovers to a non-zero count; the machine manager's run loop watches this flag and escalates to the existing VirtualDeviceManager::init() re-enumeration (which rebuilds the full SM/FMMU/PDO configuration and emits the lifecycle events). Triggering only on an actual count drop keeps it self-limiting, so a device that is merely non-OP since startup (e.g. missing firmware, stuck in BOOT) does not cause repeated re-enumerations. The slave count is the working counter of a single broadcast read, which a lost datagram under-counts exactly as it does the cyclic frame, so a drop is confirmed by a second read before anything acts on it and a count that comes back is treated as a lost datagram rather than a lost slave. Every slave's AL state and configured station address register (0x0010) are also logged immediately before the escalation, so that the next occurrence in the field shows which of the two marks a browned-out slave and the escalation can key off that reading instead of off the count.

5.5.11-beta.1 - 2026-07-09

Fixed

  • Narrow-angle encoder calibration discarded most of the recorded data on high-rate firmware, producing an incomplete phase-error graph even though the procedure reported success. The recording runs for a fixed duration (number_of_samples_ milliseconds); the readback then re-interpreted that value as a device sample count and clamped to it, which is correct only at the legacy 1 kHz HRD rate. Jasper firmware samples HRD at 16 kHz, so it returned ~16x as many samples (e.g. 384000 bytes / 96000 samples for a 6 s sweep) and 15/16 of them were dropped as "trailing bytes," leaving only the first ~0.375 s of the mechanical revolution. The readback now processes every recorded sample when the count clearly exceeds the 1 kHz baseline, while still clamping the near-baseline case to strip the trailing fixed-block padding legacy multi-file firmware can leave.
  • System identification now works again on firmware older than v5.6.0. PR #47 moved the chirp excitation entirely onto the drive via the system identification OS command, which firmware versions below v5.6.0 do not implement (the command fails with error code 254). Motion Master now checks the firmware version and, for anything below v5.6.0 (or when the version cannot be determined), computes the chirp signal itself and streams it as a cyclic target torque while recording the response, before feeding the recorded data into standalone auto-tuning exactly like the on-drive path. Firmware v5.6.0 and above continue to use the on-drive OS command.

5.5.11-beta.0 - 2026-07-08

Added

  • Verbose EtherCAT/monitoring diagnostics gated behind MM_LOG_LEVEL=1 (there is no -v flag), so they never emit at the default level. SoemMaster logs a per-second Cyclic: summary (cycles/s, short-WKC %, last WKC, loop-period min/max) and an ESC errcnt line (error counters 0x0300-0x0313, notably the processing-unit counter 0x030C), plus an init-time FMMU/SyncManager register dump read live off each ESC. MonitorMachine logs a per-topic Monitoring emit summary (frames sent vs dropped-stale vs distinct timestamps) to separate a stale server-side image from slow client rendering. MM_LOG_LEVEL=1 is also set on the SOEM remote-PC debug launch config.

Fixed

  • MM_LOG_LEVEL had no effect on the exported system.log. loguru::add_callback captures its maximum verbosity at registration time and ignores later g_stderr_verbosity changes, but configureLogging() registered the internal-log callback (which feeds system.log) before applying MM_LOG_LEVEL, pinning it to the compile-time default (0). The level is now applied before the callback is registered.
  • Cyclic process data froze on TI PRU-ICSS EtherCAT slave controllers (Jasper drives, ESC type 0x90). Their processing unit rejects SOEM's default combined read+write LRW datagram — ~95% of cyclic frames were discarded (error counter 0x030C saturated, working counter stayed 0) while physically addressed reads still worked — so the input process image froze and monitoring updated only ~2 Hz. ESC type 0x90 is now detected before ecx_config_map_group and blockLRW is set so SOEM emits split LRD (inputs) + LWR (outputs) datagrams, which the ESC accepts; scoped to type 0x90 only, so every other device is unchanged. The cyclic loop is also hardened on a degraded bus: roundtrip() uses a 1 ms receive timeout for the cyclic exchange (instead of the global 20 ms EC_TIMEOUTRET, still used for acyclic SDO/FoE) so a dropped frame costs one cycle rather than twenty, and execute() throttles the wkc<expected recovery (checkstate + g_soem_update_slavecount) to at most every 200 ms instead of every cycle.

5.5.10 - 2026-06-18

Added

  • --no-sdo-polling command-line flag (SOEM). The monitoring parameter refresher polls non-PDO objects via SDO on a separate thread that shares the single SOEM port with the cyclic process-data exchange; on some firmware that mailbox traffic starves the exchange, freezing the input process image so that every monitored value (including the PDO-mapped timestamp) stalls and repeats even though the main loop still runs at 1-2 ms. When the flag is set, the refresher is never started: PDO-mapped objects (and the timestamp) still update every cycle, while non-PDO objects hold the value read once at startup. SoemOdEntry::is_value_fresh now reports objects as fresh when polling is disabled so a single non-PDO signal cannot gate the whole monitoring frame and stall the stream.

5.5.9 - 2026-06-18

Added

  • Verbose (-v 1) PDO mapping classification dump in SoemSlave::update_pdo_mapping_od. Per device, it lists which objects are served from the PDO process image (Rx/Tx, with offsets) versus which readable objects fall back to a per-sample SDO read when monitored. MonitorMachine additionally logs whether the timestamp object is PDO- or SDO-served, making degraded traces caused by SDO polling easy to diagnose.

Fixed

  • SoemSlave::update_pdo_mapping_od derived the active PDO mapping by scanning a hardcoded 0x1600-0x16FF / 0x1A00-0x1AFF index range. Objects mapped through a PDO at any other index — such as the timestamp 0x20F0 on newer firmware — were therefore not recognized as PDO-mapped and were monitored via slow per-sample SDO reads (which stall/repeat the timestamp). The mapping is now read from the SyncManager PDO assignment objects (0x1C12/0x1C13), capturing objects mapped at any index and keeping byte offsets aligned with the device's process image, including padding/gap entries.

5.5.8 - 2026-06-18

Added

  • Added a --skip-pdo-mapping command-line flag (SOEM). When set, Motion Master does not write the custom PDO mapping from ui.config.json during initialization and instead uses whatever mapping each device already has. Useful when a device/firmware rejects the mapping SDO writes (wkc=0), which would otherwise leave monitored objects unmapped and force slow per-parameter SDO reads.

Fixed

  • setPdoMappingFromUiConfig previously wrote the PDO mapping, ignored write failures, and reported success unconditionally. On a device that rejects the mapping writes (every 0x1600/0x1A00 write returning wkc=0), the device silently kept a different (default) mapping, so objects that should have been PDO-mapped were not and monitoring them fell back to a slow per-parameter SDO refresher (seconds per sample instead of 1 ms). The mapping is now read back and verified against the intended configuration, retried up to 3 times, and a prominent error is logged if it still cannot be applied.
  • An unknown or mistyped command-line flag was swallowed as a warning and startup continued with an unconfigured master type, surfacing as a confusing repeating "Unknown Master type!" error. Invalid arguments now log a clear error, print the usage, and exit non-zero.

5.5.7 - 2026-06-17

Fixed

  • PDO mapping configuration could randomly leave the object dictionary partially configured: each SDO write in setPdoMappingFromUiConfig was issued once and, on a transient failure (wkc <= 0, e.g. a wkc=-1 mailbox timeout), the error was only logged before moving on. Depending on which mapping objects were missed, this later surfaced as a failed CiA 402 state transition or fault reset. Every such SDO write is now retried up to 3 times with a short pause between attempts, riding out the transient and keeping the mapping consistent.
  • Runtime SDO parameter reads and writes (SoemOdEntry) were likewise issued only once, so a transient mailbox timeout failed the whole client request — a write that silently did not take, or a read returning a stale value. These now use the same retry (up to 3 attempts) as the PDO mapping path.

5.5.6 - 2026-06-13

Changed

  • EtherCAT firmware installation no longer aborts when a descriptive, non-essential file (the ESI SOMANET_CiA_402.xml.zip or the stack image) fails to write — the client receives a warning and the installation continues. Integro/NetX devices only accept the ESI under chunked filenames (SOMANET_CiA_402.xml.zip.partNNN) and reject the single verbatim .zip, which previously failed the whole installation. Writing the SII and the app/com firmware binaries remains mandatory: a failure there (or being unable to reach BOOT to write the SII) now aborts the installation as before.
  • The 2 s bootloader warm-up delay is now also applied before writing the SII and the firmware binaries, but only when the device had to be transitioned back into BOOT first. The common case, where the device is already in BOOT, adds no extra wait.

5.5.5 - 2026-06-12

Added

  • SPoE IP addresses (--ips) may now include an optional port, written as ip:port (e.g. 192.168.100.10:1234). A plain IP (e.g. 192.168.100.10) keeps using the default port 8080, so existing configurations are unaffected.
  • Added a --no-file-cache command-line flag that disables the in-memory cache of device files read over FoE, so every read fetches fresh content from the device. Useful when a host-side cache (e.g. the browser's local storage) already holds the file and you need to confirm what is actually stored on the device.

Changed

  • During EtherCAT firmware installation, progress is now reported earlier and during file removal ("Switching device to BOOT state...", then "Removing the ... file..."), so the UI no longer appears stalled while the bootloader erases large files such as the ESI (~17 s).

Fixed

  • Re-flashing firmware failed with an "Internal file operation error" while removing or rewriting device files in BOOT state. Three causes on the FoE/BOOT path are addressed: (1) FoE timeouts for file removal were too short for the slow bootloader — removal now uses a 10 s timeout, and removal of the large ESI file (SOMANET_CiA_402.xml.zip) a 30 s timeout; (2) the delay after entering BOOT before the first file operation is increased from 150 ms to 2 s, so the first fs-getlist no longer hits a not-yet-ready bootloader; (3) the master's mailbox sequence counter is reset when the mailbox sync managers are reprogrammed for BOOT (and on the return to PRE-OP), so a re-entry into BOOT no longer desyncs and wedges the mailbox.

5.5.4 - 2026-06-10

Fixed

  • Some SOMANET devices silently stayed in INIT during EtherCAT bus initialization. Mailbox sync managers are no longer reprogrammed on a fresh-scan INITPRE-OP transition (only when a device returns from BOOT, e.g. after a firmware download), and EEPROM control is handed back to the device's PDI after reprogramming so the device can complete the state change.

5.5.3 - 2026-05-27

Changed

  • Refactored high-rate-data (HRD) file handling to support the Jasper firmware, which stores a recording in a single hr_data0.bin instead of splitting it across several files. HRD files are now discovered from the device file list and processed regardless of their count or layout, and the encoder calibration derives the sample count from the actual recorded data. (#44)

5.5.2 - 2026-05-27

Changed

  • Transfer device files over FoE in a single transfer without chunking or ZIP packaging, and read files more reliably in BOOT state. (#50)

5.5.1 - 2026-05-20

Fixed

  • Fixed shutdown hangs on SIGTERM: SPoE no longer hangs when started with an invalid or unreachable IP address, and SOEM no longer blocks for up to 50 seconds when a signal arrives during EtherCAT bus initialization. (#48)

5.5.0 - 2026-05-14

Changed

  • Improved the reliability and performance of initializing devices. (#40)

Fixed

  • Use standard libMU_3SL library directory layout and improve library handling during compilation and Docker image builds.

5.4.1 - 2026-04-15

Fixed

  • Circulo encoder calibration in validation-only mode now shows a distinct message and prompts the user to rerun full calibration. (#42)

5.4.0 - 2026-04-10

Changed

  • Updated the iC-Haus calibration library for Circulo integrated encoders from v2 to v3. (#34)

5.3.0 - 2026-03-24

Added

  • Ability to read device SII via SOEM. (#37)

Fixed

  • Memory access violation caused by a missing virtual parameter in SPoE monitoring mode. (#35)

5.2.2 - 2026-03-19

Fixed

  • Use copies instead of symlinks for libMU_3SL_interface.so files.

5.2.1 - 2026-03-19

Fixed

  • Missing libMU_3SL_interface.so.3 symlink.

5.2.0 - 2026-03-19

Fixed

  • Finding libraries and building on GCC 15.2.1.

Removed

  • dl_syscalls.

5.2.0-alpha.1 - 2026-03-10

Fixed

  • Fixed SPoE firmware installation to skip non-whitelisted files in the firmware package, such as GSDML and EDS files. (#32)

5.2.0-alpha.0 - 2026-02-20

Added

  • Added support for writing chirp signal parameters (start frequency, end frequency, duration, amplitude, signal type) to the plant model file. (#23)
  • Added a whitelist of non-firmware files that are allowed to be written to the device during firmware installation. (#29)

Fixed

  • Fixed conversion of start and end frequencies from Hz to mHz in System Identification. (#25)
  • Skip state changes in monitor PDO mode during SPoE device initialization. (#28)

5.1.0 - 2025-12-19

Fixed

  • Fixed a Linux-specific issue where the Proto Server failed to handle interrupt signals correctly. (4337867116)

5.1.0-beta.0 - 2025-12-12

Added

  • Added support for executing a Chirp signal for System Identification via OS command and processing the resulting HRD data. (1540930696)
  • Added Bode plot file contents to the System Identification status message to enable plotting in the UI. (4326946044)
  • Added retry mechanism for reading object dictionary entries and descriptions, attempting multiple times before failing the setup. (4337857609)
  • Added recognition of Sensodrive devices as Synapticon devices. (4337858326)

Changed

  • Migrated server implementation from IXWebSocket to uWebSockets. (1727893901)
  • Replaced moodycamel::ReaderWriterQueue with a custom ThreadSafeQueue. (4337858989)
  • Added a wait to ensure the number of slaves stabilizes before starting initialization. (4337866768)
  • Changed server to send outgoing messages from queue without delay. (4326946908)
  • Updated standalone-autotuning to version 2.3.3.

Fixed

  • SPoE state changes now correctly determine success by comparing the returned state with the requested state, instead of relying on the previous success code. (4337868474)
  • Fixed the conversion logic to correctly translate either Q15-encoded or actual recorded velocity values to SI units (rad/s). (4337869106)

Removed

  • Removed 0x2041:01 (Motor current / Vector amplitude) from the default PDO mapping, as it is not present in firmware version v5.1.x. (4337869312)

5.0.7 - 2025-09-23

Changed

  • Improved file read/write robustness by retrying when an undefined FoE error code occurs.

5.0.6 - 2025-09-19

Changed

  • Server now starts before initiating the standalone auto-tuning process.

Removed

  • Logging of the read Bode plot file content.

5.0.5 - 2025-09-19

Added

  • Support for newer Circulo Integrated Encoder revisions by forcing them to use the older revision. (1301230668)
  • Linting scripts and updated GitHub workflows.

Changed

  • Added support for skipping specified files during firmware installation (e.g., SOMANET_CiA_402.xml.zip). (1723521044)
  • Source code updated to comply with linting rules (cpplint).
  • Removed unused IgH EtherCAT Master–related source code.

5.0.4 - 2025-09-17

Added

  • Process GetSystemInformation request message. (1748104048)

5.0.3 - 2025-09-04

Changed

  • Rename the command-line driver option from 'ethernet' to 'spoe'.

5.0.2 - 2025-09-04

Added

  • Added a command-line option to configure the PDO mode of SPoE devices. (1741743151)

5.0.1 - 2025-08-14

Removed

  • Dropped support for ZeroMQ and removed it as a dependency. (1727893901)

5.0.0 - 2025-07-31

Added

  • Added support for the SOMANET Protocol over Ethernet (SPoE).

5.0.0-beta.38 - 2025-06-10

Changed

  • Switched to a newer version of the standalone auto-tuning program featuring enhanced system identification, improved exception handling, and Bode plot generation.

5.0.0-beta.37 - 2025-05-30

Changed

  • When the device enters the SAFEOP state, recovery attempt messages are now logged only in Motion Master and no longer sent to the UI.

Fixed

  • Fixed an issue where gains were zero during iterative sharpening auto-tuning at the start of the Cogging Torque Compensation recording procedure, which caused sudden motion jumps. (1621619964)

5.0.0-beta.36 - 2025-05-23

Added

  • Initial implementation of the UI PDO Mapping feature. (1654097885)

5.0.0-beta.35 - 2025-04-29

Added

  • Initial implementation of the somepp static library.

Changed

  • Log now displays SDO index and subindex values in hexadecimal instead of decimal.

5.0.0-beta.34 - 2025-04-23

Fixed

  • Fixed checkstate to re-read the master state after acknowledging a SAFE-OP error, preventing it from getting stuck in SAFEOP-ERROR.

5.0.0-beta.33 - 2025-04-22

Added

  • Added external_circulo_type attribute to the Encode Configuration procedure.

5.0.0-beta.30 - 2025-04-03

Changed

  • Updated the standalone auto-tuning functions to use IPC, significantly improving performance compared to running the program via CLI. (1571985290)

5.0.0-beta.29 - 2025-03-14

Fixed

  • Resolved the issue preventing Integro devices from recovering from the SAFE-OP+ERROR state. (1599593149)

5.0.0-beta.28 - 2025-03-02

Added

  • Added the inclusion of the chrono library where necessary.

5.0.0-beta.27 - 2025-03-02

Added

  • Added logging to capture parameters that exceed the allowed range for the specified entry type.

Changed

  • Updated the list of SI unit velocity parameters and added new options for RPM and RPS, including kilo, mega, giga, and tera.

5.0.0-beta.26 - 2025-02-18

Added

  • Added the "measurement only" flag to the Circulo encoder's narrow-angle calibration procedure. (1299302456)

5.0.0-beta.25 - 2025-02-11

Changed

  • Manual mapping and configuration of FMMUs and Sync Managers in the PREOP EtherCAT state.
  • Device recovery limited to transitioning from SAFE-OP to OP EtherCAT state.
  • Sleeping for 80 milliseconds only when FoE returns as busy.

5.0.0-beta.24 - 2025-02-03

Changed

  • On a failed roundtrip, read the number of slaves only once, with the timeout reduced from 500 ms to 100 ms.

5.0.0-beta.23 - 2025-02-03

Changed

  • Skipping the initialization of third-party devices to reduce loading times.

5.0.0-beta.22 - 2025-01-31

Changed

  • Modified the transition points for updating the sync managers.
  • Revised CTC recording log and enhanced fault state logging.
  • Prevented redundant updates to the local object dictionary PDO mapping.

5.0.0-beta.21 - 2025-01-25

Changed

  • Upgraded standalone-autotuning to version 2.0.0, now including the duration time value for each operation.

5.0.0-beta.20 - 2025-01-24

Fixed

  • Fixed write access error when running next-gen System Identification on Linux. (1582222631)

5.0.0-beta.19 - 2025-01-22

Changed

  • Updated success criteria for Circulo encoder calibration. (1553641201)

Fixed

  • Fixed an issue with SDO writes for BOOL values, which were always being set to 0. (1566713098)

5.0.0-beta.18 - 2025-01-09

Added

  • Logging for changes in slave count.

Fixed

  • Resolved an issue with slave count timeout that caused Motion Master to reinitialize all devices.

5.0.0-beta.14 - 2024-12-22

Changed

  • Improved robustness of FoE operations, adding retries for program errors and flash disk full error codes. (1472154716)
  • Updated the Quick Stop functions to reset the Torque Target and Offset values to 0 in CSP and PT modes. (1452182434)

Added

  • Include the maximum acceleration for battery mode in the Circulo Encoder Configuration request. (1366713298)
  • Enabled automatic configuration for detected SMM devices in SOEM. (1472147544)
  • Enabled PDO remapping functionality with SOEM. (1484358597)
  • Added support for specifying WebSocket ports as command-line arguments. (1150020418)

Fixed

  • Prevented slicing of fs-buffer file content when the size exceeds the IgH maximum file size limit. (1242309971)
  • Resolved segmentation fault triggered by the SIGTERM signal in Linux when using the IgH EtherCAT Master. (1377203870)
  • Fixed 'GLIBC_2.34' not found error, and occasional failure to start with an 'Alarm clock' error on Linux. (1504138772)