master.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  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. // Note: Some pins on target chip cannot be assigned for UART communication.
  22. // See UART documentation for selected board and target to configure pins using Kconfig.
  23. // The number of parameters that intended to be used in the particular control process
  24. #define MASTER_MAX_CIDS num_device_parameters
  25. // Number of reading of parameters from slave
  26. #define MASTER_MAX_RETRY 30
  27. // Timeout to update cid over Modbus
  28. #define UPDATE_CIDS_TIMEOUT_MS (500)
  29. #define UPDATE_CIDS_TIMEOUT_TICS (UPDATE_CIDS_TIMEOUT_MS / portTICK_RATE_MS)
  30. // Timeout between polls
  31. #define POLL_TIMEOUT_MS (1)
  32. #define POLL_TIMEOUT_TICS (POLL_TIMEOUT_MS / portTICK_RATE_MS)
  33. // The macro to get offset for parameter in the appropriate structure
  34. #define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) + 1))
  35. #define INPUT_OFFSET(field) ((uint16_t)(offsetof(input_reg_params_t, field) + 1))
  36. #define COIL_OFFSET(field) ((uint16_t)(offsetof(coil_reg_params_t, field) + 1))
  37. // Discrete offset macro
  38. #define DISCR_OFFSET(field) ((uint16_t)(offsetof(discrete_reg_params_t, field) + 1))
  39. #define STR(fieldname) ((const char*)( fieldname ))
  40. // Options can be used as bit masks or parameter limits
  41. #define OPTS(min_val, max_val, step_val) { .opt1 = min_val, .opt2 = max_val, .opt3 = step_val }
  42. static const char *TAG = "MASTER_TEST";
  43. // Enumeration of modbus device addresses accessed by master device
  44. enum {
  45. MB_DEVICE_ADDR1 = 1 // Only one slave device used for the test (add other slave addresses here)
  46. };
  47. // Enumeration of all supported CIDs for device (used in parameter definition table)
  48. enum {
  49. CID_INP_DATA_0 = 0,
  50. CID_HOLD_DATA_0,
  51. CID_INP_DATA_1,
  52. CID_HOLD_DATA_1,
  53. CID_INP_DATA_2,
  54. CID_HOLD_DATA_2,
  55. CID_HOLD_TEST_REG,
  56. CID_RELAY_P1,
  57. CID_RELAY_P2,
  58. CID_COUNT
  59. };
  60. // Example Data (Object) Dictionary for Modbus parameters:
  61. // The CID field in the table must be unique.
  62. // Modbus Slave Addr field defines slave address of the device with correspond parameter.
  63. // Modbus Reg Type - Type of Modbus register area (Holding register, Input Register and such).
  64. // Reg Start field defines the start Modbus register number and Reg Size defines the number of registers for the characteristic accordingly.
  65. // The Instance Offset defines offset in the appropriate parameter structure that will be used as instance to save parameter value.
  66. // Data Type, Data Size specify type of the characteristic and its data size.
  67. // Parameter Options field specifies the options that can be used to process parameter value (limits or masks).
  68. // Access Mode - can be used to implement custom options for processing of characteristic (Read/Write restrictions, factory mode values and etc).
  69. const mb_parameter_descriptor_t device_parameters[] = {
  70. // { CID, Param Name, Units, Modbus Slave Addr, Modbus Reg Type, Reg Start, Reg Size, Instance Offset, Data Type, Data Size, Parameter Options, Access Mode}
  71. { CID_INP_DATA_0, STR("Data_channel_0"), STR("Volts"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0, 2,
  72. INPUT_OFFSET(input_data0), PARAM_TYPE_FLOAT, 4, OPTS( -10, 10, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
  73. { CID_HOLD_DATA_0, STR("Humidity_1"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0, 2,
  74. HOLD_OFFSET(holding_data0), PARAM_TYPE_FLOAT, 4, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
  75. { CID_INP_DATA_1, STR("Temperature_1"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 2, 2,
  76. INPUT_OFFSET(input_data1), PARAM_TYPE_FLOAT, 4, OPTS( -40, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
  77. { CID_HOLD_DATA_1, STR("Humidity_2"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 2, 2,
  78. HOLD_OFFSET(holding_data1), PARAM_TYPE_FLOAT, 4, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
  79. { CID_INP_DATA_2, STR("Temperature_2"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 4, 2,
  80. INPUT_OFFSET(input_data2), PARAM_TYPE_FLOAT, 4, OPTS( -40, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
  81. { CID_HOLD_DATA_2, STR("Humidity_3"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 4, 2,
  82. HOLD_OFFSET(holding_data2), PARAM_TYPE_FLOAT, 4, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
  83. { CID_HOLD_TEST_REG, STR("Test_regs"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 10, 58,
  84. HOLD_OFFSET(test_regs), PARAM_TYPE_ASCII, 116, 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(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(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. if ((param_descriptor->param_type == PARAM_TYPE_ASCII) &&
  143. (param_descriptor->cid == CID_HOLD_TEST_REG)) {
  144. // Check for long array of registers of type PARAM_TYPE_ASCII
  145. err = mbc_master_get_parameter(cid, (char*)param_descriptor->param_key,
  146. (uint8_t*)temp_data_ptr, &type);
  147. if (err == ESP_OK) {
  148. ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%08x) read successful.",
  149. param_descriptor->cid,
  150. (char*)param_descriptor->param_key,
  151. (char*)param_descriptor->param_units,
  152. *(uint32_t*)temp_data_ptr);
  153. // Initialize data of test array and write to slave
  154. if (*(uint32_t*)temp_data_ptr != 0xAAAAAAAA) {
  155. memset((void*)temp_data_ptr, 0xAA, param_descriptor->param_size);
  156. *(uint32_t*)temp_data_ptr = 0xAAAAAAAA;
  157. err = mbc_master_set_parameter(cid, (char*)param_descriptor->param_key,
  158. (uint8_t*)temp_data_ptr, &type);
  159. if (err == ESP_OK) {
  160. ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%08x), write successful.",
  161. param_descriptor->cid,
  162. (char*)param_descriptor->param_key,
  163. (char*)param_descriptor->param_units,
  164. *(uint32_t*)temp_data_ptr);
  165. } else {
  166. ESP_LOGE(TAG, "Characteristic #%d (%s) write fail, err = 0x%x (%s).",
  167. param_descriptor->cid,
  168. (char*)param_descriptor->param_key,
  169. (int)err,
  170. (char*)esp_err_to_name(err));
  171. }
  172. }
  173. } else {
  174. ESP_LOGE(TAG, "Characteristic #%d (%s) read fail, err = 0x%x (%s).",
  175. param_descriptor->cid,
  176. (char*)param_descriptor->param_key,
  177. (int)err,
  178. (char*)esp_err_to_name(err));
  179. }
  180. } else {
  181. err = mbc_master_get_parameter(cid, (char*)param_descriptor->param_key,
  182. (uint8_t*)&value, &type);
  183. if (err == ESP_OK) {
  184. *(float*)temp_data_ptr = value;
  185. if ((param_descriptor->mb_param_type == MB_PARAM_HOLDING) ||
  186. (param_descriptor->mb_param_type == MB_PARAM_INPUT)) {
  187. ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %f (0x%x) read successful.",
  188. param_descriptor->cid,
  189. (char*)param_descriptor->param_key,
  190. (char*)param_descriptor->param_units,
  191. value,
  192. *(uint32_t*)temp_data_ptr);
  193. if (((value > param_descriptor->param_opts.max) ||
  194. (value < param_descriptor->param_opts.min))) {
  195. alarm_state = true;
  196. break;
  197. }
  198. } else {
  199. uint16_t state = *(uint16_t*)temp_data_ptr;
  200. const char* rw_str = (state & param_descriptor->param_opts.opt1) ? "ON" : "OFF";
  201. ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %s (0x%x) read successful.",
  202. param_descriptor->cid,
  203. (char*)param_descriptor->param_key,
  204. (char*)param_descriptor->param_units,
  205. (const char*)rw_str,
  206. *(uint16_t*)temp_data_ptr);
  207. if (state & param_descriptor->param_opts.opt1) {
  208. alarm_state = true;
  209. break;
  210. }
  211. }
  212. } else {
  213. ESP_LOGE(TAG, "Characteristic #%d (%s) read fail, err = 0x%x (%s).",
  214. param_descriptor->cid,
  215. (char*)param_descriptor->param_key,
  216. (int)err,
  217. (char*)esp_err_to_name(err));
  218. }
  219. }
  220. vTaskDelay(POLL_TIMEOUT_TICS); // timeout between polls
  221. }
  222. }
  223. vTaskDelay(UPDATE_CIDS_TIMEOUT_TICS); //
  224. }
  225. if (alarm_state) {
  226. ESP_LOGI(TAG, "Alarm triggered by cid #%d.",
  227. param_descriptor->cid);
  228. } else {
  229. ESP_LOGE(TAG, "Alarm is not triggered after %d retries.",
  230. MASTER_MAX_RETRY);
  231. }
  232. ESP_LOGI(TAG, "Destroy master...");
  233. ESP_ERROR_CHECK(mbc_master_destroy());
  234. }
  235. // Modbus master initialization
  236. static esp_err_t master_init(void)
  237. {
  238. // Initialize and start Modbus controller
  239. mb_communication_info_t comm = {
  240. .port = MB_PORT_NUM,
  241. #if CONFIG_MB_COMM_MODE_ASCII
  242. .mode = MB_MODE_ASCII,
  243. #elif CONFIG_MB_COMM_MODE_RTU
  244. .mode = MB_MODE_RTU,
  245. #endif
  246. .baudrate = MB_DEV_SPEED,
  247. .parity = MB_PARITY_NONE
  248. };
  249. void* master_handler = NULL;
  250. esp_err_t err = mbc_master_init(MB_PORT_SERIAL_MASTER, &master_handler);
  251. MB_RETURN_ON_FALSE((master_handler != NULL), ESP_ERR_INVALID_STATE, TAG,
  252. "mb controller initialization fail.");
  253. MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
  254. "mb controller initialization fail, returns(0x%x).",
  255. (uint32_t)err);
  256. err = mbc_master_setup((void*)&comm);
  257. MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
  258. "mb controller setup fail, returns(0x%x).",
  259. (uint32_t)err);
  260. // Set UART pin numbers
  261. err = uart_set_pin(MB_PORT_NUM, CONFIG_MB_UART_TXD, CONFIG_MB_UART_RXD,
  262. CONFIG_MB_UART_RTS, UART_PIN_NO_CHANGE);
  263. err = mbc_master_start();
  264. MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
  265. "mb controller start fail, returns(0x%x).",
  266. (uint32_t)err);
  267. MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
  268. "mb serial set pin failure, uart_set_pin() returned (0x%x).", (uint32_t)err);
  269. // Set driver mode to Half Duplex
  270. err = uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX);
  271. MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
  272. "mb serial set mode failure, uart_set_mode() returned (0x%x).", (uint32_t)err);
  273. vTaskDelay(5);
  274. err = mbc_master_set_descriptor(&device_parameters[0], num_device_parameters);
  275. MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
  276. "mb controller set descriptor fail, returns(0x%x).",
  277. (uint32_t)err);
  278. ESP_LOGI(TAG, "Modbus master stack initialized...");
  279. return err;
  280. }
  281. void app_main(void)
  282. {
  283. // Initialization of device peripheral and objects
  284. ESP_ERROR_CHECK(master_init());
  285. vTaskDelay(10);
  286. master_operation_func(NULL);
  287. }