Skip to main content
Associate
November 30, 2025
Solved

STM32 Nucleo-64 G070RB Board USART1 Modbus RTU Master without RTOS and UART Interrupts

  • November 30, 2025
  • 3 replies
  • 562 views

I am trying to configure STM32 Nucleo-64 G070RB Board as Modbus RTU Master to send commands to a slave and receive response. I have connected MAX485 - TTL to RS485 board. Powered with 5V, DE and RE connected to USART1 DE Pin. Board's A and B are connected to slave (VFD) at a distance of less than 2 meters.

I am able to send commands and slave works fine. The response from salve received is not echo of the command as expected. UART reports Parity error and Noise error :1h and 2h => 3h.

When the slave was connected to PC and same commands were sent using QModBus, slave works the same way (as expected) and response received is correct (echo of the command).

UART1 configuration:

 
huart1.Instance = USART1;
huart1.Init.BaudRate = 9600;
huart1.Init.WordLength = UART_WORDLENGTH_9B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_EVEN;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart1.Init.ClockPrescaler = UART_PRESCALER_DIV1;
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_RS485Ex_Init(&huart1, UART_DE_POLARITY_HIGH, 0, 0) != HAL_OK)

This is the send receive function and code:

void send_modbus_write_register(UART_HandleTypeDef *huart, uint16_t reg_addr,
uint16_t reg_value, const char* cmd_name)
{
 uint8_t request[8], response[8];
 int uart_receive_error = HAL_ERROR;

 // Build Modbus frame

...


 // TX PHASE
 HAL_StatusTypeDef tx_status = HAL_UART_Transmit(huart, request, 8, 100);
 if (tx_status != HAL_OK) {
 printf("%s: UART Transmit Error[%d]\r\n", cmd_name, tx_status);
 }

 // RX PHASE
 // RX PHASE - HAL manages DE automatically. You still need a small delay
 // for the VFD *processing time* (Modbus turnaround delay).
 // Add VFD *processing time* delay only (no pin switching delay needed)
 HAL_Delay(50); // Increased delay to 100ms for safety

 uart_receive_error = HAL_UART_Receive(huart, response, 8, 200);

 // Debug print
 printf("%s Request: ", cmd_name);
 printHexArray(request, 8);
 printf("%s Response: ", cmd_name);
 printHexArray(response, 8);

 if (uart_receive_error != HAL_OK) {
 printf("%s: UART Receive Error[%d]\r\n", cmd_name, uart_receive_error);

...
 }

This is the debug message:

Setting 40.00 Hertz
SET SPEED Request: Hex values to / from VFD: 0x01 0x06 0x20 0x01 0x0F 0xA0 0xD6 0x42
SET SPEED Response: Hex values to / from VFD: 0x1C 0x00 0x00 0x20 0x0A 0x00 0x00 0x00
SET SPEED: UART Receive Error[3]
Sending START signal
START Request: Hex values to / from VFD: 0x01 0x06 0x20 0x00 0x00 0x01 0x43 0xCA
START Response: Hex values to / from VFD: 0xF1 0x4F 0x00 0x08 0x68 0x02 0x00 0x20
START: UART Receive Error[3]
Sending STOP signal
STOP Request: Hex values to / from VFD: 0x01 0x06 0x20 0x00 0x00 0x05 0x42 0x09
STOP Response: Hex values to / from VFD: 0xF1 0x4F 0x00 0x08 0x68 0x02 0x00 0x20
STOP: UART Receive Error[3]

------------------------------------

I do not want to use manual DE / RE controlling / RTOS and Interrupt handling of UART Tx / Rx to make it simple.

Data sent by STM32 are received by slave but STM32 does not receive data sent by slave.

Any pointers?


Edited to apply source code formatting - please see How to insert source code for future reference.

Best answer by arunachalamram

STM32_ModbusMaster.png

It works fine now. D0 is data sent by STM32 (Modbus RTU Master) and D1 is reply data from VFD and D2 is DE. Rewired and cleaned by merging with previous working code. Issue was probably with

1. Wiring of Rx signal from STM32 to MAX485 (TTL to RS485 board) and / or to VFD.

2. Delay

Updated code to send and Modbus RTU frame is:

tx_status = HAL_UART_Transmit(huart, request, 8, 100);
while (__HAL_UART_GET_FLAG(huart, UART_FLAG_TC) == RESET);

rx_status = HAL_UART_Receive(huart, response, 8, 200);

Thank you all for the inputs. Issue can be closed.

3 replies

AScha.3
Super User
November 30, 2025

>The response from slave received ->UART reports Parity error and Noise error

Did you check with a scope, what signal really comes back to master ?

+

Do you have error handling , on receive : ( one (!) error stops uart, until you start it again ) ?

"If you feel a post has answered your question, please click ""Accept as Solution""."
Associate
December 1, 2025

Slave sends data which was verified using QModBus (master) and slave : STM32 not in picture in this setup.

I am about to test what signal is sent by slave using TTL Analyzer (I don't have scope) and would keep posted. I would keep in mind "one (!) error stops uart, until you start it again".

By the way, I might also try using UART ISR for receiving.

Andrew Neil
Super User
December 1, 2025

Welcome to the forum.

You need to give more details - please see:

How to write your question to maximize your chances to find a solution

 

As @AScha.3 said, have you checked with a scope what's actually arriving at your UART RX pin?

 

This doesn't look right:

 // RX PHASE - HAL manages DE automatically. You still need a small delay
 // for the VFD *processing time* (Modbus turnaround delay).
 // Add VFD *processing time* delay only (no pin switching delay needed)
 HAL_Delay(50); // Increased delay to 100ms for safety

 uart_receive_error = HAL_UART_Receive(huart, response, 8, 200);

Are you sure that the data isn't arriving before you make the HAL_UART_Receive call ?

If the data transmission has finished before you call HAL_UART_Receive, it will not see that data!

A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.
Associate
December 1, 2025

RE is tied with DE. As hardware is supposed to RESET DE when transmission is complete which would be the value of RE. Slave would start sending response only after receiving the master's data (DE is SET during this time) and after RE is RESET, according to the Modbus protocol.

So data from slave sent data would arrive after master sending data and changing value of DE & RE as they are tied.

What is the UART receive buffer size?

Ozone
Principal
December 1, 2025

As recommended above, take a scope a check the actual transmission with the STM32 master.

The Modbus RTU protocol is timing-based, a rather unfortunate circumstance tied to it's origins in the '70s. 

Here a relevent snippet from the current spec:
Following the last transmitted character, a similar interval of at least 3.5 character times marks the end of the message. A new message can begin after this interval.
The entire message frame must be transmitted as a continuous stream. If a silent interval of more than 1.5 character times occurs before completion of the frame, the receiving device flushes the incomplete message and assumes that the next byte will be the address field of a new message.
I know from experience that many devices on the market do not very strictly adhere to this requirements.
In this case, you can either widen the tolerance band of the STM32 master, use another slave device, or switch to Modbus ASCII.

Associate
December 2, 2025

>>Perhaps your management of the DE/RE pin states is not correct.

arunachalamram_0-1764661685908.png

 

My understanding of USART1 Hardware Flow Control (RS485) is DE (PA12) is SET during transmission and RESET once transmission is completed by MCU. RE tied DE shall give half-duplex channel using MAX485 based TTL to RS485 board connected to USART1 pins, DE & RE to PA12, 5V and GND. Data transmitted is received by slave. Shall see DE and RE values mapped to transmission signals and keep you posted. Thank you.

Ozone
Principal
December 2, 2025

I would check this in relation to the observed error interrupts.
Those should not happen, normally.


I would instrument the code, especially the UART error interrupt. E.g. by toggling a GPIO used as a trigger for the scope / logic analyser.
This would have to be a post-trigger kind, i.e. the scope / logic analyser should stop recording, so that you see the last few characters (Rx), and DE / RE transitions before the error.
I suppose you are aware there are possible signal/noise issues you cannot see with a logic analyser.

Associate
December 4, 2025

FirstCommandFrame.pngSecondCommandFrame.pngThirdCommandFrame.png

These are captured with Logic Analyzer:

Channel 0: Tx from MCU (bytes are good)

Channel 1: Rx from VFD (No signal at all and hence there is no image for this)

Channel 2: DE (PA12) from MCU (SET during MCU UART transmitting and then RESET)

Somehow VFD does not work now - perhaps I lost / rearranged some lines of code or delay. Need to look at backup code, merge and test.

These are only to show DE (PA12) toggled by hardware (SET during transmit) so that half-duplex mode shall be effective with MAX485. Shall keep you posted after getting code to previous state / condition  (i.e., VFD working).

There is 120 Ohm resistor on MAX485 - TTL to RS485 board and I connected 100 Ohm resistor at VFD end.

arunachalamram_0-1764857260702.jpeg