GithubHelp home page GithubHelp logo

debevv / nanomodbus Goto Github PK

View Code? Open in Web Editor NEW
255.0 16.0 52.0 24.41 MB

A compact MODBUS RTU/TCP C library for embedded/microcontrollers

License: MIT License

C 99.30% CMake 0.70%
c embedded embedded-c embedded-systems microcontroller microcontrollers modbus modbus-library modbus-rtu modbus-tcp arduino stm32

nanomodbus's Introduction

nanoMODBUS - A compact MODBUS RTU/TCP C library for embedded/microcontrollers

If you found this library useful, buy me a coffee on

nanoMODBUS is a small C library that implements the Modbus protocol. It is especially useful in embedded and resource-constrained systems like microcontrollers.
Its main features are:

  • Compact size
    • Only ~1500 lines of code
    • Client and server code can be disabled, if not needed
  • No dynamic memory allocations
  • Transports:
    • RTU
    • TCP
  • Roles:
    • Client
    • Server
  • Function codes:
    • 01 (0x01) Read Coils
    • 02 (0x02) Read Discrete Inputs
    • 03 (0x03) Read Holding Registers
    • 04 (0x04) Read Input Registers
    • 05 (0x05) Write Single Coil
    • 06 (0x06) Write Single Register
    • 15 (0x0F) Write Multiple Coils
    • 16 (0x10) Write Multiple registers
    • 20 (0x14) Read File Record
    • 21 (0x15) Write File Record
    • 23 (0x17) Read/Write Multiple registers
    • 43/14 (0x2B/0x0E) Read Device Identification
  • Platform-agnostic
    • Requires only C99 and its standard library
    • Data transport read/write function are implemented by the user
  • Broadcast requests and responses

At a glance

#include <stdio.h>

#include "nanomodbus.h"
#include "my_platform_stuff.h"

int main(int argc, char* argv[]) {
    // Set up the TCP connection
    void* conn = my_connect_tcp(argv[1], argv[2]);
    if (!conn) {
        fprintf(stderr, "Error connecting to server\n");
        return 1;
    }

    // my_transport_read() and my_transport_write() are implemented by the user 
    nmbs_platform_conf platform_conf;
    platform_conf.transport = NMBS_TRANSPORT_TCP;
    platform_conf.read = my_transport_read;
    platform_conf.write = my_transport_write;
    platform_conf.arg = conn;    // Passing our TCP connection handle to the read/write functions

    // Create the modbus client
    nmbs_t nmbs;
    nmbs_error err = nmbs_client_create(&nmbs, &platform_conf);
    if (err != NMBS_ERROR_NONE) {
        fprintf(stderr, "Error creating modbus client\n");
        return 1;
    }

    // Set only the response timeout. Byte timeout will be handled by the TCP connection
    nmbs_set_read_timeout(&nmbs, 1000);

    // Write 2 holding registers at address 26
    uint16_t w_regs[2] = {123, 124};
    err = nmbs_write_multiple_registers(&nmbs, 26, 2, w_regs);
    if (err != NMBS_ERROR_NONE) {
        fprintf(stderr, "Error writing register at address 26 - %s", nmbs_strerror(err));
        return 1;
    }

    // Read 2 holding registers from address 26
    uint16_t r_regs[2];
    err = nmbs_read_holding_registers(&nmbs, 26, 2, r_regs);
    if (err != NMBS_ERROR_NONE) {
        fprintf(stderr, "Error reading 2 holding registers at address 26 - %s\n", nmbs_strerror(err));
        return 1;
    }
    
    // Close the TCP connection
    my_disconnect(conn);
    
    return 0;
}

Installation

Just copy nanomodbus.c and nanomodbus.h inside your application codebase.

API reference

API reference is available in the repository's GitHub Pages.

Platform functions

nanoMODBUS requires the implementation of 2 platform-specific functions, defined as function pointers when creating a client/server instance.

Transport read/write

int32_t read(uint8_t* buf, uint16_t count, int32_t byte_timeout_ms, void* arg);
int32_t write(const uint8_t* buf, uint16_t count, int32_t byte_timeout_ms, void* arg);

These are your platform-specific functions that read/write data to/from a serial port or a TCP connection.
Both methods should block until either:

  • count bytes of data are read/written
  • the byte timeout, with byte_timeout_ms >= 0, expires

A value < 0 for byte_timeout_ms means no timeout.

Their return value should be the number of bytes actually read/written, or < 0 in case of error.
A return value between 0 and count - 1 will be treated as if a timeout occurred on the transport side. All other values will be treated as transport errors.

Callbacks and platform functions arguments

Server callbacks and platform functions can access arbitrary user data through their void* arg argument. The argument is useful, for example, to pass the connection a function should operate on.
Their initial values can be set via the nmbs_set_callbacks_arg and nmbs_set_platform_arg API methods.

Tests and examples

Tests and examples can be built and run on Linux with CMake:

mkdir build && cd build
cmake ..
make

Please refer to examples/arduino/README.md for more info about building and running Arduino examples.

Misc

  • To reduce code size, you can define the following #defines:
    • NMBS_CLIENT_DISABLED to disable all client code
    • NMBS_SERVER_DISABLED to disable all server code
    • To disable individual server callbacks, define the following:
      • NMBS_SERVER_READ_COILS_DISABLED
      • NMBS_SERVER_READ_DISCRETE_INPUTS_DISABLED
      • NMBS_SERVER_READ_HOLDING_REGISTERS_DISABLED
      • NMBS_SERVER_READ_INPUT_REGISTERS_DISABLED
      • NMBS_SERVER_WRITE_SINGLE_COIL_DISABLED
      • NMBS_SERVER_WRITE_SINGLE_REGISTER_DISABLED
      • NMBS_SERVER_WRITE_MULTIPLE_COILS_DISABLED
      • NMBS_SERVER_WRITE_MULTIPLE_REGISTERS_DISABLED
      • NMBS_SERVER_READ_FILE_RECORD_DISABLED
      • NMBS_SERVER_WRITE_FILE_RECORD_DISABLED
      • NMBS_SERVER_READ_WRITE_REGISTERS_DISABLED
      • NMBS_SERVER_READ_DEVICE_IDENTIFICATION_DISABLED
    • NMBS_STRERROR_DISABLED to disable the code that converts nmbs_errors to strings
  • Debug prints about received and sent messages can be enabled by defining NMBS_DEBUG

nanomodbus's People

Contributors

aguilerag avatar alimoal avatar arhiv6 avatar benjaminpritchard avatar debevv avatar dervomsee avatar hossein-m98 avatar jonathangjertsen avatar m-rashvand avatar malem-robotiq avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nanomodbus's Issues

CRC appears to have the wrong byte order

Thanks for the great library!

I was doing some tests and noticed that the CRC checksum on RTU packets seems to have the wrong endianness. I was beating my head against the wall trying to understand why my devices were not responding to requests, and decided to compare the packets emitted by nanomodbus on a raspberry pi pico to what mbpoll emits over a USB->RS485 adapter. The packets were identical, except for the last 2 bytes, which were swapped.

Manually swapping the bytes by modifying the return value of crc_calc seemed to fix the problem for me. ( I tried messing around by manually definining NMBS_BIG_ENDIAN but it swapped the body bytes as well....)

return (crc << 8) + (crc >> 8);

Communication problem with 2 servers and 1 client

Hi,

All is working good in the RTU configuration (2x ESP32 with max485-like RS485). The client send a read holding register to server ID 1 and 3.

When I only have ID 1 or ID 3, it's OK, but when I have both, the first ID the client request answer, and the second doesn't.

I'm trying to find the solution since many days. I already check the RE/DE pins and the RS485 communication with my oscilloscope, the only thing I can see is that I'm trying to explain.

On the server which doesn't answer, there is this debug which I'm not sure to understand properly :

NMBS req <- client_id 3 fc 0 NMBS req <- client_id 49 fc 0 NMBS req <- client_id 1 fc 213 NMBS req <- client_id 3 fc 3 a 49 q 1 NMBS req <- client_id 3 fc 3 a 512 q 193

If you have an idea... I precise that sometimes it's working, but very rarely

Thanks !

macOS and DEBUG

I’ve been working on getting nanoMODBUS to work on macOS and it seems to be working fine. I used the Linux files as a template.

There is still one annoyance that’s in the way. And more testing.

nanoMODBUS defines the DEBUG() macro while Apple platforms define this as a flag for debug builds (#if DEBUG…). Any chance this could be renamed to NDEBUG or something similar?

Not compiling with SDCC

Hi,

I'm using platformio to develop for an STM8 but nanoMODBUS is not compiling.

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/ststm8/stm8sblue.html
PLATFORM: ST STM8 (2.1.0) > ST STM8S103F3 Breakout Board
HARDWARE: STM8S103F3P6 16MHz, 1KB RAM, 8KB Flash
DEBUG: Current (stlink) External (stlink)
PACKAGES:
 - framework-arduinoststm8 @ 0.50.210317
 - tool-stm8binutils @ 0.230.0 (2.30)
 - toolchain-sdcc @ 1.30901.11242 (3.9.1)
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 13 compatible libraries
Scanning dependencies...
Dependency Graph
|-- nanoModbus
Building in release mode
Compiling .pio\build\stm8sblue\src\main.c.rel
Compiling .pio\build\stm8sblue\libb6b\nanoModbus\nanomodbus.c.rel
src\main.c:32: warning 85: in function read_serial unreferenced function argument : 'arg'
src\main.c:51: warning 85: in function write_serial unreferenced function argument : 'arg'
src\main.c:63: warning 85: in function handler_read_holding_registers unreferenced function argument : 'unit_id'
src\main.c:63: warning 85: in function handler_read_holding_registers unreferenced function argument : 'arg'
lib\nanoModbus\nanomodbus.c:1152: error 2: Initializer element is not a constant expression
lib\nanoModbus\nanomodbus.c:1290: error 0: Duplicate symbol 'subreq_file_number', symbol IGNORED
lib\nanoModbus\nanomodbus.c:1261: error 177: previously defined here
lib\nanoModbus\nanomodbus.c:1291: error 0: Duplicate symbol 'subreq_record_number', symbol IGNORED
lib\nanoModbus\nanomodbus.c:1262: error 177: previously defined here
lib\nanoModbus\nanomodbus.c:1292: error 0: Duplicate symbol 'subreq_record_length', symbol IGNORED
lib\nanoModbus\nanomodbus.c:1263: error 177: previously defined here
lib\nanoModbus\nanomodbus.c:1293: error 20: Undefined identifier 'subreq_record_length'
lib\nanoModbus\nanomodbus.c:1296: error 20: Undefined identifier 'subreq_file_number'
lib\nanoModbus\nanomodbus.c:1296: error 20: Undefined identifier 'subreq_record_number'
lib\nanoModbus\nanomodbus.c:1297: error 20: Undefined identifier 'subreq_record_length'
lib\nanoModbus\nanomodbus.c:1305: error 20: Undefined identifier 'subreq_record_length'
lib\nanoModbus\nanomodbus.c:1311: error 20: Undefined identifier 'subreq_record_length'
*** [.pio\build\stm8sblue\libb6b\nanoModbus\nanomodbus.c.rel] Error 1
========================================================================================= [FAILED] Took 10.02 seconds =========================================================================================

I'm new to C development and microcontrollers in general so I'm sorry if this is something obvious.

Any help is appreciated.

Possible buffer overflow using nmbs_read_holding_registers()

Situation

Accidentally connected two masters on one serial bus. This resulted in buffer overflows when calling nmbs_read_holding_registers()

Cause

Debugging found the possibility of buffer overflow in recv_read_registers_res(). Where quantity is not used to limit the amount of writes to registers.

Solution

Limit the for loop iterations or return error when (registers_bytes / 2) != quantity in recv_read_registers_res().

RTU Connection Setup

I am looking to use your library for a project going to use both RTU and TCP. I've noted there is no way to set up the RTU port number, I assume that is expected to be handled by the user? Let me know if I missed something

Out of bounds access in Arduino server example

Hi,

I don't understand why there is +1 in this if statement. It looks to me like the following array server_registers can be accessed out of bounds if quantity = REGS_ADDR_MAX+1

if (address + quantity > REGS_ADDR_MAX + 1)
return NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS;
// Read our registers values into registers_out
for (int i = 0; i < quantity; i++)
registers_out[i] = server_registers[address + i];
return NMBS_ERROR_NONE;

Missing validation of unit ID

It seems that if a response is received with a unit ID that doesn't match the request that was sent, it will be accepted as a valid response. Perhaps there should be an NMBS_ERROR_INCORRECT_UNIT_ID?

return type for nmbs_create

nmbs_create is declared:

int nmbs_create(nmbs_t* nmbs, const nmbs_platform_conf* platform_conf)

All the return values are of nmbs_error enum type. Should most likely be:

nmbs_error nmbs_create(nmbs_t* nmbs, const nmbs_platform_conf* platform_conf)

Support buffer-oriented callbacks

If the API supported buffer-oriented callbacks (int write_buffer(const uint8_t *buffer, size_t size, int32_t timeout_ms, void *arg) and int read_buffer(uint8_t *buf, size_t size, int32_t timeout_ms, void *arg)), it would be easier to take advantage of hardware FIFOs and DMAs to reduce CPU load.

I worked around the byte-oriented API by having the write_byte and read_byte callbacks read/write into an intermediate buffer and performing the actual transfer once the correct number of bytes had been requested. This works but is a little clunky and is less efficient than a zero-copy transfer of the buffer that already exists in nmbs->msg.buf.

NMBS_ERROR_TRANSPORT after disconnect

Hello,
i am using STM32F407 with freeRTOS and LwIP.
I wanted to use ModbusTCP server, so I use the Linux Example (server-tcp.c).

I do only a few changes to use it as a Task.

/**
 * Create an ModbusTCP Task
 */
void ModbusTCPTaskRun(void)
{
	osThreadDef(ModbusTCPTask, StartModbusTCPTask, osPriorityHigh, 0, 1024);
	ModbusTCPHandle = osThreadCreate(osThread(ModbusTCPTask), NULL);

}

and "int main" to StartModbusTCPTask

int StartModbusTCPTask(void)
{
	char* address = "192.168.0.50";
	char* port = "502";
.
.
.

Problem:
I can connect the Server with "ModbusPoll" and get the right Register Data (Dummy Data). I can connect the Server with a second instance of "ModbusPoll" and get the right Register Data, too. But when i make a disconnect, I cannot connect again. During the Debugging i can see that in the function read_fd_linux() the function disconnect(arg) is called. This looks ok. But there is no ping working and i cannot connect again. It looks there is a Problem with the Sockets.

Do you have this issue, too?
Or any Idea about that Problem?

Using the library in STM32 project

I'm trying to use this library inside a STM32 CubeIDE project but no success.
I've defined the two required functions but polling the registers of the sensor always returns TIMEOUT.
The sensor works fine with direct access from Modbus Poll tools ( using UART to USB).
Here is my base code :

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "nanomodbus.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define RTU_SERVER_ADDRESS 1

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart2;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

int32_t read_serial(uint8_t* buf, uint16_t count, int32_t byte_timeout_ms, void* arg) {
    HAL_StatusTypeDef status = HAL_UART_Receive(&huart2, buf, count, byte_timeout_ms * count);
    if (status == HAL_OK) {
        return count;
    } else {
        return 0;
    }
}

int32_t write_serial(const uint8_t* buf, uint16_t count, int32_t byte_timeout_ms, void* arg) {

	HAL_StatusTypeDef status = HAL_UART_Transmit(&huart2, (uint8_t *)buf, count, byte_timeout_ms * count);
    if (status == HAL_OK) {
        return count;
    } else {
        return 0;
    }
}

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */

  nmbs_platform_conf platform_conf;
  platform_conf.transport = NMBS_TRANSPORT_RTU;
  platform_conf.read = read_serial;
  platform_conf.write = write_serial;

  nmbs_t nmbs;
  nmbs_error err = nmbs_client_create(&nmbs, &platform_conf);
  if (err != NMBS_ERROR_NONE)
	  __NOP();
  nmbs_set_read_timeout(&nmbs, 1000);
  nmbs_set_byte_timeout(&nmbs, 100);

  nmbs_set_destination_rtu_address(&nmbs, RTU_SERVER_ADDRESS);

  // Read 2 holding registers from address 0023h
  uint16_t r_reg[2];
  err = nmbs_read_holding_registers(&nmbs, 0x23, 2, r_reg);
  if (err != NMBS_ERROR_NONE)
	  __NOP();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

  }

  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL12;
  RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV1;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief USART2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART2_UART_Init(void)
{

  /* USER CODE BEGIN USART2_Init 0 */

  /* USER CODE END USART2_Init 0 */

  /* USER CODE BEGIN USART2_Init 1 */

  /* USER CODE END USART2_Init 1 */
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 9600;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART2_Init 2 */

  /* USER CODE END USART2_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOF_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin : B1_Pin */
  GPIO_InitStruct.Pin = B1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : LD2_Pin */
  GPIO_InitStruct.Pin = LD2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

Any help ?

Modbus TCP client over Wifi for Raspberry Pi Pico W (RP2040)

Scope

The aim of this project is to implement the missing functionality needed to interact with a Modbus TCP Linux server using a Modbus TCP client via Wifi on the Raspberry Pi Pico W's on board CYW43439 wifi/bluetooth chip.

The way this is to be done is by implementing (as explained in the README.md of the original repository) a port of read_fd_pico, write_fd_pico and connect_tcp for this specific microcontroller.

Background

The development target is the RP2040 microcontroller using the C programming language that comes in the Pico W board package.

The work done so far in this branch has been achieved by implementing some lwIP functions using pico-examples as reference: lwIP WIFI source exaples.

Any help for getting a successful connection would be greatly appreciated.

Stack smashing protect failure

Hi,

I'm still working with your library on a modbus project and in this case, I can see some perturbations on the bus (because a high frequency signal is running near from the modbus cable, but it can be filtered easily by the transceiver). I have a crash in my program (which is a little difficult to produce without all the code, but I can try in the future if really needed), localized at nanomodbus.c @1085 : if (!first_byte_received && err == NMBS_ERROR_TIMEOUT) :

nmbs_error nmbs_server_poll(nmbs_t* nmbs) {
    msg_state_reset(nmbs);

    bool first_byte_received = false;
    nmbs_error err = recv_req_header(nmbs, &first_byte_received);
    if (err != NMBS_ERROR_NONE) {
        if (!first_byte_received && err == NMBS_ERROR_TIMEOUT)
            return NMBS_ERROR_NONE;

        return err;
    }

The ESP32 returns "Stack smashing protect failure!" at the moment of the crash.

Decoding stack results 0x40083b81: panic_abort at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_system/panic.c line 402 0x4008bc45: esp_system_abort at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_system/esp_system.c line 121 0x40090eb5: abort at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/newlib/abort.c line 46 0x400ecf98: __stack_chk_fail at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_system/stack_check.c line 36 0x400e1daa: nmbs_server_poll at lib/nanomodbus/nanomodbus.c line 1085 0x400d40fe: ModBus::loop() at src/modbus.h line 358 0x400d50d7: loop() at src/main.cpp line 956 0x400e4685: loopTask(void*) at /Users/pierreko/.platformio/packages/framework-arduinoespressif32/cores/esp32/main.cpp line 50

Image PNG

Do you have an idea ?

Thanks 😉

Split NMBS_ERROR_TRANSPORT into CRC error and others?

Hi, thanks for a very nice modbus implementation. One issue I've noticed is that the NMBS_ERROR_TRANSPORT includes several unrelated errors. In particular, it includes CRC mismatches. It would be nice to be able to separate this case from the others since this might indicate a problem with the modbus server rather than the client.

Timeout Issue

I integrated NanoModbus library with the quectel EC200u controller for RTU communication as a master. However, I'm encountering timeout errors despite successful communication. In the SDK, there isn't a timer function available to handle read and write operations. Could you please assist me in resolving this issue?

Hardfault in library (Guessing Memory address fault)

I'm porting nanoMODBUS to a microcontroller (CH32V003).

I have the issue that i get a hardfault To diaganose that error i wrote a fake_data function, that simulates a coil read, that gives always the same data.
char fakedata[8]={0x01, 0x01, 0x00, 0x00, 0x00, 0x08, 0x3D, 0xCC};
In the hardfault interrupt, i look where the hardfault happened by using a global variable. It takes several iterations until this happens, it doesn't happen strait away.

The code is based on the arduino slave example, but the error seems to be inside the library.

What do i see?
grafik
It crashes inside the routine (_hardfault_was_inside_readwrite==1) and it has always 2 chars left in the buffer. (mb_recievebuffer_index=2) So i gues it's before the checksum is there.

@debevv can you please help to investigate?

`
#include "unified.h"

/*
This example application sets up an RTU server and handles modbus requests

This server supports the following function codes:
FC 01 (0x01) Read Coils
FC 03 (0x03) Read Holding Registers
FC 15 (0x0F) Write Multiple Coils
FC 16 (0x10) Write Multiple registers
*/

nmbs_t nmbs;

// The data model of this sever will support coils addresses 0 to 100 and registers addresses from 0 to 32
#define COILS_ADDR_MAX 64
#define REGS_ADDR_MAX 32

// Our RTU address
#define RTU_SERVER_ADDRESS 1

// A single nmbs_bitfield variable can keep 2000 coils
nmbs_bitfield server_coils = {0};
uint16_t server_registers[REGS_ADDR_MAX] = {0};

#define MB_RECIEVEBUFFER_SIZE 100
uint8_t mb_recievebuffer[MB_RECIEVEBUFFER_SIZE];
uint32_t mb_recievebuffer_index=0;

void delay1msec(void){
uint32_t countdown=8000;
while(countdown--) asm("");
}

uint8_t _hardfault_was_inside_readwrite=0;

void fake_data(uint8_t* buf,uint16_t count){
_hardfault_was_inside_readwrite=5;
if((mb_recievebuffer_index+count)<=MB_RECIEVEBUFFER_SIZE){
memcpy(&mb_recievebuffer[mb_recievebuffer_index],buf,count);
mb_recievebuffer_index+=count;
}
_hardfault_was_inside_readwrite=1;
}

int32_t read_serial(uint8_t* buf, uint16_t count, int32_t byte_timeout_ms, void* arg) {
_hardfault_was_inside_readwrite=2;
while(byte_timeout_ms--){
// uint32_t recbytes=mcu_tracer_usart_recieved_bytes();
// if(recbytes>0){
// //copies the number of recieved bytes
// uint32_t datalen;
// uint8_t *data;
// data=mcu_tracer_rx_pingpong_buf(&datalen);
// if((mb_recievebuffer_index+datalen)>=MB_RECIEVEBUFFER_SIZE){
// datalen=MB_RECIEVEBUFFER_SIZE-mb_recievebuffer_index;
// }
// memcpy(&mb_recievebuffer[mb_recievebuffer_index],data,datalen);
// mb_recievebuffer_index+=datalen;
// }
if(count<=mb_recievebuffer_index){
_hardfault_was_inside_readwrite=3;
memcpy(buf,&mb_recievebuffer[0],count);
mb_recievebuffer_index=mb_recievebuffer_index-count;
if(mb_recievebuffer_index>0){
_hardfault_was_inside_readwrite=4;
memmove(&mb_recievebuffer[0],&mb_recievebuffer[count],mb_recievebuffer_index);
}
_hardfault_was_inside_readwrite=1;
return count;
}

// delay1msec();
// byte_timeout_ms--;
// if(byte_timeout_ms<1){
// memcpy(buf,&mb_recievebuffer[0],mb_recievebuffer_index);
// mb_recievebuffer_index=0;
// return mb_recievebuffer_index;
// }
_hardfault_was_inside_readwrite=1;
return -1;
}
}

int32_t write_serial(const uint8_t* buf, uint16_t count, int32_t byte_timeout_ms, void* arg) {
return count;
while(mcu_tracer_usart_send_busy()){
delay1msec();
byte_timeout_ms--;
if(byte_timeout_ms<1) break;
}
MBHW_dir_set_write();
//delay1msec();
mcu_tracer_usart_send(buf,count);
mcu_tracer_usart_txdone_wait();
//even when the transmission is done, one last byte is processing, therefore this delay!
uint32_t countdown=2000;
while(countdown--) asm("");
MBHW_dir_set_read();

return count;
}

void onError() {
// Set the led ON on error
}

nmbs_error handle_read_coils(uint16_t address, uint16_t quantity, nmbs_bitfield coils_out, uint8_t unit_id, void *arg) {
return NMBS_ERROR_NONE; //deactive funciton to avoid errors
if (address + quantity > COILS_ADDR_MAX + 1)
return NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS;

// Read our coils values into coils_out
for (int i = 0; i < quantity; i++) {
bool value = nmbs_bitfield_read(server_coils, address + i);
nmbs_bitfield_write(coils_out, i, value);
}

return NMBS_ERROR_NONE;
}

nmbs_error handle_write_multiple_coils(uint16_t address, uint16_t quantity, const nmbs_bitfield coils, uint8_t unit_id, void *arg) {
if (address + quantity > COILS_ADDR_MAX + 1)
return NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS;

// Write coils values to our server_coils
for (int i = 0; i < quantity; i++) {
nmbs_bitfield_write(server_coils, address + i, nmbs_bitfield_read(coils, i));
}

return NMBS_ERROR_NONE;
}

nmbs_error handler_read_holding_registers(uint16_t address, uint16_t quantity, uint16_t* registers_out, uint8_t unit_id, void *arg) {
if (address + quantity > REGS_ADDR_MAX + 1)
return NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS;

// Read our registers values into registers_out
for (int i = 0; i < quantity; i++)
registers_out[i] = server_registers[address + i];

return NMBS_ERROR_NONE;
}

nmbs_error handle_write_multiple_registers(uint16_t address, uint16_t quantity, const uint16_t* registers, uint8_t unit_id, void *arg) {
if (address + quantity > REGS_ADDR_MAX + 1)
return NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS;

// Write registers values to our server_registers
for (int i = 0; i < quantity; i++)
server_registers[address + i] = registers[i];

return NMBS_ERROR_NONE;
}

void Nanomb_init(void) {
nmbs_platform_conf platform_conf;
platform_conf.transport = NMBS_TRANSPORT_RTU;
platform_conf.read = read_serial;
platform_conf.write = write_serial;
platform_conf.arg = NULL;

nmbs_callbacks callbacks = {0};
callbacks.read_coils = handle_read_coils;
callbacks.write_multiple_coils = handle_write_multiple_coils;
callbacks.read_holding_registers = handler_read_holding_registers;
callbacks.write_multiple_registers = handle_write_multiple_registers;

// Create the modbus server

nmbs_error err = nmbs_server_create(&nmbs, RTU_SERVER_ADDRESS, &platform_conf, &callbacks);
if (err != NMBS_ERROR_NONE) {
onError();
}

nmbs_set_read_timeout(&nmbs, 1000);
nmbs_set_byte_timeout(&nmbs, 100);
}

void nanomb_task(void){
char fakedata[8]={0x01, 0x01, 0x00, 0x00, 0x00, 0x08, 0x3D, 0xCC};
fake_data(&fakedata[0],8);
nmbs_server_poll(&nmbs);
}
`

Doubts regarding the TCP connection in library

Hi, I am trying to find a Modbus IP library to fork it in my project SMT32. I also see that this project could work in client/server side and with RTU/IP mode, so I think it could be a good match. For me understanding, I could see that there is needed a my_connect_tcp, this function may be implemented by myself, is it correct?, maybe I can use LWIP stack or did you implemented it in your code?. Please help me to understand your library.

// Set up the TCP connection
void* conn = my_connect_tcp(argv[1], argv[2]);
if (!conn) {
fprintf(stderr, "Error connecting to server\n");
return 1;
}

// my_transport_read() and my_transport_write() are implemented by the user 
nmbs_platform_conf platform_conf;
platform_conf.transport = NMBS_TRANSPORT_TCP;
platform_conf.read = my_transport_read;
platform_conf.write = my_transport_write;
platform_conf.arg = conn;    // Passing our TCP connection handle to the read/write functions

// Create the modbus client
nmbs_t nmbs;
nmbs_error err = nmbs_client_create(&nmbs, &platform_conf);
if (err != NMBS_ERROR_NONE) {
    fprintf(stderr, "Error creating modbus client\n");
    return 1;
}

Thak you so much.

Looking formward to your response,

Regards,
Aitor

How are non-contiguous registers managed if read with FC03?

I have a map of non-contiguous registers, I took a superficial look at the code, but I did not found if and how it's managed if the client requests a range that include some of my available registers, but also some of those that are unavailable.
Let say that I choose to provide holding registers 1-10 and 21-30 for a total of 20 holding registers.
What happens if the client sends a FC03 starting at register 5 for 10 consecutive registers?

Configuration define error

v1.17.1

Hi,
There is an error on a configuration define in the handle_req_fc function, line 1763.
Currently:

#ifndef NMBS_SERVER_WRITE_MULTIPLE_COILS_DISABLED

Should be:

#ifndef NMBS_SERVER_READ_COILS_DISABLED

Regards,
Alain.

nanoMODBUS Server Support for Multiple Clients

Hello! I'm trying to use the nanoMODBUS for a server. I'm not sure how to use the code to support multiple clients. I reviewed the Linux example. It appears to poll the clients one at a time. This can create long delays when a client is slow or drops off. This will cause problems in my application. Is there a way for each client connection to have its own thread so they don't have to wait for other clients? Thanks!

Recover from link drop

Hello,

We have successfully merged nanoMODBUS into our STM32F4 project (using FreeRTOS and lwIP). However, sometime when the link drops momentarily nanoMODBUS does not recover. I have code that checks the PHY to see if the link is active, if not, it sets the netif down then the link and when it goes back active it sets them back up. Is there something that should be done when this happens to restore nanoMODBUS operation?

Mike.

Example for Arduino

Extend the examples folder with a RTU client-server, with Arduino as platform

Using NMBS_SERVER_DISABLED

I’ve written a Modbus RTU client in MacOS that works well. As a Modbus newbie I started experimenting with the compiler flags and found if I set NMBS_SERVER_DISABLED, this stopped nmbs_read_holding_registers being available. That confused me as I thought that would be a client function?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.