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, &reg_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, &reg_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