Reading MODBUS RTU acknowledgment with esp-idf

devhari
Posts: 8
Joined: Thu Mar 13, 2025 2:01 pm

Reading MODBUS RTU acknowledgment with esp-idf

Postby devhari » Mon Sep 08, 2025 11:29 am

Hi,

I'm working on implementing MODBUS communication by sending data via UART from an ESP.

When transmitting, I successfully send RS485 data to the MODBUS device by setting the RE/DE (tied together) pins high. Then, I immediately set them low to receive data on the RX line of the ESP.

However, the received data includes the correct values along with leading and trailing zeros. Sometimes, garbage data appears instead of the actual response.

can you check my source code once and let me know if any corections are needed.

Code: Select all

#include <stdio.h>
#include <stdint.h>
#include "driver/uart.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

#define ZONE_READ_REG_VAL 7000
#define MAX_ZONES 64
#define FRAME_SIZE 8
#define UART_NUM UART_NUM_2
#define TXD_PIN GPIO_NUM_41
#define RXD_PIN GPIO_NUM_42
#define RS485_ENABLE_PIN GPIO_NUM_2
#define SLAVE_ID 01
#define BAUD_RATE 9600
#define BUF_SIZE 1024

static void poll_all_zones(void) {
    uint8_t rx_data[135];
    uint8_t frame[FRAME_SIZE];

    uint16_t reg = ZONE_READ_REG_VAL;
    frame[0] = SLAVE_ID;
    frame[1] = 0x03;
    frame[2] = (reg >> 8) & 0xFF;
    frame[3] = reg & 0xFF;
    frame[4] = 0x00;
    frame[5] = MAX_ZONES & 0xFF;
    //uint16_t crc = modbus_crc16(frame, 6);
    frame[6] = 0xC3;//crc & 0xFF;
    frame[7] = 0x0D;//(crc >> 8) & 0xFF;

    gpio_set_level(RS485_ENABLE_PIN, 1);
    vTaskDelay(pdMS_TO_TICKS(10));
    uart_write_bytes(UART_NUM, (const char *)frame, FRAME_SIZE);
    uart_wait_tx_done(UART_NUM, pdMS_TO_TICKS(50));
    vTaskDelay(pdMS_TO_TICKS(10));
    gpio_set_level(RS485_ENABLE_PIN, 0);
    //vTaskDelay(pdMS_TO_TICKS(5));
    int len = uart_read_bytes(UART_NUM, rx_data, sizeof(rx_data), pdMS_TO_TICKS(100));
    printf("\n");
    if (len > 0) {
        
        for (int i = 0; i < sizeof(rx_data); i++) {
            printf("%02X ", rx_data[i]);
        }
        printf("\n");
    }
}

void rs485_task(void *arg) {
//    ESP_LOGI(TAG_RS485_DATA, "Zone polling task started");
    for (;;) {
        poll_all_zones();
        vTaskDelay(pdMS_TO_TICKS(200));
    }
}

void app_main(void) {
    uart_config_t uart_config = {
        .baud_rate = BAUD_RATE,
        .data_bits = UART_DATA_8_BITS,
        .parity    = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE
    };

    // Init UART
    ESP_ERROR_CHECK(uart_driver_install(UART_NUM, 256, 0, 0, NULL, 0));
    ESP_ERROR_CHECK(uart_param_config(UART_NUM, &uart_config));
    ESP_ERROR_CHECK(uart_set_pin(UART_NUM, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));

    // Init RS485 direction control pins
    gpio_set_direction(RS485_ENABLE_PIN, GPIO_MODE_OUTPUT);

    gpio_set_level(RS485_ENABLE_PIN, 0);   // Default to RX mode
    vTaskDelay(50);
    xTaskCreate(rs485_task, "rs485_task", 8192, NULL, 5, NULL);
}
--------------Logs-----------
Expected Data : 01 03 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1B A5

Actual Data : 00 01 03 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1B A5 00

ahsrabrifat
Posts: 201
Joined: Sat Jan 18, 2025 2:31 pm

Re: Reading MODBUS RTU acknowledgment with esp-idf

Postby ahsrabrifat » Mon Sep 08, 2025 3:36 pm

sizeof(rx_data) is always 135, so you’re printing uninitialized buffer contents (which appear as 00 or random garbage). Use the actual number of bytes read: len. Correct your code this way.

Code: Select all

static uint16_t modbus_crc16(uint8_t *buf, int len) {
    uint16_t crc = 0xFFFF;
    for (int pos = 0; pos < len; pos++) {
        crc ^= (uint16_t)buf[pos];
        for (int i = 0; i < 8; i++) {
            if (crc & 0x0001) {
                crc >>= 1;
                crc ^= 0xA001;
            } else {
                crc >>= 1;
            }
        }
    }
    return crc;
}

static void poll_all_zones(void) {
    uint8_t rx_data[256];
    uint8_t frame[FRAME_SIZE];

    uint16_t reg = ZONE_READ_REG_VAL;
    frame[0] = SLAVE_ID;
    frame[1] = 0x03;
    frame[2] = (reg >> 8) & 0xFF;
    frame[3] = reg & 0xFF;
    frame[4] = 0x00;
    frame[5] = MAX_ZONES & 0xFF;

    uint16_t crc = modbus_crc16(frame, 6);
    frame[6] = crc & 0xFF;          // CRC Low
    frame[7] = (crc >> 8) & 0xFF;   // CRC High

    uart_flush_input(UART_NUM); // clear old data

    gpio_set_level(RS485_ENABLE_PIN, 1);   // TX mode
    vTaskDelay(pdMS_TO_TICKS(2));          // guard time
    uart_write_bytes(UART_NUM, (const char *)frame, FRAME_SIZE);
    uart_wait_tx_done(UART_NUM, pdMS_TO_TICKS(50));
    vTaskDelay(pdMS_TO_TICKS(2));          // allow last byte to shift out
    gpio_set_level(RS485_ENABLE_PIN, 0);   // RX mode

    int len = uart_read_bytes(UART_NUM, rx_data, sizeof(rx_data), pdMS_TO_TICKS(200));

    if (len > 0) {
        printf("Received %d bytes: ", len);
        for (int i = 0; i < len; i++) {
            printf("%02X ", rx_data[i]);
        }
        printf("\n");
    } else {
        printf("No response\n");
    }
}

Who is online

Users browsing this forum: Amazon [Bot], PerplexityBot and 6 guests