slave.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. /*
  2. * SPDX-FileCopyrightText: 2016-2023 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. // FreeModbus Slave Example ESP32
  7. #include <stdio.h>
  8. #include <stdint.h>
  9. #include "esp_err.h"
  10. #include "mbcontroller.h" // for mbcontroller defines and api
  11. #include "modbus_params.h" // for modbus parameters structures
  12. #include "esp_log.h" // for log_write
  13. #include "sdkconfig.h"
  14. #define MB_PORT_NUM (CONFIG_MB_UART_PORT_NUM) // Number of UART port used for Modbus connection
  15. #define MB_SLAVE_ADDR (CONFIG_MB_SLAVE_ADDR) // The address of device in Modbus network
  16. #define MB_DEV_SPEED (CONFIG_MB_UART_BAUD_RATE) // The communication speed of the UART
  17. // Note: Some pins on target chip cannot be assigned for UART communication.
  18. // Please refer to documentation for selected board and target to configure pins using Kconfig.
  19. // Defines below are used to define register start address for each type of Modbus registers
  20. #define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) >> 1))
  21. #define INPUT_OFFSET(field) ((uint16_t)(offsetof(input_reg_params_t, field) >> 1))
  22. #define MB_REG_DISCRETE_INPUT_START (0x0000)
  23. #define MB_REG_COILS_START (0x0000)
  24. #define MB_REG_INPUT_START_AREA0 (INPUT_OFFSET(input_data0)) // register offset input area 0
  25. #define MB_REG_INPUT_START_AREA1 (INPUT_OFFSET(input_data4)) // register offset input area 1
  26. #define MB_REG_HOLDING_START_AREA0 (HOLD_OFFSET(holding_data0))
  27. #define MB_REG_HOLDING_START_AREA1 (HOLD_OFFSET(holding_data4))
  28. #define MB_PAR_INFO_GET_TOUT (10) // Timeout for get parameter info
  29. #define MB_CHAN_DATA_MAX_VAL (6)
  30. #define MB_CHAN_DATA_OFFSET (0.2f)
  31. #define MB_READ_MASK (MB_EVENT_INPUT_REG_RD \
  32. | MB_EVENT_HOLDING_REG_RD \
  33. | MB_EVENT_DISCRETE_RD \
  34. | MB_EVENT_COILS_RD)
  35. #define MB_WRITE_MASK (MB_EVENT_HOLDING_REG_WR \
  36. | MB_EVENT_COILS_WR)
  37. #define MB_READ_WRITE_MASK (MB_READ_MASK | MB_WRITE_MASK)
  38. static const char *TAG = "SLAVE_TEST";
  39. static portMUX_TYPE param_lock = portMUX_INITIALIZER_UNLOCKED;
  40. // Set register values into known state
  41. static void setup_reg_data(void)
  42. {
  43. // Define initial state of parameters
  44. discrete_reg_params.discrete_input0 = 1;
  45. discrete_reg_params.discrete_input1 = 0;
  46. discrete_reg_params.discrete_input2 = 1;
  47. discrete_reg_params.discrete_input3 = 0;
  48. discrete_reg_params.discrete_input4 = 1;
  49. discrete_reg_params.discrete_input5 = 0;
  50. discrete_reg_params.discrete_input6 = 1;
  51. discrete_reg_params.discrete_input7 = 0;
  52. holding_reg_params.holding_data0 = 1.34;
  53. holding_reg_params.holding_data1 = 2.56;
  54. holding_reg_params.holding_data2 = 3.78;
  55. holding_reg_params.holding_data3 = 4.90;
  56. holding_reg_params.holding_data4 = 5.67;
  57. holding_reg_params.holding_data5 = 6.78;
  58. holding_reg_params.holding_data6 = 7.79;
  59. holding_reg_params.holding_data7 = 8.80;
  60. coil_reg_params.coils_port0 = 0x55;
  61. coil_reg_params.coils_port1 = 0xAA;
  62. input_reg_params.input_data0 = 1.12;
  63. input_reg_params.input_data1 = 2.34;
  64. input_reg_params.input_data2 = 3.56;
  65. input_reg_params.input_data3 = 4.78;
  66. input_reg_params.input_data4 = 1.12;
  67. input_reg_params.input_data5 = 2.34;
  68. input_reg_params.input_data6 = 3.56;
  69. input_reg_params.input_data7 = 4.78;
  70. }
  71. // An example application of Modbus slave. It is based on freemodbus stack.
  72. // See deviceparams.h file for more information about assigned Modbus parameters.
  73. // These parameters can be accessed from main application and also can be changed
  74. // by external Modbus master host.
  75. void app_main(void)
  76. {
  77. mb_param_info_t reg_info; // keeps the Modbus registers access information
  78. mb_communication_info_t comm_info; // Modbus communication parameters
  79. mb_register_area_descriptor_t reg_area; // Modbus register area descriptor structure
  80. // Set UART log level
  81. esp_log_level_set(TAG, ESP_LOG_INFO);
  82. void* mbc_slave_handler = NULL;
  83. ESP_ERROR_CHECK(mbc_slave_init(MB_PORT_SERIAL_SLAVE, &mbc_slave_handler)); // Initialization of Modbus controller
  84. // Setup communication parameters and start stack
  85. #if CONFIG_MB_COMM_MODE_ASCII
  86. comm_info.mode = MB_MODE_ASCII,
  87. #elif CONFIG_MB_COMM_MODE_RTU
  88. comm_info.mode = MB_MODE_RTU,
  89. #endif
  90. comm_info.slave_addr = MB_SLAVE_ADDR;
  91. comm_info.port = MB_PORT_NUM;
  92. comm_info.baudrate = MB_DEV_SPEED;
  93. comm_info.parity = MB_PARITY_NONE;
  94. ESP_ERROR_CHECK(mbc_slave_setup((void*)&comm_info));
  95. // The code below initializes Modbus register area descriptors
  96. // for Modbus Holding Registers, Input Registers, Coils and Discrete Inputs
  97. // Initialization should be done for each supported Modbus register area according to register map.
  98. // When external master trying to access the register in the area that is not initialized
  99. // by mbc_slave_set_descriptor() API call then Modbus stack
  100. // will send exception response for this register area.
  101. reg_area.type = MB_PARAM_HOLDING; // Set type of register area
  102. reg_area.start_offset = MB_REG_HOLDING_START_AREA0; // Offset of register area in Modbus protocol
  103. reg_area.address = (void*)&holding_reg_params.holding_data0; // Set pointer to storage instance
  104. // Set the size of register storage instance = 150 holding registers
  105. reg_area.size = (size_t)(HOLD_OFFSET(holding_data4) - HOLD_OFFSET(test_regs));
  106. ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
  107. reg_area.type = MB_PARAM_HOLDING; // Set type of register area
  108. reg_area.start_offset = MB_REG_HOLDING_START_AREA1; // Offset of register area in Modbus protocol
  109. reg_area.address = (void*)&holding_reg_params.holding_data4; // Set pointer to storage instance
  110. reg_area.size = sizeof(float) << 2; // Set the size of register storage instance
  111. ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
  112. // Initialization of Input Registers area
  113. reg_area.type = MB_PARAM_INPUT;
  114. reg_area.start_offset = MB_REG_INPUT_START_AREA0;
  115. reg_area.address = (void*)&input_reg_params.input_data0;
  116. reg_area.size = sizeof(float) << 2;
  117. ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
  118. reg_area.type = MB_PARAM_INPUT;
  119. reg_area.start_offset = MB_REG_INPUT_START_AREA1;
  120. reg_area.address = (void*)&input_reg_params.input_data4;
  121. reg_area.size = sizeof(float) << 2;
  122. ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
  123. // Initialization of Coils register area
  124. reg_area.type = MB_PARAM_COIL;
  125. reg_area.start_offset = MB_REG_COILS_START;
  126. reg_area.address = (void*)&coil_reg_params;
  127. reg_area.size = sizeof(coil_reg_params);
  128. ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
  129. // Initialization of Discrete Inputs register area
  130. reg_area.type = MB_PARAM_DISCRETE;
  131. reg_area.start_offset = MB_REG_DISCRETE_INPUT_START;
  132. reg_area.address = (void*)&discrete_reg_params;
  133. reg_area.size = sizeof(discrete_reg_params);
  134. ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
  135. setup_reg_data(); // Set values into known state
  136. // Starts of modbus controller and stack
  137. ESP_ERROR_CHECK(mbc_slave_start());
  138. // Set UART pin numbers
  139. ESP_ERROR_CHECK(uart_set_pin(MB_PORT_NUM, CONFIG_MB_UART_TXD,
  140. CONFIG_MB_UART_RXD, CONFIG_MB_UART_RTS,
  141. UART_PIN_NO_CHANGE));
  142. // Set UART driver mode to Half Duplex
  143. ESP_ERROR_CHECK(uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX));
  144. ESP_LOGI(TAG, "Modbus slave stack initialized.");
  145. ESP_LOGI(TAG, "Start modbus test...");
  146. // The cycle below will be terminated when parameter holdingRegParams.dataChan0
  147. // incremented each access cycle reaches the CHAN_DATA_MAX_VAL value.
  148. for(;holding_reg_params.holding_data0 < MB_CHAN_DATA_MAX_VAL;) {
  149. // Check for read/write events of Modbus master for certain events
  150. mb_event_group_t event = mbc_slave_check_event(MB_READ_WRITE_MASK);
  151. const char* rw_str = (event & MB_READ_MASK) ? "READ" : "WRITE";
  152. // Filter events and process them accordingly
  153. if(event & (MB_EVENT_HOLDING_REG_WR | MB_EVENT_HOLDING_REG_RD)) {
  154. // Get parameter information from parameter queue
  155. ESP_ERROR_CHECK(mbc_slave_get_param_info(&reg_info, MB_PAR_INFO_GET_TOUT));
  156. ESP_LOGI(TAG, "HOLDING %s (%" PRIu32 " us), ADDR:%u, TYPE:%u, INST_ADDR:0x%" PRIx32 ", SIZE:%u",
  157. rw_str,
  158. reg_info.time_stamp,
  159. (unsigned)reg_info.mb_offset,
  160. (unsigned)reg_info.type,
  161. (uint32_t)reg_info.address,
  162. (unsigned)reg_info.size);
  163. if (reg_info.address == (uint8_t*)&holding_reg_params.holding_data0)
  164. {
  165. portENTER_CRITICAL(&param_lock);
  166. holding_reg_params.holding_data0 += MB_CHAN_DATA_OFFSET;
  167. if (holding_reg_params.holding_data0 >= (MB_CHAN_DATA_MAX_VAL - MB_CHAN_DATA_OFFSET)) {
  168. coil_reg_params.coils_port1 = 0xFF;
  169. }
  170. portEXIT_CRITICAL(&param_lock);
  171. }
  172. } else if (event & MB_EVENT_INPUT_REG_RD) {
  173. ESP_ERROR_CHECK(mbc_slave_get_param_info(&reg_info, MB_PAR_INFO_GET_TOUT));
  174. ESP_LOGI(TAG, "INPUT READ (%" PRIu32 " us), ADDR:%u, TYPE:%u, INST_ADDR:0x%" PRIx32 ", SIZE:%u",
  175. reg_info.time_stamp,
  176. (unsigned)reg_info.mb_offset,
  177. (unsigned)reg_info.type,
  178. (uint32_t)reg_info.address,
  179. (unsigned)reg_info.size);
  180. } else if (event & MB_EVENT_DISCRETE_RD) {
  181. ESP_ERROR_CHECK(mbc_slave_get_param_info(&reg_info, MB_PAR_INFO_GET_TOUT));
  182. ESP_LOGI(TAG, "DISCRETE READ (%" PRIu32 " us): ADDR:%u, TYPE:%u, INST_ADDR:0x%" PRIx32 ", SIZE:%u",
  183. reg_info.time_stamp,
  184. (unsigned)reg_info.mb_offset,
  185. (unsigned)reg_info.type,
  186. (uint32_t)reg_info.address,
  187. (unsigned)reg_info.size);
  188. } else if (event & (MB_EVENT_COILS_RD | MB_EVENT_COILS_WR)) {
  189. ESP_ERROR_CHECK(mbc_slave_get_param_info(&reg_info, MB_PAR_INFO_GET_TOUT));
  190. ESP_LOGI(TAG, "COILS %s (%" PRIu32 " us), ADDR:%u, TYPE:%u, INST_ADDR:0x%" PRIx32 ", SIZE:%u",
  191. rw_str,
  192. reg_info.time_stamp,
  193. (unsigned)reg_info.mb_offset,
  194. (unsigned)reg_info.type,
  195. (uint32_t)reg_info.address,
  196. (unsigned)reg_info.size);
  197. if (coil_reg_params.coils_port1 == 0xFF) break;
  198. }
  199. }
  200. // Destroy of Modbus controller on alarm
  201. ESP_LOGI(TAG,"Modbus controller destroyed.");
  202. vTaskDelay(100);
  203. ESP_ERROR_CHECK(mbc_slave_destroy());
  204. }