master.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. /*
  2. * SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include "string.h"
  7. #include "esp_log.h"
  8. #include "modbus_params.h" // for modbus parameters structures
  9. #include "mbcontroller.h"
  10. #include "sdkconfig.h"
  11. #define MB_PORT_NUM (CONFIG_MB_UART_PORT_NUM) // Number of UART port used for Modbus connection
  12. #define MB_DEV_SPEED (CONFIG_MB_UART_BAUD_RATE) // The communication speed of the UART
  13. // Note: Some pins on target chip cannot be assigned for UART communication.
  14. // See UART documentation for selected board and target to configure pins using Kconfig.
  15. // The number of parameters that intended to be used in the particular control process
  16. #define MASTER_MAX_CIDS num_device_parameters
  17. // Number of reading of parameters from slave
  18. #define MASTER_MAX_RETRY 30
  19. // Timeout to update cid over Modbus
  20. #define UPDATE_CIDS_TIMEOUT_MS (500)
  21. #define UPDATE_CIDS_TIMEOUT_TICS (UPDATE_CIDS_TIMEOUT_MS / portTICK_RATE_MS)
  22. // Timeout between polls
  23. #define POLL_TIMEOUT_MS (1)
  24. #define POLL_TIMEOUT_TICS (POLL_TIMEOUT_MS / portTICK_RATE_MS)
  25. #define MASTER_TAG "MASTER_TEST"
  26. #define MASTER_CHECK(a, ret_val, str, ...) \
  27. if (!(a)) { \
  28. ESP_LOGE(MASTER_TAG, "%s(%u): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
  29. return (ret_val); \
  30. }
  31. // The macro to get offset for parameter in the appropriate structure
  32. #define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) + 1))
  33. #define INPUT_OFFSET(field) ((uint16_t)(offsetof(input_reg_params_t, field) + 1))
  34. #define COIL_OFFSET(field) ((uint16_t)(offsetof(coil_reg_params_t, field) + 1))
  35. // Discrete offset macro
  36. #define DISCR_OFFSET(field) ((uint16_t)(offsetof(discrete_reg_params_t, field) + 1))
  37. #define STR(fieldname) ((const char*)( fieldname ))
  38. // Options can be used as bit masks or parameter limits
  39. #define OPTS(min_val, max_val, step_val) { .opt1 = min_val, .opt2 = max_val, .opt3 = step_val }
  40. // Enumeration of modbus device addresses accessed by master device
  41. enum {
  42. MB_DEVICE_ADDR1 = 1 // Only one slave device used for the test (add other slave addresses here)
  43. };
  44. // Enumeration of all supported CIDs for device (used in parameter definition table)
  45. enum {
  46. CID_INP_DATA_0 = 0,
  47. CID_HOLD_DATA_0,
  48. CID_INP_DATA_1,
  49. CID_HOLD_DATA_1,
  50. CID_INP_DATA_2,
  51. CID_HOLD_DATA_2,
  52. CID_HOLD_TEST_REG,
  53. CID_RELAY_P1,
  54. CID_RELAY_P2,
  55. CID_COUNT
  56. };
  57. // Example Data (Object) Dictionary for Modbus parameters:
  58. // The CID field in the table must be unique.
  59. // Modbus Slave Addr field defines slave address of the device with correspond parameter.
  60. // Modbus Reg Type - Type of Modbus register area (Holding register, Input Register and such).
  61. // Reg Start field defines the start Modbus register number and Reg Size defines the number of registers for the characteristic accordingly.
  62. // The Instance Offset defines offset in the appropriate parameter structure that will be used as instance to save parameter value.
  63. // Data Type, Data Size specify type of the characteristic and its data size.
  64. // Parameter Options field specifies the options that can be used to process parameter value (limits or masks).
  65. // Access Mode - can be used to implement custom options for processing of characteristic (Read/Write restrictions, factory mode values and etc).
  66. const mb_parameter_descriptor_t device_parameters[] = {
  67. // { CID, Param Name, Units, Modbus Slave Addr, Modbus Reg Type, Reg Start, Reg Size, Instance Offset, Data Type, Data Size, Parameter Options, Access Mode}
  68. { CID_INP_DATA_0, STR("Data_channel_0"), STR("Volts"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0, 2,
  69. INPUT_OFFSET(input_data0), PARAM_TYPE_FLOAT, 4, OPTS( -10, 10, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
  70. { CID_HOLD_DATA_0, STR("Humidity_1"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0, 2,
  71. HOLD_OFFSET(holding_data0), PARAM_TYPE_FLOAT, 4, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
  72. { CID_INP_DATA_1, STR("Temperature_1"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 2, 2,
  73. INPUT_OFFSET(input_data1), PARAM_TYPE_FLOAT, 4, OPTS( -40, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
  74. { CID_HOLD_DATA_1, STR("Humidity_2"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 2, 2,
  75. HOLD_OFFSET(holding_data1), PARAM_TYPE_FLOAT, 4, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
  76. { CID_INP_DATA_2, STR("Temperature_2"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 4, 2,
  77. INPUT_OFFSET(input_data2), PARAM_TYPE_FLOAT, 4, OPTS( -40, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
  78. { CID_HOLD_DATA_2, STR("Humidity_3"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 4, 2,
  79. HOLD_OFFSET(holding_data2), PARAM_TYPE_FLOAT, 4, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
  80. { CID_HOLD_TEST_REG, STR("Test_regs"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 10, 58,
  81. HOLD_OFFSET(test_regs), PARAM_TYPE_ASCII, 116, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
  82. { CID_RELAY_P1, STR("RelayP1"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_COIL, 0, 8,
  83. COIL_OFFSET(coils_port0), PARAM_TYPE_U16, 2, OPTS( BIT1, 0, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
  84. { CID_RELAY_P2, STR("RelayP2"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_COIL, 8, 8,
  85. COIL_OFFSET(coils_port1), PARAM_TYPE_U16, 2, OPTS( BIT0, 0, 0 ), PAR_PERMS_READ_WRITE_TRIGGER }
  86. };
  87. // Calculate number of parameters in the table
  88. const uint16_t num_device_parameters = (sizeof(device_parameters)/sizeof(device_parameters[0]));
  89. // The function to get pointer to parameter storage (instance) according to parameter description table
  90. static void* master_get_param_data(const mb_parameter_descriptor_t* param_descriptor)
  91. {
  92. assert(param_descriptor != NULL);
  93. void* instance_ptr = NULL;
  94. if (param_descriptor->param_offset != 0) {
  95. switch(param_descriptor->mb_param_type)
  96. {
  97. case MB_PARAM_HOLDING:
  98. instance_ptr = ((void*)&holding_reg_params + param_descriptor->param_offset - 1);
  99. break;
  100. case MB_PARAM_INPUT:
  101. instance_ptr = ((void*)&input_reg_params + param_descriptor->param_offset - 1);
  102. break;
  103. case MB_PARAM_COIL:
  104. instance_ptr = ((void*)&coil_reg_params + param_descriptor->param_offset - 1);
  105. break;
  106. case MB_PARAM_DISCRETE:
  107. instance_ptr = ((void*)&discrete_reg_params + param_descriptor->param_offset - 1);
  108. break;
  109. default:
  110. instance_ptr = NULL;
  111. break;
  112. }
  113. } else {
  114. ESP_LOGE(MASTER_TAG, "Wrong parameter offset for CID #%d", param_descriptor->cid);
  115. assert(instance_ptr != NULL);
  116. }
  117. return instance_ptr;
  118. }
  119. // User operation function to read slave values and check alarm
  120. static void master_operation_func(void *arg)
  121. {
  122. esp_err_t err = ESP_OK;
  123. float value = 0;
  124. bool alarm_state = false;
  125. const mb_parameter_descriptor_t* param_descriptor = NULL;
  126. ESP_LOGI(MASTER_TAG, "Start modbus test...");
  127. for(uint16_t retry = 0; retry <= MASTER_MAX_RETRY && (!alarm_state); retry++) {
  128. // Read all found characteristics from slave(s)
  129. for (uint16_t cid = 0; (err != ESP_ERR_NOT_FOUND) && cid < MASTER_MAX_CIDS; cid++)
  130. {
  131. // Get data from parameters description table
  132. // and use this information to fill the characteristics description table
  133. // and having all required fields in just one table
  134. err = mbc_master_get_cid_info(cid, &param_descriptor);
  135. if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) {
  136. void* temp_data_ptr = master_get_param_data(param_descriptor);
  137. assert(temp_data_ptr);
  138. uint8_t type = 0;
  139. if ((param_descriptor->param_type == PARAM_TYPE_ASCII) &&
  140. (param_descriptor->cid == CID_HOLD_TEST_REG)) {
  141. // Check for long array of registers of type PARAM_TYPE_ASCII
  142. err = mbc_master_get_parameter(cid, (char*)param_descriptor->param_key,
  143. (uint8_t*)temp_data_ptr, &type);
  144. if (err == ESP_OK) {
  145. ESP_LOGI(MASTER_TAG, "Characteristic #%d %s (%s) value = (0x%08x) read successful.",
  146. param_descriptor->cid,
  147. (char*)param_descriptor->param_key,
  148. (char*)param_descriptor->param_units,
  149. *(uint32_t*)temp_data_ptr);
  150. // Initialize data of test array and write to slave
  151. if (*(uint32_t*)temp_data_ptr != 0xAAAAAAAA) {
  152. memset((void*)temp_data_ptr, 0xAA, param_descriptor->param_size);
  153. *(uint32_t*)temp_data_ptr = 0xAAAAAAAA;
  154. err = mbc_master_set_parameter(cid, (char*)param_descriptor->param_key,
  155. (uint8_t*)temp_data_ptr, &type);
  156. if (err == ESP_OK) {
  157. ESP_LOGI(MASTER_TAG, "Characteristic #%d %s (%s) value = (0x%08x), write successful.",
  158. param_descriptor->cid,
  159. (char*)param_descriptor->param_key,
  160. (char*)param_descriptor->param_units,
  161. *(uint32_t*)temp_data_ptr);
  162. } else {
  163. ESP_LOGE(MASTER_TAG, "Characteristic #%d (%s) write fail, err = 0x%x (%s).",
  164. param_descriptor->cid,
  165. (char*)param_descriptor->param_key,
  166. (int)err,
  167. (char*)esp_err_to_name(err));
  168. }
  169. }
  170. } else {
  171. ESP_LOGE(MASTER_TAG, "Characteristic #%d (%s) read fail, err = 0x%x (%s).",
  172. param_descriptor->cid,
  173. (char*)param_descriptor->param_key,
  174. (int)err,
  175. (char*)esp_err_to_name(err));
  176. }
  177. } else {
  178. err = mbc_master_get_parameter(cid, (char*)param_descriptor->param_key,
  179. (uint8_t*)&value, &type);
  180. if (err == ESP_OK) {
  181. *(float*)temp_data_ptr = value;
  182. if ((param_descriptor->mb_param_type == MB_PARAM_HOLDING) ||
  183. (param_descriptor->mb_param_type == MB_PARAM_INPUT)) {
  184. ESP_LOGI(MASTER_TAG, "Characteristic #%d %s (%s) value = %f (0x%x) read successful.",
  185. param_descriptor->cid,
  186. (char*)param_descriptor->param_key,
  187. (char*)param_descriptor->param_units,
  188. value,
  189. *(uint32_t*)temp_data_ptr);
  190. if (((value > param_descriptor->param_opts.max) ||
  191. (value < param_descriptor->param_opts.min))) {
  192. alarm_state = true;
  193. break;
  194. }
  195. } else {
  196. uint16_t state = *(uint16_t*)temp_data_ptr;
  197. const char* rw_str = (state & param_descriptor->param_opts.opt1) ? "ON" : "OFF";
  198. ESP_LOGI(MASTER_TAG, "Characteristic #%d %s (%s) value = %s (0x%x) read successful.",
  199. param_descriptor->cid,
  200. (char*)param_descriptor->param_key,
  201. (char*)param_descriptor->param_units,
  202. (const char*)rw_str,
  203. *(uint16_t*)temp_data_ptr);
  204. if (state & param_descriptor->param_opts.opt1) {
  205. alarm_state = true;
  206. break;
  207. }
  208. }
  209. } else {
  210. ESP_LOGE(MASTER_TAG, "Characteristic #%d (%s) read fail, err = 0x%x (%s).",
  211. param_descriptor->cid,
  212. (char*)param_descriptor->param_key,
  213. (int)err,
  214. (char*)esp_err_to_name(err));
  215. }
  216. }
  217. vTaskDelay(POLL_TIMEOUT_TICS); // timeout between polls
  218. }
  219. }
  220. vTaskDelay(UPDATE_CIDS_TIMEOUT_TICS); //
  221. }
  222. if (alarm_state) {
  223. ESP_LOGI(MASTER_TAG, "Alarm triggered by cid #%d.",
  224. param_descriptor->cid);
  225. } else {
  226. ESP_LOGE(MASTER_TAG, "Alarm is not triggered after %d retries.",
  227. MASTER_MAX_RETRY);
  228. }
  229. ESP_LOGI(MASTER_TAG, "Destroy master...");
  230. ESP_ERROR_CHECK(mbc_master_destroy());
  231. }
  232. // Modbus master initialization
  233. static esp_err_t master_init(void)
  234. {
  235. // Initialize and start Modbus controller
  236. mb_communication_info_t comm = {
  237. .port = MB_PORT_NUM,
  238. #if CONFIG_MB_COMM_MODE_ASCII
  239. .mode = MB_MODE_ASCII,
  240. #elif CONFIG_MB_COMM_MODE_RTU
  241. .mode = MB_MODE_RTU,
  242. #endif
  243. .baudrate = MB_DEV_SPEED,
  244. .parity = MB_PARITY_NONE
  245. };
  246. void* master_handler = NULL;
  247. esp_err_t err = mbc_master_init(MB_PORT_SERIAL_MASTER, &master_handler);
  248. MASTER_CHECK((master_handler != NULL), ESP_ERR_INVALID_STATE,
  249. "mb controller initialization fail.");
  250. MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
  251. "mb controller initialization fail, returns(0x%x).",
  252. (uint32_t)err);
  253. err = mbc_master_setup((void*)&comm);
  254. MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
  255. "mb controller setup fail, returns(0x%x).",
  256. (uint32_t)err);
  257. // Set UART pin numbers
  258. err = uart_set_pin(MB_PORT_NUM, CONFIG_MB_UART_TXD, CONFIG_MB_UART_RXD,
  259. CONFIG_MB_UART_RTS, UART_PIN_NO_CHANGE);
  260. err = mbc_master_start();
  261. MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
  262. "mb controller start fail, returns(0x%x).",
  263. (uint32_t)err);
  264. MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
  265. "mb serial set pin failure, uart_set_pin() returned (0x%x).", (uint32_t)err);
  266. // Set driver mode to Half Duplex
  267. err = uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX);
  268. MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
  269. "mb serial set mode failure, uart_set_mode() returned (0x%x).", (uint32_t)err);
  270. vTaskDelay(5);
  271. err = mbc_master_set_descriptor(&device_parameters[0], num_device_parameters);
  272. MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
  273. "mb controller set descriptor fail, returns(0x%x).",
  274. (uint32_t)err);
  275. ESP_LOGI(MASTER_TAG, "Modbus master stack initialized...");
  276. return err;
  277. }
  278. void app_main(void)
  279. {
  280. // Initialization of device peripheral and objects
  281. ESP_ERROR_CHECK(master_init());
  282. vTaskDelay(10);
  283. master_operation_func(NULL);
  284. }