1 /* Copyright 2018 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 */
15
16 // mbc_serial_master.c
17 // Serial master implementation of the Modbus controller
18
19 #include <sys/time.h> // for calculation of time stamp in milliseconds
20 #include "esp_log.h" // for log_write
21 #include <string.h> // for memcpy
22 #include "freertos/FreeRTOS.h" // for task creation and queue access
23 #include "freertos/task.h" // for task api access
24 #include "freertos/event_groups.h" // for event groups
25 #include "freertos/queue.h" // for queue api access
26 #include "mb_m.h" // for modbus stack master types definition
27 #include "port.h" // for port callback functions
28 #include "mbutils.h" // for mbutils functions definition for stack callback
29 #include "sdkconfig.h" // for KConfig values
30 #include "esp_modbus_common.h" // for common types
31 #include "esp_modbus_master.h" // for public master types
32 #include "mbc_master.h" // for private master types
33 #include "mbc_serial_master.h" // for serial master create function and types
34
35 // The Modbus Transmit Poll function defined in port
36 extern BOOL xMBMasterPortSerialTxPoll(void);
37
38 /*-----------------------Master mode use these variables----------------------*/
39 #define MB_RESPONSE_TICS pdMS_TO_TICKS(CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND + 10)
40
41
42 static mb_master_interface_t* mbm_interface_ptr = NULL; //&default_interface_inst;
43 static const char *TAG = "MB_CONTROLLER_MASTER";
44
45 // Modbus event processing task
modbus_master_task(void * pvParameters)46 static void modbus_master_task(void *pvParameters)
47 {
48 // The interface must be initialized before start of state machine
49 MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
50 mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
51 // Main Modbus stack processing cycle
52 for (;;) {
53 // Wait for poll events
54 BaseType_t status = xEventGroupWaitBits(mbm_opts->mbm_event_group,
55 (BaseType_t)(MB_EVENT_STACK_STARTED),
56 pdFALSE, // do not clear bits
57 pdFALSE,
58 portMAX_DELAY);
59 // Check if stack started then poll for data
60 if (status & MB_EVENT_STACK_STARTED) {
61 (void)eMBMasterPoll(); // Allow stack to process data
62 // Send response buffer if ready to be sent
63 BOOL xSentState = xMBMasterPortSerialTxPoll();
64 if (xSentState) {
65 // Let state machine know that request frame was transmitted out
66 (void)xMBMasterPortEventPost(EV_MASTER_FRAME_SENT);
67 }
68 }
69 }
70 }
71
72 // Setup Modbus controller parameters
mbc_serial_master_setup(void * comm_info)73 static esp_err_t mbc_serial_master_setup(void* comm_info)
74 {
75 MB_MASTER_CHECK((mbm_interface_ptr != NULL),
76 ESP_ERR_INVALID_STATE,
77 "Master interface uninitialized.");
78 mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
79
80 const mb_master_comm_info_t* comm_info_ptr = (mb_master_comm_info_t*)comm_info;
81 // Check communication options
82 MB_MASTER_CHECK(((comm_info_ptr->mode == MB_MODE_RTU) || (comm_info_ptr->mode == MB_MODE_ASCII)),
83 ESP_ERR_INVALID_ARG, "mb incorrect mode = (0x%x).",
84 (uint32_t)comm_info_ptr->mode);
85 MB_MASTER_CHECK((comm_info_ptr->port <= UART_NUM_MAX), ESP_ERR_INVALID_ARG,
86 "mb wrong port to set = (0x%x).", (uint32_t)comm_info_ptr->port);
87 MB_MASTER_CHECK((comm_info_ptr->parity <= UART_PARITY_ODD), ESP_ERR_INVALID_ARG,
88 "mb wrong parity option = (0x%x).", (uint32_t)comm_info_ptr->parity);
89 // Save the communication options
90 mbm_opts->mbm_comm = *(mb_communication_info_t*)comm_info_ptr;
91 return ESP_OK;
92 }
93
94 // Modbus controller stack start function
mbc_serial_master_start(void)95 static esp_err_t mbc_serial_master_start(void)
96 {
97 MB_MASTER_CHECK((mbm_interface_ptr != NULL),
98 ESP_ERR_INVALID_STATE,
99 "Master interface uninitialized.");
100 eMBErrorCode status = MB_EIO;
101 mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
102 const mb_communication_info_t* comm_info = (mb_communication_info_t*)&mbm_opts->mbm_comm;
103
104 // Initialize Modbus stack using mbcontroller parameters
105 status = eMBMasterSerialInit((eMBMode)comm_info->mode, (UCHAR)comm_info->port,
106 (ULONG)comm_info->baudrate,
107 MB_PORT_PARITY_GET(comm_info->parity));
108
109 MB_MASTER_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
110 "mb stack initialization failure, eMBInit() returns (0x%x).", status);
111 status = eMBMasterEnable();
112 MB_MASTER_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
113 "mb stack set slave ID failure, eMBEnable() returned (0x%x).", (uint32_t)status);
114 // Set the mbcontroller start flag
115 EventBits_t flag = xEventGroupSetBits(mbm_opts->mbm_event_group,
116 (EventBits_t)MB_EVENT_STACK_STARTED);
117 MB_MASTER_CHECK((flag & MB_EVENT_STACK_STARTED),
118 ESP_ERR_INVALID_STATE, "mb stack start event set error.");
119 return ESP_OK;
120 }
121
122 // Modbus controller destroy function
mbc_serial_master_destroy(void)123 static esp_err_t mbc_serial_master_destroy(void)
124 {
125 MB_MASTER_CHECK((mbm_interface_ptr != NULL),
126 ESP_ERR_INVALID_STATE,
127 "Master interface uninitialized.");
128 mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
129 eMBErrorCode mb_error = MB_ENOERR;
130 // Stop polling by clearing correspondent bit in the event group
131 EventBits_t flag = xEventGroupClearBits(mbm_opts->mbm_event_group,
132 (EventBits_t)MB_EVENT_STACK_STARTED);
133 MB_MASTER_CHECK((flag & MB_EVENT_STACK_STARTED),
134 ESP_ERR_INVALID_STATE, "mb stack stop event failure.");
135 // Desable and then destroy the Modbus stack
136 mb_error = eMBMasterDisable();
137 MB_MASTER_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack disable failure.");
138 (void)vTaskDelete(mbm_opts->mbm_task_handle);
139 (void)vEventGroupDelete(mbm_opts->mbm_event_group);
140 mb_error = eMBMasterClose();
141 MB_MASTER_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE,
142 "mb stack close failure returned (0x%x).", (uint32_t)mb_error);
143 free(mbm_interface_ptr); // free the memory allocated for options
144 vMBPortSetMode((UCHAR)MB_PORT_INACTIVE);
145 mbm_interface_ptr = NULL;
146 return ESP_OK;
147 }
148
149 // Set Modbus parameter description table
mbc_serial_master_set_descriptor(const mb_parameter_descriptor_t * descriptor,const uint16_t num_elements)150 static esp_err_t mbc_serial_master_set_descriptor(const mb_parameter_descriptor_t* descriptor, const uint16_t num_elements)
151 {
152 MB_MASTER_CHECK((descriptor != NULL),
153 ESP_ERR_INVALID_ARG, "mb incorrect descriptor.");
154 MB_MASTER_CHECK((num_elements >= 1),
155 ESP_ERR_INVALID_ARG, "mb table size is incorrect.");
156 mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
157 const mb_parameter_descriptor_t *reg_ptr = descriptor;
158 // Go through all items in the table to check all Modbus registers
159 for (uint16_t counter = 0; counter < (num_elements); counter++, reg_ptr++)
160 {
161 // Below is the code to check consistency of the table format and required fields.
162 MB_MASTER_CHECK((reg_ptr->cid == counter),
163 ESP_ERR_INVALID_ARG, "mb descriptor cid field is incorrect.");
164 MB_MASTER_CHECK((reg_ptr->param_key != NULL),
165 ESP_ERR_INVALID_ARG, "mb descriptor param key is incorrect.");
166 MB_MASTER_CHECK((reg_ptr->mb_size > 0),
167 ESP_ERR_INVALID_ARG, "mb descriptor param size is incorrect.");
168 }
169 mbm_opts->mbm_param_descriptor_table = descriptor;
170 mbm_opts->mbm_param_descriptor_size = num_elements;
171 return ESP_OK;
172 }
173
174 // Send custom Modbus request defined as mb_param_request_t structure
mbc_serial_master_send_request(mb_param_request_t * request,void * data_ptr)175 static esp_err_t mbc_serial_master_send_request(mb_param_request_t* request, void* data_ptr)
176 {
177 MB_MASTER_CHECK((mbm_interface_ptr != NULL),
178 ESP_ERR_INVALID_STATE,
179 "Master interface uninitialized.");
180 mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
181 MB_MASTER_CHECK((request != NULL),
182 ESP_ERR_INVALID_ARG, "mb request structure.");
183 MB_MASTER_CHECK((data_ptr != NULL),
184 ESP_ERR_INVALID_ARG, "mb incorrect data pointer.");
185
186 eMBMasterReqErrCode mb_error = MB_MRE_NO_REG;
187 esp_err_t error = ESP_FAIL;
188
189 uint8_t mb_slave_addr = request->slave_addr;
190 uint8_t mb_command = request->command;
191 uint16_t mb_offset = request->reg_start;
192 uint16_t mb_size = request->reg_size;
193
194 // Set the buffer for callback function processing of received data
195 mbm_opts->mbm_reg_buffer_ptr = (uint8_t*)data_ptr;
196 mbm_opts->mbm_reg_buffer_size = mb_size;
197
198 // Calls appropriate request function to send request and waits response
199 switch(mb_command)
200 {
201 case MB_FUNC_READ_COILS:
202 mb_error = eMBMasterReqReadCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset,
203 (USHORT)mb_size , (LONG)MB_RESPONSE_TICS );
204 break;
205 case MB_FUNC_WRITE_SINGLE_COIL:
206 mb_error = eMBMasterReqWriteCoil((UCHAR)mb_slave_addr, (USHORT)mb_offset,
207 *(USHORT*)data_ptr, (LONG)MB_RESPONSE_TICS );
208 break;
209 case MB_FUNC_WRITE_MULTIPLE_COILS:
210 mb_error = eMBMasterReqWriteMultipleCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset,
211 (USHORT)mb_size, (UCHAR*)data_ptr, (LONG)MB_RESPONSE_TICS);
212 break;
213 case MB_FUNC_READ_DISCRETE_INPUTS:
214 mb_error = eMBMasterReqReadDiscreteInputs((UCHAR)mb_slave_addr, (USHORT)mb_offset,
215 (USHORT)mb_size, (LONG)MB_RESPONSE_TICS );
216 break;
217 case MB_FUNC_READ_HOLDING_REGISTER:
218 mb_error = eMBMasterReqReadHoldingRegister((UCHAR)mb_slave_addr, (USHORT)mb_offset,
219 (USHORT)mb_size, (LONG)MB_RESPONSE_TICS );
220 break;
221 case MB_FUNC_WRITE_REGISTER:
222 mb_error = eMBMasterReqWriteHoldingRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
223 *(USHORT*)data_ptr, (LONG)MB_RESPONSE_TICS );
224 break;
225
226 case MB_FUNC_WRITE_MULTIPLE_REGISTERS:
227 mb_error = eMBMasterReqWriteMultipleHoldingRegister( (UCHAR)mb_slave_addr,
228 (USHORT)mb_offset, (USHORT)mb_size,
229 (USHORT*)data_ptr, (LONG)MB_RESPONSE_TICS );
230 break;
231 case MB_FUNC_READWRITE_MULTIPLE_REGISTERS:
232 mb_error = eMBMasterReqReadWriteMultipleHoldingRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
233 (USHORT)mb_size, (USHORT*)data_ptr,
234 (USHORT)mb_offset, (USHORT)mb_size,
235 (LONG)MB_RESPONSE_TICS );
236 break;
237 case MB_FUNC_READ_INPUT_REGISTER:
238 mb_error = eMBMasterReqReadInputRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
239 (USHORT)mb_size, (LONG) MB_RESPONSE_TICS );
240 break;
241 default:
242 ESP_LOGE(TAG, "%s: Incorrect function in request (%u) ",
243 __FUNCTION__, mb_command);
244 mb_error = MB_MRE_NO_REG;
245 break;
246 }
247
248 // Propagate the Modbus errors to higher level
249 switch(mb_error)
250 {
251 case MB_MRE_NO_ERR:
252 error = ESP_OK;
253 break;
254
255 case MB_MRE_NO_REG:
256 error = ESP_ERR_NOT_SUPPORTED; // Invalid register request
257 break;
258
259 case MB_MRE_TIMEDOUT:
260 error = ESP_ERR_TIMEOUT; // Slave did not send response
261 break;
262
263 case MB_MRE_EXE_FUN:
264 case MB_MRE_REV_DATA:
265 error = ESP_ERR_INVALID_RESPONSE; // Invalid response from slave
266 break;
267
268 case MB_MRE_MASTER_BUSY:
269 error = ESP_ERR_INVALID_STATE; // Master is busy (previous request is pending)
270 break;
271
272 default:
273 ESP_LOGE(TAG, "%s: Incorrect return code (%x) ",
274 __FUNCTION__, mb_error);
275 error = ESP_FAIL;
276 break;
277 }
278
279 return error;
280 }
281
mbc_serial_master_get_cid_info(uint16_t cid,const mb_parameter_descriptor_t ** param_buffer)282 static esp_err_t mbc_serial_master_get_cid_info(uint16_t cid, const mb_parameter_descriptor_t** param_buffer)
283 {
284 MB_MASTER_CHECK((mbm_interface_ptr != NULL),
285 ESP_ERR_INVALID_STATE,
286 "Master interface uninitialized.");
287 mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
288
289 MB_MASTER_CHECK((param_buffer != NULL),
290 ESP_ERR_INVALID_ARG, "mb incorrect data buffer pointer.");
291 MB_MASTER_CHECK((mbm_opts->mbm_param_descriptor_table != NULL),
292 ESP_ERR_INVALID_ARG, "mb incorrect descriptor table or not set.");
293 MB_MASTER_CHECK((cid < mbm_opts->mbm_param_descriptor_size),
294 ESP_ERR_NOT_FOUND, "mb incorrect cid of characteristic.");
295
296 // It is assumed that characteristics cid increased in the table
297 const mb_parameter_descriptor_t* reg_info = &mbm_opts->mbm_param_descriptor_table[cid];
298
299 MB_MASTER_CHECK((reg_info->param_key != NULL),
300 ESP_ERR_INVALID_ARG, "mb incorrect characteristic key.");
301 *param_buffer = reg_info;
302 return ESP_OK;
303 }
304
305 // Helper function to get modbus command for each type of Modbus register area
mbc_serial_master_get_command(mb_param_type_t param_type,mb_param_mode_t mode)306 static uint8_t mbc_serial_master_get_command(mb_param_type_t param_type, mb_param_mode_t mode)
307 {
308 uint8_t command = 0;
309 switch(param_type)
310 { //
311 case MB_PARAM_HOLDING:
312 command = (mode == MB_PARAM_WRITE) ?
313 MB_FUNC_WRITE_MULTIPLE_REGISTERS :
314 MB_FUNC_READ_HOLDING_REGISTER;
315 break;
316 case MB_PARAM_INPUT:
317 command = MB_FUNC_READ_INPUT_REGISTER;
318 break;
319 case MB_PARAM_COIL:
320 command = (mode == MB_PARAM_WRITE) ?
321 MB_FUNC_WRITE_MULTIPLE_COILS :
322 MB_FUNC_READ_COILS;
323 break;
324 case MB_PARAM_DISCRETE:
325 if (mode != MB_PARAM_WRITE) {
326 command = MB_FUNC_READ_DISCRETE_INPUTS;
327 } else {
328 ESP_LOGE(TAG, "%s: Incorrect mode (%u)",
329 __FUNCTION__, (uint8_t)mode);
330 }
331 break;
332 default:
333 ESP_LOGE(TAG, "%s: Incorrect param type (%u)",
334 __FUNCTION__, param_type);
335 break;
336 }
337 return command;
338 }
339
340 // Helper to search parameter by name in the parameter description table
341 // and fills Modbus request fields accordingly
mbc_serial_master_set_request(char * name,mb_param_mode_t mode,mb_param_request_t * request,mb_parameter_descriptor_t * reg_data)342 static esp_err_t mbc_serial_master_set_request(char* name, mb_param_mode_t mode,
343 mb_param_request_t* request,
344 mb_parameter_descriptor_t* reg_data)
345 {
346 MB_MASTER_CHECK((mbm_interface_ptr != NULL),
347 ESP_ERR_INVALID_STATE,
348 "Master interface uninitialized.");
349 mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
350 esp_err_t error = ESP_ERR_NOT_FOUND;
351 MB_MASTER_CHECK((name != NULL),
352 ESP_ERR_INVALID_ARG, "mb incorrect parameter name.");
353 MB_MASTER_CHECK((request != NULL),
354 ESP_ERR_INVALID_ARG, "mb incorrect request parameter.");
355 MB_MASTER_CHECK((mode <= MB_PARAM_WRITE),
356 ESP_ERR_INVALID_ARG, "mb incorrect mode.");
357 MB_MASTER_ASSERT(mbm_opts->mbm_param_descriptor_table != NULL);
358 const mb_parameter_descriptor_t* reg_ptr = mbm_opts->mbm_param_descriptor_table;
359 for (uint16_t counter = 0; counter < (mbm_opts->mbm_param_descriptor_size); counter++, reg_ptr++)
360 {
361 // Check the cid of the parameter is equal to record number in the table
362 // Check the length of name and parameter key strings from table
363 size_t param_key_len = strlen((const char*)reg_ptr->param_key);
364 if (param_key_len != strlen((const char*)name)) {
365 continue; // The length of strings is different then check next record in the table
366 }
367 // Compare the name of parameter with parameter key from table
368 int comp_result = memcmp((const void*)name, (const void*)reg_ptr->param_key, (size_t)param_key_len);
369 if (comp_result == 0) {
370 // The correct line is found in the table and reg_ptr points to the found parameter description
371 request->slave_addr = reg_ptr->mb_slave_addr;
372 request->reg_start = reg_ptr->mb_reg_start;
373 request->reg_size = reg_ptr->mb_size;
374 request->command = mbc_serial_master_get_command(reg_ptr->mb_param_type, mode);
375 MB_MASTER_CHECK((request->command > 0),
376 ESP_ERR_INVALID_ARG,
377 "mb incorrect command or parameter type.");
378 if (reg_data != NULL) {
379 *reg_data = *reg_ptr; // Set the cid registered parameter data
380 }
381 error = ESP_OK;
382 break;
383 }
384 }
385 return error;
386 }
387
388 // Get parameter data for corresponding characteristic
mbc_serial_master_get_parameter(uint16_t cid,char * name,uint8_t * value_ptr,uint8_t * type)389 static esp_err_t mbc_serial_master_get_parameter(uint16_t cid, char* name,
390 uint8_t* value_ptr, uint8_t *type)
391 {
392 MB_MASTER_CHECK((name != NULL),
393 ESP_ERR_INVALID_ARG, "mb incorrect descriptor.");
394 MB_MASTER_CHECK((type != NULL),
395 ESP_ERR_INVALID_ARG, "type pointer is incorrect.");
396 esp_err_t error = ESP_ERR_INVALID_RESPONSE;
397 mb_param_request_t request ;
398 mb_parameter_descriptor_t reg_info = { 0 };
399
400 error = mbc_serial_master_set_request(name, MB_PARAM_READ, &request, ®_info);
401 if ((error == ESP_OK) && (cid == reg_info.cid)) {
402 // Send request to read characteristic data
403 error = mbc_serial_master_send_request(&request, value_ptr);
404 if (error == ESP_OK) {
405 ESP_LOGD(TAG, "%s: Good response for get cid(%u) = %s",
406 __FUNCTION__, (int)reg_info.cid, (char*)esp_err_to_name(error));
407 } else {
408 ESP_LOGD(TAG, "%s: Bad response to get cid(%u) = %s",
409 __FUNCTION__, reg_info.cid, (char*)esp_err_to_name(error));
410 }
411 // Set the type of parameter found in the table
412 *type = reg_info.param_type;
413 } else {
414 ESP_LOGE(TAG, "%s: The cid(%u) not found in the data dictionary.",
415 __FUNCTION__, reg_info.cid);
416 error = ESP_ERR_INVALID_ARG;
417 }
418 return error;
419 }
420
421 // Set parameter value for characteristic selected by name and cid
mbc_serial_master_set_parameter(uint16_t cid,char * name,uint8_t * value_ptr,uint8_t * type)422 static esp_err_t mbc_serial_master_set_parameter(uint16_t cid, char* name,
423 uint8_t* value_ptr, uint8_t *type)
424 {
425 MB_MASTER_CHECK((name != NULL),
426 ESP_ERR_INVALID_ARG, "mb incorrect descriptor.");
427 MB_MASTER_CHECK((value_ptr != NULL),
428 ESP_ERR_INVALID_ARG, "value pointer is incorrect.");
429 MB_MASTER_CHECK((type != NULL),
430 ESP_ERR_INVALID_ARG, "type pointer is incorrect.");
431 esp_err_t error = ESP_ERR_INVALID_RESPONSE;
432 mb_param_request_t request ;
433 mb_parameter_descriptor_t reg_info = { 0 };
434
435 error = mbc_serial_master_set_request(name, MB_PARAM_WRITE, &request, ®_info);
436 if ((error == ESP_OK) && (cid == reg_info.cid)) {
437 // Send request to write characteristic data
438 error = mbc_serial_master_send_request(&request, value_ptr);
439 if (error == ESP_OK) {
440 ESP_LOGD(TAG, "%s: Good response for set cid(%u) = %s",
441 __FUNCTION__, (int)reg_info.cid, (char*)esp_err_to_name(error));
442 } else {
443 ESP_LOGD(TAG, "%s: Bad response to set cid(%u) = %s",
444 __FUNCTION__, reg_info.cid, (char*)esp_err_to_name(error));
445 }
446 // Set the type of parameter found in the table
447 *type = reg_info.param_type;
448 } else {
449 ESP_LOGE(TAG, "%s: The requested cid(%u) not found in the data dictionary.",
450 __FUNCTION__, reg_info.cid);
451 error = ESP_ERR_INVALID_ARG;
452 }
453 return error;
454 }
455
456 /* ----------------------- Callback functions for Modbus stack ---------------------------------*/
457 // These are executed by modbus stack to read appropriate type of registers.
458
459 /**
460 * Modbus master input register callback function.
461 *
462 * @param pucRegBuffer input register buffer
463 * @param usAddress input register address
464 * @param usNRegs input register number
465 *
466 * @return result
467 */
468 // Callback function for reading of MB Input Registers
eMBRegInputCBSerialMaster(UCHAR * pucRegBuffer,USHORT usAddress,USHORT usNRegs)469 eMBErrorCode eMBRegInputCBSerialMaster(UCHAR * pucRegBuffer, USHORT usAddress,
470 USHORT usNRegs)
471 {
472 MB_MASTER_CHECK((mbm_interface_ptr != NULL),
473 MB_EILLSTATE,
474 "Master interface uninitialized.");
475 MB_MASTER_CHECK((pucRegBuffer != NULL), MB_EINVAL,
476 "Master stack processing error.");
477 mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
478 // Number of input registers to be transferred
479 USHORT usRegInputNregs = (USHORT)mbm_opts->mbm_reg_buffer_size;
480 UCHAR* pucInputBuffer = (UCHAR*)mbm_opts->mbm_reg_buffer_ptr; // Get instance address
481 USHORT usRegs = usNRegs;
482 eMBErrorCode eStatus = MB_ENOERR;
483 // If input or configuration parameters are incorrect then return an error to stack layer
484 if ((pucInputBuffer != NULL)
485 && (usNRegs >= 1)
486 && (usRegInputNregs == usRegs)) {
487 while (usRegs > 0) {
488 _XFER_2_RD(pucInputBuffer, pucRegBuffer);
489 usRegs -= 1;
490 }
491 } else {
492 eStatus = MB_ENOREG;
493 }
494 return eStatus;
495 }
496
497 /**
498 * Modbus master holding register callback function.
499 *
500 * @param pucRegBuffer holding register buffer
501 * @param usAddress holding register address
502 * @param usNRegs holding register number
503 * @param eMode read or write
504 *
505 * @return result
506 */
507 // Callback function for reading of MB Holding Registers
508 // Executed by stack when request to read/write holding registers is received
eMBRegHoldingCBSerialMaster(UCHAR * pucRegBuffer,USHORT usAddress,USHORT usNRegs,eMBRegisterMode eMode)509 eMBErrorCode eMBRegHoldingCBSerialMaster(UCHAR * pucRegBuffer, USHORT usAddress,
510 USHORT usNRegs, eMBRegisterMode eMode)
511 {
512 MB_MASTER_CHECK((mbm_interface_ptr != NULL),
513 MB_EILLSTATE,
514 "Master interface uninitialized.");
515 MB_MASTER_CHECK((pucRegBuffer != NULL), MB_EINVAL,
516 "Master stack processing error.");
517 mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
518 USHORT usRegHoldingNregs = (USHORT)mbm_opts->mbm_reg_buffer_size;
519 UCHAR* pucHoldingBuffer = (UCHAR*)mbm_opts->mbm_reg_buffer_ptr;
520 eMBErrorCode eStatus = MB_ENOERR;
521 USHORT usRegs = usNRegs;
522 // Check input and configuration parameters for correctness
523 if ((pucHoldingBuffer != NULL)
524 && (usRegHoldingNregs == usNRegs)
525 && (usNRegs >= 1)) {
526 switch (eMode) {
527 case MB_REG_WRITE:
528 while (usRegs > 0) {
529 _XFER_2_RD(pucRegBuffer, pucHoldingBuffer);
530 usRegs -= 1;
531 };
532 break;
533 case MB_REG_READ:
534 while (usRegs > 0) {
535 _XFER_2_WR(pucHoldingBuffer, pucRegBuffer);
536 pucHoldingBuffer += 2;
537 usRegs -= 1;
538 };
539 break;
540 }
541 } else {
542 eStatus = MB_ENOREG;
543 }
544 return eStatus;
545 }
546
547 /**
548 * Modbus master coils callback function.
549 *
550 * @param pucRegBuffer coils buffer
551 * @param usAddress coils address
552 * @param usNCoils coils number
553 * @param eMode read or write
554 *
555 * @return result
556 */
557 // Callback function for reading of MB Coils Registers
eMBRegCoilsCBSerialMaster(UCHAR * pucRegBuffer,USHORT usAddress,USHORT usNCoils,eMBRegisterMode eMode)558 eMBErrorCode eMBRegCoilsCBSerialMaster(UCHAR* pucRegBuffer, USHORT usAddress,
559 USHORT usNCoils, eMBRegisterMode eMode)
560 {
561 MB_MASTER_CHECK((mbm_interface_ptr != NULL),
562 MB_EILLSTATE, "Master interface uninitialized.");
563 MB_MASTER_CHECK((pucRegBuffer != NULL),
564 MB_EINVAL, "Master stack processing error.");
565 mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
566 USHORT usRegCoilNregs = (USHORT)mbm_opts->mbm_reg_buffer_size;
567 UCHAR* pucRegCoilsBuf = (UCHAR*)mbm_opts->mbm_reg_buffer_ptr;
568 eMBErrorCode eStatus = MB_ENOERR;
569 USHORT iRegIndex;
570 USHORT usCoils = usNCoils;
571 usAddress--; // The address is already + 1
572 if ((usRegCoilNregs >= 1)
573 && (pucRegCoilsBuf != NULL)
574 && (usNCoils == usRegCoilNregs)) {
575 iRegIndex = (usAddress % 8);
576 switch (eMode) {
577 case MB_REG_WRITE:
578 while (usCoils > 0) {
579 UCHAR ucResult = xMBUtilGetBits((UCHAR*)pucRegCoilsBuf, iRegIndex, 1);
580 xMBUtilSetBits(pucRegBuffer, iRegIndex - (usAddress % 8) , 1, ucResult);
581 iRegIndex++;
582 usCoils--;
583 }
584 break;
585 case MB_REG_READ:
586 while (usCoils > 0) {
587 UCHAR ucResult = xMBUtilGetBits(pucRegBuffer, iRegIndex - (usAddress % 8), 1);
588 xMBUtilSetBits((uint8_t*)pucRegCoilsBuf, iRegIndex, 1, ucResult);
589 iRegIndex++;
590 usCoils--;
591 }
592 break;
593 } // switch ( eMode )
594 } else {
595 // If the configuration or input parameters are incorrect then return error to stack
596 eStatus = MB_ENOREG;
597 }
598 return eStatus;
599 }
600
601 /**
602 * Modbus master discrete callback function.
603 *
604 * @param pucRegBuffer discrete buffer
605 * @param usAddress discrete address
606 * @param usNDiscrete discrete number
607 *
608 * @return result
609 */
610 // Callback function for reading of MB Discrete Input Registers
eMBRegDiscreteCBSerialMaster(UCHAR * pucRegBuffer,USHORT usAddress,USHORT usNDiscrete)611 eMBErrorCode eMBRegDiscreteCBSerialMaster(UCHAR * pucRegBuffer, USHORT usAddress,
612 USHORT usNDiscrete)
613 {
614 MB_MASTER_CHECK((mbm_interface_ptr != NULL),
615 MB_EILLSTATE, "Master interface uninitialized.");
616 MB_MASTER_CHECK((pucRegBuffer != NULL),
617 MB_EINVAL, "Master stack processing error.");
618 mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
619 USHORT usRegDiscreteNregs = (USHORT)mbm_opts->mbm_reg_buffer_size;
620 UCHAR* pucRegDiscreteBuf = (UCHAR*)mbm_opts->mbm_reg_buffer_ptr;
621 eMBErrorCode eStatus = MB_ENOERR;
622 USHORT iRegBitIndex, iNReg;
623 UCHAR* pucDiscreteInputBuf;
624 iNReg = usNDiscrete / 8 + 1;
625 pucDiscreteInputBuf = (UCHAR*) pucRegDiscreteBuf;
626 // It is already plus one in Modbus function method.
627 usAddress--;
628 if ((usRegDiscreteNregs >= 1)
629 && (pucRegDiscreteBuf != NULL)
630 && (usNDiscrete >= 1)) {
631 iRegBitIndex = (USHORT)(usAddress) % 8; // Get bit index
632 while (iNReg > 1)
633 {
634 xMBUtilSetBits(pucDiscreteInputBuf++, iRegBitIndex, 8, *pucRegBuffer++);
635 iNReg--;
636 }
637 // last discrete
638 usNDiscrete = usNDiscrete % 8;
639 // xMBUtilSetBits has bug when ucNBits is zero
640 if (usNDiscrete != 0)
641 {
642 xMBUtilSetBits(pucDiscreteInputBuf, iRegBitIndex, usNDiscrete, *pucRegBuffer++);
643 }
644 } else {
645 eStatus = MB_ENOREG;
646 }
647 return eStatus;
648 }
649
650 // Initialization of resources for Modbus serial master controller
mbc_serial_master_create(void ** handler)651 esp_err_t mbc_serial_master_create(void** handler)
652 {
653 // Allocate space for master interface structure
654 if (mbm_interface_ptr == NULL) {
655 mbm_interface_ptr = malloc(sizeof(mb_master_interface_t));
656 }
657 MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
658
659 // Initialize interface properties
660 mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
661 mbm_opts->port_type = MB_PORT_SERIAL_MASTER;
662
663 vMBPortSetMode((UCHAR)MB_PORT_SERIAL_MASTER);
664
665 mbm_opts->mbm_comm.mode = MB_MODE_RTU;
666 mbm_opts->mbm_comm.port = MB_UART_PORT;
667 mbm_opts->mbm_comm.baudrate = MB_DEVICE_SPEED;
668 mbm_opts->mbm_comm.parity = MB_PARITY_NONE;
669
670 // Initialization of active context of the modbus controller
671 BaseType_t status = 0;
672 // Parameter change notification queue
673 mbm_opts->mbm_event_group = xEventGroupCreate();
674 MB_MASTER_CHECK((mbm_opts->mbm_event_group != NULL),
675 ESP_ERR_NO_MEM, "mb event group error.");
676 // Create modbus controller task
677 status = xTaskCreatePinnedToCore((void*)&modbus_master_task,
678 "modbus_matask",
679 MB_CONTROLLER_STACK_SIZE,
680 NULL, // No parameters
681 MB_CONTROLLER_PRIORITY,
682 &mbm_opts->mbm_task_handle,
683 MB_PORT_TASK_AFFINITY);
684 if (status != pdPASS) {
685 vTaskDelete(mbm_opts->mbm_task_handle);
686 MB_MASTER_CHECK((status == pdPASS), ESP_ERR_NO_MEM,
687 "mb controller task creation error, xTaskCreate() returns (0x%x).",
688 (uint32_t)status);
689 }
690 MB_MASTER_ASSERT(mbm_opts->mbm_task_handle != NULL); // The task is created but handle is incorrect
691
692 // Initialize public interface methods of the interface
693 mbm_interface_ptr->init = mbc_serial_master_create;
694 mbm_interface_ptr->destroy = mbc_serial_master_destroy;
695 mbm_interface_ptr->setup = mbc_serial_master_setup;
696 mbm_interface_ptr->start = mbc_serial_master_start;
697 mbm_interface_ptr->get_cid_info = mbc_serial_master_get_cid_info;
698 mbm_interface_ptr->get_parameter = mbc_serial_master_get_parameter;
699 mbm_interface_ptr->send_request = mbc_serial_master_send_request;
700 mbm_interface_ptr->set_descriptor = mbc_serial_master_set_descriptor;
701 mbm_interface_ptr->set_parameter = mbc_serial_master_set_parameter;
702
703 mbm_interface_ptr->master_reg_cb_discrete = eMBRegDiscreteCBSerialMaster;
704 mbm_interface_ptr->master_reg_cb_input = eMBRegInputCBSerialMaster;
705 mbm_interface_ptr->master_reg_cb_holding = eMBRegHoldingCBSerialMaster;
706 mbm_interface_ptr->master_reg_cb_coils = eMBRegCoilsCBSerialMaster;
707
708 *handler = mbm_interface_ptr;
709
710 return ESP_OK;
711 }
712