Skip to content

Gotchas

No response from PLC

Symptom Root cause Fix
The request is sent, but no response arrives. Baud rate, parity, and stop bits do not match the PLC serial module. All three must be correct at the same time. Confirm the PLC serial module DIP switch or parameter settings, then set PosixSerialConfig or your MCU UART to the same values.

PlcProfile::Unspecified

Symptom Root cause Fix
Encoding or open() fails with a PLC profile error. ProtocolConfig::plc_profile was left as PlcProfile::Unspecified. Select an explicit profile such as PlcProfile::MelsecQ before opening or encoding.

Frame and PLC profile must be explicit

Symptom Root cause Fix
A Linux CLI wrapper exits before sending a request. MCPROTOCOL_FRAME or MCPROTOCOL_PLC_PROFILE was not set. The wrapper does not infer either value. Set both values explicitly, for example MCPROTOCOL_FRAME=c4-binary and MCPROTOCOL_PLC_PROFILE=melsec:qcpu.
A program appears to use the wrong command family. The serial frame/profile was copied from another target or left at a sample value. Choose the frame and PlcProfile for the actual PLC serial module; CPU model text, serial settings, and device strings are not used for inference.

Zeros from MCU sample

Symptom Root cause Fix
The MCU sample runs, but the values are always zero or do not match the PLC. The sample UART pins are board defaults, not universal wiring rules. Change the TX/RX pins to match your board wiring and check the shared MC Protocol Serial setup guide.

Frame type mismatch

Symptom Root cause Fix
The PLC returns framing errors or ignores the request. make_c4_binary_protocol() selects 4C Binary. A PLC module configured for 4C ASCII, 3C, or another frame will not treat that as the same protocol. Configure the PLC serial module for the same frame type and code mode, or choose a matching ProtocolConfig helper.
Format4 works but Format5 does not, or the reverse. The serial module is configured for one MC protocol format at a time. This is a frame-mode mismatch, not remote-password behavior. Match the PLC serial-module MC protocol format to the client helper, such as make_c4_ascii_format4_protocol() for Format4 or make_c4_binary_protocol() for Format5.

RS-485 multi-drop: wrong station

Symptom Root cause Fix
Only one station responds, or no RS-485 station responds. The selected frame-specific multidrop station/network/PC/destination-module target or connection topology does not match the serial-network configuration. Construct the appropriate frame-specific route. For normal/1:n use a *StandardMultidropRoute; for m:n use a *MnMultidropRoute and the assigned mandatory SelfStationNo (0..31). Do not reuse a C24-side station number or infer station-count rules. For 3C/4C supply a C34PcTarget; 4C also requires C4DestinationModule; 1E requires E1PcTarget. For an RS-232C point-to-point connected station, select RouteConfig {HostStationRoute {}}.

1E timer changes when communication timeout changes

Symptom Root cause Fix
A 1E request needs a different PLC processing timer than the host response deadline. These are independent limits: response timeout controls host waiting, while the 1E monitoring timer is encoded into the PLC request. Leave the common response timeout at 3000 ms or set it explicitly, and configure E1MonitoringTimer::milliseconds(...) separately in exact 250 ms units. No rounding or saturation is performed.

Response starts but times out before completion

Symptom Root cause Fix
Response bytes keep arriving slowly, but the request still times out. The absolute deadline covers first TX through decode, while the 250 ms default inter-byte deadline limits inactivity after a possible response is retained. Chunk progress restarts only the inter-byte deadline. Increase inter_byte_timeout_ms if valid chunk gaps exceed 250 ms, and increase response_timeout_ms only if the whole transaction needs longer. After any timeout, close/reopen and reconfigure.

Timeout occurs while UART or DMA transmit is still active

Symptom Root cause Fix
requires_transport_reset() becomes true, but the request remains busy and neither on_tx_end nor the completion callback runs. The absolute deadline expired while the external transport still owned physical TX. The client cannot safely infer that the UART/DMA operation stopped. Finish or abort the physical TX and call notify_tx_complete() exactly once. The client then releases the hook/callback and publishes the already-latched timeout result. If no physical notification is supplied, it intentionally remains busy.

A state-changing result is unknown

Symptom Root cause Fix
A write, monitor registration, remote-control, password, user-frame, signal, mode-switch, or initialization command returns StatusCode::OperationOutcomeUnknown. Transmission may have begun, but the PLC result could not be confirmed. The requested state change may already have occurred. Do not resend automatically. Inspect the affected PLC state and reset/reopen the transport when required before deciding the next operation.
Remote RUN returns StatusCode::OperationOutcomeUnknown. Transmission started, but transport failure, timeout, cancellation, or an invalid response prevented confirmation. The PLC may already have applied the requested RUN and clear policies. Do not resend automatically. Inspect the PLC state, reset/reopen the transport when required, and decide the next action explicitly.
remote_run() does not compile, or CLI remote-run exits with usage. Conflict and clear policies are mandatory. Pass RemoteOperationMode::{DoNotExecuteForcibly, ExecuteForcibly} and one RemoteRunClearMode, or CLI no-force|force plus no-clear|outside-latch|all-clear.
Remote PAUSE returns StatusCode::OperationOutcomeUnknown. PAUSE transmission started, but its result was not confirmed. The library does not retry or escalate to forced execution. Inspect the PLC state before deciding the next operation. Reopen/reset the transport when required.
remote_pause() does not compile, or CLI remote-pause exits with usage. The conflict policy is mandatory and only the exact CLI names no-force and force are accepted. Pass one RemoteOperationMode or one exact CLI policy. Do not use numeric or compatibility aliases.

Bit-in-word updates are two PLC requests

Symptom Root cause Fix
Another bit in the word unexpectedly reverts. A bit-in-word helper reads the complete word and then writes the modified word. The local client reserves the sequence, but PLC logic or another connection can write between those requests. Use a PLC-side ownership or handshake contract when the complete word is shared.
A bit-in-word helper returns OperationOutcomeUnknown. Cancellation or failure occurred after the word write may have started. Reset/reopen the transport, read the authoritative PLC state, and decide explicitly whether another update is safe. Do not resend automatically.
A bit device, standalone G/HG, long-state route, random item, or byte-buffer route is rejected. Bit-in-word helpers intentionally cover only routes that already support a complete 16-bit word read and write. Use the route's native bit API or an explicit application-owned operation; the library does not change routes or fall back automatically.

host_sync.hpp symbol not found

Symptom Root cause Fix
PosixSyncClient or PosixSerialConfig is not found in a PlatformIO project. The PlatformIO package intentionally compiles only the transport-agnostic core. Defining MCPROTOCOL_SERIAL_ENABLE_HOST_API=1 would expose declarations whose implementations are not in that package. Vendor the source repository in a CMake host project, link the host-enabled mcprotocol_serial target, and include #include <mcprotocol/serial/host_sync.hpp> explicitly.

Constrained standard library

Symptom Root cause Fix
A constrained MCU toolchain has no <array>, <algorithm>, or related C++ standard header. The library auto-detects missing headers and uses fallbacks from mcprotocol/serial/compat/. The compatibility files no longer use standard header names in the public include root. If the toolchain's header probe is inaccurate, add -DMCPROTOCOL_SERIAL_USE_BUNDLED_STDLIB_COMPAT=1 to that environment. Do not copy compatibility files into the include root under names such as array or algorithm.
An Arduino Mega 2560 or another AVR/8-bit build is requested. AVR/8-bit targets are outside the supported memory and toolchain contract. Migrate to a supported ESP32 target or maintain an explicitly unsupported downstream port.

PlatformIO UART pins wrong

Symptom Root cause Fix
The PlatformIO UART sample builds but does not communicate. The sample pin defaults are starting values: RP2040 TX 0 / RX 1 and ESP32-C3 TX 7 / RX 6. Match those definitions to your actual board, level shifter, and cable wiring.

Target-dependent native commands

Symptom Root cause Fix
A qualified Un\G / Un\HG command, link-direct command, or native helper fails on one target but not another. Support depends on the selected PLC profile, serial module, mounted route, and command route. The 0601/1601 helper route is not a fallback for profiles that require native-qualified access. Use the route required by the selected profile and check the shared MC Protocol Serial supported registers page before changing code.

Typed suffix does not parse

Symptom Root cause Fix
D100:D, D100:F, or D100.0 fails in the high-level string parser. The parser accepts plain device strings; width is selected by the API type rather than a suffix. Use D100, M100, or X10 with the explicit Word or DWord API. For CLI sparse reads, use word:D100 or dword:D100.
A random-write item does not compile without a value, or CLI rejects D100/M100. Random-write values are mandatory so an omitted value cannot silently become zero or OFF. Construct the item/spec with both device and value, or use CLI DEVICE=VALUE. Explicit =0 is valid.
A random write returns OperationOutcomeUnknown. Transmission started, but the PLC response was not confirmed. The value may already have changed. Inspect the PLC/device state and reconfigure/reset the transport as required. Do not retry automatically.