S7-300 CP341 Modbus Master: Configuring FC03 and FC06 Writes
The Siemens CP341 point-to-point communication module turns an S7-300 CPU into a Modbus RTU master or slave using the loadable Modbus Master or Modbus Slave driver (order numbers 6ES7341-1xH02-0AE0 drivers). Combining Function Code 03 (Read Holding Registers) and Function Code 06 (Write Single Register) on the same CP341 is fully supported, but several field traps cause the two function codes to interfere when both are active in the same scan. This reference walks through the diagnostic logic, the meaning of the +4.0 address field for each function code, and the DONE/ERROR sequencing pattern that prevents job collision.
1. Overview of the CP341 Modbus Master Driver
The Modbus Master driver replaces the standard ASCII/3964R driver on the CP341 and provides parameter assignment, message framing, and CRC handling automatically. The driver exposes two system function blocks:
| FB | Name | Purpose |
|---|---|---|
| FB7 (older) | SEND/RECEIVE for PtP | Older P_SND_RK / P_RCV_RK pair used with the Modbus Master driver on CP341 |
| FB8 (older) | RECEIVE for PtP | Receive counterpart of FB7 |
| P_SND_RK | Send character-oriented | Triggers one master request and waits for slave response |
| P_RCV_RK | Receive character-oriented | Reads the response frame into the configured destination DB |
The driver uses two cooperating data blocks:
- Configuration DB (e.g., DB42) — holds the Modbus job parameters per slot (slave address, function code, starting register, register count, destination DB number, destination DBW offset).
- Send/Receive data DB (e.g., DB43) — the user data buffer where the response is stored.
The Siemens sample project "PTP_Modbus_Master" (entry ID 109482710) seeds a working single-job application. Multi-function-code applications must extend this template.
2. Prerequisites
- Hardware: S7-300 CPU (any 31x series), CP341 module (6ES7341-1AH02-0AE0 for RS-232, or 1BH02-0AE0 for RS-485/422) with hardware revision ≥ 4 for the Modbus master driver, RS-485 bus terminator (120 Ω at both ends), and a Modbus slave (any vendor, e.g., a Modbus simulator running on a PC).
- Driver license: The Modbus Master driver is a loadable firmware that ships as a separate license. Install via SIMATIC Manager → Options → Install CP PtP Driver using the driver disk referenced in the CP341 manual (entry 1117393).
- Software: STEP 7 V5.5 SPx (entry ID 109751376) or compatible, S7-PLCSIM optional.
- Slave tool: MODSIM (Modbus Tools) or any Modbus slave simulator supporting FC03 and FC06.
3. Hardware Configuration and Driver Loading
Open the hardware configuration (HW Config) and double-click the CP341 slot. In the Parameter dialog select the protocol Modbus Master (RS-485) and configure the baud rate, parity, and stop bits to match the slave. Load the configuration to the CPU. The CP341 will now accept the Modbus Master driver blocks.
The Modbus Master driver requires the following FB/DB pattern in OB1 (or OB35 for cyclic polling):
- CALL P_SND_RK, DB50 — with
SEND_DB= DB42 (configuration),SEND_DBB_NO= 0 (job slot start),SNDLEN= 32 (job size),RTS_ON= TRUE. - CALL P_RCV_RK, DB51 — with
RCV_DB= DB43 (data),RCV_DBB_NO= 0,RTS_OFF= TRUE. - Evaluate
STATUS,ERROR, andDONEoutputs of P_SND_RK before triggering the next request.
Reference the official CP341 manual chapter "Modbus Master Driver — Function Blocks" for the exact I/O layout of P_SND_RK and P_RCV_RK.
4. Function Code 03 (Read Holding Registers) Job Layout
For FC03 the configuration DB slot uses the following fields (assuming DB42 as the Modbus Master configuration DB):
| DB42 Offset | Field | Value for FC03 | Meaning |
|---|---|---|---|
| DBW0 | Function code | 3 | Read Holding Registers |
| DBW2 | Slave address | 1..247 | Modbus RTU slave address |
| DBW4 | Register count (LEN) | N (1..125) | Number of 16-bit registers to read — this is the +4.0 Address field in some STEP 7 variants |
| DBW6 | Start register | 0..65535 | Zero-based offset (Modbus address 40001 + offset) |
| DBW8 | Dest DB number | e.g., 43 | Where the response payload is written |
| DBW10 | Dest DBW offset | 0, 2, 4... | Byte offset inside the destination DB |
With FC03 the field at offset +4.0 of the job slot is the quantity of registers to read. The driver reads N consecutive registers starting at the configured start register and writes N words into DB43 beginning at the destination offset.
5. Function Code 06 (Write Single Register) Job Layout
For FC06 the configuration DB slot fields have a different meaning at the same offset:
| DB42 Offset | Field | Value for FC06 | Meaning |
|---|---|---|---|
| DBW0 | Function code | 6 | Write Single Register |
| DBW2 | Slave address | 1..247 | Modbus RTU slave address |
| DBW4 | Register value (DATA) | 0..65535 | The 16-bit value to write into the slave register — this is the +4.0 Address field in some STEP 7 variants |
| DBW6 | Target register | 0..65535 | Zero-based offset of the holding register to write |
| DBW8 | Source DB number | 43 (or 0) | Optional DB holding the value; when zero the value comes from DBW4 directly |
| DBW10 | Source DBW offset | 0..n | Optional byte offset for sourcing the value |
FC06 writes exactly one register; the field that FC03 interpreted as "register count" is the actual data payload for FC06. This is the root cause of the cross-function-code conflict that this article addresses.
6. The Critical Difference: +4.0 Address Field Semantics
Both function codes use the same configuration DB slot, but DBW4 (often labeled "Address +4.0" in STEP 7 LAD/FBD) is reused:
- FC03: DBW4 = LEN (register count).
- FC06: DBW4 = register value to write.
If a single configuration DB slot is rewritten alternately with FC03 and FC06, the value written into DBW4 must be reloaded between each trigger to reflect the new function code's interpretation. Failing to do so yields the symptom described in the field report: a constant value of 8 appearing at DB43.DBW0, because DBW4 retains the prior value (8) when the FC03 job has just finished and the FC06 job overwrites it with its current length-thinks-value semantics.
SEND trigger. The CP341 driver does not remember prior values across function codes.7. Sequential Trigger Logic with DONE/ERROR
The CP341 master is single-threaded — it can only execute one outstanding request at a time. The correct polling pattern between FC03 and FC06 is a strict state machine: Idle → FC03 Trigger → FC03 Wait (DONE|ERROR) → Idle → FC06 Trigger → FC06 Wait (DONE|ERROR) → Idle.
Recommended implementation in LAD/FBD on the trigger input of P_SND_RK:
- Use two boolean tags (e.g.,
M10.0= "FC03 pending",M10.1= "FC06 pending") instead of a free-running clock bit. - In the FC03 branch, unconditionally write DB42.DBW0 = 3, DB42.DBW2 = slave, DB42.DBW4 = LEN, DB42.DBW6 = start register, DB42.DBW8 = 43, DB42.DBW10 = 0. Then set
REQ=M10.0 AND NOT FC03_BUSY. - On rising edge of
DONEorERRORfrom P_SND_RK, clearM10.0. - In the FC06 branch, unconditionally write DB42.DBW0 = 6, DB42.DBW2 = slave, DB42.DBW4 = value, DB42.DBW6 = target register, then set
REQ=M10.1 AND NOT FC06_BUSY AND NOT FC03_BUSY. - On rising edge of
DONEorERROR, clearM10.1.
The clock-bit pattern shown in the field report is the source of the duplication bug: a periodic clock fires regardless of DONE, so the next job can start before the prior one completes. This corrupts the configuration DB before P_SND_RK has finished reading it.
8. Source DB Isolation Strategy
Two viable strategies prevent FC03 and FC06 from corrupting each other's job slots.
8.1 Single Send DB with Overwrite Discipline
Use one configuration DB (DB42) with one job slot, but rewrite the entire slot before each trigger. This is the leanest approach and works for a small number of function codes. It is sensitive to scan-time races because the same slot is shared. Recommended only when both function codes are guaranteed to interleave at DONE boundaries (see Section 7).
8.2 Separate Send DB per Function Code
Use two configuration DBs:
- DB42 — FC03 jobs: dedicated to read-holding-register jobs.
- DB44 — FC06 jobs: dedicated to write-single-register jobs.
Each function block call references its own DB. The application code can interleave them safely because the destination DBs are physically separate. This pattern scales cleanly when adding FC05 (Force Single Coil) and FC16 (Write Multiple Registers).
9. Function Code 16 (Write Multiple Registers) as a Substitute for FC06
If the application needs to update multiple holding registers per write transaction, FC06 cannot be used — FC06 always writes exactly one register. Per the Modbus protocol reference, FC16 (Write Multiple Registers) accepts up to 123 contiguous registers in a single request and is functionally a superset of FC06 for writes greater than one register. Verify the slave device supports FC16 by reading its Modbus reference manual; many VFDs and energy meters expose only FC06, in which case the application must loop the FC06 request or upgrade the slave firmware.
FC16 can also be used to write a single register (set Quantity of Registers = 1 and provide a two-byte data payload), but the slave's exception behavior must be checked — some slaves reject FC16 with quantity=1 if FC06 is supported.
10. Common Fault Matrix
| Symptom | Likely Cause | Resolution |
|---|---|---|
| FC03 reads correctly; FC06 always writes 0 (or a constant) | Configuration DB slot not refreshed between function codes — DBW4 retains FC03's LEN | Rewrite all six fields before each trigger; use separate DBs |
| DB43.DBW0 = 8 persistently, even after writing with FC06 | Clock bit retriggers FC03 faster than the slave can answer; FC03 reads 8 from the simulated slave before FC06 writes | Sequence jobs on DONE/ERROR edges only; remove free-running clock |
| P_SND_RK ERROR=TRUE, status word = 0x8001 | Slave address invalid or no response within timeout | Check RS-485 termination, baud rate parity, slave ID, and CP341 cable pinout (TIA-485-A and TIA-485-B not swapped) |
| P_SND_RK ERROR=TRUE, status word = 0x8304 | Modbus CRC error from slave reply | Inspect cabling for noise; lower baud rate; verify slave's parity matches CP341 setting |
| Slave replies with exception code 02 (ILLEGAL_DATA_ADDRESS) | Start register out of slave's valid range | Confirm slave's Modbus map; reduce address; many slaves use 1-based addresses |
| Slave replies with exception code 03 (ILLEGAL_DATA_VALUE) | FC06 attempted to write a register declared read-only by the slave | Check the slave's register map; some VFD control words are read-only |
| FC06 succeeds once, then all subsequent requests fail until CPU restart | P_SND_RK REQ left set high; driver is in a stuck state |
Pulse REQ for one scan only; reset REQ on DONE or ERROR |
| Both FC03 and FC06 produce data, but values are swapped between tags | Destination DB offsets overlap (e.g., FC03 at DB43.DBW0 and FC06 echo at DB43.DBW0) | Allocate distinct destination offsets; reserve DB43.DBW0..30 for FC03 and DB43.DBW40..42 for FC06 echo |
11. Verification Steps
After implementing the corrected polling sequence, validate with these checks:
- Online watch DB42: Confirm that DBW0 alternates between 3 and 6 and that DBW4 is overwritten each time with the appropriate value (LEN for FC03, DATA for FC06).
- Online watch DB43: Confirm that after FC03 completes, DB43.DBW0 holds the value the slave exposes at the start register. After FC06 completes, the slave's tool (MODSIM or slave HMI) shows the same value at the target register.
-
Force a known FC06 write from STEP 7 watch table: Set
M10.1= TRUE; observe that the slave register changes and that P_SND_RKDONErises within the configured timeout (default 2000 ms). - Force a slave-side register change: Use MODSIM to update a holding register; confirm that the next FC03 read returns the new value at DB43.DBW0.
-
Cross-check exception handling: Program a target register one above the slave's valid range; observe exception code 02 returned and
ERROR=TRUEwithSTATUSshowing the exception. -
Disconnect the slave: Verify that
ERROR=TRUEis asserted within the timeout window and that the application does not hang.
12. Best Practices for Field Deployments
- Always drive the master trigger from DONE/ERROR feedback, never from an unconditional clock or pulse generator. The clock pattern introduces race conditions that corrupt the configuration DB.
- Use a single watchdog timer in the application to detect a stuck master state (no DONE/ERROR transition within N seconds) and force a P_SND_RK restart.
- Maintain the Modbus slave address map in a documentation table and cross-check it against the S7 program comments to avoid off-by-one errors between STEP 7 (0-based) and slave (1-based) addressing.
- For more than two function codes, build a "job queue" of FC/LEN/ADDR/VALUE entries in a DB and dispatch one per cycle. This scales to FC05, FC06, FC16, and FC23 without rewriting the master state machine.
- Configure CP341 to 19200 8E1 for industrial VFDs and 9600 8N1 for most energy meters — confirm from the slave manual before commissioning.
Why does my FC06 write appear as a constant value like 8 even after triggering?
The configuration DB slot was not refreshed between FC03 and FC06 triggers. DBW4 holds the FC03 register count (often 8), and FC06 reads the same slot as the data payload. Rewrite all six job fields (DBW0, DBW2, DBW4, DBW6, DBW8, DBW10) before each trigger, or use separate configuration DBs per function code.
Can I send FC03 and FC06 requests simultaneously from one CP341?
No. The CP341 Modbus Master driver is single-threaded. A new request may only be issued after the prior request completes (DONE=TRUE) or errors (ERROR=TRUE). Sequence the triggers on DONE/ERROR rising edges, not on a clock bit.
What is the meaning of the +4.0 address field for FC03 versus FC06?
For FC03 it is the register count (LEN), and for FC06 it is the 16-bit value to write. Both function codes reuse the same DBW4 offset but interpret it differently. Always confirm the meaning by reading the CP341 manual entry for your driver version.
Can I use FC16 (Write Multiple Registers) in place of FC06?
Yes, when the slave supports FC16. Set Quantity of Registers = 1 and provide a two-byte payload. Some slaves reject FC16 with quantity=1 and require FC06 — verify the slave's Modbus reference. For multi-register writes FC16 is the correct function code.
What status word values does P_SND_RK return on Modbus errors?
Common values include 0x8001 (timeout, no slave response), 0x8304 (CRC error), 0x8318 (slave exception, exception code in low byte of STATUS), and 0x8085 (parameter assignment error). Cross-check with the CP341 manual chapter on the Modbus Master driver's STATUS return codes for your specific driver version.
Why does FC03 read correctly but FC06 always returns ERROR with a 02 exception code?
The slave's holding register map does not include the target register, or the address is off by one (Modbus 40001 = 0 in the driver). Adjust the start register by the offset shown in the slave's documentation and retry.