Configuring Modbus RTU Writes for Wemos Relay Outputs

Daniel Price13 min read
ModbusOther ManufacturerTechnical Reference
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

The Raspberry Pi 3 runs Node-RED and sends Modbus RTU requests through a USB-to-RS485 adapter, an RS485 link, and a TTL-to-RS485 converter to a Wemos D1 mini configured as a slave. Function Code 5 can change one addressed coil; it cannot select a bit inside a 16-bit register. To change one field in a register, the Wemos application must implement a register-level mapping and accept a write of the whole register.

Where does the Node-RED request stop?

Trace the request through each device before changing the payload. Node-RED is the Modbus client (often called the master); the Wemos firmware is the server (often called the slave). The two RS485 converters carry the serial link, but they do not implement the Modbus data map or decide what a relay output does. The Wemos code must receive the request, expose the requested coil or register address, and map that point to relay logic.

  1. Node-RED constructs a write request with a function code, unit ID, starting address, quantity, and value or values.
  2. The Pi's selected Modbus client sends it through its configured transport to the serial interface or gateway.
  3. The USB-to-RS485 converter drives the bus; the far-end TTL-to-RS485 converter passes the data to the Wemos serial interface.
  4. The Wemos Modbus server checks the unit ID, function code, and address, then updates its exposed coil or register map.
  5. The Wemos application maps the changed point to a relay output. A successful protocol response alone does not prove that the application changed the physical relay.

The source describes the Wemos as configured as a slave, but the output mapping depends on its sketch or firmware. A heat pump's readable status word does not establish that the Wemos implements the same kind of register map: the heat pump firmware interprets its own defined registers and bit fields.

Check: Identify which Wemos code implements the Modbus server and record its unit ID, supported write functions, and output map before sending a write.

Which physical and serial path does the Pi actually use?

Confirm the RS485 path and the Node-RED endpoint as separate commissioning tasks. The installation description names a USB-to-RS485 converter at the Pi and a TTL-to-RS485 converter at the Wemos. A Modbus client still needs to open the correct local serial device or connect to the actual TCP gateway; selecting RTU in the payload does not select or repair that transport.

The example Node-RED client configuration includes a TCP host of 192.168.0.66 and TCP port 502, as well as a serial port of /dev/ttyS1 and RTU serial settings. Those entries describe different parts of a possible routed or bridged arrangement. Do not copy both paths as if a TCP socket and a local USB serial port were interchangeable. For a direct Pi USB adapter, identify the adapter's actual serial device name in the Node-RED host and configure the client for that serial connection. For a TCP-to-RTU gateway, configure its reachable host and port and verify which serial bus it forwards to.

At the physical layer, check that both converters are RS485 devices suitable for their respective interfaces, that the bus conductors reach the intended terminals at each end, and that the Wemos serial pins connect to the TTL side of its converter. Check the converter and Wemos documentation for signal labeling and wiring requirements rather than inferring A/B polarity from a connector position. If requests time out, inspect the adapter's port selection, bus wiring, and serial activity before changing function codes.

Configuration item shown in the example Value Commissioning use
TCP host and port 192.168.0.66:502 Use only if Node-RED is meant to reach a TCP endpoint at that address.
Serial port /dev/ttyS1 Confirm this is the actual serial interface used by the client or gateway.
RTU framing 19200 baud, 8 data bits, no parity, 1 stop bit Match the Wemos server configuration at both ends of the RTU segment.

Check: With the Wemos powered and its server running, confirm the selected Node-RED endpoint is the one physically connected to this bus and that both ends use matching RTU framing.

Which timing values belong to the example client?

The flow configuration includes several timing values. Treat them as settings from the supplied example, not as universal RTU requirements or a measured optimum. The Wemos response time, adapter, bus, and any gateway all affect the time the client needs to wait. A timeout reports that the client did not receive a response in its configured interval; it does not identify whether the failure was caused by framing, address, wiring, or server logic.

Setting in the example Value What to verify
Serial connection delay Confirm the client and converter can establish the connection before requests begin.
Command delay Check whether the configured pacing suits the server and bus traffic.
Client timeout Compare with observed Wemos response time and available diagnostic logs.
Reconnect timeout Check the configured delay before reconnect after a timeout.

Keep timeout and reconnect settings stable while diagnosing the first request. Repeated retries can obscure whether the Wemos received a request at all. Node-RED's state, error, and failure logs can show whether the client attempted the operation or timed out, but pair those logs with Wemos-side request diagnostics or a relay-state check.

Check: Send a known supported read request first and confirm a response using the configured endpoint and framing; proceed to writes only after the transport responds consistently.

What does Function Code 5 write?

Function Code 5 writes one coil at one Modbus coil address. Its data is a single ON or OFF command, not a bit index into an arbitrary 16-bit holding register. The request's quantity is therefore 1. In the protocol encoding described in the evidence, ON is represented by FF 00 and OFF by 00 00; other values are not alternate bit patterns for selecting outputs.

Field FC5 meaning Example value
Function code Write one coil 5
Unit ID Target Modbus server on the link 2 in the supplied FC5 example; use the Wemos configured ID.
Address One coil address 5 in the example; confirm the Wemos map.
Quantity Number of coils in this request 1
Value Boolean state of that coil true or false

A Modbus coil is a one-bit logical point. A holding register is a separate 16-bit data point. The fact that a protocol request encodes the FC5 ON/OFF state using two bytes does not turn that state into a user-selectable 16-bit word. Node-RED can express the true/false value directly; it does not expose a sub-bit selector for FC5 because FC5 has no such field.

Check: Confirm the Wemos maps coil address 5 to the intended relay, then issue one FC5 request with quantity 1 and verify that relay changes state.

When should Function Code 15 replace FC5?

Use Function Code 15 when the Wemos server supports a write to multiple coils and the command should set a contiguous group of coil states. The value is an array of booleans or zero/one values; the quantity is the number of coil states being written. In the example, the array has ten elements, the starting address is 5, and the quantity is 10. Quantity does not mean ten bits inside one word or ten registers.

msg.payload = {
  value: [0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
  fc: 15,
  unitid: 1,
  address: 5,
  quantity: 10
};
return msg;

The example sends the complete ten-coil state vector. If a later update creates a fresh array of zeros and changes only one element, the other coils in that request are also written OFF. The alternative shown in the flow is to retain an array, change one element, and transmit the complete array. That requires the retained values to reflect the current desired state. The Wemos must support FC15; the Node-RED node cannot add a function code the server does not implement.

The two examples use different unit IDs: the FC5 payload uses 2, while the FC15 payload uses 1. These are example payloads, not a reason to send to either ID without checking the Wemos configuration. Similarly, confirm how the server numbers coil addresses. In JavaScript, array position 5 is the sixth element, while the payload's address is the Modbus starting address; the device map defines the relation between them.

Check: Verify the server supports FC15, the starting coil address and array length match its map, and a test array preserves the intended state of every coil in the transmitted range.

How can the Wemos change one bit in a register?

FC5 cannot edit one bit of a register. The Wemos application and the Node-RED client must agree on a register-level design if the relay states are stored together in a word. The usual application-level sequence is to obtain the current writable register value, set or clear the selected mask, then write the entire updated register with a supported single-register write. FC6 is one option named in the evidence for writing one unsigned integer register; the Wemos code must interpret that integer as relay bits.


This logic is application code, not an FC5 payload. For example, an integer value of 3 has the low two bits set in a 16-bit representation, but the Wemos must explicitly map those bits to the intended relay outputs. A Modbus register does not automatically become sixteen separately addressable coils. If the Wemos exposes only coils, use coil writes instead; if it exposes a writable register, document its bit-to-relay map and accepted function codes.

Read-modify-write has a state-coherency hazard: another writer or a stale cached word can overwrite a relay change made elsewhere between the read and write. Use one authoritative writer or serialize updates, and refresh the word from the server before changing a bit when another writer can update it. If the Wemos firmware can be changed, exposing a separate coil address for each relay avoids this shared-word update problem and is the simpler arrangement recommended in the discussion.

Check: On a test output, read the register, set one mapped bit, write the resulting word, read it back, and verify that unrelated relay bits stayed unchanged.

Which Node-RED payload matches each operation?

Build the message fields to match the operation and the server map. The supplied Modbus Flex Write flow puts the request object in msg.payload. For FC5, pass a boolean and set quantity to one. For FC15, pass an array and set quantity to the number of coil values in that array. Do not interpret FC5's quantity as a bit position or use FC15's array to describe a register word.

// One coil: use only if the Wemos maps address 5 as the required output.
msg.payload = {
  value: true,
  fc: 5,
  unitid: 2,
  address: 5,
  quantity: 1
};
return msg;

For OFF, set value to false. To update an FC15 coil array incrementally, the supplied function constructs an array, changes the element at msg.position, and writes ten coil values starting at address 5:

let array = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
array[msg.position] = msg.payload;
msg.payload = {
  value: array,
  fc: 15,
  unitid: 1,
  address: 5,
  quantity: 10
};
return msg;

That exact pattern initializes all other entries to zero on each invocation, so it does not preserve earlier states unless the application updates the array from stored or read-back state. Also validate incoming position and value before using them; the example injects positions and values but does not show range checking. For the FC6 register option, consult the installed Node-RED Modbus node's accepted message format and the Wemos map rather than assuming the FC5 or FC15 object shape applies unchanged.

Check: Inspect the outgoing Node-RED message before transmission: verify the function code, target unit ID, start address, quantity, data type, and array length all match the chosen server map.

How should the Wemos map relay commands?

Choose one consistent representation for the outputs. If each relay can be assigned its own coil address, the client can issue FC5 writes independently and the server can map each coil directly to a relay. If the server uses FC15, it must map the addressed coil range to outputs and accept the array length. If it uses an integer register, the Wemos code must define which integer bits control which relays and handle the complete register value.

Design Client operation Server responsibility
One Modbus coil per relay FC5 with one boolean and quantity 1 Map each coil address to one output.
Contiguous group of relay coils FC15 with a full state array and matching quantity Support FC15 and map the addressed coil range.
Relays encoded as bits in a register Write a complete integer register, such as with FC6 if supported Define the bit map and translate the register value into relay states.

FC15 may reduce the number of separate requests when the server already exposes multiple coils, but it does not write a register's bit fields. An integer-register interface can preserve a packed word, but it requires explicit Wemos logic and disciplined state handling. The heat pump may expose status bits packed into a readable register; that vendor-defined mapping is not automatically available for relay writes on the Wemos.

Check: Compare the selected design with the Wemos implementation and confirm every client address and function code has a corresponding server-side handler before commissioning more than one relay.

How do you verify the complete write path?

Verify transport, protocol, and output action in that order. A failure at an earlier hop makes later checks inconclusive; a protocol reply without a physical output change points toward address mapping or Wemos application logic rather than the RS485 path.

  1. Confirm Node-RED opens the intended serial adapter or TCP gateway and logs a response to a supported read.
  2. Confirm the request targets the Wemos unit ID and uses a function code the Wemos server implements.
  3. For FC5, send one state to one documented coil address with quantity 1. For FC15, send a complete state array with quantity equal to its length. For a packed register, write the complete calculated word with the supported register-write function.
  4. Check the Node-RED response and error logs, then read back the coil or register if the server provides a corresponding read.
  5. Observe the mapped relay output and compare it with the requested state. If the reply succeeds but the output does not move, inspect the Wemos address-to-output logic and relay drive code.

When a write times out, return to the earlier path checks: selected endpoint, serial framing, bus wiring, unit ID, and whether the Wemos server is running. When the server responds but reports an invalid operation or address, check its supported function codes and data map. When the server acknowledges the request but the wrong relay changes, correct the address or array-offset mapping in the application.

Check: Repeat the operation with one relay in a controlled state, read the addressed data point back, and confirm the physical relay matches before enabling temperature-driven automatic writes.

What should engineers verify about Modbus relay writes?

Can I use FC5 to set one bit inside a 16-bit word?

No. FC5 writes one addressed coil ON or OFF; it has no bit-offset field for editing a holding register. Read-modify-write the full register with server-side bit mapping, or expose each relay as its own coil.

Does quantity 15 mean 15 bits for FC5?

No. FC5 writes one coil, so set quantity to 1. FC15 writes multiple coils, and its quantity is the number of coil states sent in the array.

Can FC15 preserve the other relay states?

Only if the array contains the intended state of every coil in the addressed range. The example that initializes a new all-zero array and changes one position will send zeros for the other entries.

Can I use FC6 to control several relays from one word?

FC6 can be used for a single register write when the Wemos server supports it. The Wemos application must interpret that integer's bits and map them to relays; Modbus does not perform that mapping automatically.

Does a successful Node-RED response prove the relay changed?

No. It proves the request received a protocol response, not that the Wemos mapped the addressed data point to the intended output. Read back the coil or register and verify the physical relay state as the final commissioning step.

Back to blog