Skip to main content
ksale.1
Senior II
June 17, 2026
Question

NUCLEO-F446RE Modbus Slave - Writes Work (LED dimming via PWM), Reads Fail with Truncated Exception Responses

  • June 17, 2026
  • 2 replies
  • 26 views

 The Issue

I'm using **nanoMODBUS** (slave mode) on NUCLEO-F446RE via USART2 (115200 baud, 8N1). The slave exposes two holding registers:

- `0x0000`: Potentiometer value (read-only) → `usPotValue`
- `0x0001`: LED brightness (read/write) → `usLedBrightness`

**The LED is controlled via PWM (TIM2).**

Behavior

Operation                                                           Result 

**Write to `0x0001`** (set LED brightness)      Works consistently — LED dims 
**Read from `0x0000`** (potentiometer)          Fails most of the time 
**Read from `0x0001`** (LED brightness)        Fails most of the time 
**Rare successful read**                                  Both registers return correct values 

Master Log

```
Response: empty
Response: empty
Read OK: [609]              ← Rare success
Response: empty
Response: empty
Response: 0180018000         ← Truncated exception (5 bytes instead of 7+)
Response: empty
Read OK: [609]              ← Another rare success
```

The 5-byte response `0180018000`
- `01` = Slave address (0x01)
- `80` = Function code 0x03 + error bit (exception)
- `01 80 00` = Truncated exception data

 

 Observation and Implication 

| Writes work | UART RX is working, Modbus parser is working |
| PWM updates work | `write_single_register_cb()` is called and executes correctly |
| Reads fail | `read_holding_registers_cb()` is **not called consistently**, or returns an error that truncates the response |
| 5-byte exception response | The slave is generating an **exception response** (likely `ILLEGAL_DATA_ADDRESS` or `ILLEGAL_FUNCTION`) but the UART TX stops mid-frame |

 

1. **Does nanoMODBUS handle the case where `read_holding_registers_cb()` returns an error after already writing some registers to `registers_out`?** Could this cause the truncated response?

2. **Should the master read exactly one register at a time** to avoid the callback returning `ILLEGAL_DATA_ADDRESS` for the second register? Or should the callback handle `quantity > 1` differently?

3. **Could `HAL_Delay(5)` in the main loop** be causing the slave to miss the 3.5 character gap between bytes, causing the frame to be interpreted as incomplete?

4. **Would moving Modbus polling to a timer interrupt** (e.g., 1ms) improve stability by guaranteeing consistent RTU timing?

5. **Are there known issues with `HAL_UART_Transmit()` in polling mode** when used with PWM (TIM2) on STM32F4? Should I use `HAL_UART_Transmit_IT()` instead?

Environment

| **MCU** | STM32F446RE (NUCLEO-F446RE) |
| **Modbus Library** | nanoMODBUS (Slave) |
| **UART** | USART2, 115200, 8N1 |
| **PWM** | TIM2 Channel 1 on PA5 |
| **ADC** | ADC1 Channel 0 on PA0 |
| **CubeIDE** | 2.1.1 |
| **CubeF4** | 1.28.3 |
| **OS** | Windows 11 (ST-Link VCP driver 2.2.0.0) |

thank you for you help

2 replies

Ozone
Principal
June 19, 2026

First, if I remember correctly, the original Modbus specification defines serial baudrates only up to 38400.
Most deviced on the market offer more, so much for sure.

>  **Should the master read exactly one register at a time** to avoid the callback returning `ILLEGAL_DATA_ADDRESS` for the second register? Or should the callback handle `quantity > 1` differently?

Without looking at the code, the term “callback” makes me very suspicious.
Callbacks in HAL code are executed in interrupt context, which is not a good idea in general.
More complex routines very likely cause Rx overflow and lost characters.
Not “industrial strength” code, if you ask me.

I would suggest to set a breakpoint in the UART interrupt handler, on the code that handles Rx overflow errors.
If necessary, add respective instrumentation code.

Pavel A.
June 19, 2026

> The 5-byte response `0180018000`

> `01 80 00` = Truncated exception data

The 5-byte error response format is: <slave addr><0x80|function><Exception code> <CRC MSB><CRC LSB>

0x80 looks like an error code, but the function is 0 ??.

Exception code 01 means “function not supported”.

The CRC of the response is good. So this looks like a valid (not truncated) error reply due to bad function code 0.

Also note that your python code does not synchronize access to the serial port from the main GUI thread and the background polling thread; the modbus functions don’t seem to handle error responses. Looks like it was written by a human :)

 

> **Could `HAL_Delay(5)` in the main loop** be causing the slave to miss the 3.5 character gap between bytes

It’s very likely for a slave. A slave should not sleep like this.

> **Would moving Modbus polling to a timer interrupt** (e.g., 1ms) improve stability 

Polling on the slave side is a bad idea. Use interrupt based receive. By the way, the ST HAL driver is not good for interrupt based RX. Consider writing your own handler.

 

ksale.1
ksale.1Author
Senior II
June 26, 2026

Please find below my reply

ksale.1
ksale.1Author
Senior II
June 26, 2026

 Thank you all  for your valuable insight. Everything works except Modbus parsing                                                                  

Component Status
UART TX (115200, 8N1) ✅ Working (startup message appears in Tera Term)
UART RX ✅ Working (echo test passes, raw bytes received)
ADC (potentiometer reading) ✅ Working (values update continuously)
PWM (LED brightness) ✅ Working
Ring buffer ✅ Working
Interrupts (UART, ADC) ✅ Working

                                                                

The Problem

When the Python master sends a Modbus read request, I get:

Wrong slave ID: 48, expected 1

Or other random ASCII values (58, 65, etc.). This tells me:

  1. Bytes are arriving (the UART RX interrupt is firing)

  2. The bytes are being stored in the ring buffer

  3. nanoMODBUS is seeing the first byte, but interpreting it as something other than 0x01

The raw bytes sent by the master are correct (01 03 00 00 00 01 84 0A), but the library is not parsing them correctly.