master.c 13 KB

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