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.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.
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.
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 INIT→PRE-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
Fixed
- Fixed SPoE firmware installation to skip non-whitelisted files in the firmware package, such as GSDML and EDS files. (#32)
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)
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).
Changed
- Switched to a newer version of the standalone auto-tuning program featuring enhanced system identification, improved exception handling, and Bode plot generation.
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)
Added
- Initial implementation of the UI PDO Mapping feature. (1654097885)
Added
- Initial implementation of the somepp static library.
Changed
- Log now displays SDO index and subindex values in hexadecimal instead of decimal.
Fixed
- Fixed checkstate to re-read the master state after acknowledging a SAFE-OP error, preventing it from getting stuck in SAFEOP-ERROR.
Added
- Added external_circulo_type attribute to the Encode Configuration procedure.
Changed
- Updated the standalone auto-tuning functions to use IPC, significantly improving performance compared to running the program via CLI. (1571985290)
Fixed
- Resolved the issue preventing Integro devices from recovering from the SAFE-OP+ERROR state. (1599593149)
Added
- Added the inclusion of the chrono library where necessary.
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.
Added
- Added the "measurement only" flag to the Circulo encoder's narrow-angle calibration procedure. (1299302456)
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.
Changed
- On a failed roundtrip, read the number of slaves only once, with the timeout reduced from 500 ms to 100 ms.
Changed
- Skipping the initialization of third-party devices to reduce loading times.
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.
Changed
- Upgraded standalone-autotuning to version 2.0.0, now including the duration time value for each operation.
Fixed
- Fixed write access error when running next-gen System Identification on Linux. (1582222631)
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)
Added
- Logging for changes in slave count.
Fixed
- Resolved an issue with slave count timeout that caused Motion Master to reinitialize all devices.
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)