slave.c 11 KB

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