mirror of
https://github.com/espressif/ESP8266_RTOS_SDK.git
synced 2025-07-02 06:38:00 +08:00
feat(modbus): modify modbus tcp port that can run on ESP8266
This commit is contained in:
@ -5,11 +5,6 @@ set(srcs
|
||||
"common/esp_modbus_slave.c"
|
||||
"modbus/mb.c"
|
||||
"modbus/mb_m.c"
|
||||
"modbus/ascii/mbascii.c"
|
||||
"modbus/ascii/mbascii_m.c"
|
||||
"modbus/rtu/mbrtu_m.c"
|
||||
"modbus/rtu/mbrtu.c"
|
||||
"modbus/rtu/mbcrc.c"
|
||||
"modbus/tcp/mbtcp.c"
|
||||
"modbus/tcp/mbtcp_m.c"
|
||||
"port/port.c"
|
||||
@ -17,8 +12,6 @@ set(srcs
|
||||
"port/portevent_m.c"
|
||||
"port/portother.c"
|
||||
"port/portother_m.c"
|
||||
"port/portserial.c"
|
||||
"port/portserial_m.c"
|
||||
"port/porttimer.c"
|
||||
"port/porttimer_m.c"
|
||||
"modbus/functions/mbfunccoils.c"
|
||||
@ -32,28 +25,21 @@ set(srcs
|
||||
"modbus/functions/mbfuncinput_m.c"
|
||||
"modbus/functions/mbfuncother.c"
|
||||
"modbus/functions/mbutils.c"
|
||||
"serial_slave/modbus_controller/mbc_serial_slave.c"
|
||||
"serial_master/modbus_controller/mbc_serial_master.c"
|
||||
"tcp_slave/port/port_tcp_slave.c"
|
||||
"tcp_slave/modbus_controller/mbc_tcp_slave.c"
|
||||
"tcp_master/modbus_controller/mbc_tcp_master.c"
|
||||
"tcp_master/port/port_tcp_master.c"
|
||||
"common/esp_modbus_master_tcp.c"
|
||||
"common/esp_modbus_slave_tcp.c"
|
||||
"common/esp_modbus_master_serial.c"
|
||||
"common/esp_modbus_slave_serial.c")
|
||||
"common/esp_modbus_slave_tcp.c")
|
||||
|
||||
set(include_dirs common/include)
|
||||
|
||||
set(priv_include_dirs common port modbus modbus/ascii modbus/functions
|
||||
modbus/rtu modbus/tcp modbus/include)
|
||||
|
||||
list(APPEND priv_include_dirs serial_slave/port serial_slave/modbus_controller
|
||||
serial_master/port serial_master/modbus_controller
|
||||
tcp_slave/port tcp_slave/modbus_controller
|
||||
list(APPEND priv_include_dirs tcp_slave/port tcp_slave/modbus_controller
|
||||
tcp_master/port tcp_master/modbus_controller)
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS "${include_dirs}"
|
||||
PRIV_INCLUDE_DIRS "${priv_include_dirs}"
|
||||
REQUIRES driver)
|
||||
PRIV_INCLUDE_DIRS "${priv_include_dirs}")
|
||||
|
@ -35,18 +35,6 @@ menu "Modbus configuration"
|
||||
Once expired the current connection with the client will be closed
|
||||
and Modbus slave will be waiting for new connection to accept.
|
||||
|
||||
config FMB_COMM_MODE_RTU_EN
|
||||
bool "Enable Modbus stack support for RTU mode"
|
||||
default y
|
||||
help
|
||||
Enable RTU Modbus communication mode option for Modbus serial stack.
|
||||
|
||||
config FMB_COMM_MODE_ASCII_EN
|
||||
bool "Enable Modbus stack support for ASCII mode"
|
||||
default y
|
||||
help
|
||||
Enable ASCII Modbus communication mode option for Modbus serial stack.
|
||||
|
||||
config FMB_MASTER_TIMEOUT_MS_RESPOND
|
||||
int "Slave respond timeout (Milliseconds)"
|
||||
default 150
|
||||
@ -88,23 +76,6 @@ menu "Modbus configuration"
|
||||
This buffer is used for modbus frame transfer. The Modbus protocol maximum
|
||||
frame size is 256 bytes. Bigger size can be used for non standard implementations.
|
||||
|
||||
config FMB_SERIAL_ASCII_BITS_PER_SYMB
|
||||
int "Number of data bits per ASCII character"
|
||||
default 8
|
||||
range 7 8
|
||||
depends on FMB_COMM_MODE_ASCII_EN
|
||||
help
|
||||
This option defines the number of data bits per ASCII character.
|
||||
|
||||
config FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS
|
||||
int "Response timeout for ASCII communication mode (ms)"
|
||||
default 1000
|
||||
range 300 2000
|
||||
depends on FMB_COMM_MODE_ASCII_EN
|
||||
help
|
||||
This option defines response timeout of slave in milliseconds for ASCII communication mode.
|
||||
Thus the timeout will expire and allow the master program to handle the error.
|
||||
|
||||
config FMB_PORT_TASK_PRIO
|
||||
int "Modbus port task priority"
|
||||
range 3 10
|
||||
@ -163,13 +134,6 @@ menu "Modbus configuration"
|
||||
Modbus stack event queue timeout in milliseconds. This may help to optimize
|
||||
Modbus stack event processing time.
|
||||
|
||||
config FMB_TIMER_PORT_ENABLED
|
||||
bool "Modbus slave stack use timer for 3.5T symbol time measurement"
|
||||
default y
|
||||
help
|
||||
If this option is set the Modbus stack uses timer for T3.5 time measurement.
|
||||
Else the internal UART TOUT timeout is used for 3.5T symbol time measurement.
|
||||
|
||||
config FMB_TIMER_GROUP
|
||||
int "Modbus Timer group number"
|
||||
range 0 1
|
||||
|
@ -1,41 +0,0 @@
|
||||
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "esp_err.h" // for esp_err_t
|
||||
#include "mbc_master.h" // for master interface define
|
||||
#include "esp_modbus_master.h" // for public slave defines
|
||||
#include "mbc_serial_master.h" // for public interface defines
|
||||
|
||||
/**
|
||||
* Initialization of Modbus master serial
|
||||
*/
|
||||
esp_err_t mbc_master_init(mb_port_type_t port_type, void** handler)
|
||||
{
|
||||
void* port_handler = NULL;
|
||||
esp_err_t error = ESP_ERR_NOT_SUPPORTED;
|
||||
switch(port_type)
|
||||
{
|
||||
case MB_PORT_SERIAL_MASTER:
|
||||
error = mbc_serial_master_create(&port_handler);
|
||||
break;
|
||||
default:
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
if ((port_handler != NULL) && (error == ESP_OK)) {
|
||||
mbc_master_init_iface(port_handler);
|
||||
*handler = port_handler;
|
||||
}
|
||||
return error;
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "esp_err.h" // for esp_err_t
|
||||
#include "sdkconfig.h" // for KConfig defines
|
||||
#include "mbc_slave.h" // for slave interface define
|
||||
#include "esp_modbus_slave.h" // for public slave defines
|
||||
#include "mbc_serial_slave.h" // for public interface defines
|
||||
|
||||
/**
|
||||
* Initialization of Modbus Serial slave controller
|
||||
*/
|
||||
esp_err_t mbc_slave_init(mb_port_type_t port_type, void** handler)
|
||||
{
|
||||
void* port_handler = NULL;
|
||||
esp_err_t error = ESP_ERR_NOT_SUPPORTED;
|
||||
switch(port_type)
|
||||
{
|
||||
case MB_PORT_SERIAL_SLAVE:
|
||||
// Call constructor function of actual port implementation
|
||||
error = mbc_serial_slave_create(&port_handler);
|
||||
break;
|
||||
default:
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
if ((port_handler != NULL) && (error == ESP_OK)) {
|
||||
mbc_slave_init_iface(port_handler);
|
||||
*handler = port_handler;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
#ifndef _MB_IFACE_COMMON_H
|
||||
#define _MB_IFACE_COMMON_H
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "driver/uart.h" // for UART types
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -18,7 +18,6 @@
|
||||
|
||||
#include <stdint.h> // for standard int types definition
|
||||
#include <stddef.h> // for NULL and std defines
|
||||
#include "soc/soc.h" // for BITN definitions
|
||||
#include "esp_modbus_common.h" // for common types
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -19,7 +19,6 @@
|
||||
// Public interface header for slave
|
||||
#include <stdint.h> // for standard int types definition
|
||||
#include <stddef.h> // for NULL and std defines
|
||||
#include "soc/soc.h" // for BITN definitions
|
||||
#include "freertos/FreeRTOS.h" // for task creation and queues access
|
||||
#include "freertos/event_groups.h" // for event groups
|
||||
#include "esp_modbus_common.h" // for common types
|
||||
|
@ -14,7 +14,7 @@
|
||||
*/
|
||||
#ifndef _MB_CONTROLLER_SLAVE_H
|
||||
#define _MB_CONTROLLER_SLAVE_H
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "driver/uart.h" // for uart defines
|
||||
#include "errno.h" // for errno
|
||||
#include "esp_log.h" // for log write
|
||||
|
@ -351,7 +351,6 @@ eMBMasterPoll( void )
|
||||
if ( ( eStatus == MB_ENOERR ) && ( ( ucRcvAddress == ucMBMasterGetDestAddress() )
|
||||
|| ( ucRcvAddress == MB_TCP_PSEUDO_ADDRESS ) ) )
|
||||
{
|
||||
( void ) xMBMasterPortEventPost( EV_MASTER_EXECUTE );
|
||||
ESP_LOGD(MB_PORT_TAG, "%s: Packet data received successfully (%u).", __func__, eStatus);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL("POLL receive buffer", (void*)ucMBFrame, (uint16_t)usLength, ESP_LOG_DEBUG);
|
||||
( void ) xMBMasterPortEventPost( EV_MASTER_EXECUTE );
|
||||
|
@ -17,7 +17,6 @@
|
||||
#define PORT_COMMON_H_
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/xtensa_api.h"
|
||||
#include "esp_log.h" // for ESP_LOGE macro
|
||||
#include "mbconfig.h"
|
||||
|
||||
|
@ -52,7 +52,6 @@
|
||||
#include "mbport.h"
|
||||
#include "port.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "port_serial_slave.h"
|
||||
/* ----------------------- Variables ----------------------------------------*/
|
||||
static xQueueHandle xQueueHdl;
|
||||
|
||||
|
@ -44,7 +44,7 @@
|
||||
#include "port.h"
|
||||
#include "mbport.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "port_serial_master.h"
|
||||
|
||||
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
|
||||
/* ----------------------- Defines ------------------------------------------*/
|
||||
|
@ -1,286 +0,0 @@
|
||||
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* FreeModbus Libary: ESP32 Port Demo Application
|
||||
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* File: $Id: portother.c,v 1.1 2010/06/06 13:07:20 wolti Exp $
|
||||
*/
|
||||
#include "port.h"
|
||||
#include "driver/uart.h"
|
||||
#include "freertos/queue.h" // for queue support
|
||||
#include "soc/uart_periph.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h" // for esp_log
|
||||
#include "esp_err.h" // for ESP_ERROR_CHECK macro
|
||||
|
||||
/* ----------------------- Modbus includes ----------------------------------*/
|
||||
#include "mb.h"
|
||||
#include "mbport.h"
|
||||
#include "sdkconfig.h" // for KConfig options
|
||||
#include "port_serial_slave.h"
|
||||
|
||||
// Note: This code uses mixed coding standard from legacy IDF code and used freemodbus stack
|
||||
|
||||
// A queue to handle UART event.
|
||||
static QueueHandle_t xMbUartQueue;
|
||||
static TaskHandle_t xMbTaskHandle;
|
||||
static const CHAR *TAG = "MB_SERIAL";
|
||||
|
||||
// The UART hardware port number
|
||||
static UCHAR ucUartNumber = UART_NUM_MAX - 1;
|
||||
|
||||
static BOOL bRxStateEnabled = FALSE; // Receiver enabled flag
|
||||
static BOOL bTxStateEnabled = FALSE; // Transmitter enabled flag
|
||||
|
||||
void vMBPortSerialEnable(BOOL bRxEnable, BOOL bTxEnable)
|
||||
{
|
||||
// This function can be called from xMBRTUTransmitFSM() of different task
|
||||
if (bTxEnable) {
|
||||
bTxStateEnabled = TRUE;
|
||||
} else {
|
||||
bTxStateEnabled = FALSE;
|
||||
}
|
||||
if (bRxEnable) {
|
||||
//uart_enable_rx_intr(ucUartNumber);
|
||||
bRxStateEnabled = TRUE;
|
||||
vTaskResume(xMbTaskHandle); // Resume receiver task
|
||||
} else {
|
||||
vTaskSuspend(xMbTaskHandle); // Block receiver task
|
||||
bRxStateEnabled = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static USHORT usMBPortSerialRxPoll(size_t xEventSize)
|
||||
{
|
||||
BOOL xReadStatus = TRUE;
|
||||
USHORT usCnt = 0;
|
||||
|
||||
if (bRxStateEnabled) {
|
||||
// Get received packet into Rx buffer
|
||||
while(xReadStatus && (usCnt++ <= MB_SERIAL_BUF_SIZE)) {
|
||||
// Call the Modbus stack callback function and let it fill the buffers.
|
||||
xReadStatus = pxMBFrameCBByteReceived(); // callback to execute receive FSM
|
||||
}
|
||||
uart_flush_input(ucUartNumber);
|
||||
// Send event EV_FRAME_RECEIVED to allow stack process packet
|
||||
#if !CONFIG_FMB_TIMER_PORT_ENABLED
|
||||
// Let the stack know that T3.5 time is expired and data is received
|
||||
(void)pxMBPortCBTimerExpired(); // calls callback xMBRTUTimerT35Expired();
|
||||
#endif
|
||||
ESP_LOGD(TAG, "RX: %d bytes\n", usCnt);
|
||||
}
|
||||
return usCnt;
|
||||
}
|
||||
|
||||
BOOL xMBPortSerialTxPoll(void)
|
||||
{
|
||||
USHORT usCount = 0;
|
||||
BOOL bNeedPoll = TRUE;
|
||||
|
||||
if( bTxStateEnabled ) {
|
||||
// Continue while all response bytes put in buffer or out of buffer
|
||||
while((bNeedPoll) && (usCount++ < MB_SERIAL_BUF_SIZE)) {
|
||||
// Calls the modbus stack callback function to let it fill the UART transmit buffer.
|
||||
bNeedPoll = pxMBFrameCBTransmitterEmpty( ); // callback to transmit FSM
|
||||
}
|
||||
ESP_LOGD(TAG, "MB_TX_buffer send: (%d) bytes\n", (uint16_t)usCount);
|
||||
// Waits while UART sending the packet
|
||||
esp_err_t xTxStatus = uart_wait_tx_done(ucUartNumber, MB_SERIAL_TX_TOUT_TICKS);
|
||||
vMBPortSerialEnable(TRUE, FALSE);
|
||||
MB_PORT_CHECK((xTxStatus == ESP_OK), FALSE, "mb serial sent buffer failure.");
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void vUartTask(void *pvParameters)
|
||||
{
|
||||
uart_event_t xEvent;
|
||||
USHORT usResult = 0;
|
||||
for(;;) {
|
||||
if (xQueueReceive(xMbUartQueue, (void*)&xEvent, portMAX_DELAY) == pdTRUE) {
|
||||
ESP_LOGD(TAG, "MB_uart[%d] event:", ucUartNumber);
|
||||
switch(xEvent.type) {
|
||||
//Event of UART receving data
|
||||
case UART_DATA:
|
||||
ESP_LOGD(TAG,"Data event, length: %d", xEvent.size);
|
||||
// This flag set in the event means that no more
|
||||
// data received during configured timeout and UART TOUT feature is triggered
|
||||
if (xEvent.timeout_flag) {
|
||||
// Read received data and send it to modbus stack
|
||||
usResult = usMBPortSerialRxPoll(xEvent.size);
|
||||
ESP_LOGD(TAG,"Timeout occured, processed: %d bytes", usResult);
|
||||
}
|
||||
break;
|
||||
//Event of HW FIFO overflow detected
|
||||
case UART_FIFO_OVF:
|
||||
ESP_LOGD(TAG, "hw fifo overflow\n");
|
||||
xQueueReset(xMbUartQueue);
|
||||
break;
|
||||
//Event of UART ring buffer full
|
||||
case UART_BUFFER_FULL:
|
||||
ESP_LOGD(TAG, "ring buffer full\n");
|
||||
xQueueReset(xMbUartQueue);
|
||||
uart_flush_input(ucUartNumber);
|
||||
break;
|
||||
//Event of UART RX break detected
|
||||
case UART_BREAK:
|
||||
ESP_LOGD(TAG, "uart rx break\n");
|
||||
break;
|
||||
//Event of UART parity check error
|
||||
case UART_PARITY_ERR:
|
||||
ESP_LOGD(TAG, "uart parity error\n");
|
||||
break;
|
||||
//Event of UART frame error
|
||||
case UART_FRAME_ERR:
|
||||
ESP_LOGD(TAG, "uart frame error\n");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGD(TAG, "uart event type: %d\n", xEvent.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate,
|
||||
UCHAR ucDataBits, eMBParity eParity)
|
||||
{
|
||||
esp_err_t xErr = ESP_OK;
|
||||
MB_PORT_CHECK((eParity <= MB_PAR_EVEN), FALSE, "mb serial set parity failure.");
|
||||
// Set communication port number
|
||||
ucUartNumber = ucPORT;
|
||||
// Configure serial communication parameters
|
||||
UCHAR ucParity = UART_PARITY_DISABLE;
|
||||
UCHAR ucData = UART_DATA_8_BITS;
|
||||
switch(eParity){
|
||||
case MB_PAR_NONE:
|
||||
ucParity = UART_PARITY_DISABLE;
|
||||
break;
|
||||
case MB_PAR_ODD:
|
||||
ucParity = UART_PARITY_ODD;
|
||||
break;
|
||||
case MB_PAR_EVEN:
|
||||
ucParity = UART_PARITY_EVEN;
|
||||
break;
|
||||
}
|
||||
switch(ucDataBits){
|
||||
case 5:
|
||||
ucData = UART_DATA_5_BITS;
|
||||
break;
|
||||
case 6:
|
||||
ucData = UART_DATA_6_BITS;
|
||||
break;
|
||||
case 7:
|
||||
ucData = UART_DATA_7_BITS;
|
||||
break;
|
||||
case 8:
|
||||
ucData = UART_DATA_8_BITS;
|
||||
break;
|
||||
default:
|
||||
ucData = UART_DATA_8_BITS;
|
||||
break;
|
||||
}
|
||||
uart_config_t xUartConfig = {
|
||||
.baud_rate = ulBaudRate,
|
||||
.data_bits = ucData,
|
||||
.parity = ucParity,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
.rx_flow_ctrl_thresh = 2,
|
||||
.use_ref_tick = UART_SCLK_APB,
|
||||
};
|
||||
// Set UART config
|
||||
xErr = uart_param_config(ucUartNumber, &xUartConfig);
|
||||
MB_PORT_CHECK((xErr == ESP_OK),
|
||||
FALSE, "mb config failure, uart_param_config() returned (0x%x).", xErr);
|
||||
// Install UART driver, and get the queue.
|
||||
xErr = uart_driver_install(ucUartNumber, MB_SERIAL_BUF_SIZE, MB_SERIAL_BUF_SIZE,
|
||||
MB_QUEUE_LENGTH, &xMbUartQueue, MB_PORT_SERIAL_ISR_FLAG);
|
||||
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||
"mb serial driver failure, uart_driver_install() returned (0x%x).", xErr);
|
||||
#if !CONFIG_FMB_TIMER_PORT_ENABLED
|
||||
// Set timeout for TOUT interrupt (T3.5 modbus time)
|
||||
xErr = uart_set_rx_timeout(ucUartNumber, MB_SERIAL_TOUT);
|
||||
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||
"mb serial set rx timeout failure, uart_set_rx_timeout() returned (0x%x).", xErr);
|
||||
#endif
|
||||
|
||||
// Set always timeout flag to trigger timeout interrupt even after rx fifo full
|
||||
uart_set_always_rx_timeout(ucUartNumber, true);
|
||||
|
||||
// Create a task to handle UART events
|
||||
BaseType_t xStatus = xTaskCreate(vUartTask, "uart_queue_task", MB_SERIAL_TASK_STACK_SIZE,
|
||||
NULL, MB_SERIAL_TASK_PRIO, &xMbTaskHandle);
|
||||
if (xStatus != pdPASS) {
|
||||
vTaskDelete(xMbTaskHandle);
|
||||
// Force exit from function with failure
|
||||
MB_PORT_CHECK(FALSE, FALSE,
|
||||
"mb stack serial task creation error. xTaskCreate() returned (0x%x).",
|
||||
xStatus);
|
||||
} else {
|
||||
vTaskSuspend(xMbTaskHandle); // Suspend serial task while stack is not started
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void vMBPortSerialClose(void)
|
||||
{
|
||||
(void)vTaskSuspend(xMbTaskHandle);
|
||||
(void)vTaskDelete(xMbTaskHandle);
|
||||
ESP_ERROR_CHECK(uart_driver_delete(ucUartNumber));
|
||||
}
|
||||
|
||||
BOOL xMBPortSerialPutByte(CHAR ucByte)
|
||||
{
|
||||
// Send one byte to UART transmission buffer
|
||||
// This function is called by Modbus stack
|
||||
UCHAR ucLength = uart_write_bytes(ucUartNumber, &ucByte, 1);
|
||||
return (ucLength == 1);
|
||||
}
|
||||
|
||||
// Get one byte from intermediate RX buffer
|
||||
BOOL xMBPortSerialGetByte(CHAR* pucByte)
|
||||
{
|
||||
assert(pucByte != NULL);
|
||||
USHORT usLength = uart_read_bytes(ucUartNumber, (uint8_t*)pucByte, 1, MB_SERIAL_RX_TOUT_TICKS);
|
||||
return (usLength == 1);
|
||||
}
|
||||
|
@ -1,279 +0,0 @@
|
||||
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* FreeModbus Libary: ESP32 Port Demo Application
|
||||
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* File: $Id: portserial.c,v 1.60 2013/08/13 15:07:05 Armink add Master Functions $
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "driver/uart.h"
|
||||
#include "soc/dport_access.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
/* ----------------------- Modbus includes ----------------------------------*/
|
||||
#include "port.h"
|
||||
#include "mbport.h"
|
||||
#include "mb_m.h"
|
||||
#include "mbrtu.h"
|
||||
#include "mbconfig.h"
|
||||
#include "port_serial_master.h"
|
||||
|
||||
/* ----------------------- Defines ------------------------------------------*/
|
||||
|
||||
/* ----------------------- Static variables ---------------------------------*/
|
||||
static const CHAR *TAG = "MB_MASTER_SERIAL";
|
||||
|
||||
// A queue to handle UART event.
|
||||
static QueueHandle_t xMbUartQueue;
|
||||
static TaskHandle_t xMbTaskHandle;
|
||||
|
||||
// The UART hardware port number
|
||||
static UCHAR ucUartNumber = UART_NUM_MAX - 1;
|
||||
|
||||
static BOOL bRxStateEnabled = FALSE; // Receiver enabled flag
|
||||
static BOOL bTxStateEnabled = FALSE; // Transmitter enabled flag
|
||||
|
||||
void vMBMasterPortSerialEnable(BOOL bRxEnable, BOOL bTxEnable)
|
||||
{
|
||||
// This function can be called from xMBRTUTransmitFSM() of different task
|
||||
if (bTxEnable) {
|
||||
bTxStateEnabled = TRUE;
|
||||
} else {
|
||||
bTxStateEnabled = FALSE;
|
||||
}
|
||||
if (bRxEnable) {
|
||||
bRxStateEnabled = TRUE;
|
||||
vTaskResume(xMbTaskHandle); // Resume receiver task
|
||||
} else {
|
||||
vTaskSuspend(xMbTaskHandle); // Block receiver task
|
||||
bRxStateEnabled = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static USHORT usMBMasterPortSerialRxPoll(size_t xEventSize)
|
||||
{
|
||||
BOOL xReadStatus = TRUE;
|
||||
USHORT usCnt = 0;
|
||||
|
||||
if (bRxStateEnabled) {
|
||||
while(xReadStatus && (usCnt++ <= MB_SERIAL_BUF_SIZE)) {
|
||||
// Call the Modbus stack callback function and let it fill the stack buffers.
|
||||
xReadStatus = pxMBMasterFrameCBByteReceived(); // callback to receive FSM
|
||||
}
|
||||
// The buffer is transferred into Modbus stack and is not needed here any more
|
||||
uart_flush_input(ucUartNumber);
|
||||
ESP_LOGD(TAG, "Received data: %d(bytes in buffer)\n", (uint32_t)usCnt);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "%s: bRxState disabled but junk data (%d bytes) received. ", __func__, xEventSize);
|
||||
}
|
||||
return usCnt;
|
||||
}
|
||||
|
||||
BOOL xMBMasterPortSerialTxPoll(void)
|
||||
{
|
||||
USHORT usCount = 0;
|
||||
BOOL bNeedPoll = TRUE;
|
||||
|
||||
if( bTxStateEnabled ) {
|
||||
// Continue while all response bytes put in buffer or out of buffer
|
||||
while(bNeedPoll && (usCount++ < MB_SERIAL_BUF_SIZE)) {
|
||||
// Calls the modbus stack callback function to let it fill the UART transmit buffer.
|
||||
bNeedPoll = pxMBMasterFrameCBTransmitterEmpty( ); // callback to transmit FSM
|
||||
}
|
||||
ESP_LOGD(TAG, "MB_TX_buffer sent: (%d) bytes.", (uint16_t)(usCount - 1));
|
||||
// Waits while UART sending the packet
|
||||
esp_err_t xTxStatus = uart_wait_tx_done(ucUartNumber, MB_SERIAL_TX_TOUT_TICKS);
|
||||
vMBMasterPortSerialEnable( TRUE, FALSE );
|
||||
MB_PORT_CHECK((xTxStatus == ESP_OK), FALSE, "mb serial sent buffer failure.");
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// UART receive event task
|
||||
static void vUartTask(void* pvParameters)
|
||||
{
|
||||
uart_event_t xEvent;
|
||||
USHORT usResult = 0;
|
||||
for(;;) {
|
||||
if (xQueueReceive(xMbUartQueue, (void*)&xEvent, portMAX_DELAY) == pdTRUE) {
|
||||
ESP_LOGD(TAG, "MB_uart[%d] event:", ucUartNumber);
|
||||
switch(xEvent.type) {
|
||||
//Event of UART receiving data
|
||||
case UART_DATA:
|
||||
ESP_LOGD(TAG,"Data event, len: %d.", xEvent.size);
|
||||
// This flag set in the event means that no more
|
||||
// data received during configured timeout and UART TOUT feature is triggered
|
||||
if (xEvent.timeout_flag) {
|
||||
// Read received data and send it to modbus stack
|
||||
usResult = usMBMasterPortSerialRxPoll(xEvent.size);
|
||||
ESP_LOGD(TAG,"Timeout occured, processed: %d bytes", usResult);
|
||||
// Block receiver task until data is not processed
|
||||
vTaskSuspend(NULL);
|
||||
}
|
||||
break;
|
||||
//Event of HW FIFO overflow detected
|
||||
case UART_FIFO_OVF:
|
||||
ESP_LOGD(TAG, "hw fifo overflow.");
|
||||
xQueueReset(xMbUartQueue);
|
||||
break;
|
||||
//Event of UART ring buffer full
|
||||
case UART_BUFFER_FULL:
|
||||
ESP_LOGD(TAG, "ring buffer full.");
|
||||
xQueueReset(xMbUartQueue);
|
||||
uart_flush_input(ucUartNumber);
|
||||
break;
|
||||
//Event of UART RX break detected
|
||||
case UART_BREAK:
|
||||
ESP_LOGD(TAG, "uart rx break.");
|
||||
break;
|
||||
//Event of UART parity check error
|
||||
case UART_PARITY_ERR:
|
||||
ESP_LOGD(TAG, "uart parity error.");
|
||||
break;
|
||||
//Event of UART frame error
|
||||
case UART_FRAME_ERR:
|
||||
ESP_LOGD(TAG, "uart frame error.");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGD(TAG, "uart event type: %d.", xEvent.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
BOOL xMBMasterPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
|
||||
{
|
||||
esp_err_t xErr = ESP_OK;
|
||||
MB_PORT_CHECK((eParity <= MB_PAR_EVEN), FALSE, "mb serial set parity failure.");
|
||||
// Set communication port number
|
||||
ucUartNumber = ucPORT;
|
||||
// Configure serial communication parameters
|
||||
UCHAR ucParity = UART_PARITY_DISABLE;
|
||||
UCHAR ucData = UART_DATA_8_BITS;
|
||||
switch(eParity){
|
||||
case MB_PAR_NONE:
|
||||
ucParity = UART_PARITY_DISABLE;
|
||||
break;
|
||||
case MB_PAR_ODD:
|
||||
ucParity = UART_PARITY_ODD;
|
||||
break;
|
||||
case MB_PAR_EVEN:
|
||||
ucParity = UART_PARITY_EVEN;
|
||||
break;
|
||||
}
|
||||
switch(ucDataBits){
|
||||
case 5:
|
||||
ucData = UART_DATA_5_BITS;
|
||||
break;
|
||||
case 6:
|
||||
ucData = UART_DATA_6_BITS;
|
||||
break;
|
||||
case 7:
|
||||
ucData = UART_DATA_7_BITS;
|
||||
break;
|
||||
case 8:
|
||||
ucData = UART_DATA_8_BITS;
|
||||
break;
|
||||
default:
|
||||
ucData = UART_DATA_8_BITS;
|
||||
break;
|
||||
}
|
||||
uart_config_t xUartConfig = {
|
||||
.baud_rate = ulBaudRate,
|
||||
.data_bits = ucData,
|
||||
.parity = ucParity,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
.rx_flow_ctrl_thresh = 2,
|
||||
.source_clk = UART_SCLK_APB,
|
||||
};
|
||||
// Set UART config
|
||||
xErr = uart_param_config(ucUartNumber, &xUartConfig);
|
||||
MB_PORT_CHECK((xErr == ESP_OK),
|
||||
FALSE, "mb config failure, uart_param_config() returned (0x%x).", xErr);
|
||||
// Install UART driver, and get the queue.
|
||||
xErr = uart_driver_install(ucUartNumber, MB_SERIAL_BUF_SIZE, MB_SERIAL_BUF_SIZE,
|
||||
MB_QUEUE_LENGTH, &xMbUartQueue, MB_PORT_SERIAL_ISR_FLAG);
|
||||
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||
"mb serial driver failure, uart_driver_install() returned (0x%x).", xErr);
|
||||
// Set timeout for TOUT interrupt (T3.5 modbus time)
|
||||
xErr = uart_set_rx_timeout(ucUartNumber, MB_SERIAL_TOUT);
|
||||
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||
"mb serial set rx timeout failure, uart_set_rx_timeout() returned (0x%x).", xErr);
|
||||
|
||||
// Set always timeout flag to trigger timeout interrupt even after rx fifo full
|
||||
uart_set_always_rx_timeout(ucUartNumber, true);
|
||||
|
||||
// Create a task to handle UART events
|
||||
BaseType_t xStatus = xTaskCreate(vUartTask, "uart_queue_task", MB_SERIAL_TASK_STACK_SIZE,
|
||||
NULL, MB_SERIAL_TASK_PRIO, &xMbTaskHandle);
|
||||
if (xStatus != pdPASS) {
|
||||
vTaskDelete(xMbTaskHandle);
|
||||
// Force exit from function with failure
|
||||
MB_PORT_CHECK(FALSE, FALSE,
|
||||
"mb stack serial task creation error. xTaskCreate() returned (0x%x).",
|
||||
xStatus);
|
||||
} else {
|
||||
vTaskSuspend(xMbTaskHandle); // Suspend serial task while stack is not started
|
||||
}
|
||||
ESP_LOGD(MB_PORT_TAG,"%s Init serial.", __func__);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void vMBMasterPortSerialClose(void)
|
||||
{
|
||||
(void)vTaskDelete(xMbTaskHandle);
|
||||
ESP_ERROR_CHECK(uart_driver_delete(ucUartNumber));
|
||||
}
|
||||
|
||||
BOOL xMBMasterPortSerialPutByte(CHAR ucByte)
|
||||
{
|
||||
// Send one byte to UART transmission buffer
|
||||
// This function is called by Modbus stack
|
||||
UCHAR ucLength = uart_write_bytes(ucUartNumber, &ucByte, 1);
|
||||
return (ucLength == 1);
|
||||
}
|
||||
|
||||
// Get one byte from intermediate RX buffer
|
||||
BOOL xMBMasterPortSerialGetByte(CHAR* pucByte)
|
||||
{
|
||||
assert(pucByte != NULL);
|
||||
USHORT usLength = uart_read_bytes(ucUartNumber, (uint8_t*)pucByte, 1, MB_SERIAL_RX_TOUT_TICKS);
|
||||
return (usLength == 1);
|
||||
}
|
@ -48,8 +48,6 @@
|
||||
#include "sdkconfig.h"
|
||||
#include "mb.h"
|
||||
#include "mbport.h"
|
||||
#include "driver/timer.h"
|
||||
#include "port_serial_slave.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_FMB_TIMER_PORT_ENABLED
|
||||
|
@ -39,161 +39,82 @@
|
||||
/* ----------------------- Modbus includes ----------------------------------*/
|
||||
#include "mb_m.h"
|
||||
#include "mbport.h"
|
||||
#include "port_serial_master.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#define MB_US50_FREQ (20000) // 20kHz 1/20000 = 50mks
|
||||
#define MB_TICK_TIME_US (50) // 50uS = one tick for timer
|
||||
|
||||
#define MB_TIMER_PRESCALLER ((TIMER_BASE_CLK / MB_US50_FREQ) - 1);
|
||||
#define MB_TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER)
|
||||
#define MB_TIMER_DIVIDER ((TIMER_BASE_CLK / 1000000UL) * MB_TICK_TIME_US - 1) // divider for 50uS
|
||||
#define MB_TIMER_WITH_RELOAD (1)
|
||||
|
||||
// Timer group and timer number to measure time (configurable in KConfig)
|
||||
#define MB_TIMER_INDEX (CONFIG_FMB_TIMER_INDEX)
|
||||
#define MB_TIMER_GROUP (CONFIG_FMB_TIMER_GROUP)
|
||||
#include "esp_timer.h"
|
||||
|
||||
/* ----------------------- Variables ----------------------------------------*/
|
||||
static USHORT usT35TimeOut50us;
|
||||
|
||||
static const USHORT usTimerIndex = MB_TIMER_INDEX; // Initialize Modbus Timer index used by stack,
|
||||
static const USHORT usTimerGroupIndex = MB_TIMER_GROUP; // Timer group index used by stack
|
||||
static timer_isr_handle_t xTimerIntHandle; // Timer interrupt handle
|
||||
|
||||
static USHORT msT35TimeOut;
|
||||
static esp_timer_handle_t xTimerIntHandle;
|
||||
/* ----------------------- static functions ---------------------------------*/
|
||||
static void IRAM_ATTR vTimerGroupIsr(void *param)
|
||||
{
|
||||
assert((int)param == usTimerIndex);
|
||||
// Retrieve the the counter value from the timer that reported the interrupt
|
||||
timer_group_clr_intr_status_in_isr(usTimerGroupIndex, usTimerIndex);
|
||||
(void)pxMBMasterPortCBTimerExpired(); // Timer expired callback function
|
||||
// Enable alarm
|
||||
timer_group_enable_alarm_in_isr(usTimerGroupIndex, usTimerIndex);
|
||||
}
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
BOOL xMBMasterPortTimersInit(USHORT usTimeOut50us)
|
||||
BOOL xMBMasterPortTimersInit(USHORT msTimeOut)
|
||||
{
|
||||
MB_PORT_CHECK((usTimeOut50us > 0), FALSE,
|
||||
MB_PORT_CHECK((msTimeOut > 0), FALSE,
|
||||
"Modbus timeout discreet is incorrect.");
|
||||
// Save timer reload value for Modbus T35 period
|
||||
usT35TimeOut50us = usTimeOut50us;
|
||||
esp_err_t xErr;
|
||||
timer_config_t config;
|
||||
config.alarm_en = TIMER_ALARM_EN;
|
||||
config.auto_reload = MB_TIMER_WITH_RELOAD;
|
||||
config.counter_dir = TIMER_COUNT_UP;
|
||||
config.divider = MB_TIMER_PRESCALLER;
|
||||
config.intr_type = TIMER_INTR_LEVEL;
|
||||
config.counter_en = TIMER_PAUSE;
|
||||
// Configure timer
|
||||
xErr = timer_init(usTimerGroupIndex, usTimerIndex, &config);
|
||||
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||
"timer init failure, timer_init() returned (0x%x).", (uint32_t)xErr);
|
||||
// Stop timer counter
|
||||
xErr = timer_pause(usTimerGroupIndex, usTimerIndex);
|
||||
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||
"stop timer failure, timer_pause() returned (0x%x).", (uint32_t)xErr);
|
||||
// Reset counter value
|
||||
xErr = timer_set_counter_value(usTimerGroupIndex, usTimerIndex, 0x00000000ULL);
|
||||
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||
"timer set value failure, timer_set_counter_value() returned (0x%x).",
|
||||
(uint32_t)xErr);
|
||||
// wait3T5_us = 35 * 11 * 100000 / baud; // the 3.5T symbol time for baudrate
|
||||
// Set alarm value for usTimeOut50us * 50uS
|
||||
xErr = timer_set_alarm_value(usTimerGroupIndex, usTimerIndex, (uint32_t)(usTimeOut50us));
|
||||
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||
"failure to set alarm failure, timer_set_alarm_value() returned (0x%x).",
|
||||
(uint32_t)xErr);
|
||||
// Register ISR for timer
|
||||
xErr = timer_isr_register(usTimerGroupIndex, usTimerIndex,
|
||||
vTimerGroupIsr, (void*)(uint32_t)usTimerIndex, MB_PORT_TIMER_ISR_FLAG, &xTimerIntHandle);
|
||||
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||
"timer set value failure, timer_isr_register() returned (0x%x).",
|
||||
(uint32_t)xErr);
|
||||
msT35TimeOut = msTimeOut;
|
||||
esp_timer_create_args_t timer_conf = {
|
||||
.callback = vTimerGroupIsr,
|
||||
.arg = NULL,
|
||||
.dispatch_method = ESP_TIMER_TASK,
|
||||
.name = "PORT_TIMER_M"
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_timer_create(&timer_conf, &xTimerIntHandle));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Set alarm value for usTimerTimeOut50us * 50uS
|
||||
static BOOL xMBMasterPortTimersEnable(USHORT usTimerTics50us)
|
||||
static BOOL xMBMasterPortTimersEnable(USHORT msTimeOut)
|
||||
{
|
||||
MB_PORT_CHECK((usTimerTics50us > 0), FALSE,
|
||||
MB_PORT_CHECK((msTimeOut > 0), FALSE,
|
||||
"incorrect tick value for timer = (0x%x).",
|
||||
(uint32_t)usTimerTics50us);
|
||||
esp_err_t xErr;
|
||||
xErr = timer_pause(usTimerGroupIndex, usTimerIndex); // stop timer
|
||||
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||
"timer pause failure, timer_pause() returned (0x%x).",
|
||||
(uint32_t)xErr);
|
||||
xErr = timer_set_counter_value(usTimerGroupIndex, usTimerIndex, 0ULL); // reset timer
|
||||
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||
"timer set counter failure, timer_set_counter_value() returned (0x%x).",
|
||||
(uint32_t)xErr);
|
||||
// Set alarm value to number of 50uS ticks
|
||||
xErr = timer_set_alarm_value(usTimerGroupIndex, usTimerIndex,
|
||||
(uint32_t)(usTimerTics50us));
|
||||
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||
"timer set alarm failure, timer_set_alarm_value() returned (0x%x).",
|
||||
(uint32_t)xErr);
|
||||
xErr = timer_enable_intr(usTimerGroupIndex, usTimerIndex);
|
||||
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||
"timer enable interrupt failure, timer_enable_intr() returned (0x%x).",
|
||||
(uint32_t)xErr);
|
||||
xErr = timer_start(usTimerGroupIndex, usTimerIndex); // start timer
|
||||
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||
"timer start failure, timer_start() returned (0x%x).",
|
||||
(uint32_t)xErr);
|
||||
(uint32_t)msTimeOut);
|
||||
ESP_ERROR_CHECK(esp_timer_stop(xTimerIntHandle));
|
||||
ESP_ERROR_CHECK(esp_timer_start_once(xTimerIntHandle, msTimeOut * 1000));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void vMBMasterPortTimersT35Enable(void)
|
||||
{
|
||||
USHORT usTimerTicks = usT35TimeOut50us;
|
||||
USHORT msTimeOut = msT35TimeOut;
|
||||
|
||||
// Set current timer mode, don't change it.
|
||||
vMBMasterSetCurTimerMode(MB_TMODE_T35);
|
||||
// Set timer period
|
||||
(void)xMBMasterPortTimersEnable(usTimerTicks);
|
||||
(void)xMBMasterPortTimersEnable(msTimeOut);
|
||||
}
|
||||
|
||||
void vMBMasterPortTimersConvertDelayEnable(void)
|
||||
{
|
||||
// Covert time in milliseconds into ticks
|
||||
USHORT usTimerTicks = ((MB_MASTER_DELAY_MS_CONVERT * 1000) / MB_TICK_TIME_US);
|
||||
|
||||
USHORT msTimeOut = MB_MASTER_DELAY_MS_CONVERT;
|
||||
// Set current timer mode
|
||||
vMBMasterSetCurTimerMode(MB_TMODE_CONVERT_DELAY);
|
||||
ESP_LOGD(MB_PORT_TAG,"%s Convert delay enable.", __func__);
|
||||
(void)xMBMasterPortTimersEnable(usTimerTicks);
|
||||
(void)xMBMasterPortTimersEnable(msTimeOut);
|
||||
}
|
||||
|
||||
void vMBMasterPortTimersRespondTimeoutEnable(void)
|
||||
{
|
||||
USHORT usTimerTicks = (MB_MASTER_TIMEOUT_MS_RESPOND * 1000 / MB_TICK_TIME_US);
|
||||
|
||||
USHORT msTimeOut = MB_MASTER_TIMEOUT_MS_RESPOND;
|
||||
vMBMasterSetCurTimerMode(MB_TMODE_RESPOND_TIMEOUT);
|
||||
ESP_LOGD(MB_PORT_TAG,"%s Respond enable timeout.", __func__);
|
||||
(void)xMBMasterPortTimersEnable(usTimerTicks);
|
||||
(void)xMBMasterPortTimersEnable(msTimeOut);
|
||||
}
|
||||
|
||||
void MB_PORT_ISR_ATTR
|
||||
vMBMasterPortTimersDisable()
|
||||
{
|
||||
if( (BOOL)xPortInIsrContext() ) {
|
||||
timer_group_set_counter_enable_in_isr(usTimerGroupIndex, usTimerIndex, TIMER_PAUSE);
|
||||
} else {
|
||||
// Stop timer and then reload timer counter value
|
||||
ESP_ERROR_CHECK(timer_pause(usTimerGroupIndex, usTimerIndex));
|
||||
ESP_ERROR_CHECK(timer_set_counter_value(usTimerGroupIndex, usTimerIndex, 0ULL));
|
||||
// Disable timer interrupt
|
||||
ESP_ERROR_CHECK(timer_disable_intr(usTimerGroupIndex, usTimerIndex));
|
||||
}
|
||||
ESP_ERROR_CHECK(esp_timer_stop(xTimerIntHandle));
|
||||
}
|
||||
|
||||
void vMBMasterPortTimerClose(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(timer_pause(usTimerGroupIndex, usTimerIndex));
|
||||
ESP_ERROR_CHECK(timer_disable_intr(usTimerGroupIndex, usTimerIndex));
|
||||
ESP_ERROR_CHECK(esp_intr_free(xTimerIntHandle));
|
||||
ESP_ERROR_CHECK(esp_timer_delete(xTimerIntHandle));
|
||||
}
|
||||
|
@ -1,705 +0,0 @@
|
||||
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// mbc_serial_master.c
|
||||
// Serial master implementation of the Modbus controller
|
||||
|
||||
#include <sys/time.h> // for calculation of time stamp in milliseconds
|
||||
#include "esp_log.h" // for log_write
|
||||
#include <string.h> // for memcpy
|
||||
#include "freertos/FreeRTOS.h" // for task creation and queue access
|
||||
#include "freertos/task.h" // for task api access
|
||||
#include "freertos/event_groups.h" // for event groups
|
||||
#include "freertos/queue.h" // for queue api access
|
||||
#include "mb_m.h" // for modbus stack master types definition
|
||||
#include "port.h" // for port callback functions
|
||||
#include "mbutils.h" // for mbutils functions definition for stack callback
|
||||
#include "sdkconfig.h" // for KConfig values
|
||||
#include "esp_modbus_common.h" // for common types
|
||||
#include "esp_modbus_master.h" // for public master types
|
||||
#include "mbc_master.h" // for private master types
|
||||
#include "mbc_serial_master.h" // for serial master create function and types
|
||||
|
||||
// The Modbus Transmit Poll function defined in port
|
||||
extern BOOL xMBMasterPortSerialTxPoll(void);
|
||||
|
||||
/*-----------------------Master mode use these variables----------------------*/
|
||||
#define MB_RESPONSE_TICS pdMS_TO_TICKS(CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND + 10)
|
||||
|
||||
|
||||
static mb_master_interface_t* mbm_interface_ptr = NULL; //&default_interface_inst;
|
||||
|
||||
// Modbus event processing task
|
||||
static void modbus_master_task(void *pvParameters)
|
||||
{
|
||||
// The interface must be initialized before start of state machine
|
||||
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
// Main Modbus stack processing cycle
|
||||
for (;;) {
|
||||
// Wait for poll events
|
||||
BaseType_t status = xEventGroupWaitBits(mbm_opts->mbm_event_group,
|
||||
(BaseType_t)(MB_EVENT_STACK_STARTED),
|
||||
pdFALSE, // do not clear bits
|
||||
pdFALSE,
|
||||
portMAX_DELAY);
|
||||
// Check if stack started then poll for data
|
||||
if (status & MB_EVENT_STACK_STARTED) {
|
||||
(void)eMBMasterPoll(); // Allow stack to process data
|
||||
// Send response buffer if ready to be sent
|
||||
BOOL xSentState = xMBMasterPortSerialTxPoll();
|
||||
if (xSentState) {
|
||||
// Let state machine know that response was transmitted out
|
||||
(void)xMBMasterPortEventPost(EV_MASTER_FRAME_SENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup Modbus controller parameters
|
||||
static esp_err_t mbc_serial_master_setup(void* comm_info)
|
||||
{
|
||||
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"Master interface uninitialized.");
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
|
||||
const mb_master_comm_info_t* comm_info_ptr = (mb_master_comm_info_t*)comm_info;
|
||||
// Check communication options
|
||||
MB_MASTER_CHECK(((comm_info_ptr->mode == MB_MODE_RTU) || (comm_info_ptr->mode == MB_MODE_ASCII)),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect mode = (0x%x).",
|
||||
(uint32_t)comm_info_ptr->mode);
|
||||
MB_MASTER_CHECK((comm_info_ptr->port <= UART_NUM_MAX), ESP_ERR_INVALID_ARG,
|
||||
"mb wrong port to set = (0x%x).", (uint32_t)comm_info_ptr->port);
|
||||
MB_MASTER_CHECK((comm_info_ptr->parity <= UART_PARITY_EVEN), ESP_ERR_INVALID_ARG,
|
||||
"mb wrong parity option = (0x%x).", (uint32_t)comm_info_ptr->parity);
|
||||
// Save the communication options
|
||||
mbm_opts->mbm_comm = *(mb_communication_info_t*)comm_info_ptr;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Modbus controller stack start function
|
||||
static esp_err_t mbc_serial_master_start(void)
|
||||
{
|
||||
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"Master interface uninitialized.");
|
||||
eMBErrorCode status = MB_EIO;
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
const mb_communication_info_t* comm_info = (mb_communication_info_t*)&mbm_opts->mbm_comm;
|
||||
|
||||
// Initialize Modbus stack using mbcontroller parameters
|
||||
status = eMBMasterSerialInit((eMBMode)comm_info->mode, (UCHAR)comm_info->port,
|
||||
(ULONG)comm_info->baudrate, (eMBParity)comm_info->parity);
|
||||
MB_MASTER_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
|
||||
"mb stack initialization failure, eMBInit() returns (0x%x).", status);
|
||||
status = eMBMasterEnable();
|
||||
MB_MASTER_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
|
||||
"mb stack set slave ID failure, eMBEnable() returned (0x%x).", (uint32_t)status);
|
||||
// Set the mbcontroller start flag
|
||||
EventBits_t flag = xEventGroupSetBits(mbm_opts->mbm_event_group,
|
||||
(EventBits_t)MB_EVENT_STACK_STARTED);
|
||||
MB_MASTER_CHECK((flag & MB_EVENT_STACK_STARTED),
|
||||
ESP_ERR_INVALID_STATE, "mb stack start event set error.");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Modbus controller destroy function
|
||||
static esp_err_t mbc_serial_master_destroy(void)
|
||||
{
|
||||
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"Master interface uninitialized.");
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
eMBErrorCode mb_error = MB_ENOERR;
|
||||
// Stop polling by clearing correspondent bit in the event group
|
||||
EventBits_t flag = xEventGroupClearBits(mbm_opts->mbm_event_group,
|
||||
(EventBits_t)MB_EVENT_STACK_STARTED);
|
||||
MB_MASTER_CHECK((flag & MB_EVENT_STACK_STARTED),
|
||||
ESP_ERR_INVALID_STATE, "mb stack stop event failure.");
|
||||
// Desable and then destroy the Modbus stack
|
||||
mb_error = eMBMasterDisable();
|
||||
MB_MASTER_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack disable failure.");
|
||||
(void)vTaskDelete(mbm_opts->mbm_task_handle);
|
||||
(void)vEventGroupDelete(mbm_opts->mbm_event_group);
|
||||
mb_error = eMBMasterClose();
|
||||
MB_MASTER_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE,
|
||||
"mb stack close failure returned (0x%x).", (uint32_t)mb_error);
|
||||
free(mbm_interface_ptr); // free the memory allocated for options
|
||||
vMBPortSetMode((UCHAR)MB_PORT_INACTIVE);
|
||||
mbm_interface_ptr = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Set Modbus parameter description table
|
||||
static esp_err_t mbc_serial_master_set_descriptor(const mb_parameter_descriptor_t* descriptor, const uint16_t num_elements)
|
||||
{
|
||||
MB_MASTER_CHECK((descriptor != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect descriptor.");
|
||||
MB_MASTER_CHECK((num_elements >= 1),
|
||||
ESP_ERR_INVALID_ARG, "mb table size is incorrect.");
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
const mb_parameter_descriptor_t *reg_ptr = descriptor;
|
||||
// Go through all items in the table to check all Modbus registers
|
||||
for (uint16_t counter = 0; counter < (num_elements); counter++, reg_ptr++)
|
||||
{
|
||||
// Below is the code to check consistency of the table format and required fields.
|
||||
MB_MASTER_CHECK((reg_ptr->cid == counter),
|
||||
ESP_ERR_INVALID_ARG, "mb descriptor cid field is incorrect.");
|
||||
MB_MASTER_CHECK((reg_ptr->param_key != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb descriptor param key is incorrect.");
|
||||
MB_MASTER_CHECK((reg_ptr->mb_size > 0),
|
||||
ESP_ERR_INVALID_ARG, "mb descriptor param size is incorrect.");
|
||||
}
|
||||
mbm_opts->mbm_param_descriptor_table = descriptor;
|
||||
mbm_opts->mbm_param_descriptor_size = num_elements;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Send custom Modbus request defined as mb_param_request_t structure
|
||||
static esp_err_t mbc_serial_master_send_request(mb_param_request_t* request, void* data_ptr)
|
||||
{
|
||||
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"Master interface uninitialized.");
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
MB_MASTER_CHECK((request != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb request structure.");
|
||||
MB_MASTER_CHECK((data_ptr != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect data pointer.");
|
||||
|
||||
eMBMasterReqErrCode mb_error = MB_MRE_NO_REG;
|
||||
esp_err_t error = ESP_FAIL;
|
||||
|
||||
uint8_t mb_slave_addr = request->slave_addr;
|
||||
uint8_t mb_command = request->command;
|
||||
uint16_t mb_offset = request->reg_start;
|
||||
uint16_t mb_size = request->reg_size;
|
||||
|
||||
// Set the buffer for callback function processing of received data
|
||||
mbm_opts->mbm_reg_buffer_ptr = (uint8_t*)data_ptr;
|
||||
mbm_opts->mbm_reg_buffer_size = mb_size;
|
||||
|
||||
// Calls appropriate request function to send request and waits response
|
||||
switch(mb_command)
|
||||
{
|
||||
case MB_FUNC_READ_COILS:
|
||||
mb_error = eMBMasterReqReadCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
(USHORT)mb_size , (LONG)MB_RESPONSE_TICS );
|
||||
break;
|
||||
case MB_FUNC_WRITE_SINGLE_COIL:
|
||||
mb_error = eMBMasterReqWriteCoil((UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
*(USHORT*)data_ptr, (LONG)MB_RESPONSE_TICS );
|
||||
break;
|
||||
case MB_FUNC_WRITE_MULTIPLE_COILS:
|
||||
mb_error = eMBMasterReqWriteMultipleCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
(USHORT)mb_size, (UCHAR*)data_ptr, (LONG)MB_RESPONSE_TICS);
|
||||
break;
|
||||
case MB_FUNC_READ_DISCRETE_INPUTS:
|
||||
mb_error = eMBMasterReqReadDiscreteInputs((UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
(USHORT)mb_size, (LONG)MB_RESPONSE_TICS );
|
||||
break;
|
||||
case MB_FUNC_READ_HOLDING_REGISTER:
|
||||
mb_error = eMBMasterReqReadHoldingRegister((UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
(USHORT)mb_size, (LONG)MB_RESPONSE_TICS );
|
||||
break;
|
||||
case MB_FUNC_WRITE_REGISTER:
|
||||
mb_error = eMBMasterReqWriteHoldingRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
*(USHORT*)data_ptr, (LONG)MB_RESPONSE_TICS );
|
||||
break;
|
||||
|
||||
case MB_FUNC_WRITE_MULTIPLE_REGISTERS:
|
||||
mb_error = eMBMasterReqWriteMultipleHoldingRegister( (UCHAR)mb_slave_addr,
|
||||
(USHORT)mb_offset, (USHORT)mb_size,
|
||||
(USHORT*)data_ptr, (LONG)MB_RESPONSE_TICS );
|
||||
break;
|
||||
case MB_FUNC_READWRITE_MULTIPLE_REGISTERS:
|
||||
mb_error = eMBMasterReqReadWriteMultipleHoldingRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
(USHORT)mb_size, (USHORT*)data_ptr,
|
||||
(USHORT)mb_offset, (USHORT)mb_size,
|
||||
(LONG)MB_RESPONSE_TICS );
|
||||
break;
|
||||
case MB_FUNC_READ_INPUT_REGISTER:
|
||||
mb_error = eMBMasterReqReadInputRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
(USHORT)mb_size, (LONG) MB_RESPONSE_TICS );
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(MB_MASTER_TAG, "%s: Incorrect function in request (%u) ",
|
||||
__FUNCTION__, mb_command);
|
||||
mb_error = MB_MRE_NO_REG;
|
||||
break;
|
||||
}
|
||||
|
||||
// Propagate the Modbus errors to higher level
|
||||
switch(mb_error)
|
||||
{
|
||||
case MB_MRE_NO_ERR:
|
||||
error = ESP_OK;
|
||||
break;
|
||||
|
||||
case MB_MRE_NO_REG:
|
||||
error = ESP_ERR_NOT_SUPPORTED; // Invalid register request
|
||||
break;
|
||||
|
||||
case MB_MRE_TIMEDOUT:
|
||||
error = ESP_ERR_TIMEOUT; // Slave did not send response
|
||||
break;
|
||||
|
||||
case MB_MRE_EXE_FUN:
|
||||
case MB_MRE_REV_DATA:
|
||||
error = ESP_ERR_INVALID_RESPONSE; // Invalid response from slave
|
||||
break;
|
||||
|
||||
case MB_MRE_MASTER_BUSY:
|
||||
error = ESP_ERR_INVALID_STATE; // Master is busy (previous request is pending)
|
||||
break;
|
||||
|
||||
default:
|
||||
ESP_LOGE(MB_MASTER_TAG, "%s: Incorrect return code (%x) ",
|
||||
__FUNCTION__, mb_error);
|
||||
error = ESP_FAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static esp_err_t mbc_serial_master_get_cid_info(uint16_t cid, const mb_parameter_descriptor_t** param_buffer)
|
||||
{
|
||||
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"Master interface uninitialized.");
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
|
||||
MB_MASTER_CHECK((param_buffer != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect data buffer pointer.");
|
||||
MB_MASTER_CHECK((mbm_opts->mbm_param_descriptor_table != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect descriptor table or not set.");
|
||||
MB_MASTER_CHECK((cid < mbm_opts->mbm_param_descriptor_size),
|
||||
ESP_ERR_NOT_FOUND, "mb incorrect cid of characteristic.");
|
||||
|
||||
// It is assumed that characteristics cid increased in the table
|
||||
const mb_parameter_descriptor_t* reg_info = &mbm_opts->mbm_param_descriptor_table[cid];
|
||||
|
||||
MB_MASTER_CHECK((reg_info->param_key != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect characteristic key.");
|
||||
*param_buffer = reg_info;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Helper function to get modbus command for each type of Modbus register area
|
||||
static uint8_t mbc_serial_master_get_command(mb_param_type_t param_type, mb_param_mode_t mode)
|
||||
{
|
||||
uint8_t command = 0;
|
||||
switch(param_type)
|
||||
{ //
|
||||
case MB_PARAM_HOLDING:
|
||||
command = (mode == MB_PARAM_WRITE) ?
|
||||
MB_FUNC_WRITE_MULTIPLE_REGISTERS :
|
||||
MB_FUNC_READ_HOLDING_REGISTER;
|
||||
break;
|
||||
case MB_PARAM_INPUT:
|
||||
command = MB_FUNC_READ_INPUT_REGISTER;
|
||||
break;
|
||||
case MB_PARAM_COIL:
|
||||
command = (mode == MB_PARAM_WRITE) ?
|
||||
MB_FUNC_WRITE_MULTIPLE_COILS :
|
||||
MB_FUNC_READ_COILS;
|
||||
break;
|
||||
case MB_PARAM_DISCRETE:
|
||||
if (mode != MB_PARAM_WRITE) {
|
||||
command = MB_FUNC_READ_DISCRETE_INPUTS;
|
||||
} else {
|
||||
ESP_LOGE(MB_MASTER_TAG, "%s: Incorrect mode (%u)",
|
||||
__FUNCTION__, (uint8_t)mode);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(MB_MASTER_TAG, "%s: Incorrect param type (%u)",
|
||||
__FUNCTION__, param_type);
|
||||
break;
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
// Helper to search parameter by name in the parameter description table
|
||||
// and fills Modbus request fields accordingly
|
||||
static esp_err_t mbc_serial_master_set_request(char* name, mb_param_mode_t mode,
|
||||
mb_param_request_t* request,
|
||||
mb_parameter_descriptor_t* reg_data)
|
||||
{
|
||||
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"Master interface uninitialized.");
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
esp_err_t error = ESP_ERR_NOT_FOUND;
|
||||
MB_MASTER_CHECK((name != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect parameter name.");
|
||||
MB_MASTER_CHECK((request != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect request parameter.");
|
||||
MB_MASTER_CHECK((mode <= MB_PARAM_WRITE),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect mode.");
|
||||
MB_MASTER_ASSERT(mbm_opts->mbm_param_descriptor_table != NULL);
|
||||
const mb_parameter_descriptor_t* reg_ptr = mbm_opts->mbm_param_descriptor_table;
|
||||
for (uint16_t counter = 0; counter < (mbm_opts->mbm_param_descriptor_size); counter++, reg_ptr++)
|
||||
{
|
||||
// Check the cid of the parameter is equal to record number in the table
|
||||
// Check the length of name and parameter key strings from table
|
||||
size_t param_key_len = strlen((const char*)reg_ptr->param_key);
|
||||
if (param_key_len != strlen((const char*)name)) {
|
||||
continue; // The length of strings is different then check next record in the table
|
||||
}
|
||||
// Compare the name of parameter with parameter key from table
|
||||
int comp_result = memcmp((const void*)name, (const void*)reg_ptr->param_key, (size_t)param_key_len);
|
||||
if (comp_result == 0) {
|
||||
// The correct line is found in the table and reg_ptr points to the found parameter description
|
||||
request->slave_addr = reg_ptr->mb_slave_addr;
|
||||
request->reg_start = reg_ptr->mb_reg_start;
|
||||
request->reg_size = reg_ptr->mb_size;
|
||||
request->command = mbc_serial_master_get_command(reg_ptr->mb_param_type, mode);
|
||||
MB_MASTER_CHECK((request->command > 0),
|
||||
ESP_ERR_INVALID_ARG,
|
||||
"mb incorrect command or parameter type.");
|
||||
if (reg_data != NULL) {
|
||||
*reg_data = *reg_ptr; // Set the cid registered parameter data
|
||||
}
|
||||
error = ESP_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// Get parameter data for corresponding characteristic
|
||||
static esp_err_t mbc_serial_master_get_parameter(uint16_t cid, char* name,
|
||||
uint8_t* value_ptr, uint8_t *type)
|
||||
{
|
||||
MB_MASTER_CHECK((name != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect descriptor.");
|
||||
MB_MASTER_CHECK((type != NULL),
|
||||
ESP_ERR_INVALID_ARG, "type pointer is incorrect.");
|
||||
esp_err_t error = ESP_ERR_INVALID_RESPONSE;
|
||||
mb_param_request_t request ;
|
||||
mb_parameter_descriptor_t reg_info = { 0 };
|
||||
|
||||
error = mbc_serial_master_set_request(name, MB_PARAM_READ, &request, ®_info);
|
||||
if ((error == ESP_OK) && (cid == reg_info.cid)) {
|
||||
// Send request to read characteristic data
|
||||
error = mbc_serial_master_send_request(&request, value_ptr);
|
||||
if (error == ESP_OK) {
|
||||
ESP_LOGD(MB_MASTER_TAG, "%s: Good response for get cid(%u) = %s",
|
||||
__FUNCTION__, (int)reg_info.cid, (char*)esp_err_to_name(error));
|
||||
} else {
|
||||
ESP_LOGD(MB_MASTER_TAG, "%s: Bad response to get cid(%u) = %s",
|
||||
__FUNCTION__, reg_info.cid, (char*)esp_err_to_name(error));
|
||||
}
|
||||
// Set the type of parameter found in the table
|
||||
*type = reg_info.param_type;
|
||||
} else {
|
||||
ESP_LOGD(MB_MASTER_TAG, "%s: The cid(%u) not found in the data dictionary.",
|
||||
__FUNCTION__, reg_info.cid);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// Set parameter value for characteristic selected by name and cid
|
||||
static esp_err_t mbc_serial_master_set_parameter(uint16_t cid, char* name,
|
||||
uint8_t* value_ptr, uint8_t *type)
|
||||
{
|
||||
MB_MASTER_CHECK((name != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect descriptor.");
|
||||
MB_MASTER_CHECK((value_ptr != NULL),
|
||||
ESP_ERR_INVALID_ARG, "value pointer is incorrect.");
|
||||
MB_MASTER_CHECK((type != NULL),
|
||||
ESP_ERR_INVALID_ARG, "type pointer is incorrect.");
|
||||
esp_err_t error = ESP_ERR_INVALID_RESPONSE;
|
||||
mb_param_request_t request ;
|
||||
mb_parameter_descriptor_t reg_info = { 0 };
|
||||
|
||||
error = mbc_serial_master_set_request(name, MB_PARAM_WRITE, &request, ®_info);
|
||||
if ((error == ESP_OK) && (cid == reg_info.cid)) {
|
||||
// Send request to write characteristic data
|
||||
error = mbc_serial_master_send_request(&request, value_ptr);
|
||||
if (error == ESP_OK) {
|
||||
ESP_LOGD(MB_MASTER_TAG, "%s: Good response for set cid(%u) = %s",
|
||||
__FUNCTION__, (int)reg_info.cid, (char*)esp_err_to_name(error));
|
||||
} else {
|
||||
ESP_LOGD(MB_MASTER_TAG, "%s: Bad response to set cid(%u) = %s",
|
||||
__FUNCTION__, reg_info.cid, (char*)esp_err_to_name(error));
|
||||
}
|
||||
// Set the type of parameter found in the table
|
||||
*type = reg_info.param_type;
|
||||
} else {
|
||||
ESP_LOGE(MB_MASTER_TAG, "%s: The requested cid(%u) not found in the data dictionary.",
|
||||
__FUNCTION__, reg_info.cid);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
/* ----------------------- Callback functions for Modbus stack ---------------------------------*/
|
||||
// These are executed by modbus stack to read appropriate type of registers.
|
||||
|
||||
/**
|
||||
* Modbus master input register callback function.
|
||||
*
|
||||
* @param pucRegBuffer input register buffer
|
||||
* @param usAddress input register address
|
||||
* @param usNRegs input register number
|
||||
*
|
||||
* @return result
|
||||
*/
|
||||
// Callback function for reading of MB Input Registers
|
||||
eMBErrorCode eMBRegInputCBSerialMaster(UCHAR * pucRegBuffer, USHORT usAddress,
|
||||
USHORT usNRegs)
|
||||
{
|
||||
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
|
||||
MB_EILLSTATE,
|
||||
"Master interface uninitialized.");
|
||||
MB_MASTER_CHECK((pucRegBuffer != NULL), MB_EINVAL,
|
||||
"Master stack processing error.");
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
// Number of input registers to be transferred
|
||||
USHORT usRegInputNregs = (USHORT)mbm_opts->mbm_reg_buffer_size;
|
||||
UCHAR* pucInputBuffer = (UCHAR*)mbm_opts->mbm_reg_buffer_ptr; // Get instance address
|
||||
USHORT usRegs = usNRegs;
|
||||
eMBErrorCode eStatus = MB_ENOERR;
|
||||
// If input or configuration parameters are incorrect then return an error to stack layer
|
||||
if ((pucInputBuffer != NULL)
|
||||
&& (usNRegs >= 1)
|
||||
&& (usRegInputNregs == usRegs)) {
|
||||
while (usRegs > 0) {
|
||||
_XFER_2_RD(pucInputBuffer, pucRegBuffer);
|
||||
usRegs -= 1;
|
||||
}
|
||||
} else {
|
||||
eStatus = MB_ENOREG;
|
||||
}
|
||||
return eStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modbus master holding register callback function.
|
||||
*
|
||||
* @param pucRegBuffer holding register buffer
|
||||
* @param usAddress holding register address
|
||||
* @param usNRegs holding register number
|
||||
* @param eMode read or write
|
||||
*
|
||||
* @return result
|
||||
*/
|
||||
// Callback function for reading of MB Holding Registers
|
||||
// Executed by stack when request to read/write holding registers is received
|
||||
eMBErrorCode eMBRegHoldingCBSerialMaster(UCHAR * pucRegBuffer, USHORT usAddress,
|
||||
USHORT usNRegs, eMBRegisterMode eMode)
|
||||
{
|
||||
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
|
||||
MB_EILLSTATE,
|
||||
"Master interface uninitialized.");
|
||||
MB_MASTER_CHECK((pucRegBuffer != NULL), MB_EINVAL,
|
||||
"Master stack processing error.");
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
USHORT usRegHoldingNregs = (USHORT)mbm_opts->mbm_reg_buffer_size;
|
||||
UCHAR* pucHoldingBuffer = (UCHAR*)mbm_opts->mbm_reg_buffer_ptr;
|
||||
eMBErrorCode eStatus = MB_ENOERR;
|
||||
USHORT usRegs = usNRegs;
|
||||
// Check input and configuration parameters for correctness
|
||||
if ((pucHoldingBuffer != NULL)
|
||||
&& (usRegHoldingNregs == usNRegs)
|
||||
&& (usNRegs >= 1)) {
|
||||
switch (eMode) {
|
||||
case MB_REG_WRITE:
|
||||
while (usRegs > 0) {
|
||||
_XFER_2_RD(pucRegBuffer, pucHoldingBuffer);
|
||||
usRegs -= 1;
|
||||
};
|
||||
break;
|
||||
case MB_REG_READ:
|
||||
while (usRegs > 0) {
|
||||
_XFER_2_WR(pucHoldingBuffer, pucRegBuffer);
|
||||
pucHoldingBuffer += 2;
|
||||
usRegs -= 1;
|
||||
};
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
eStatus = MB_ENOREG;
|
||||
}
|
||||
return eStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modbus master coils callback function.
|
||||
*
|
||||
* @param pucRegBuffer coils buffer
|
||||
* @param usAddress coils address
|
||||
* @param usNCoils coils number
|
||||
* @param eMode read or write
|
||||
*
|
||||
* @return result
|
||||
*/
|
||||
// Callback function for reading of MB Coils Registers
|
||||
eMBErrorCode eMBRegCoilsCBSerialMaster(UCHAR* pucRegBuffer, USHORT usAddress,
|
||||
USHORT usNCoils, eMBRegisterMode eMode)
|
||||
{
|
||||
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
|
||||
MB_EILLSTATE, "Master interface uninitialized.");
|
||||
MB_MASTER_CHECK((pucRegBuffer != NULL),
|
||||
MB_EINVAL, "Master stack processing error.");
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
USHORT usRegCoilNregs = (USHORT)mbm_opts->mbm_reg_buffer_size;
|
||||
UCHAR* pucRegCoilsBuf = (UCHAR*)mbm_opts->mbm_reg_buffer_ptr;
|
||||
eMBErrorCode eStatus = MB_ENOERR;
|
||||
USHORT iRegIndex;
|
||||
USHORT usCoils = usNCoils;
|
||||
usAddress--; // The address is already + 1
|
||||
if ((usRegCoilNregs >= 1)
|
||||
&& (pucRegCoilsBuf != NULL)
|
||||
&& (usNCoils == usRegCoilNregs)) {
|
||||
iRegIndex = (usAddress % 8);
|
||||
switch (eMode) {
|
||||
case MB_REG_WRITE:
|
||||
while (usCoils > 0) {
|
||||
UCHAR ucResult = xMBUtilGetBits((UCHAR*)pucRegCoilsBuf, iRegIndex, 1);
|
||||
xMBUtilSetBits(pucRegBuffer, iRegIndex - (usAddress % 8) , 1, ucResult);
|
||||
iRegIndex++;
|
||||
usCoils--;
|
||||
}
|
||||
break;
|
||||
case MB_REG_READ:
|
||||
while (usCoils > 0) {
|
||||
UCHAR ucResult = xMBUtilGetBits(pucRegBuffer, iRegIndex - (usAddress % 8), 1);
|
||||
xMBUtilSetBits((uint8_t*)pucRegCoilsBuf, iRegIndex, 1, ucResult);
|
||||
iRegIndex++;
|
||||
usCoils--;
|
||||
}
|
||||
break;
|
||||
} // switch ( eMode )
|
||||
} else {
|
||||
// If the configuration or input parameters are incorrect then return error to stack
|
||||
eStatus = MB_ENOREG;
|
||||
}
|
||||
return eStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modbus master discrete callback function.
|
||||
*
|
||||
* @param pucRegBuffer discrete buffer
|
||||
* @param usAddress discrete address
|
||||
* @param usNDiscrete discrete number
|
||||
*
|
||||
* @return result
|
||||
*/
|
||||
// Callback function for reading of MB Discrete Input Registers
|
||||
eMBErrorCode eMBRegDiscreteCBSerialMaster(UCHAR * pucRegBuffer, USHORT usAddress,
|
||||
USHORT usNDiscrete)
|
||||
{
|
||||
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
|
||||
MB_EILLSTATE, "Master interface uninitialized.");
|
||||
MB_MASTER_CHECK((pucRegBuffer != NULL),
|
||||
MB_EINVAL, "Master stack processing error.");
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
USHORT usRegDiscreteNregs = (USHORT)mbm_opts->mbm_reg_buffer_size;
|
||||
UCHAR* pucRegDiscreteBuf = (UCHAR*)mbm_opts->mbm_reg_buffer_ptr;
|
||||
eMBErrorCode eStatus = MB_ENOERR;
|
||||
USHORT iRegBitIndex, iNReg;
|
||||
UCHAR* pucDiscreteInputBuf;
|
||||
iNReg = usNDiscrete / 8 + 1;
|
||||
pucDiscreteInputBuf = (UCHAR*) pucRegDiscreteBuf;
|
||||
// It is already plus one in Modbus function method.
|
||||
usAddress--;
|
||||
if ((usRegDiscreteNregs >= 1)
|
||||
&& (pucRegDiscreteBuf != NULL)
|
||||
&& (usNDiscrete >= 1)) {
|
||||
iRegBitIndex = (USHORT)(usAddress) % 8; // Get bit index
|
||||
while (iNReg > 1)
|
||||
{
|
||||
xMBUtilSetBits(pucDiscreteInputBuf++, iRegBitIndex, 8, *pucRegBuffer++);
|
||||
iNReg--;
|
||||
}
|
||||
// last discrete
|
||||
usNDiscrete = usNDiscrete % 8;
|
||||
// xMBUtilSetBits has bug when ucNBits is zero
|
||||
if (usNDiscrete != 0)
|
||||
{
|
||||
xMBUtilSetBits(pucDiscreteInputBuf, iRegBitIndex, usNDiscrete, *pucRegBuffer++);
|
||||
}
|
||||
} else {
|
||||
eStatus = MB_ENOREG;
|
||||
}
|
||||
return eStatus;
|
||||
}
|
||||
|
||||
// Initialization of resources for Modbus serial master controller
|
||||
esp_err_t mbc_serial_master_create(void** handler)
|
||||
{
|
||||
// Allocate space for master interface structure
|
||||
if (mbm_interface_ptr == NULL) {
|
||||
mbm_interface_ptr = malloc(sizeof(mb_master_interface_t));
|
||||
}
|
||||
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
|
||||
|
||||
// Initialize interface properties
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
mbm_opts->port_type = MB_PORT_SERIAL_MASTER;
|
||||
|
||||
vMBPortSetMode((UCHAR)MB_PORT_SERIAL_MASTER);
|
||||
|
||||
mbm_opts->mbm_comm.mode = MB_MODE_RTU;
|
||||
mbm_opts->mbm_comm.port = MB_UART_PORT;
|
||||
mbm_opts->mbm_comm.baudrate = MB_DEVICE_SPEED;
|
||||
mbm_opts->mbm_comm.parity = MB_PARITY_NONE;
|
||||
|
||||
// Initialization of active context of the modbus controller
|
||||
BaseType_t status = 0;
|
||||
// Parameter change notification queue
|
||||
mbm_opts->mbm_event_group = xEventGroupCreate();
|
||||
MB_MASTER_CHECK((mbm_opts->mbm_event_group != NULL),
|
||||
ESP_ERR_NO_MEM, "mb event group error.");
|
||||
// Create modbus controller task
|
||||
status = xTaskCreate((void*)&modbus_master_task,
|
||||
"modbus_matask",
|
||||
MB_CONTROLLER_STACK_SIZE,
|
||||
NULL, // No parameters
|
||||
MB_CONTROLLER_PRIORITY,
|
||||
&mbm_opts->mbm_task_handle);
|
||||
if (status != pdPASS) {
|
||||
vTaskDelete(mbm_opts->mbm_task_handle);
|
||||
MB_MASTER_CHECK((status == pdPASS), ESP_ERR_NO_MEM,
|
||||
"mb controller task creation error, xTaskCreate() returns (0x%x).",
|
||||
(uint32_t)status);
|
||||
}
|
||||
MB_MASTER_ASSERT(mbm_opts->mbm_task_handle != NULL); // The task is created but handle is incorrect
|
||||
|
||||
// Initialize public interface methods of the interface
|
||||
mbm_interface_ptr->init = mbc_serial_master_create;
|
||||
mbm_interface_ptr->destroy = mbc_serial_master_destroy;
|
||||
mbm_interface_ptr->setup = mbc_serial_master_setup;
|
||||
mbm_interface_ptr->start = mbc_serial_master_start;
|
||||
mbm_interface_ptr->get_cid_info = mbc_serial_master_get_cid_info;
|
||||
mbm_interface_ptr->get_parameter = mbc_serial_master_get_parameter;
|
||||
mbm_interface_ptr->send_request = mbc_serial_master_send_request;
|
||||
mbm_interface_ptr->set_descriptor = mbc_serial_master_set_descriptor;
|
||||
mbm_interface_ptr->set_parameter = mbc_serial_master_set_parameter;
|
||||
|
||||
mbm_interface_ptr->master_reg_cb_discrete = eMBRegDiscreteCBSerialMaster;
|
||||
mbm_interface_ptr->master_reg_cb_input = eMBRegInputCBSerialMaster;
|
||||
mbm_interface_ptr->master_reg_cb_holding = eMBRegHoldingCBSerialMaster;
|
||||
mbm_interface_ptr->master_reg_cb_coils = eMBRegCoilsCBSerialMaster;
|
||||
|
||||
*handler = mbm_interface_ptr;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// mbc_serial_master.h Modbus controller serial master implementation header file
|
||||
|
||||
#ifndef _MODBUS_SERIAL_CONTROLLER_MASTER
|
||||
#define _MODBUS_SERIAL_CONTROLLER_MASTER
|
||||
|
||||
#include <stdint.h> // for standard int types definition
|
||||
#include <stddef.h> // for NULL and std defines
|
||||
#include "soc/soc.h" // for BITN definitions
|
||||
#include "esp_err.h" // for esp_err_t
|
||||
#include "esp_modbus_common.h" // for common defines
|
||||
|
||||
/**
|
||||
* @brief Initialize Modbus controller and stack
|
||||
*
|
||||
* @param[out] handler handler(pointer) to master data structure
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_NO_MEM Parameter error
|
||||
*/
|
||||
esp_err_t mbc_serial_master_create(void** handler);
|
||||
|
||||
#endif // _MODBUS_SERIAL_CONTROLLER_MASTER
|
||||
|
@ -1,65 +0,0 @@
|
||||
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* FreeModbus Libary: ESP32 Port Demo Application
|
||||
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* File: $Id: portother.c,v 1.1 2010/06/06 13:07:20 wolti Exp $
|
||||
*/
|
||||
#ifndef _PORT_SERIAL_MASTER_H
|
||||
#define _PORT_SERIAL_MASTER_H
|
||||
|
||||
/* ----------------------- Platform includes --------------------------------*/
|
||||
#include "driver/uart.h"
|
||||
#include "driver/timer.h"
|
||||
#include "esp_log.h" // for ESP_LOGE macro
|
||||
#include "mb_m.h"
|
||||
#include "port.h"
|
||||
|
||||
/* ----------------------- Defines ------------------------------------------*/
|
||||
#ifdef __cplusplus
|
||||
PR_BEGIN_EXTERN_C
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void vMBPortSetMode( UCHAR ucMode );
|
||||
|
||||
#ifdef __cplusplus
|
||||
PR_END_EXTERN_C
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif
|
@ -1,521 +0,0 @@
|
||||
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// mbc_serial_slave.c
|
||||
// Implementation of the Modbus controller serial slave
|
||||
|
||||
#include <sys/time.h> // for calculation of time stamp in milliseconds
|
||||
#include "esp_log.h" // for log_write
|
||||
#include "mb.h" // for mb types definition
|
||||
#include "mbutils.h" // for mbutils functions definition for stack callback
|
||||
#include "sdkconfig.h" // for KConfig values
|
||||
#include "esp_modbus_common.h" // for common defines
|
||||
#include "esp_modbus_slave.h" // for public slave interface types
|
||||
#include "mbc_slave.h" // for private slave interface types
|
||||
#include "mbc_serial_slave.h" // for serial slave implementation definitions
|
||||
#include "port_serial_slave.h"
|
||||
|
||||
// Shared pointer to interface structure
|
||||
static mb_slave_interface_t* mbs_interface_ptr = NULL;
|
||||
|
||||
// Modbus task function
|
||||
static void modbus_slave_task(void *pvParameters)
|
||||
{
|
||||
// Modbus interface must be initialized before start
|
||||
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
|
||||
MB_SLAVE_ASSERT(mbs_opts != NULL);
|
||||
// Main Modbus stack processing cycle
|
||||
for (;;) {
|
||||
BaseType_t status = xEventGroupWaitBits(mbs_opts->mbs_event_group,
|
||||
(BaseType_t)(MB_EVENT_STACK_STARTED),
|
||||
pdFALSE, // do not clear bits
|
||||
pdFALSE,
|
||||
portMAX_DELAY);
|
||||
// Check if stack started then poll for data
|
||||
if (status & MB_EVENT_STACK_STARTED) {
|
||||
(void)eMBPoll(); // allow stack to process data
|
||||
// Send response buffer
|
||||
BOOL xSentState = xMBPortSerialTxPoll();
|
||||
if (xSentState) {
|
||||
(void)xMBPortEventPost( EV_FRAME_SENT );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup Modbus controller parameters
|
||||
static esp_err_t mbc_serial_slave_setup(void* comm_info)
|
||||
{
|
||||
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"Slave interface is not correctly initialized.");
|
||||
MB_SLAVE_CHECK((comm_info != NULL), ESP_ERR_INVALID_ARG,
|
||||
"mb wrong communication settings.");
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
mb_slave_comm_info_t* comm_settings = (mb_slave_comm_info_t*)comm_info;
|
||||
MB_SLAVE_CHECK(((comm_settings->mode == MB_MODE_RTU) || (comm_settings->mode == MB_MODE_ASCII)),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect mode = (0x%x).",
|
||||
(uint32_t)comm_settings->mode);
|
||||
MB_SLAVE_CHECK((comm_settings->slave_addr <= MB_ADDRESS_MAX),
|
||||
ESP_ERR_INVALID_ARG, "mb wrong slave address = (0x%x).",
|
||||
(uint32_t)comm_settings->slave_addr);
|
||||
MB_SLAVE_CHECK((comm_settings->port < UART_NUM_MAX), ESP_ERR_INVALID_ARG,
|
||||
"mb wrong port to set = (0x%x).", (uint32_t)comm_settings->port);
|
||||
MB_SLAVE_CHECK((comm_settings->parity <= UART_PARITY_EVEN), ESP_ERR_INVALID_ARG,
|
||||
"mb wrong parity option = (0x%x).", (uint32_t)comm_settings->parity);
|
||||
|
||||
// Set communication options of the controller
|
||||
mbs_opts->mbs_comm = *(mb_communication_info_t*)comm_settings;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Start Modbus controller start function
|
||||
static esp_err_t mbc_serial_slave_start(void)
|
||||
{
|
||||
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"Slave interface is not correctly initialized.");
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
eMBErrorCode status = MB_EIO;
|
||||
// Initialize Modbus stack using mbcontroller parameters
|
||||
status = eMBInit((eMBMode)mbs_opts->mbs_comm.mode,
|
||||
(UCHAR)mbs_opts->mbs_comm.slave_addr,
|
||||
(UCHAR)mbs_opts->mbs_comm.port,
|
||||
(ULONG)mbs_opts->mbs_comm.baudrate,
|
||||
(eMBParity)mbs_opts->mbs_comm.parity);
|
||||
MB_SLAVE_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
|
||||
"mb stack initialization failure, eMBInit() returns (0x%x).", status);
|
||||
status = eMBEnable();
|
||||
MB_SLAVE_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
|
||||
"mb stack set slave ID failure, eMBEnable() returned (0x%x).", (uint32_t)status);
|
||||
// Set the mbcontroller start flag
|
||||
EventBits_t flag = xEventGroupSetBits(mbs_opts->mbs_event_group,
|
||||
(EventBits_t)MB_EVENT_STACK_STARTED);
|
||||
MB_SLAVE_CHECK((flag & MB_EVENT_STACK_STARTED),
|
||||
ESP_ERR_INVALID_STATE, "mb stack start event set error.");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Modbus controller destroy function
|
||||
static esp_err_t mbc_serial_slave_destroy(void)
|
||||
{
|
||||
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"Slave interface is not correctly initialized.");
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
eMBErrorCode mb_error = MB_ENOERR;
|
||||
// Stop polling by clearing correspondent bit in the event group
|
||||
EventBits_t flag = xEventGroupClearBits(mbs_opts->mbs_event_group,
|
||||
(EventBits_t)MB_EVENT_STACK_STARTED);
|
||||
MB_SLAVE_CHECK((flag & MB_EVENT_STACK_STARTED),
|
||||
ESP_ERR_INVALID_STATE, "mb stack stop event failure.");
|
||||
// Disable and then destroy the Modbus stack
|
||||
mb_error = eMBDisable();
|
||||
MB_SLAVE_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack disable failure.");
|
||||
(void)vTaskDelete(mbs_opts->mbs_task_handle);
|
||||
(void)vQueueDelete(mbs_opts->mbs_notification_queue_handle);
|
||||
(void)vEventGroupDelete(mbs_opts->mbs_event_group);
|
||||
mb_error = eMBClose();
|
||||
MB_SLAVE_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE,
|
||||
"mb stack close failure returned (0x%x).", (uint32_t)mb_error);
|
||||
free(mbs_interface_ptr);
|
||||
vMBPortSetMode((UCHAR)MB_PORT_INACTIVE);
|
||||
mbs_interface_ptr = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mbc_serial_slave_set_descriptor(const mb_register_area_descriptor_t descr_info)
|
||||
{
|
||||
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"Slave interface is not correctly initialized.");
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
MB_SLAVE_CHECK(((descr_info.type < MB_PARAM_COUNT) && (descr_info.type >= MB_PARAM_HOLDING)),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect modbus instance type = (0x%x).",
|
||||
(uint32_t)descr_info.type);
|
||||
MB_SLAVE_CHECK((descr_info.address != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb instance pointer is NULL.");
|
||||
MB_SLAVE_CHECK((descr_info.size >= MB_INST_MIN_SIZE) && (descr_info.size < (MB_INST_MAX_SIZE)),
|
||||
ESP_ERR_INVALID_ARG, "mb instance size is incorrect = (0x%x).",
|
||||
(uint32_t)descr_info.size);
|
||||
mbs_opts->mbs_area_descriptors[descr_info.type] = descr_info;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// The helper function to get time stamp in microseconds
|
||||
static uint64_t get_time_stamp(void)
|
||||
{
|
||||
uint64_t time_stamp = esp_timer_get_time();
|
||||
return time_stamp;
|
||||
}
|
||||
|
||||
// Helper function to send parameter information to application task
|
||||
static esp_err_t send_param_info(mb_event_group_t par_type, uint16_t mb_offset,
|
||||
uint8_t* par_address, uint16_t par_size)
|
||||
{
|
||||
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"Slave interface is not correctly initialized.");
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
esp_err_t error = ESP_FAIL;
|
||||
mb_param_info_t par_info;
|
||||
// Check if queue is not full the send parameter information
|
||||
par_info.type = par_type;
|
||||
par_info.size = par_size;
|
||||
par_info.address = par_address;
|
||||
par_info.time_stamp = get_time_stamp();
|
||||
par_info.mb_offset = mb_offset;
|
||||
BaseType_t status = xQueueSend(mbs_opts->mbs_notification_queue_handle,
|
||||
&par_info, MB_PAR_INFO_TOUT);
|
||||
if (pdTRUE == status) {
|
||||
ESP_LOGD(MB_SLAVE_TAG, "Queue send parameter info (type, address, size): %d, 0x%.4x, %d",
|
||||
par_type, (uint32_t)par_address, par_size);
|
||||
error = ESP_OK;
|
||||
} else if (errQUEUE_FULL == status) {
|
||||
ESP_LOGD(MB_SLAVE_TAG, "Parameter queue is overflowed.");
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// Helper function to send notification
|
||||
static esp_err_t send_param_access_notification(mb_event_group_t event)
|
||||
{
|
||||
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"Slave interface is not correctly initialized.");
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
esp_err_t err = ESP_FAIL;
|
||||
mb_event_group_t bits = (mb_event_group_t)xEventGroupSetBits(mbs_opts->mbs_event_group,
|
||||
(EventBits_t)event);
|
||||
if (bits & event) {
|
||||
ESP_LOGD(MB_SLAVE_TAG, "The MB_REG_CHANGE_EVENT = 0x%.2x is set.", (uint8_t)event);
|
||||
err = ESP_OK;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
// Blocking function to get event on parameter group change for application task
|
||||
static mb_event_group_t mbc_serial_slave_check_event(mb_event_group_t group)
|
||||
{
|
||||
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"Slave interface is not correctly initialized.");
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
MB_SLAVE_ASSERT(mbs_opts->mbs_event_group != NULL);
|
||||
BaseType_t status = xEventGroupWaitBits(mbs_opts->mbs_event_group, (BaseType_t)group,
|
||||
pdTRUE , pdFALSE, portMAX_DELAY);
|
||||
return (mb_event_group_t)status;
|
||||
}
|
||||
|
||||
// Function to get notification about parameter change from application task
|
||||
static esp_err_t mbc_serial_slave_get_param_info(mb_param_info_t* reg_info, uint32_t timeout)
|
||||
{
|
||||
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"Slave interface is not correctly initialized.");
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
esp_err_t err = ESP_ERR_TIMEOUT;
|
||||
MB_SLAVE_CHECK((mbs_opts->mbs_notification_queue_handle != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb queue handle is invalid.");
|
||||
MB_SLAVE_CHECK((reg_info != NULL), ESP_ERR_INVALID_ARG, "mb register information is invalid.");
|
||||
BaseType_t status = xQueueReceive(mbs_opts->mbs_notification_queue_handle,
|
||||
reg_info, pdMS_TO_TICKS(timeout));
|
||||
if (status == pdTRUE) {
|
||||
err = ESP_OK;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/* ----------------------- Callback functions for Modbus stack ---------------------------------*/
|
||||
// These are executed by modbus stack to read appropriate type of registers.
|
||||
|
||||
// This is required to suppress warning when register start address is zero
|
||||
#pragma GCC diagnostic ignored "-Wtype-limits"
|
||||
|
||||
// Callback function for reading of MB Input Registers
|
||||
eMBErrorCode eMBRegInputCBSerialSlave(UCHAR * pucRegBuffer, USHORT usAddress,
|
||||
USHORT usNRegs)
|
||||
{
|
||||
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
|
||||
MB_EILLSTATE, "Slave stack uninitialized.");
|
||||
MB_SLAVE_CHECK((pucRegBuffer != NULL),
|
||||
MB_EINVAL, "Slave stack call failed.");
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
USHORT usRegInputNregs = (USHORT)(mbs_opts->mbs_area_descriptors[MB_PARAM_INPUT].size >> 1); // Number of input registers
|
||||
USHORT usInputRegStart = (USHORT)mbs_opts->mbs_area_descriptors[MB_PARAM_INPUT].start_offset; // Get Modbus start address
|
||||
UCHAR* pucInputBuffer = (UCHAR*)mbs_opts->mbs_area_descriptors[MB_PARAM_INPUT].address; // Get instance address
|
||||
USHORT usRegs = usNRegs;
|
||||
eMBErrorCode eStatus = MB_ENOERR;
|
||||
USHORT iRegIndex;
|
||||
// If input or configuration parameters are incorrect then return an error to stack layer
|
||||
if ((usAddress >= usInputRegStart)
|
||||
&& (pucInputBuffer != NULL)
|
||||
&& (usNRegs >= 1)
|
||||
&& ((usAddress + usRegs) <= (usInputRegStart + usRegInputNregs + 1))
|
||||
&& (usRegInputNregs >= 1)) {
|
||||
iRegIndex = (USHORT)(usAddress - usInputRegStart - 1);
|
||||
iRegIndex <<= 1; // register Address to byte address
|
||||
pucInputBuffer += iRegIndex;
|
||||
UCHAR* pucBufferStart = pucInputBuffer;
|
||||
while (usRegs > 0) {
|
||||
_XFER_2_RD(pucRegBuffer, pucInputBuffer);
|
||||
iRegIndex += 2;
|
||||
usRegs -= 1;
|
||||
}
|
||||
// Send access notification
|
||||
(void)send_param_access_notification(MB_EVENT_INPUT_REG_RD);
|
||||
// Send parameter info to application task
|
||||
(void)send_param_info(MB_EVENT_INPUT_REG_RD, (uint16_t)usAddress,
|
||||
(uint8_t*)pucBufferStart, (uint16_t)usNRegs);
|
||||
} else {
|
||||
eStatus = MB_ENOREG;
|
||||
}
|
||||
return eStatus;
|
||||
}
|
||||
|
||||
// Callback function for reading of MB Holding Registers
|
||||
// Executed by stack when request to read/write holding registers is received
|
||||
eMBErrorCode eMBRegHoldingCBSerialSlave(UCHAR * pucRegBuffer, USHORT usAddress,
|
||||
USHORT usNRegs, eMBRegisterMode eMode)
|
||||
{
|
||||
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
|
||||
MB_EILLSTATE, "Slave stack uninitialized.");
|
||||
MB_SLAVE_CHECK((pucRegBuffer != NULL),
|
||||
MB_EINVAL, "Slave stack call failed.");
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
USHORT usRegHoldingNregs = (USHORT)(mbs_opts->mbs_area_descriptors[MB_PARAM_HOLDING].size >> 1);
|
||||
USHORT usRegHoldingStart = (USHORT)mbs_opts->mbs_area_descriptors[MB_PARAM_HOLDING].start_offset;
|
||||
UCHAR* pucHoldingBuffer = (UCHAR*)mbs_opts->mbs_area_descriptors[MB_PARAM_HOLDING].address;
|
||||
eMBErrorCode eStatus = MB_ENOERR;
|
||||
USHORT iRegIndex;
|
||||
USHORT usRegs = usNRegs;
|
||||
// Check input and configuration parameters for correctness
|
||||
if ((usAddress >= usRegHoldingStart)
|
||||
&& (pucHoldingBuffer != NULL)
|
||||
&& ((usAddress + usRegs) <= (usRegHoldingStart + usRegHoldingNregs + 1))
|
||||
&& (usRegHoldingNregs >= 1)
|
||||
&& (usNRegs >= 1)) {
|
||||
iRegIndex = (USHORT) (usAddress - usRegHoldingStart - 1);
|
||||
iRegIndex <<= 1; // register Address to byte address
|
||||
pucHoldingBuffer += iRegIndex;
|
||||
UCHAR* pucBufferStart = pucHoldingBuffer;
|
||||
switch (eMode) {
|
||||
case MB_REG_READ:
|
||||
while (usRegs > 0) {
|
||||
_XFER_2_RD(pucRegBuffer, pucHoldingBuffer);
|
||||
iRegIndex += 2;
|
||||
usRegs -= 1;
|
||||
};
|
||||
// Send access notification
|
||||
(void)send_param_access_notification(MB_EVENT_HOLDING_REG_RD);
|
||||
// Send parameter info
|
||||
(void)send_param_info(MB_EVENT_HOLDING_REG_RD, (uint16_t)usAddress,
|
||||
(uint8_t*)pucBufferStart, (uint16_t)usNRegs);
|
||||
break;
|
||||
case MB_REG_WRITE:
|
||||
while (usRegs > 0) {
|
||||
_XFER_2_WR(pucHoldingBuffer, pucRegBuffer);
|
||||
pucHoldingBuffer += 2;
|
||||
iRegIndex += 2;
|
||||
usRegs -= 1;
|
||||
};
|
||||
// Send access notification
|
||||
(void)send_param_access_notification(MB_EVENT_HOLDING_REG_WR);
|
||||
// Send parameter info
|
||||
(void)send_param_info(MB_EVENT_HOLDING_REG_WR, (uint16_t)usAddress,
|
||||
(uint8_t*)pucBufferStart, (uint16_t)usNRegs);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
eStatus = MB_ENOREG;
|
||||
}
|
||||
return eStatus;
|
||||
}
|
||||
|
||||
// Callback function for reading of MB Coils Registers
|
||||
eMBErrorCode eMBRegCoilsCBSerialSlave(UCHAR* pucRegBuffer, USHORT usAddress,
|
||||
USHORT usNCoils, eMBRegisterMode eMode)
|
||||
{
|
||||
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
|
||||
MB_EILLSTATE, "Slave stack uninitialized.");
|
||||
MB_SLAVE_CHECK((pucRegBuffer != NULL),
|
||||
MB_EINVAL, "Slave stack call failed.");
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
USHORT usRegCoilNregs = (USHORT)(mbs_opts->mbs_area_descriptors[MB_PARAM_COIL].size >> 1); // number of registers in storage area
|
||||
USHORT usRegCoilsStart = (USHORT)mbs_opts->mbs_area_descriptors[MB_PARAM_COIL].start_offset; // MB offset of coils registers
|
||||
UCHAR* pucRegCoilsBuf = (UCHAR*)mbs_opts->mbs_area_descriptors[MB_PARAM_COIL].address;
|
||||
eMBErrorCode eStatus = MB_ENOERR;
|
||||
USHORT iRegIndex;
|
||||
USHORT usCoils = usNCoils;
|
||||
usAddress--; // The address is already +1
|
||||
if ((usAddress >= usRegCoilsStart)
|
||||
&& (usRegCoilNregs >= 1)
|
||||
&& ((usAddress + usCoils) <= (usRegCoilsStart + (usRegCoilNregs << 4) + 1))
|
||||
&& (pucRegCoilsBuf != NULL)
|
||||
&& (usNCoils >= 1)) {
|
||||
iRegIndex = (USHORT) (usAddress - usRegCoilsStart);
|
||||
CHAR* pucCoilsDataBuf = (CHAR*)(pucRegCoilsBuf + (iRegIndex >> 3));
|
||||
switch (eMode) {
|
||||
case MB_REG_READ:
|
||||
while (usCoils > 0) {
|
||||
UCHAR ucResult = xMBUtilGetBits((UCHAR*)pucRegCoilsBuf, iRegIndex, 1);
|
||||
xMBUtilSetBits(pucRegBuffer, iRegIndex - (usAddress - usRegCoilsStart), 1, ucResult);
|
||||
iRegIndex++;
|
||||
usCoils--;
|
||||
}
|
||||
// Send an event to notify application task about event
|
||||
(void)send_param_access_notification(MB_EVENT_COILS_RD);
|
||||
(void)send_param_info(MB_EVENT_COILS_RD, (uint16_t)usAddress,
|
||||
(uint8_t*)(pucCoilsDataBuf), (uint16_t)usNCoils);
|
||||
break;
|
||||
case MB_REG_WRITE:
|
||||
while (usCoils > 0) {
|
||||
UCHAR ucResult = xMBUtilGetBits(pucRegBuffer,
|
||||
iRegIndex - (usAddress - usRegCoilsStart), 1);
|
||||
xMBUtilSetBits((uint8_t*)pucRegCoilsBuf, iRegIndex, 1, ucResult);
|
||||
iRegIndex++;
|
||||
usCoils--;
|
||||
}
|
||||
// Send an event to notify application task about event
|
||||
(void)send_param_access_notification(MB_EVENT_COILS_WR);
|
||||
(void)send_param_info(MB_EVENT_COILS_WR, (uint16_t)usAddress,
|
||||
(uint8_t*)pucCoilsDataBuf, (uint16_t)usNCoils);
|
||||
break;
|
||||
} // switch ( eMode )
|
||||
} else {
|
||||
// If the configuration or input parameters are incorrect then return error to stack
|
||||
eStatus = MB_ENOREG;
|
||||
}
|
||||
return eStatus;
|
||||
}
|
||||
|
||||
// Callback function for reading of MB Discrete Input Registers
|
||||
eMBErrorCode eMBRegDiscreteCBSerialSlave(UCHAR* pucRegBuffer, USHORT usAddress,
|
||||
USHORT usNDiscrete)
|
||||
{
|
||||
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
|
||||
MB_EILLSTATE, "Slave stack uninitialized.");
|
||||
MB_SLAVE_CHECK((pucRegBuffer != NULL),
|
||||
MB_EINVAL, "Slave stack call failed.");
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
USHORT usRegDiscreteNregs = (USHORT)(mbs_opts->mbs_area_descriptors[MB_PARAM_DISCRETE].size >> 1); // number of registers in storage area
|
||||
USHORT usRegDiscreteStart = (USHORT)mbs_opts->mbs_area_descriptors[MB_PARAM_DISCRETE].start_offset; // MB offset of registers
|
||||
UCHAR* pucRegDiscreteBuf = (UCHAR*)mbs_opts->mbs_area_descriptors[MB_PARAM_DISCRETE].address; // the storage address
|
||||
eMBErrorCode eStatus = MB_ENOERR;
|
||||
USHORT iRegIndex, iRegBitIndex, iNReg;
|
||||
UCHAR* pucDiscreteInputBuf;
|
||||
iNReg = usNDiscrete / 8 + 1;
|
||||
pucDiscreteInputBuf = (UCHAR*) pucRegDiscreteBuf;
|
||||
// It already plus one in modbus function method.
|
||||
usAddress--;
|
||||
if ((usAddress >= usRegDiscreteStart)
|
||||
&& (usRegDiscreteNregs >= 1)
|
||||
&& (pucRegDiscreteBuf != NULL)
|
||||
&& ((usAddress + usNDiscrete) <= (usRegDiscreteStart + (usRegDiscreteNregs * 16)))
|
||||
&& (usNDiscrete >= 1)) {
|
||||
iRegIndex = (USHORT) (usAddress - usRegDiscreteStart) / 8; // Get register index in the buffer for bit number
|
||||
iRegBitIndex = (USHORT)(usAddress - usRegDiscreteStart) % 8; // Get bit index
|
||||
UCHAR* pucTempBuf = &pucDiscreteInputBuf[iRegIndex];
|
||||
while (iNReg > 0) {
|
||||
*pucRegBuffer++ = xMBUtilGetBits(&pucDiscreteInputBuf[iRegIndex++], iRegBitIndex, 8);
|
||||
iNReg--;
|
||||
}
|
||||
pucRegBuffer--;
|
||||
// Last discrete
|
||||
usNDiscrete = usNDiscrete % 8;
|
||||
// Filling zero to high bit
|
||||
*pucRegBuffer = *pucRegBuffer << (8 - usNDiscrete);
|
||||
*pucRegBuffer = *pucRegBuffer >> (8 - usNDiscrete);
|
||||
// Send an event to notify application task about event
|
||||
(void)send_param_access_notification(MB_EVENT_DISCRETE_RD);
|
||||
(void)send_param_info(MB_EVENT_DISCRETE_RD, (uint16_t)usAddress,
|
||||
(uint8_t*)pucTempBuf, (uint16_t)usNDiscrete);
|
||||
} else {
|
||||
eStatus = MB_ENOREG;
|
||||
}
|
||||
return eStatus;
|
||||
}
|
||||
#pragma GCC diagnostic pop // require GCC
|
||||
|
||||
// Initialization of Modbus controller
|
||||
esp_err_t mbc_serial_slave_create(void** handler)
|
||||
{
|
||||
// Allocate space for options
|
||||
if (mbs_interface_ptr == NULL) {
|
||||
mbs_interface_ptr = malloc(sizeof(mb_slave_interface_t));
|
||||
}
|
||||
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
|
||||
|
||||
vMBPortSetMode((UCHAR)MB_PORT_SERIAL_SLAVE);
|
||||
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
mbs_opts->port_type = MB_PORT_SERIAL_SLAVE; // set interface port type
|
||||
|
||||
// Set default values of communication options
|
||||
mbs_opts->mbs_comm.mode = MB_MODE_RTU;
|
||||
mbs_opts->mbs_comm.slave_addr = MB_DEVICE_ADDRESS;
|
||||
mbs_opts->mbs_comm.port = MB_UART_PORT;
|
||||
mbs_opts->mbs_comm.baudrate = MB_DEVICE_SPEED;
|
||||
mbs_opts->mbs_comm.parity = MB_PARITY_NONE;
|
||||
|
||||
// Initialization of active context of the Modbus controller
|
||||
BaseType_t status = 0;
|
||||
// Parameter change notification queue
|
||||
mbs_opts->mbs_event_group = xEventGroupCreate();
|
||||
MB_SLAVE_CHECK((mbs_opts->mbs_event_group != NULL),
|
||||
ESP_ERR_NO_MEM, "mb event group error.");
|
||||
// Parameter change notification queue
|
||||
mbs_opts->mbs_notification_queue_handle = xQueueCreate(
|
||||
MB_CONTROLLER_NOTIFY_QUEUE_SIZE,
|
||||
sizeof(mb_param_info_t));
|
||||
MB_SLAVE_CHECK((mbs_opts->mbs_notification_queue_handle != NULL),
|
||||
ESP_ERR_NO_MEM, "mb notify queue creation error.");
|
||||
// Create Modbus controller task
|
||||
status = xTaskCreate((void*)&modbus_slave_task,
|
||||
"modbus_slave_task",
|
||||
MB_CONTROLLER_STACK_SIZE,
|
||||
NULL,
|
||||
MB_CONTROLLER_PRIORITY,
|
||||
&mbs_opts->mbs_task_handle);
|
||||
if (status != pdPASS) {
|
||||
vTaskDelete(mbs_opts->mbs_task_handle);
|
||||
MB_SLAVE_CHECK((status == pdPASS), ESP_ERR_NO_MEM,
|
||||
"mb controller task creation error, xTaskCreate() returns (0x%x).",
|
||||
(uint32_t)status);
|
||||
}
|
||||
MB_SLAVE_ASSERT(mbs_opts->mbs_task_handle != NULL); // The task is created but handle is incorrect
|
||||
|
||||
// Initialize interface function pointers
|
||||
mbs_interface_ptr->check_event = mbc_serial_slave_check_event;
|
||||
mbs_interface_ptr->destroy = mbc_serial_slave_destroy;
|
||||
mbs_interface_ptr->get_param_info = mbc_serial_slave_get_param_info;
|
||||
mbs_interface_ptr->init = mbc_serial_slave_create;
|
||||
mbs_interface_ptr->set_descriptor = mbc_serial_slave_set_descriptor;
|
||||
mbs_interface_ptr->setup = mbc_serial_slave_setup;
|
||||
mbs_interface_ptr->start = mbc_serial_slave_start;
|
||||
|
||||
// Initialize stack callback function pointers
|
||||
mbs_interface_ptr->slave_reg_cb_discrete = eMBRegDiscreteCBSerialSlave;
|
||||
mbs_interface_ptr->slave_reg_cb_input = eMBRegInputCBSerialSlave;
|
||||
mbs_interface_ptr->slave_reg_cb_holding = eMBRegHoldingCBSerialSlave;
|
||||
mbs_interface_ptr->slave_reg_cb_coils = eMBRegCoilsCBSerialSlave;
|
||||
|
||||
*handler = (void*)mbs_interface_ptr;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
@ -1,40 +0,0 @@
|
||||
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// mbc_serial_slave.h Modbus controller serial slave implementation header file
|
||||
|
||||
#ifndef _MODBUS_SERIAL_CONTROLLER_SLAVE
|
||||
#define _MODBUS_SERIAL_CONTROLLER_SLAVE
|
||||
|
||||
#include <stdint.h> // for standard int types definition
|
||||
#include <stddef.h> // for NULL and std defines
|
||||
#include "esp_modbus_common.h" // for common defines
|
||||
|
||||
/* ----------------------- Defines ------------------------------------------*/
|
||||
#define MB_CONTROLLER_NOTIFY_QUEUE_SIZE (CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE) // Number of messages in parameter notification queue
|
||||
#define MB_CONTROLLER_NOTIFY_TIMEOUT (pdMS_TO_TICKS(CONFIG_FMB_CONTROLLER_NOTIFY_TIMEOUT)) // notification timeout
|
||||
|
||||
/*
|
||||
* @brief Initialize Modbus controller and stack
|
||||
*
|
||||
* @param[out] handler handler(pointer) to master data structure
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_NO_MEM Parameter error
|
||||
*/
|
||||
esp_err_t mbc_serial_slave_create(void** handler);
|
||||
|
||||
#endif // _MODBUS_SERIAL_CONTROLLER_SLAVE
|
||||
|
@ -1,67 +0,0 @@
|
||||
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* FreeModbus Libary: ESP32 Port Demo Application
|
||||
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* File: $Id: portother.c,v 1.1 2010/06/06 13:07:20 wolti Exp $
|
||||
*/
|
||||
#ifndef _PORT_SERIAL_SLAVE_H
|
||||
#define _PORT_SERIAL_SLAVE_H
|
||||
|
||||
/* ----------------------- Platform includes --------------------------------*/
|
||||
|
||||
#include "driver/uart.h"
|
||||
#include "driver/timer.h"
|
||||
#include "esp_log.h"
|
||||
#include "port.h"
|
||||
|
||||
/* ----------------------- Defines ------------------------------------------*/
|
||||
#ifdef __cplusplus
|
||||
PR_BEGIN_EXTERN_C
|
||||
#endif /* __cplusplus */
|
||||
|
||||
BOOL xMBPortSerialTxPoll( void );
|
||||
|
||||
void vMBPortSetMode( UCHAR ucMode );
|
||||
|
||||
#ifdef __cplusplus
|
||||
PR_END_EXTERN_C
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif
|
@ -38,7 +38,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
#include "esp_timer.h"
|
||||
/* ----------------------- lwIP includes ------------------------------------*/
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sockets.h"
|
||||
@ -432,11 +432,14 @@ static BOOL xMBTCPPortMasterCheckHost(const CHAR* pcHostStr, ip_addr_t* pxHostAd
|
||||
struct in_addr addr4 = ((struct sockaddr_in *) (pxAddrList->ai_addr))->sin_addr;
|
||||
inet_addr_to_ip4addr(ip_2_ip4(&xTargetAddr), &addr4);
|
||||
pcStr = ip4addr_ntoa_r(ip_2_ip4(&xTargetAddr), cStr, sizeof(cStr));
|
||||
} else {
|
||||
}
|
||||
#if LWIP_IPV6
|
||||
else {
|
||||
struct in6_addr addr6 = ((struct sockaddr_in6 *) (pxAddrList->ai_addr))->sin6_addr;
|
||||
inet6_addr_to_ip6addr(ip_2_ip6(&xTargetAddr), &addr6);
|
||||
pcStr = ip6addr_ntoa_r(ip_2_ip6(&xTargetAddr), cStr, sizeof(cStr));
|
||||
}
|
||||
#endif
|
||||
if (pxHostAddr) {
|
||||
*pxHostAddr = xTargetAddr;
|
||||
}
|
||||
@ -496,15 +499,17 @@ static err_t xMBTCPPortMasterConnect(MbSlaveInfo_t* pxInfo)
|
||||
struct in_addr addr4 = ((struct sockaddr_in *) (pxCurAddr->ai_addr))->sin_addr;
|
||||
inet_addr_to_ip4addr(ip_2_ip4(&xTargetAddr), &addr4);
|
||||
pcStr = ip4addr_ntoa_r(ip_2_ip4(&xTargetAddr), cStr, sizeof(cStr));
|
||||
} else if (pxCurAddr->ai_family == AF_INET6) {
|
||||
}
|
||||
#if LWIP_IPV6
|
||||
else if (pxCurAddr->ai_family == AF_INET6) {
|
||||
struct in6_addr addr6 = ((struct sockaddr_in6 *) (pxCurAddr->ai_addr))->sin6_addr;
|
||||
inet6_addr_to_ip6addr(ip_2_ip6(&xTargetAddr), &addr6);
|
||||
pcStr = ip6addr_ntoa_r(ip_2_ip6(&xTargetAddr), cStr, sizeof(cStr));
|
||||
// Set scope id to fix routing issues with local address
|
||||
((struct sockaddr_in6 *) (pxCurAddr->ai_addr))->sin6_scope_id =
|
||||
esp_netif_get_netif_impl_index(xMbPortConfig.pvNetIface);
|
||||
tcpip_adapter_get_netif_index(TCPIP_ADAPTER_IF_STA);
|
||||
}
|
||||
|
||||
#endif
|
||||
if (pxInfo->xSockId <= 0) {
|
||||
pxInfo->xSockId = socket(pxCurAddr->ai_family, pxCurAddr->ai_socktype, pxCurAddr->ai_protocol);
|
||||
if (pxInfo->xSockId < 0) {
|
||||
|
@ -27,7 +27,7 @@
|
||||
#include "mbc_slave.h" // for private slave interface types
|
||||
#include "mbc_tcp_slave.h" // for tcp slave mb controller defines
|
||||
#include "port_tcp_slave.h" // for tcp slave port defines
|
||||
|
||||
#include "esp_timer.h"
|
||||
// Shared pointer to interface structure
|
||||
static mb_slave_interface_t* mbs_interface_ptr = NULL;
|
||||
|
||||
|
@ -40,12 +40,11 @@
|
||||
#include "esp_err.h"
|
||||
#include "sys/time.h"
|
||||
#include "esp_netif.h"
|
||||
|
||||
#include "esp_timer.h"
|
||||
/* ----------------------- lwIP includes ------------------------------------*/
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/netdb.h"
|
||||
#include "net/if.h"
|
||||
|
||||
/* ----------------------- Modbus includes ----------------------------------*/
|
||||
#include "mb.h"
|
||||
@ -59,6 +58,9 @@
|
||||
#define MB_TCP_DISCONNECT_TIMEOUT ( CONFIG_FMB_TCP_CONNECTION_TOUT_SEC * 1000000 ) // disconnect timeout in uS
|
||||
#define MB_TCP_RESP_TIMEOUT_MS ( MB_MASTER_TIMEOUT_MS_RESPOND - 2 ) // slave response time limit
|
||||
#define MB_TCP_SLAVE_PORT_TAG "MB_TCP_SLAVE_PORT"
|
||||
#ifndef SOMAXCONN
|
||||
#define SOMAXCONN 128
|
||||
#endif
|
||||
#define MB_TCP_NET_LISTEN_BACKLOG ( SOMAXCONN )
|
||||
|
||||
/* ----------------------- Prototypes ---------------------------------------*/
|
||||
@ -183,12 +185,16 @@ static int xMBTCPPortAcceptConnection(int xListenSockId, char** pcIPAddr)
|
||||
MB_PORT_CHECK((xListenSockId > 0), -1, "Incorrect listen socket ID.");
|
||||
|
||||
// Address structure large enough for both IPv4 or IPv6 address
|
||||
#if LWIP_IPV6
|
||||
struct sockaddr_in6 xSrcAddr;
|
||||
|
||||
socklen_t xSize = sizeof(struct sockaddr_in6);
|
||||
#else
|
||||
struct sockaddr_in xSrcAddr;
|
||||
socklen_t xSize = sizeof(struct sockaddr_in);
|
||||
#endif
|
||||
CHAR cAddrStr[128];
|
||||
int xSockId = -1;
|
||||
CHAR* pcStr = NULL;
|
||||
socklen_t xSize = sizeof(struct sockaddr_in6);
|
||||
|
||||
// Accept new socket connection if not active
|
||||
xSockId = accept(xListenSockId, (struct sockaddr *)&xSrcAddr, &xSize);
|
||||
@ -197,11 +203,15 @@ static int xMBTCPPortAcceptConnection(int xListenSockId, char** pcIPAddr)
|
||||
close(xSockId);
|
||||
} else {
|
||||
// Get the sender's ip address as string
|
||||
#if LWIP_IPV6
|
||||
if (xSrcAddr.sin6_family == PF_INET) {
|
||||
inet_ntoa_r(((struct sockaddr_in *)&xSrcAddr)->sin_addr.s_addr, cAddrStr, sizeof(cAddrStr) - 1);
|
||||
} else if (xSrcAddr.sin6_family == PF_INET6) {
|
||||
inet6_ntoa_r(xSrcAddr.sin6_addr, cAddrStr, sizeof(cAddrStr) - 1);
|
||||
}
|
||||
#else
|
||||
inet_ntoa_r(((struct sockaddr_in *)&xSrcAddr)->sin_addr.s_addr, cAddrStr, sizeof(cAddrStr) - 1);
|
||||
#endif
|
||||
ESP_LOGI(MB_TCP_SLAVE_PORT_TAG, "Socket (#%d), accept client connection from address: %s", xSockId, cAddrStr);
|
||||
pcStr = calloc(1, strlen(cAddrStr) + 1);
|
||||
if (pcStr && pcIPAddr) {
|
||||
|
@ -3,8 +3,5 @@
|
||||
This directory contains component that is common for Modbus master and slave examples. The component defines Modbus parameters that are shared between examples and provide code that you can copy and adapt into your own projects.
|
||||
For more information please refer to Modbus example README.md files located in the folders:
|
||||
|
||||
* `examples/protocols/modbus/serial/mb_master` Modbus serial master implementation (RTU and ASCII)
|
||||
* `examples/protocols/modbus/serial/mb_slave` Modbus serial slave implementation (RTU and ASCII)
|
||||
* `examples/protocols/modbus/serial/mb_master` Modbus serial master implementation (RTU and ASCII)
|
||||
* `examples/protocols/modbus/tcp/mb_tcp_slave` Modbus serial slave implementation (TCP)
|
||||
* `examples/protocols/modbus/tcp/mb_tcp_master` Modbus serial master implementation (TCP)
|
@ -1,82 +0,0 @@
|
||||
# Modbus Master-Slave Example
|
||||
|
||||
## Overview
|
||||
|
||||
These two projects illustrate the communication between Modbus master and slave device in the segment.
|
||||
Master initializes Modbus interface driver and then reads parameters from slave device in the segment.
|
||||
After several successful read attempts slave sets the alarm relay (end of test condition).
|
||||
Once master reads the alarm it stops communication and destroy driver.
|
||||
|
||||
The examples:
|
||||
|
||||
* `examples/protocols/modbus/serial/mb_master` - Modbus serial master ASCII/RTU
|
||||
* `examples/protocols/modbus/serial/mb_slave` - Modbus serial slave ASCII/RTU
|
||||
|
||||
See README.md for each individual project for more information.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
This example can be run on any commonly available ESP32 development board.
|
||||
The master and slave boards should be connected to each other through the RS485 interface line driver.
|
||||
See the connection schematic in README.md files of each example.
|
||||
|
||||
### Configure the project
|
||||
|
||||
This example test requires communication mode setting for master and slave be the same and slave address set to 1.
|
||||
Please refer to README.md files of each example project for more information.
|
||||
|
||||
## About common_component in this example
|
||||
|
||||
The folder "mb_example_common" includes definitions of parameter structures for master and slave device (both projects share the same parameters).
|
||||
However, currently it is for example purpose only and can be modified for particular application.
|
||||
|
||||
## Example Output
|
||||
|
||||
Example of Slave output:
|
||||
|
||||
```
|
||||
I (343) SLAVE_TEST: Modbus slave stack initialized.
|
||||
I (343) SLAVE_TEST: Start modbus test...
|
||||
I (81463) SLAVE_TEST: HOLDING READ (81150420 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffb2868, SIZE:6
|
||||
I (82463) SLAVE_TEST: HOLDING READ (82150720 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffb2868, SIZE:6
|
||||
I (83573) SLAVE_TEST: HOLDING READ (83260630 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffb2868, SIZE:6
|
||||
I (84603) SLAVE_TEST: HOLDING READ (84290530 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffb2868, SIZE:6
|
||||
I (85703) SLAVE_TEST: HOLDING READ (85396692 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffb2868, SIZE:6
|
||||
```
|
||||
|
||||
Example of Modbus Master output:
|
||||
|
||||
```
|
||||
I (399) MASTER_TEST: Modbus master stack initialized...
|
||||
I (499) MASTER_TEST: Start modbus test...
|
||||
I (549) MASTER_TEST: Characteristic #0 Data_channel_0 (Volts) value = 1.230000 (0x3f9d70a4) read successful.
|
||||
I (629) MASTER_TEST: Characteristic #1 Humidity_1 (%rH) value = 12.100000 (0x4141999a) read successful.
|
||||
I (709) MASTER_TEST: Characteristic #2 Temperature_1 (C) value = 3.560000 (0x4063d70a) read successful.
|
||||
I (769) MASTER_TEST: Characteristic #3 Humidity_2 (%rH) value = 23.400000 (0x41bb3333) read successful.
|
||||
I (829) MASTER_TEST: Characteristic #4 Temperature_2 (C) value = 5.890000 (0x40bc7ae1) read successful.
|
||||
I (889) MASTER_TEST: Characteristic #5 Humidity_3 (%rH) value = 34.500000 (0x420a0000) read successful.
|
||||
E (949) MB_CONTROLLER_MASTER: mbc_master_get_parameter(111): SERIAL master get parameter failure error=(0x108) (ESP_ERR_INVALID_RESPONSE).
|
||||
E (949) MASTER_TEST: Characteristic #6 (RelayP1) read fail, err = 264 (ESP_ERR_INVALID_RESPONSE).
|
||||
E (1029) MB_CONTROLLER_MASTER: mbc_master_get_parameter(111): SERIAL master get parameter failure error=(0x108) (ESP_ERR_INVALID_RESPONSE).
|
||||
E (1029) MASTER_TEST: Characteristic #7 (RelayP2) read fail, err = 264 (ESP_ERR_INVALID_RESPONSE).
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the examples do not work as expected and slave and master boards are not able to communicate correctly it is possible to find the reason for errors.
|
||||
The most important errors are described in master example output and formatted as below:
|
||||
|
||||
```
|
||||
E (1692332) MB_CONTROLLER_MASTER: mbc_master_get_parameter(111): SERIAL master get parameter failure error=(0x107) (ESP_ERR_TIMEOUT).
|
||||
```
|
||||
|
||||
ESP_ERR_TIMEOUT (0x107) - Modbus slave device does not respond during configured timeout. Check the connection and ability for communication using uart_echo_rs485 example or increase
|
||||
Kconfig value CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND (CONFIG_FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS).
|
||||
|
||||
ESP_ERR_NOT_SUPPORTED (0x106), ESP_ERR_INVALID_RESPONSE (0x108) - Modbus slave device does not support requested command or register and sent exeption response.
|
||||
|
||||
ESP_ERR_INVALID_STATE (0x103) - Modbus stack is not configured correctly or can't work correctly due to critical failure.
|
||||
|
||||
|
@ -1,288 +0,0 @@
|
||||
# Need Python 3 string formatting functions
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import re
|
||||
import logging
|
||||
from threading import Thread
|
||||
|
||||
import ttfw_idf
|
||||
|
||||
LOG_LEVEL = logging.DEBUG
|
||||
LOGGER_NAME = "modbus_test"
|
||||
|
||||
# Allowed parameter reads
|
||||
TEST_READ_MIN_COUNT = 10 # Minimum number of correct readings
|
||||
TEST_READ_MAX_ERR_COUNT = 2 # Maximum allowed read errors during initialization
|
||||
|
||||
TEST_THREAD_EXPECT_TIMEOUT = 120 # Test theread expect timeout in seconds
|
||||
TEST_THREAD_JOIN_TIMEOUT = 180 # Test theread join timeout in seconds
|
||||
|
||||
# Test definitions
|
||||
TEST_MASTER_RTU = 'master_rtu'
|
||||
TEST_SLAVE_RTU = 'slave_rtu'
|
||||
|
||||
TEST_MASTER_ASCII = 'master_ascii'
|
||||
TEST_SLAVE_ASCII = 'slave_ascii'
|
||||
|
||||
# Define tuple of strings to expect for each DUT.
|
||||
#
|
||||
master_expect = ("MASTER_TEST: Modbus master stack initialized...", "MASTER_TEST: Start modbus test...", "MASTER_TEST: Destroy master...")
|
||||
slave_expect = ("SLAVE_TEST: Modbus slave stack initialized.", "SLAVE_TEST: Start modbus test...", "SLAVE_TEST: Modbus controller destroyed.")
|
||||
|
||||
# The dictionary for expected values in listing
|
||||
expect_dict_master_ok = {"START": (),
|
||||
"READ_PAR_OK": (),
|
||||
"ALARM_MSG": (u'7',)}
|
||||
|
||||
expect_dict_master_err = {"READ_PAR_ERR": (u'263', u'ESP_ERR_TIMEOUT'),
|
||||
"READ_STK_ERR": (u'107', u'ESP_ERR_TIMEOUT')}
|
||||
|
||||
# The dictionary for regular expression patterns to check in listing
|
||||
pattern_dict_master_ok = {"START": (r'.*I \([0-9]+\) MASTER_TEST: Start modbus test...'),
|
||||
"READ_PAR_OK": (r'.*I\s\([0-9]+\) MASTER_TEST: Characteristic #[0-9]+ [a-zA-Z0-9_]+'
|
||||
r'\s\([a-zA-Z\%\/]+\) value = [a-zA-Z0-9\.\s]*\(0x[a-zA-Z0-9]+\) read successful.'),
|
||||
"ALARM_MSG": (r'.*I \([0-9]*\) MASTER_TEST: Alarm triggered by cid #([0-9]+).')}
|
||||
|
||||
pattern_dict_master_err = {"READ_PAR_ERR_TOUT": (r'.*E \([0-9]+\) MASTER_TEST: Characteristic #[0-9]+'
|
||||
r'\s\([a-zA-Z0-9_]+\) read fail, err = [0-9]+ \([_A-Z]+\).'),
|
||||
"READ_STK_ERR_TOUT": (r'.*E \([0-9]+\) MB_CONTROLLER_MASTER: [a-zA-Z0-9_]+\([0-9]+\):\s'
|
||||
r'SERIAL master get parameter failure error=\(0x([a-zA-Z0-9]+)\) \(([_A-Z]+)\).')}
|
||||
|
||||
# The dictionary for expected values in listing
|
||||
expect_dict_slave_ok = {"START": (),
|
||||
"READ_PAR_OK": (),
|
||||
"DESTROY": ()}
|
||||
|
||||
# The dictionary for regular expression patterns to check in listing
|
||||
pattern_dict_slave_ok = {"START": (r'.*I \([0-9]+\) SLAVE_TEST: Start modbus test...'),
|
||||
"READ_PAR_OK": (r'.*I\s\([0-9]+\) SLAVE_TEST: [A-Z]+ READ \([a-zA-Z0-9_]+ us\),\s'
|
||||
r'ADDR:[0-9]+, TYPE:[0-9]+, INST_ADDR:0x[a-zA-Z0-9]+, SIZE:[0-9]+'),
|
||||
"DESTROY": (r'.*I\s\([0-9]+\) SLAVE_TEST: Modbus controller destroyed.')}
|
||||
|
||||
logger = logging.getLogger(LOGGER_NAME)
|
||||
|
||||
|
||||
class DutTestThread(Thread):
|
||||
def __init__(self, dut=None, name=None, expect=None):
|
||||
""" Initialize the thread parameters
|
||||
"""
|
||||
self.tname = name
|
||||
self.dut = dut
|
||||
self.expected = expect
|
||||
self.result = False
|
||||
self.data = None
|
||||
super(DutTestThread, self).__init__()
|
||||
|
||||
def run(self):
|
||||
""" The function implements thread functionality
|
||||
"""
|
||||
# Must reset again as flashing during start_app will reset multiple times, causing unexpected results
|
||||
self.dut.reset()
|
||||
|
||||
# Capture output from the DUT
|
||||
self.dut.start_capture_raw_data()
|
||||
|
||||
# Check expected strings in the listing
|
||||
for string in self.expected:
|
||||
self.dut.expect(string, TEST_THREAD_EXPECT_TIMEOUT)
|
||||
|
||||
# Check DUT exceptions
|
||||
dut_exceptions = self.dut.get_exceptions()
|
||||
if "Guru Meditation Error:" in dut_exceptions:
|
||||
raise Exception("%s generated an exception: %s\n" % (str(self.dut), dut_exceptions))
|
||||
|
||||
# Mark thread has run to completion without any exceptions
|
||||
self.data = self.dut.stop_capture_raw_data()
|
||||
self.result = True
|
||||
|
||||
|
||||
def test_filter_output(data=None, start_pattern=None, end_pattern=None):
|
||||
"""Use patters to filter output
|
||||
"""
|
||||
start_index = str(data).find(start_pattern)
|
||||
end_index = str(data).find(end_pattern)
|
||||
logger.debug("Listing start index= %d, end=%d" % (start_index, end_index))
|
||||
if start_index == -1 or end_index == -1:
|
||||
return data
|
||||
return data[start_index:end_index + len(end_pattern)]
|
||||
|
||||
|
||||
def test_expect_re(data, pattern):
|
||||
"""
|
||||
Check if re pattern is matched in data cache
|
||||
:param data: data to process
|
||||
:param pattern: compiled RegEx pattern
|
||||
:return: match groups if match succeed otherwise None
|
||||
"""
|
||||
ret = None
|
||||
if isinstance(pattern, type(u'')):
|
||||
pattern = pattern.encode('utf-8')
|
||||
regex = re.compile(pattern)
|
||||
if isinstance(data, type(u'')):
|
||||
data = data.encode('utf-8')
|
||||
match = regex.search(data)
|
||||
if match:
|
||||
ret = tuple(None if x is None else x.decode() for x in match.groups())
|
||||
index = match.end()
|
||||
else:
|
||||
index = None
|
||||
return ret, index
|
||||
|
||||
|
||||
def test_check_output(data=None, check_dict=None, expect_dict=None):
|
||||
""" Check output for the test
|
||||
Check log using regular expressions:
|
||||
"""
|
||||
global logger
|
||||
match_count = 0
|
||||
index = 0
|
||||
data_lines = data.splitlines()
|
||||
for key, pattern in check_dict.items():
|
||||
if key not in expect_dict:
|
||||
break
|
||||
# Check pattern in the each line
|
||||
for line in data_lines:
|
||||
group, index = test_expect_re(line, pattern)
|
||||
if index is not None:
|
||||
logger.debug("Found key{%s}=%s, line: \n%s" % (key, group, line))
|
||||
if expect_dict[key] == group:
|
||||
logger.debug("The result is correct for the key:%s, expected:%s == returned:%s" % (key, str(expect_dict[key]), str(group)))
|
||||
match_count += 1
|
||||
return match_count
|
||||
|
||||
|
||||
def test_check_mode(dut=None, mode_str=None, value=None):
|
||||
""" Check communication mode for dut
|
||||
"""
|
||||
global logger
|
||||
try:
|
||||
opt = dut.app.get_sdkconfig()[mode_str]
|
||||
logger.info("%s {%s} = %s.\n" % (str(dut), mode_str, opt))
|
||||
return value == opt
|
||||
except Exception:
|
||||
logger.info('ENV_TEST_FAILURE: %s: Cannot find option %s in sdkconfig.' % (str(dut), mode_str))
|
||||
return False
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='UT_T2_RS485', ignore=True)
|
||||
def test_modbus_communication(env, comm_mode):
|
||||
global logger
|
||||
|
||||
# Get device under test. "dut1 - master", "dut2 - slave" must be properly connected through RS485 interface driver
|
||||
dut_master = env.get_dut("modbus_master", "examples/protocols/modbus/serial/mb_master", dut_class=ttfw_idf.ESP32DUT)
|
||||
dut_slave = env.get_dut("modbus_slave", "examples/protocols/modbus/serial/mb_slave", dut_class=ttfw_idf.ESP32DUT)
|
||||
|
||||
try:
|
||||
logger.debug("Environment vars: %s\r\n" % os.environ)
|
||||
logger.debug("DUT slave sdkconfig: %s\r\n" % dut_slave.app.get_sdkconfig())
|
||||
logger.debug("DUT master sdkconfig: %s\r\n" % dut_master.app.get_sdkconfig())
|
||||
|
||||
# Check Kconfig configuration options for each built example
|
||||
if test_check_mode(dut_master, "CONFIG_MB_COMM_MODE_ASCII", "y") and test_check_mode(dut_slave, "CONFIG_MB_COMM_MODE_ASCII", "y"):
|
||||
logger.info("ENV_TEST_INFO: Modbus ASCII test mode selected in the configuration. \n")
|
||||
slave_name = TEST_SLAVE_ASCII
|
||||
master_name = TEST_MASTER_ASCII
|
||||
elif test_check_mode(dut_master, "CONFIG_MB_COMM_MODE_RTU", "y") and test_check_mode(dut_slave, "CONFIG_MB_COMM_MODE_RTU", "y"):
|
||||
logger.info("ENV_TEST_INFO: Modbus RTU test mode selected in the configuration. \n")
|
||||
slave_name = TEST_SLAVE_RTU
|
||||
master_name = TEST_MASTER_RTU
|
||||
else:
|
||||
logger.error("ENV_TEST_FAILURE: Communication mode in master and slave configuration don't match.\n")
|
||||
raise Exception("ENV_TEST_FAILURE: Communication mode in master and slave configuration don't match.\n")
|
||||
# Check if slave address for example application is default one to be able to communicate
|
||||
if not test_check_mode(dut_slave, "CONFIG_MB_SLAVE_ADDR", "1"):
|
||||
logger.error("ENV_TEST_FAILURE: Slave address option is incorrect.\n")
|
||||
raise Exception("ENV_TEST_FAILURE: Slave address option is incorrect.\n")
|
||||
|
||||
# Flash app onto each DUT
|
||||
dut_master.start_app()
|
||||
dut_slave.start_app()
|
||||
|
||||
# Create thread for each dut
|
||||
dut_master_thread = DutTestThread(dut=dut_master, name=master_name, expect=master_expect)
|
||||
dut_slave_thread = DutTestThread(dut=dut_slave, name=slave_name, expect=slave_expect)
|
||||
|
||||
# Start each thread
|
||||
dut_slave_thread.start()
|
||||
dut_master_thread.start()
|
||||
|
||||
# Wait for threads to complete
|
||||
dut_slave_thread.join(timeout=TEST_THREAD_JOIN_TIMEOUT)
|
||||
dut_master_thread.join(timeout=TEST_THREAD_JOIN_TIMEOUT)
|
||||
|
||||
if dut_slave_thread.isAlive():
|
||||
logger.error("ENV_TEST_FAILURE: The thread %s is not completed successfully after %d seconds.\n" %
|
||||
(dut_slave_thread.tname, TEST_THREAD_JOIN_TIMEOUT))
|
||||
raise Exception("ENV_TEST_FAILURE: The thread %s is not completed successfully after %d seconds.\n" %
|
||||
(dut_slave_thread.tname, TEST_THREAD_JOIN_TIMEOUT))
|
||||
|
||||
if dut_master_thread.isAlive():
|
||||
logger.error("ENV_TEST_FAILURE: The thread %s is not completed successfully after %d seconds.\n" %
|
||||
(dut_master_thread.tname, TEST_THREAD_JOIN_TIMEOUT))
|
||||
raise Exception("ENV_TEST_FAILURE: The thread %s is not completed successfully after %d seconds.\n" %
|
||||
(dut_master_thread.tname, TEST_THREAD_JOIN_TIMEOUT))
|
||||
finally:
|
||||
dut_master.close()
|
||||
dut_slave.close()
|
||||
|
||||
# Check if test threads completed successfully and captured data
|
||||
if not dut_slave_thread.result or dut_slave_thread.data is None:
|
||||
logger.error("The thread %s was not run successfully." % dut_slave_thread.tname)
|
||||
raise Exception("The thread %s was not run successfully." % dut_slave_thread.tname)
|
||||
|
||||
if not dut_master_thread.result or dut_master_thread.data is None:
|
||||
logger.error("The thread %s was not run successfully." % dut_slave_thread.tname)
|
||||
raise Exception("The thread %s was not run successfully." % dut_master_thread.tname)
|
||||
|
||||
# Filter output to get test messages
|
||||
master_output = test_filter_output(dut_master_thread.data, master_expect[0], master_expect[len(master_expect) - 1])
|
||||
if master_output is not None:
|
||||
logger.info("The data for master thread is captured.")
|
||||
logger.debug(master_output)
|
||||
|
||||
slave_output = test_filter_output(dut_slave_thread.data, slave_expect[0], slave_expect[len(slave_expect) - 1])
|
||||
if slave_output is not None:
|
||||
logger.info("The data for slave thread is captured.")
|
||||
logger.debug(slave_output)
|
||||
|
||||
# Check if parameters are read correctly by master
|
||||
match_count = test_check_output(master_output, pattern_dict_master_ok, expect_dict_master_ok)
|
||||
if match_count < TEST_READ_MIN_COUNT:
|
||||
logger.error("There are errors reading parameters from %s, %d" % (dut_master_thread.tname, match_count))
|
||||
raise Exception("There are errors reading parameters from %s, %d" % (dut_master_thread.tname, match_count))
|
||||
logger.info("OK pattern test for %s, match_count=%d." % (dut_master_thread.tname, match_count))
|
||||
|
||||
# If the test completed successfully (alarm triggered) but there are some errors during reading of parameters
|
||||
match_count = test_check_output(master_output, pattern_dict_master_err, expect_dict_master_err)
|
||||
if match_count > TEST_READ_MAX_ERR_COUNT:
|
||||
logger.error("There are errors reading parameters from %s, %d" % (dut_master_thread.tname, match_count))
|
||||
raise Exception("There are errors reading parameters from %s, %d" % (dut_master_thread.tname, match_count))
|
||||
logger.info("ERROR pattern test for %s, match_count=%d." % (dut_master_thread.tname, match_count))
|
||||
|
||||
match_count = test_check_output(slave_output, pattern_dict_slave_ok, expect_dict_slave_ok)
|
||||
if match_count < TEST_READ_MIN_COUNT:
|
||||
logger.error("There are errors reading parameters from %s, %d" % (dut_slave_thread.tname, match_count))
|
||||
raise Exception("There are errors reading parameters from %s, %d" % (dut_slave_thread.tname, match_count))
|
||||
logger.info("OK pattern test for %s, match_count=%d." % (dut_slave_thread.tname, match_count))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
logger = logging.getLogger(LOGGER_NAME)
|
||||
# create file handler which logs even debug messages
|
||||
fh = logging.FileHandler('modbus_test.log')
|
||||
fh.setLevel(logging.DEBUG)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
# create console handler
|
||||
ch = logging.StreamHandler()
|
||||
ch.setLevel(logging.INFO)
|
||||
# set format of output for both handlers
|
||||
formatter = logging.Formatter('%(levelname)s:%(message)s')
|
||||
ch.setFormatter(formatter)
|
||||
fh.setFormatter(formatter)
|
||||
logger.addHandler(fh)
|
||||
logger.addHandler(ch)
|
||||
logger.info("Start script %s." % os.path.basename(__file__))
|
||||
print("Logging file name: %s" % logger.handlers[0].baseFilename)
|
||||
test_modbus_communication()
|
||||
logging.shutdown()
|
@ -1,8 +0,0 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/protocols/modbus/mb_example_common)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(modbus_master)
|
@ -1,11 +0,0 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := modbus_master
|
||||
|
||||
EXTRA_COMPONENT_DIRS := $(IDF_PATH)/examples/protocols/modbus/mb_example_common
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
@ -1,155 +0,0 @@
|
||||
| Supported Targets | ESP32 |
|
||||
| ----------------- | ----- |
|
||||
|
||||
# Modbus Master Example
|
||||
|
||||
This example demonstrates using of FreeModbus stack port implementation for ESP32 as a master device.
|
||||
This implementation is able to read/write values of slave devices connected into Modbus segment. All parameters to be accessed are defined in data dictionary of the modbus master example source file.
|
||||
The values represented as characteristics with its name and characteristic CID which are linked into registers of slave devices connected into Modbus segment.
|
||||
The example implements simple control algorithm and checks parameters from slave device and gets alarm (relay in the slave device) when value of holding_data0 parameter exceeded limit.
|
||||
The instances for the modbus parameters are common for master and slave examples and located in `examples/protocols/modbus/mb_example_common` folder.
|
||||
|
||||
Example parameters definition:
|
||||
--------------------------------------------------------------------------------------------------
|
||||
| Slave Address | Characteristic ID | Characteristic name | Description |
|
||||
|---------------------|----------------------|----------------------|----------------------------|
|
||||
| MB_DEVICE_ADDR1 | CID_INP_DATA_0, | Data_channel_0 | Data channel 1 |
|
||||
| MB_DEVICE_ADDR1 | CID_HOLD_DATA_0, | Humidity_1 | Humidity 1 |
|
||||
| MB_DEVICE_ADDR1 | CID_INP_DATA_1 | Temperature_1 | Sensor temperature |
|
||||
| MB_DEVICE_ADDR1 | CID_HOLD_DATA_1, | Humidity_2 | Humidity 2 |
|
||||
| MB_DEVICE_ADDR1 | CID_INP_DATA_2 | Temperature_2 | Ambient temperature |
|
||||
| MB_DEVICE_ADDR1 | CID_HOLD_DATA_2 | Humidity_3 | Humidity 3 |
|
||||
| MB_DEVICE_ADDR1 | CID_RELAY_P1 | RelayP1 | Alarm Relay outputs on/off |
|
||||
| MB_DEVICE_ADDR1 | CID_RELAY_P2 | RelayP2 | Alarm Relay outputs on/off |
|
||||
--------------------------------------------------------------------------------------------------
|
||||
Note: The Slave Address is the same for all parameters for example test but it can be changed in the ```Example Data (Object) Dictionary``` table of master example to address parameters from other slaves.
|
||||
The Kconfig ```Modbus slave address``` - CONFIG_MB_SLAVE_ADDR parameter in slave example can be configured to create Modbus multi slave segment.
|
||||
|
||||
Simplified Modbus connection schematic for example test:
|
||||
```
|
||||
MB_DEVICE_ADDR1
|
||||
------------- -------------
|
||||
| | RS485 network | |
|
||||
| Slave 1 |---<>--+---<>---| Master |
|
||||
| | | |
|
||||
------------- -------------
|
||||
```
|
||||
Modbus multi slave segment connection schematic:
|
||||
```
|
||||
MB_DEVICE_ADDR1
|
||||
-------------
|
||||
| |
|
||||
| Slave 1 |---<>--+
|
||||
| | |
|
||||
------------- |
|
||||
MB_DEVICE_ADDR2 |
|
||||
------------- | -------------
|
||||
| | | | |
|
||||
| Slave 2 |---<>--+---<>---| Master |
|
||||
| | | | |
|
||||
------------- | -------------
|
||||
MB_DEVICE_ADDR3 |
|
||||
------------- RS485 network
|
||||
| | |
|
||||
| Slave 3 |---<>--+
|
||||
| |
|
||||
-------------
|
||||
```
|
||||
|
||||
## Hardware required :
|
||||
Option 1:
|
||||
PC (Modbus Slave app) + USB Serial adapter connected to USB port + RS485 line drivers + ESP32 WROVER-KIT board.
|
||||
|
||||
Option 2:
|
||||
Several ESP32 WROVER-KIT board flashed with modbus_slave example software to represent slave device with specific slave address (See CONFIG_MB_SLAVE_ADDR). The slave addresses for each board have to be configured as defined in "connection schematic" above.
|
||||
One ESP32 WROVER-KIT board flashed with modbus_master example. All the boards require connection of RS485 line drivers (see below).
|
||||
|
||||
The MAX485 line driver is used as an example below but other similar chips can be used as well.
|
||||
RS485 example circuit schematic for connection of master and slave devices into segment:
|
||||
```
|
||||
VCC ---------------+ +--------------- VCC
|
||||
| |
|
||||
+-------x-------+ +-------x-------+
|
||||
RXD <------| RO | DIFFERENTIAL | RO|-----> RXD
|
||||
| B|---------------|B |
|
||||
TXD ------>| DI MAX485 | \ / | MAX485 DI|<----- TXD
|
||||
ESP32 WROVER KIT 1 | | RS-485 side | | External PC (emulator) with USB to serial or
|
||||
RTS --+--->| DE | / \ | DE|---+ ESP32 WROVER KIT 2 (slave)
|
||||
| | A|---------------|A | |
|
||||
+----| /RE | PAIR | /RE|---+-- RTS
|
||||
+-------x-------+ +-------x-------+
|
||||
| |
|
||||
--- ---
|
||||
Modbus Master device Modbus Slave device
|
||||
|
||||
```
|
||||
|
||||
## How to setup and use an example:
|
||||
|
||||
### Configure the application
|
||||
Start the command below to setup configuration:
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
Configure the UART pins used for modbus communication using and table below.
|
||||
Define the communication mode parameter for master and slave in Kconfig - CONFIG_MB_COMM_MODE (must be the same for master and slave devices in one segment).
|
||||
Configure the slave address for each slave in the Modbus segment (the CONFIG_MB_SLAVE_ADDR in Kconfig).
|
||||
```
|
||||
--------------------------------------------------------------------------------------------------------------------------
|
||||
| ESP32 Interface | #define | Default ESP32 Pin | Default ESP32-S2 Pins | External RS485 Driver Pin |
|
||||
| ----------------------|--------------------|-----------------------|-----------------------|---------------------------|
|
||||
| Transmit Data (TxD) | CONFIG_MB_UART_TXD | GPIO23 | GPIO20 | DI |
|
||||
| Receive Data (RxD) | CONFIG_MB_UART_RXD | GPIO22 | GPIO19 | RO |
|
||||
| Request To Send (RTS) | CONFIG_MB_UART_RTS | GPIO18 | GPIO18 | ~RE/DE |
|
||||
| Ground | n/a | GND | GND | GND |
|
||||
--------------------------------------------------------------------------------------------------------------------------
|
||||
```
|
||||
Note: The GPIO22 - GPIO25 can not be used with ESP32-S2 chip because they are used for flash chip connection. Please refer to UART documentation for selected target.
|
||||
|
||||
Connect USB to RS485 adapter to computer and connect its D+, D- output lines with the D+, D- lines of RS485 line driver connected to ESP32 (See picture above).
|
||||
|
||||
The communication parameters of Modbus stack allow to configure it appropriately but usually it is enough to use default settings.
|
||||
See the help string of parameters for more information.
|
||||
|
||||
### Setup external Modbus slave devices or emulator
|
||||
Option 1:
|
||||
Configure the external Modbus master software according to port configuration parameters used in the example. The Modbus Slave application can be used with this example to emulate slave devices with its parameters. Use official documentation for software to setup emulation of slave devices.
|
||||
|
||||
Option 2:
|
||||
Other option is to have the modbus_slave example application flashed into ESP32 WROVER KIT board and connect boards together as showed on the Modbus connection schematic above. See the Modbus slave API documentation to configure communication parameters and slave addresses as defined in "Example parameters definition" table above.
|
||||
|
||||
### Build and flash software of master device
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
Example output of the application:
|
||||
```
|
||||
I (9035) MASTER_TEST: Characteristic #0 Data_channel_0 (Volts) value = 1.120000 (0x3f8f5c29) read successful.
|
||||
I (9045) MASTER_TEST: Characteristic #1 Humidity_1 (%rH) value = 5.539999 (0x40b147ac) read successful.
|
||||
I (9045) MASTER_TEST: Characteristic #2 Temperature_1 (C) value = 2.340000 (0x4015c28f) read successful.
|
||||
I (9055) MASTER_TEST: Characteristic #3 Humidity_2 (%rH) value = 2.560000 (0x4023d70a) read successful.
|
||||
I (9065) MASTER_TEST: Characteristic #4 Temperature_2 (C) value = 3.560000 (0x4063d70a) read successful.
|
||||
I (9075) MASTER_TEST: Characteristic #5 Humidity_3 (%rH) value = 3.780000 (0x4071eb85) read successful.
|
||||
I (9085) MASTER_TEST: Characteristic #6 RelayP1 (on/off) value = OFF (0x55) read successful.
|
||||
I (9095) MASTER_TEST: Characteristic #7 RelayP2 (on/off) value = OFF (0xaa) read successful.
|
||||
I (9605) MASTER_TEST: Characteristic #0 Data_channel_0 (Volts) value = 1.120000 (0x3f8f5c29) read successful.
|
||||
I (9615) MASTER_TEST: Characteristic #1 Humidity_1 (%rH) value = 5.739999 (0x40b7ae12) read successful.
|
||||
I (9615) MASTER_TEST: Characteristic #2 Temperature_1 (C) value = 2.340000 (0x4015c28f) read successful.
|
||||
I (9625) MASTER_TEST: Characteristic #3 Humidity_2 (%rH) value = 2.560000 (0x4023d70a) read successful.
|
||||
I (9635) MASTER_TEST: Characteristic #4 Temperature_2 (C) value = 3.560000 (0x4063d70a) read successful.
|
||||
I (9645) MASTER_TEST: Characteristic #5 Humidity_3 (%rH) value = 3.780000 (0x4071eb85) read successful.
|
||||
I (9655) MASTER_TEST: Characteristic #6 RelayP1 (on/off) value = OFF (0x55) read successful.
|
||||
I (9665) MASTER_TEST: Characteristic #7 RelayP2 (on/off) value = ON (0xff) read successful.
|
||||
I (10175) MASTER_TEST: Alarm triggered by cid #7.
|
||||
I (10175) MASTER_TEST: Destroy master...
|
||||
|
||||
```
|
||||
The example reads the characteristics from slave device(s), while alarm is not triggered in the slave device (See the "Example parameters definition"). The output line describes Timestamp, Cid of characteristic, Characteristic name (Units), Characteristic value (Hex).
|
||||
|
@ -1,5 +0,0 @@
|
||||
set(PROJECT_NAME "modbus_master")
|
||||
|
||||
idf_component_register(SRCS "master.c"
|
||||
INCLUDE_DIRS ".")
|
||||
|
@ -1,66 +0,0 @@
|
||||
menu "Modbus Example Configuration"
|
||||
|
||||
config MB_UART_PORT_NUM
|
||||
int "UART port number"
|
||||
range 0 2 if IDF_TARGET_ESP32
|
||||
default 2 if IDF_TARGET_ESP32
|
||||
range 0 1 if IDF_TARGET_ESP32S2
|
||||
default 1 if IDF_TARGET_ESP32S2
|
||||
help
|
||||
UART communication port number for Modbus example.
|
||||
|
||||
config MB_UART_BAUD_RATE
|
||||
int "UART communication speed"
|
||||
range 1200 115200
|
||||
default 115200
|
||||
help
|
||||
UART communication speed for Modbus example.
|
||||
|
||||
config MB_UART_RXD
|
||||
int "UART RXD pin number"
|
||||
range 0 34 if IDF_TARGET_ESP32
|
||||
default 22 if IDF_TARGET_ESP32
|
||||
range 0 46 if IDF_TARGET_ESP32S2
|
||||
default 19 if IDF_TARGET_ESP32S2
|
||||
help
|
||||
GPIO number for UART RX pin. See UART documentation for more information
|
||||
about available pin numbers for UART.
|
||||
|
||||
config MB_UART_TXD
|
||||
int "UART TXD pin number"
|
||||
range 0 34 if IDF_TARGET_ESP32
|
||||
default 23 if IDF_TARGET_ESP32
|
||||
range 0 46 if IDF_TARGET_ESP32S2
|
||||
default 20 if IDF_TARGET_ESP32S2
|
||||
help
|
||||
GPIO number for UART TX pin. See UART documentation for more information
|
||||
about available pin numbers for UART.
|
||||
|
||||
config MB_UART_RTS
|
||||
int "UART RTS pin number"
|
||||
range 0 34 if IDF_TARGET_ESP32
|
||||
range 0 46 if IDF_TARGET_ESP32S2
|
||||
default 18
|
||||
help
|
||||
GPIO number for UART RTS pin. This pin is connected to
|
||||
~RE/DE pin of RS485 transceiver to switch direction.
|
||||
See UART documentation for more information about available pin
|
||||
numbers for UART.
|
||||
|
||||
choice MB_COMM_MODE
|
||||
prompt "Modbus communication mode"
|
||||
default MB_COMM_MODE_RTU if CONFIG_FMB_COMM_MODE_RTU_EN
|
||||
help
|
||||
Selection of Modbus communication mode option for Modbus.
|
||||
|
||||
config MB_COMM_MODE_RTU
|
||||
bool "RTU mode"
|
||||
depends on FMB_COMM_MODE_RTU_EN
|
||||
|
||||
config MB_COMM_MODE_ASCII
|
||||
bool "ASCII mode"
|
||||
depends on FMB_COMM_MODE_ASCII_EN
|
||||
|
||||
endchoice
|
||||
|
||||
endmenu
|
@ -1,4 +0,0 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
@ -1,320 +0,0 @@
|
||||
// Copyright 2016-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "string.h"
|
||||
#include "esp_log.h"
|
||||
#include "modbus_params.h" // for modbus parameters structures
|
||||
#include "mbcontroller.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#define MB_PORT_NUM (CONFIG_MB_UART_PORT_NUM) // Number of UART port used for Modbus connection
|
||||
#define MB_DEV_SPEED (CONFIG_MB_UART_BAUD_RATE) // The communication speed of the UART
|
||||
|
||||
// Note: Some pins on target chip cannot be assigned for UART communication.
|
||||
// See UART documentation for selected board and target to configure pins using Kconfig.
|
||||
|
||||
// The number of parameters that intended to be used in the particular control process
|
||||
#define MASTER_MAX_CIDS num_device_parameters
|
||||
|
||||
// Number of reading of parameters from slave
|
||||
#define MASTER_MAX_RETRY 30
|
||||
|
||||
// Timeout to update cid over Modbus
|
||||
#define UPDATE_CIDS_TIMEOUT_MS (500)
|
||||
#define UPDATE_CIDS_TIMEOUT_TICS (UPDATE_CIDS_TIMEOUT_MS / portTICK_RATE_MS)
|
||||
|
||||
// Timeout between polls
|
||||
#define POLL_TIMEOUT_MS (1)
|
||||
#define POLL_TIMEOUT_TICS (POLL_TIMEOUT_MS / portTICK_RATE_MS)
|
||||
|
||||
#define MASTER_TAG "MASTER_TEST"
|
||||
|
||||
#define MASTER_CHECK(a, ret_val, str, ...) \
|
||||
if (!(a)) { \
|
||||
ESP_LOGE(MASTER_TAG, "%s(%u): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||
return (ret_val); \
|
||||
}
|
||||
|
||||
// The macro to get offset for parameter in the appropriate structure
|
||||
#define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) + 1))
|
||||
#define INPUT_OFFSET(field) ((uint16_t)(offsetof(input_reg_params_t, field) + 1))
|
||||
#define COIL_OFFSET(field) ((uint16_t)(offsetof(coil_reg_params_t, field) + 1))
|
||||
// Discrete offset macro
|
||||
#define DISCR_OFFSET(field) ((uint16_t)(offsetof(discrete_reg_params_t, field) + 1))
|
||||
|
||||
#define STR(fieldname) ((const char*)( fieldname ))
|
||||
// Options can be used as bit masks or parameter limits
|
||||
#define OPTS(min_val, max_val, step_val) { .opt1 = min_val, .opt2 = max_val, .opt3 = step_val }
|
||||
|
||||
// Enumeration of modbus device addresses accessed by master device
|
||||
enum {
|
||||
MB_DEVICE_ADDR1 = 1 // Only one slave device used for the test (add other slave addresses here)
|
||||
};
|
||||
|
||||
// Enumeration of all supported CIDs for device (used in parameter definition table)
|
||||
enum {
|
||||
CID_INP_DATA_0 = 0,
|
||||
CID_HOLD_DATA_0,
|
||||
CID_INP_DATA_1,
|
||||
CID_HOLD_DATA_1,
|
||||
CID_INP_DATA_2,
|
||||
CID_HOLD_DATA_2,
|
||||
CID_HOLD_TEST_REG,
|
||||
CID_RELAY_P1,
|
||||
CID_RELAY_P2,
|
||||
CID_COUNT
|
||||
};
|
||||
|
||||
// Example Data (Object) Dictionary for Modbus parameters:
|
||||
// The CID field in the table must be unique.
|
||||
// Modbus Slave Addr field defines slave address of the device with correspond parameter.
|
||||
// Modbus Reg Type - Type of Modbus register area (Holding register, Input Register and such).
|
||||
// Reg Start field defines the start Modbus register number and Reg Size defines the number of registers for the characteristic accordingly.
|
||||
// The Instance Offset defines offset in the appropriate parameter structure that will be used as instance to save parameter value.
|
||||
// Data Type, Data Size specify type of the characteristic and its data size.
|
||||
// Parameter Options field specifies the options that can be used to process parameter value (limits or masks).
|
||||
// Access Mode - can be used to implement custom options for processing of characteristic (Read/Write restrictions, factory mode values and etc).
|
||||
const mb_parameter_descriptor_t device_parameters[] = {
|
||||
// { CID, Param Name, Units, Modbus Slave Addr, Modbus Reg Type, Reg Start, Reg Size, Instance Offset, Data Type, Data Size, Parameter Options, Access Mode}
|
||||
{ CID_INP_DATA_0, STR("Data_channel_0"), STR("Volts"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0, 2,
|
||||
INPUT_OFFSET(input_data0), PARAM_TYPE_FLOAT, 4, OPTS( -10, 10, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
{ CID_HOLD_DATA_0, STR("Humidity_1"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0, 2,
|
||||
HOLD_OFFSET(holding_data0), PARAM_TYPE_FLOAT, 4, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
{ CID_INP_DATA_1, STR("Temperature_1"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 2, 2,
|
||||
INPUT_OFFSET(input_data1), PARAM_TYPE_FLOAT, 4, OPTS( -40, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
{ CID_HOLD_DATA_1, STR("Humidity_2"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 2, 2,
|
||||
HOLD_OFFSET(holding_data1), PARAM_TYPE_FLOAT, 4, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
{ CID_INP_DATA_2, STR("Temperature_2"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 4, 2,
|
||||
INPUT_OFFSET(input_data2), PARAM_TYPE_FLOAT, 4, OPTS( -40, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
{ CID_HOLD_DATA_2, STR("Humidity_3"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 4, 2,
|
||||
HOLD_OFFSET(holding_data2), PARAM_TYPE_FLOAT, 4, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
{ CID_HOLD_TEST_REG, STR("Test_regs"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 10, 58,
|
||||
HOLD_OFFSET(test_regs), PARAM_TYPE_ASCII, 116, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
{ CID_RELAY_P1, STR("RelayP1"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_COIL, 0, 8,
|
||||
COIL_OFFSET(coils_port0), PARAM_TYPE_U16, 2, OPTS( BIT1, 0, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||
{ CID_RELAY_P2, STR("RelayP2"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_COIL, 8, 8,
|
||||
COIL_OFFSET(coils_port1), PARAM_TYPE_U16, 2, OPTS( BIT0, 0, 0 ), PAR_PERMS_READ_WRITE_TRIGGER }
|
||||
};
|
||||
|
||||
// Calculate number of parameters in the table
|
||||
const uint16_t num_device_parameters = (sizeof(device_parameters)/sizeof(device_parameters[0]));
|
||||
|
||||
// The function to get pointer to parameter storage (instance) according to parameter description table
|
||||
static void* master_get_param_data(const mb_parameter_descriptor_t* param_descriptor)
|
||||
{
|
||||
assert(param_descriptor != NULL);
|
||||
void* instance_ptr = NULL;
|
||||
if (param_descriptor->param_offset != 0) {
|
||||
switch(param_descriptor->mb_param_type)
|
||||
{
|
||||
case MB_PARAM_HOLDING:
|
||||
instance_ptr = ((void*)&holding_reg_params + param_descriptor->param_offset - 1);
|
||||
break;
|
||||
case MB_PARAM_INPUT:
|
||||
instance_ptr = ((void*)&input_reg_params + param_descriptor->param_offset - 1);
|
||||
break;
|
||||
case MB_PARAM_COIL:
|
||||
instance_ptr = ((void*)&coil_reg_params + param_descriptor->param_offset - 1);
|
||||
break;
|
||||
case MB_PARAM_DISCRETE:
|
||||
instance_ptr = ((void*)&discrete_reg_params + param_descriptor->param_offset - 1);
|
||||
break;
|
||||
default:
|
||||
instance_ptr = NULL;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(MASTER_TAG, "Wrong parameter offset for CID #%d", param_descriptor->cid);
|
||||
assert(instance_ptr != NULL);
|
||||
}
|
||||
return instance_ptr;
|
||||
}
|
||||
|
||||
// User operation function to read slave values and check alarm
|
||||
static void master_operation_func(void *arg)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
float value = 0;
|
||||
bool alarm_state = false;
|
||||
const mb_parameter_descriptor_t* param_descriptor = NULL;
|
||||
|
||||
ESP_LOGI(MASTER_TAG, "Start modbus test...");
|
||||
|
||||
for(uint16_t retry = 0; retry <= MASTER_MAX_RETRY && (!alarm_state); retry++) {
|
||||
// Read all found characteristics from slave(s)
|
||||
for (uint16_t cid = 0; (err != ESP_ERR_NOT_FOUND) && cid < MASTER_MAX_CIDS; cid++)
|
||||
{
|
||||
// Get data from parameters description table
|
||||
// and use this information to fill the characteristics description table
|
||||
// and having all required fields in just one table
|
||||
err = mbc_master_get_cid_info(cid, ¶m_descriptor);
|
||||
if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) {
|
||||
void* temp_data_ptr = master_get_param_data(param_descriptor);
|
||||
assert(temp_data_ptr);
|
||||
uint8_t type = 0;
|
||||
if ((param_descriptor->param_type == PARAM_TYPE_ASCII) &&
|
||||
(param_descriptor->cid == CID_HOLD_TEST_REG)) {
|
||||
// Check for long array of registers of type PARAM_TYPE_ASCII
|
||||
err = mbc_master_get_parameter(cid, (char*)param_descriptor->param_key,
|
||||
(uint8_t*)temp_data_ptr, &type);
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGI(MASTER_TAG, "Characteristic #%d %s (%s) value = (0x%08x) read successful.",
|
||||
param_descriptor->cid,
|
||||
(char*)param_descriptor->param_key,
|
||||
(char*)param_descriptor->param_units,
|
||||
*(uint32_t*)temp_data_ptr);
|
||||
// Initialize data of test array and write to slave
|
||||
if (*(uint32_t*)temp_data_ptr != 0xAAAAAAAA) {
|
||||
memset((void*)temp_data_ptr, 0xAA, param_descriptor->param_size);
|
||||
*(uint32_t*)temp_data_ptr = 0xAAAAAAAA;
|
||||
err = mbc_master_set_parameter(cid, (char*)param_descriptor->param_key,
|
||||
(uint8_t*)temp_data_ptr, &type);
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGI(MASTER_TAG, "Characteristic #%d %s (%s) value = (0x%08x), write successful.",
|
||||
param_descriptor->cid,
|
||||
(char*)param_descriptor->param_key,
|
||||
(char*)param_descriptor->param_units,
|
||||
*(uint32_t*)temp_data_ptr);
|
||||
} else {
|
||||
ESP_LOGE(MASTER_TAG, "Characteristic #%d (%s) write fail, err = 0x%x (%s).",
|
||||
param_descriptor->cid,
|
||||
(char*)param_descriptor->param_key,
|
||||
(int)err,
|
||||
(char*)esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(MASTER_TAG, "Characteristic #%d (%s) read fail, err = 0x%x (%s).",
|
||||
param_descriptor->cid,
|
||||
(char*)param_descriptor->param_key,
|
||||
(int)err,
|
||||
(char*)esp_err_to_name(err));
|
||||
}
|
||||
} else {
|
||||
err = mbc_master_get_parameter(cid, (char*)param_descriptor->param_key,
|
||||
(uint8_t*)&value, &type);
|
||||
if (err == ESP_OK) {
|
||||
*(float*)temp_data_ptr = value;
|
||||
if ((param_descriptor->mb_param_type == MB_PARAM_HOLDING) ||
|
||||
(param_descriptor->mb_param_type == MB_PARAM_INPUT)) {
|
||||
ESP_LOGI(MASTER_TAG, "Characteristic #%d %s (%s) value = %f (0x%x) read successful.",
|
||||
param_descriptor->cid,
|
||||
(char*)param_descriptor->param_key,
|
||||
(char*)param_descriptor->param_units,
|
||||
value,
|
||||
*(uint32_t*)temp_data_ptr);
|
||||
if (((value > param_descriptor->param_opts.max) ||
|
||||
(value < param_descriptor->param_opts.min))) {
|
||||
alarm_state = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
uint16_t state = *(uint16_t*)temp_data_ptr;
|
||||
const char* rw_str = (state & param_descriptor->param_opts.opt1) ? "ON" : "OFF";
|
||||
ESP_LOGI(MASTER_TAG, "Characteristic #%d %s (%s) value = %s (0x%x) read successful.",
|
||||
param_descriptor->cid,
|
||||
(char*)param_descriptor->param_key,
|
||||
(char*)param_descriptor->param_units,
|
||||
(const char*)rw_str,
|
||||
*(uint16_t*)temp_data_ptr);
|
||||
if (state & param_descriptor->param_opts.opt1) {
|
||||
alarm_state = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(MASTER_TAG, "Characteristic #%d (%s) read fail, err = 0x%x (%s).",
|
||||
param_descriptor->cid,
|
||||
(char*)param_descriptor->param_key,
|
||||
(int)err,
|
||||
(char*)esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
vTaskDelay(POLL_TIMEOUT_TICS); // timeout between polls
|
||||
}
|
||||
}
|
||||
vTaskDelay(UPDATE_CIDS_TIMEOUT_TICS); //
|
||||
}
|
||||
|
||||
if (alarm_state) {
|
||||
ESP_LOGI(MASTER_TAG, "Alarm triggered by cid #%d.",
|
||||
param_descriptor->cid);
|
||||
} else {
|
||||
ESP_LOGE(MASTER_TAG, "Alarm is not triggered after %d retries.",
|
||||
MASTER_MAX_RETRY);
|
||||
}
|
||||
ESP_LOGI(MASTER_TAG, "Destroy master...");
|
||||
ESP_ERROR_CHECK(mbc_master_destroy());
|
||||
}
|
||||
|
||||
// Modbus master initialization
|
||||
static esp_err_t master_init(void)
|
||||
{
|
||||
// Initialize and start Modbus controller
|
||||
mb_communication_info_t comm = {
|
||||
.port = MB_PORT_NUM,
|
||||
#if CONFIG_MB_COMM_MODE_ASCII
|
||||
.mode = MB_MODE_ASCII,
|
||||
#elif CONFIG_MB_COMM_MODE_RTU
|
||||
.mode = MB_MODE_RTU,
|
||||
#endif
|
||||
.baudrate = MB_DEV_SPEED,
|
||||
.parity = MB_PARITY_NONE
|
||||
};
|
||||
void* master_handler = NULL;
|
||||
|
||||
esp_err_t err = mbc_master_init(MB_PORT_SERIAL_MASTER, &master_handler);
|
||||
MASTER_CHECK((master_handler != NULL), ESP_ERR_INVALID_STATE,
|
||||
"mb controller initialization fail.");
|
||||
MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
"mb controller initialization fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
err = mbc_master_setup((void*)&comm);
|
||||
MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
"mb controller setup fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
|
||||
// Set UART pin numbers
|
||||
err = uart_set_pin(MB_PORT_NUM, CONFIG_MB_UART_TXD, CONFIG_MB_UART_RXD,
|
||||
CONFIG_MB_UART_RTS, UART_PIN_NO_CHANGE);
|
||||
|
||||
err = mbc_master_start();
|
||||
MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
"mb controller start fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
|
||||
MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
"mb serial set pin failure, uart_set_pin() returned (0x%x).", (uint32_t)err);
|
||||
// Set driver mode to Half Duplex
|
||||
err = uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX);
|
||||
MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
"mb serial set mode failure, uart_set_mode() returned (0x%x).", (uint32_t)err);
|
||||
|
||||
vTaskDelay(5);
|
||||
err = mbc_master_set_descriptor(&device_parameters[0], num_device_parameters);
|
||||
MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||
"mb controller set descriptor fail, returns(0x%x).",
|
||||
(uint32_t)err);
|
||||
ESP_LOGI(MASTER_TAG, "Modbus master stack initialized...");
|
||||
return err;
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// Initialization of device peripheral and objects
|
||||
ESP_ERROR_CHECK(master_init());
|
||||
vTaskDelay(10);
|
||||
|
||||
master_operation_func(NULL);
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
#
|
||||
# Modbus configuration
|
||||
#
|
||||
CONFIG_MB_COMM_MODE_ASCII=y
|
||||
CONFIG_MB_UART_BAUD_RATE=115200
|
||||
CONFIG_FMB_TIMER_PORT_ENABLED=y
|
||||
CONFIG_FMB_TIMER_GROUP=0
|
||||
CONFIG_FMB_TIMER_INDEX=0
|
||||
CONFIG_FMB_TIMER_ISR_IN_IRAM=y
|
||||
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=200
|
||||
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=150
|
@ -1,9 +0,0 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/protocols/modbus/mb_example_common)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
project(modbus_slave)
|
@ -1,11 +0,0 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := modbus_slave
|
||||
|
||||
EXTRA_COMPONENT_DIRS := $(IDF_PATH)/examples/protocols/modbus/mb_example_common
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
@ -1,96 +0,0 @@
|
||||
| Supported Targets | ESP32 |
|
||||
| ----------------- | ----- |
|
||||
|
||||
# Modbus Slave Example
|
||||
|
||||
This example demonstrates using of FreeModbus stack port implementation for ESP32. The external Modbus host is able to read/write device parameters using Modbus protocol transport. The parameters accessible thorough Modbus are located in `mb_example_common/modbus_params.h\c` files and can be updated by user.
|
||||
These are represented in structures holding_reg_params, input_reg_params, coil_reg_params, discrete_reg_params for holding registers, input parameters, coils and discrete inputs accordingly. The app_main application demonstrates how to setup Modbus stack and use notifications about parameters change from host system.
|
||||
The FreeModbus stack located in `components/freemodbus` folder and contains the `/port` folder inside with FreeModbus stack port for ESP32. There are some parameters that can be configured in KConfig file to start stack correctly (See description below for more information).
|
||||
|
||||
The slave example uses shared parameter structures defined in `examples/protocols/modbus/mb_example_common` folder.
|
||||
|
||||
## Hardware required :
|
||||
Option 1:
|
||||
PC + USB Serial adapter connected to USB port + RS485 line drivers + ESP32 WROVER-KIT board.
|
||||
The MAX485 line driver is used as an example below but other similar chips can be used as well.
|
||||
|
||||
Option 2:
|
||||
The modbus_master example application configured as described in its README.md file and flashed into ESP32 WROVER-KIT board.
|
||||
Note: The ```Example Data (Object) Dictionary``` in the modbus_master example can be edited to address parameters from other slaves connected into Modbus segment.
|
||||
|
||||
RS485 example circuit schematic:
|
||||
```
|
||||
VCC ---------------+ +--------------- VCC
|
||||
| |
|
||||
+-------x-------+ +-------x-------+
|
||||
RXD <------| RO | DIFFERENTIAL | RO|-----> RXD
|
||||
| B|---------------|B |
|
||||
TXD ------>| DI MAX485 | \ / | MAX485 DI|<----- TXD
|
||||
ESP32 WROVER KIT 1 | | RS-485 side | | Modbus master
|
||||
RTS --+--->| DE | / \ | DE|---+
|
||||
| | A|---------------|A | |
|
||||
+----| /RE | PAIR | /RE|---+-- RTS
|
||||
+-------x--------+ +-------x-------+
|
||||
| |
|
||||
--- ---
|
||||
```
|
||||
|
||||
## How to setup and use an example:
|
||||
|
||||
### Configure the application
|
||||
Start the command below to show the configuration menu:
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
Select Modbus Example Configuration menu item.
|
||||
Configure the UART pins used for modbus communication using command and table below.
|
||||
```
|
||||
--------------------------------------------------------------------------------------------------------------------------
|
||||
| ESP32 Interface | #define | Default ESP32 Pin | Default ESP32-S2 Pins | External RS485 Driver Pin |
|
||||
| ----------------------|--------------------|-----------------------|-----------------------|---------------------------|
|
||||
| Transmit Data (TxD) | CONFIG_MB_UART_TXD | GPIO23 | GPIO20 | DI |
|
||||
| Receive Data (RxD) | CONFIG_MB_UART_RXD | GPIO22 | GPIO19 | RO |
|
||||
| Request To Send (RTS) | CONFIG_MB_UART_RTS | GPIO18 | GPIO18 | ~RE/DE |
|
||||
| Ground | n/a | GND | GND | GND |
|
||||
--------------------------------------------------------------------------------------------------------------------------
|
||||
```
|
||||
Note: The GPIO22 - GPIO25 can not be used with ESP32-S2 chip because they are used for flash chip connection. Please refer to UART documentation for selected target.
|
||||
|
||||
Define the ```Modbus communiction mode``` for slave in Kconfig - CONFIG_MB_COMM_MODE (must be the same for master and slave application).
|
||||
Set ```Modbus slave address``` for the example application (by default for example script is set to 1).
|
||||
The communication parameters of freemodbus stack (Component config->Modbus configuration) allow to configure it appropriately but usually it is enough to use default settings.
|
||||
See the help strings of parameters for more information.
|
||||
|
||||
### Setup external Modbus master software
|
||||
Option 1:
|
||||
Configure the external Modbus master software according to port configuration parameters used in application.
|
||||
As an example the Modbus Poll application can be used with this example.
|
||||
Option 2:
|
||||
Setup ESP32 WROVER-KIT board and set modbus_master example configuration as described in its README.md file.
|
||||
Setup one or more slave boards with different slave addresses and connect them into the same Modbus segment (See configuration above).
|
||||
Note: The ```Modbus communiction mode``` parameter must be the same for master and slave example application to be able to communicate with each other.
|
||||
|
||||
### Build and flash software
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
Example output of the application:
|
||||
```
|
||||
I (13941) SLAVE_TEST: INPUT READ (13651163 us), ADDR:1, TYPE:8, INST_ADDR:0x3ffb2fd0, SIZE:2
|
||||
I (13951) SLAVE_TEST: HOLDING READ (13656431 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffb2fe0, SIZE:2
|
||||
I (13961) SLAVE_TEST: INPUT READ (13665877 us), ADDR:3, TYPE:8, INST_ADDR:0x3ffb2fd4, SIZE:2
|
||||
I (13971) SLAVE_TEST: HOLDING READ (13676010 us), ADDR:3, TYPE:2, INST_ADDR:0x3ffb2fe4, SIZE:2
|
||||
I (13981) SLAVE_TEST: INPUT READ (13686130 us), ADDR:5, TYPE:8, INST_ADDR:0x3ffb2fd8, SIZE:2
|
||||
I (13991) SLAVE_TEST: HOLDING READ (13696267 us), ADDR:5, TYPE:2, INST_ADDR:0x3ffb2fe8, SIZE:2
|
||||
I (14001) SLAVE_TEST: COILS READ (13706331 us), ADDR:0, TYPE:32, INST_ADDR:0x3ffb2fcc, SIZE:8
|
||||
I (14001) SLAVE_TEST: Modbus controller destroyed.
|
||||
```
|
||||
The output lines describe type of operation, its timestamp, modbus address, access type, storage address in parameter structure and number of registers accordingly.
|
||||
|
@ -1,4 +0,0 @@
|
||||
set(PROJECT_NAME "modbus_slave")
|
||||
|
||||
idf_component_register(SRCS "slave.c"
|
||||
INCLUDE_DIRS ".")
|
@ -1,75 +0,0 @@
|
||||
menu "Modbus Example Configuration"
|
||||
|
||||
config MB_UART_PORT_NUM
|
||||
int "UART port number"
|
||||
range 0 2 if IDF_TARGET_ESP32
|
||||
default 2 if IDF_TARGET_ESP32
|
||||
range 0 1 if IDF_TARGET_ESP32S2
|
||||
default 1 if IDF_TARGET_ESP32S2
|
||||
help
|
||||
UART communication port number for Modbus example.
|
||||
|
||||
config MB_UART_BAUD_RATE
|
||||
int "UART communication speed"
|
||||
range 1200 115200
|
||||
default 115200
|
||||
help
|
||||
UART communication speed for Modbus example.
|
||||
|
||||
config MB_UART_RXD
|
||||
int "UART RXD pin number"
|
||||
range 0 34 if IDF_TARGET_ESP32
|
||||
default 22 if IDF_TARGET_ESP32
|
||||
range 0 46 if IDF_TARGET_ESP32S2
|
||||
default 19 if IDF_TARGET_ESP32S2
|
||||
help
|
||||
GPIO number for UART RX pin. See UART documentation for more information
|
||||
about available pin numbers for UART.
|
||||
|
||||
config MB_UART_TXD
|
||||
int "UART TXD pin number"
|
||||
range 0 34 if IDF_TARGET_ESP32
|
||||
default 23 if IDF_TARGET_ESP32
|
||||
range 0 46 if IDF_TARGET_ESP32S2
|
||||
default 20 if IDF_TARGET_ESP32S2
|
||||
help
|
||||
GPIO number for UART TX pin. See UART documentation for more information
|
||||
about available pin numbers for UART.
|
||||
|
||||
config MB_UART_RTS
|
||||
int "UART RTS pin number"
|
||||
range 0 34 if IDF_TARGET_ESP32
|
||||
range 0 46 if IDF_TARGET_ESP32S2
|
||||
default 18
|
||||
help
|
||||
GPIO number for UART RTS pin. This pin is connected to
|
||||
~RE/DE pin of RS485 transceiver to switch direction.
|
||||
See UART documentation for more information about available pin
|
||||
numbers for UART.
|
||||
|
||||
choice MB_COMM_MODE
|
||||
prompt "Modbus communication mode"
|
||||
default MB_COMM_MODE_RTU if CONFIG_FMB_COMM_MODE_RTU_EN
|
||||
help
|
||||
Selection of Modbus communication mode option for Modbus.
|
||||
|
||||
config MB_COMM_MODE_RTU
|
||||
bool "RTU mode"
|
||||
depends on FMB_COMM_MODE_RTU_EN
|
||||
|
||||
config MB_COMM_MODE_ASCII
|
||||
bool "ASCII mode"
|
||||
depends on FMB_COMM_MODE_ASCII_EN
|
||||
|
||||
endchoice
|
||||
|
||||
config MB_SLAVE_ADDR
|
||||
int "Modbus slave address"
|
||||
range 1 127
|
||||
default 1
|
||||
help
|
||||
This is the Modbus slave address in the network.
|
||||
It is used to organize Modbus network with several slaves connected into the same segment.
|
||||
|
||||
|
||||
endmenu
|
@ -1,4 +0,0 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
@ -1,202 +0,0 @@
|
||||
/* FreeModbus Slave Example ESP32
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
#include "mbcontroller.h" // for mbcontroller defines and api
|
||||
#include "modbus_params.h" // for modbus parameters structures
|
||||
#include "esp_log.h" // for log_write
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#define MB_PORT_NUM (CONFIG_MB_UART_PORT_NUM) // Number of UART port used for Modbus connection
|
||||
#define MB_SLAVE_ADDR (CONFIG_MB_SLAVE_ADDR) // The address of device in Modbus network
|
||||
#define MB_DEV_SPEED (CONFIG_MB_UART_BAUD_RATE) // The communication speed of the UART
|
||||
|
||||
// Note: Some pins on target chip cannot be assigned for UART communication.
|
||||
// Please refer to documentation for selected board and target to configure pins using Kconfig.
|
||||
|
||||
// Defines below are used to define register start address for each type of Modbus registers
|
||||
#define MB_REG_DISCRETE_INPUT_START (0x0000)
|
||||
#define MB_REG_INPUT_START (0x0000)
|
||||
#define MB_REG_HOLDING_START (0x0000)
|
||||
#define MB_REG_COILS_START (0x0000)
|
||||
|
||||
#define MB_PAR_INFO_GET_TOUT (10) // Timeout for get parameter info
|
||||
#define MB_CHAN_DATA_MAX_VAL (6)
|
||||
#define MB_CHAN_DATA_OFFSET (0.2f)
|
||||
#define MB_READ_MASK (MB_EVENT_INPUT_REG_RD \
|
||||
| MB_EVENT_HOLDING_REG_RD \
|
||||
| MB_EVENT_DISCRETE_RD \
|
||||
| MB_EVENT_COILS_RD)
|
||||
#define MB_WRITE_MASK (MB_EVENT_HOLDING_REG_WR \
|
||||
| MB_EVENT_COILS_WR)
|
||||
#define MB_READ_WRITE_MASK (MB_READ_MASK | MB_WRITE_MASK)
|
||||
|
||||
static const char *SLAVE_TAG = "SLAVE_TEST";
|
||||
|
||||
static portMUX_TYPE param_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
// Set register values into known state
|
||||
static void setup_reg_data(void)
|
||||
{
|
||||
// Define initial state of parameters
|
||||
discrete_reg_params.discrete_input1 = 1;
|
||||
discrete_reg_params.discrete_input3 = 1;
|
||||
discrete_reg_params.discrete_input5 = 1;
|
||||
discrete_reg_params.discrete_input7 = 1;
|
||||
|
||||
holding_reg_params.holding_data0 = 1.34;
|
||||
holding_reg_params.holding_data1 = 2.56;
|
||||
holding_reg_params.holding_data2 = 3.78;
|
||||
holding_reg_params.holding_data3 = 4.90;
|
||||
|
||||
coil_reg_params.coils_port0 = 0x55;
|
||||
coil_reg_params.coils_port1 = 0xAA;
|
||||
|
||||
input_reg_params.input_data0 = 1.12;
|
||||
input_reg_params.input_data1 = 2.34;
|
||||
input_reg_params.input_data2 = 3.56;
|
||||
input_reg_params.input_data3 = 4.78;
|
||||
}
|
||||
|
||||
// An example application of Modbus slave. It is based on freemodbus stack.
|
||||
// See deviceparams.h file for more information about assigned Modbus parameters.
|
||||
// These parameters can be accessed from main application and also can be changed
|
||||
// by external Modbus master host.
|
||||
void app_main(void)
|
||||
{
|
||||
mb_param_info_t reg_info; // keeps the Modbus registers access information
|
||||
mb_communication_info_t comm_info; // Modbus communication parameters
|
||||
mb_register_area_descriptor_t reg_area; // Modbus register area descriptor structure
|
||||
|
||||
// Set UART log level
|
||||
esp_log_level_set(SLAVE_TAG, ESP_LOG_INFO);
|
||||
void* mbc_slave_handler = NULL;
|
||||
|
||||
ESP_ERROR_CHECK(mbc_slave_init(MB_PORT_SERIAL_SLAVE, &mbc_slave_handler)); // Initialization of Modbus controller
|
||||
|
||||
// Setup communication parameters and start stack
|
||||
#if CONFIG_MB_COMM_MODE_ASCII
|
||||
comm_info.mode = MB_MODE_ASCII,
|
||||
#elif CONFIG_MB_COMM_MODE_RTU
|
||||
comm_info.mode = MB_MODE_RTU,
|
||||
#endif
|
||||
comm_info.slave_addr = MB_SLAVE_ADDR;
|
||||
comm_info.port = MB_PORT_NUM;
|
||||
comm_info.baudrate = MB_DEV_SPEED;
|
||||
comm_info.parity = MB_PARITY_NONE;
|
||||
ESP_ERROR_CHECK(mbc_slave_setup((void*)&comm_info));
|
||||
|
||||
// The code below initializes Modbus register area descriptors
|
||||
// for Modbus Holding Registers, Input Registers, Coils and Discrete Inputs
|
||||
// Initialization should be done for each supported Modbus register area according to register map.
|
||||
// When external master trying to access the register in the area that is not initialized
|
||||
// by mbc_slave_set_descriptor() API call then Modbus stack
|
||||
// will send exception response for this register area.
|
||||
reg_area.type = MB_PARAM_HOLDING; // Set type of register area
|
||||
reg_area.start_offset = MB_REG_HOLDING_START; // Offset of register area in Modbus protocol
|
||||
reg_area.address = (void*)&holding_reg_params; // Set pointer to storage instance
|
||||
reg_area.size = sizeof(holding_reg_params); // Set the size of register storage instance
|
||||
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
|
||||
|
||||
// Initialization of Input Registers area
|
||||
reg_area.type = MB_PARAM_INPUT;
|
||||
reg_area.start_offset = MB_REG_INPUT_START;
|
||||
reg_area.address = (void*)&input_reg_params;
|
||||
reg_area.size = sizeof(input_reg_params);
|
||||
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
|
||||
|
||||
// Initialization of Coils register area
|
||||
reg_area.type = MB_PARAM_COIL;
|
||||
reg_area.start_offset = MB_REG_COILS_START;
|
||||
reg_area.address = (void*)&coil_reg_params;
|
||||
reg_area.size = sizeof(coil_reg_params);
|
||||
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
|
||||
|
||||
// Initialization of Discrete Inputs register area
|
||||
reg_area.type = MB_PARAM_DISCRETE;
|
||||
reg_area.start_offset = MB_REG_DISCRETE_INPUT_START;
|
||||
reg_area.address = (void*)&discrete_reg_params;
|
||||
reg_area.size = sizeof(discrete_reg_params);
|
||||
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
|
||||
|
||||
setup_reg_data(); // Set values into known state
|
||||
|
||||
// Starts of modbus controller and stack
|
||||
ESP_ERROR_CHECK(mbc_slave_start());
|
||||
|
||||
// Set UART pin numbers
|
||||
ESP_ERROR_CHECK(uart_set_pin(MB_PORT_NUM, CONFIG_MB_UART_TXD,
|
||||
CONFIG_MB_UART_RXD, CONFIG_MB_UART_RTS,
|
||||
UART_PIN_NO_CHANGE));
|
||||
|
||||
// Set UART driver mode to Half Duplex
|
||||
ESP_ERROR_CHECK(uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX));
|
||||
|
||||
ESP_LOGI(SLAVE_TAG, "Modbus slave stack initialized.");
|
||||
ESP_LOGI(SLAVE_TAG, "Start modbus test...");
|
||||
|
||||
// The cycle below will be terminated when parameter holdingRegParams.dataChan0
|
||||
// incremented each access cycle reaches the CHAN_DATA_MAX_VAL value.
|
||||
for(;holding_reg_params.holding_data0 < MB_CHAN_DATA_MAX_VAL;) {
|
||||
// Check for read/write events of Modbus master for certain events
|
||||
mb_event_group_t event = mbc_slave_check_event(MB_READ_WRITE_MASK);
|
||||
const char* rw_str = (event & MB_READ_MASK) ? "READ" : "WRITE";
|
||||
|
||||
// Filter events and process them accordingly
|
||||
if(event & (MB_EVENT_HOLDING_REG_WR | MB_EVENT_HOLDING_REG_RD)) {
|
||||
// Get parameter information from parameter queue
|
||||
ESP_ERROR_CHECK(mbc_slave_get_param_info(®_info, MB_PAR_INFO_GET_TOUT));
|
||||
ESP_LOGI(SLAVE_TAG, "HOLDING %s (%u us), ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u",
|
||||
rw_str,
|
||||
(uint32_t)reg_info.time_stamp,
|
||||
(uint32_t)reg_info.mb_offset,
|
||||
(uint32_t)reg_info.type,
|
||||
(uint32_t)reg_info.address,
|
||||
(uint32_t)reg_info.size);
|
||||
if (reg_info.address == (uint8_t*)&holding_reg_params.holding_data0)
|
||||
{
|
||||
portENTER_CRITICAL(¶m_lock);
|
||||
holding_reg_params.holding_data0 += MB_CHAN_DATA_OFFSET;
|
||||
if (holding_reg_params.holding_data0 >= (MB_CHAN_DATA_MAX_VAL - MB_CHAN_DATA_OFFSET)) {
|
||||
coil_reg_params.coils_port1 = 0xFF;
|
||||
}
|
||||
portEXIT_CRITICAL(¶m_lock);
|
||||
}
|
||||
} else if (event & MB_EVENT_INPUT_REG_RD) {
|
||||
ESP_ERROR_CHECK(mbc_slave_get_param_info(®_info, MB_PAR_INFO_GET_TOUT));
|
||||
ESP_LOGI(SLAVE_TAG, "INPUT READ (%u us), ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u",
|
||||
(uint32_t)reg_info.time_stamp,
|
||||
(uint32_t)reg_info.mb_offset,
|
||||
(uint32_t)reg_info.type,
|
||||
(uint32_t)reg_info.address,
|
||||
(uint32_t)reg_info.size);
|
||||
} else if (event & MB_EVENT_DISCRETE_RD) {
|
||||
ESP_ERROR_CHECK(mbc_slave_get_param_info(®_info, MB_PAR_INFO_GET_TOUT));
|
||||
ESP_LOGI(SLAVE_TAG, "DISCRETE READ (%u us): ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u",
|
||||
(uint32_t)reg_info.time_stamp,
|
||||
(uint32_t)reg_info.mb_offset,
|
||||
(uint32_t)reg_info.type,
|
||||
(uint32_t)reg_info.address,
|
||||
(uint32_t)reg_info.size);
|
||||
} else if (event & (MB_EVENT_COILS_RD | MB_EVENT_COILS_WR)) {
|
||||
ESP_ERROR_CHECK(mbc_slave_get_param_info(®_info, MB_PAR_INFO_GET_TOUT));
|
||||
ESP_LOGI(SLAVE_TAG, "COILS %s (%u us), ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u",
|
||||
rw_str,
|
||||
(uint32_t)reg_info.time_stamp,
|
||||
(uint32_t)reg_info.mb_offset,
|
||||
(uint32_t)reg_info.type,
|
||||
(uint32_t)reg_info.address,
|
||||
(uint32_t)reg_info.size);
|
||||
if (coil_reg_params.coils_port1 == 0xFF) break;
|
||||
}
|
||||
}
|
||||
// Destroy of Modbus controller on alarm
|
||||
ESP_LOGI(SLAVE_TAG,"Modbus controller destroyed.");
|
||||
vTaskDelay(100);
|
||||
ESP_ERROR_CHECK(mbc_slave_destroy());
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
#
|
||||
# Modbus configuration
|
||||
#
|
||||
CONFIG_MB_COMM_MODE_ASCII=y
|
||||
CONFIG_MB_SLAVE_ADDR=1
|
||||
CONFIG_MB_UART_BAUD_RATE=115200
|
||||
CONFIG_FMB_TIMER_PORT_ENABLED=y
|
||||
CONFIG_FMB_TIMER_GROUP=0
|
||||
CONFIG_FMB_TIMER_INDEX=0
|
||||
CONFIG_FMB_TIMER_ISR_IN_IRAM=y
|
@ -18,7 +18,7 @@ See README.md for each individual project for more information.
|
||||
|
||||
### Hardware Required
|
||||
|
||||
This example can be run on any commonly available ESP32(-S2) development board.
|
||||
This example can be run on any commonly available ESP8266 development board.
|
||||
The master and slave boards should be connected to the same network (see the README.md file in example folder) and slave address `CONFIG_MB_SLAVE_ADDR` be defined for slave board(s).
|
||||
See the connection schematic in README.md files of each example.
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Modbus TCP Master Example
|
||||
|
||||
This example demonstrates using of FreeModbus stack port implementation for ESP32 as a TCP master device.
|
||||
This example demonstrates using of FreeModbus stack port implementation for ESP8266 as a TCP master device.
|
||||
This implementation is able to read/write values of slave devices connected into Modbus segment. All parameters to be accessed are defined in data dictionary of the modbus master example source file.
|
||||
The values represented as characteristics with its name and characteristic CID which are linked into registers of slave devices connected into Modbus segment.
|
||||
The example implements simple control algorithm and checks parameters from slave device and gets alarm (relay in the slave device) when value of parameter exceeded limit.
|
||||
@ -46,7 +46,7 @@ Modbus multi slave segment connection schematic:
|
||||
| | | | |
|
||||
------------- | -------------
|
||||
MB_DEVICE_ADDR3 |
|
||||
------------- Network (Ethernet or WiFi connection)
|
||||
------------- Network (WiFi connection)
|
||||
| | |
|
||||
| Slave 3 |---<>--+
|
||||
| |
|
||||
@ -55,18 +55,18 @@ Modbus multi slave segment connection schematic:
|
||||
|
||||
## Hardware required :
|
||||
Option 1:
|
||||
PC (Modbus TCP Slave application) + ESP32(-S2) development board with modbus_tcp_slave example.
|
||||
PC (Modbus TCP Slave application) + ESP8266 development board with modbus_tcp_slave example.
|
||||
|
||||
Option 2:
|
||||
Several ESP32(-S2) boards flashed with modbus_tcp_slave example software to represent slave devices. The IP slave addresses for each board have to be configured in `Modbus Example Configuration` menu according to the communication table of example.
|
||||
One ESP32(-S2) development board should be flashed with modbus_master example and connected to the same network. All the boards require configuration of network settings as described in `examples/common_components/protocol_examples_common`.
|
||||
Several ESP8266 boards flashed with modbus_tcp_slave example software to represent slave devices. The IP slave addresses for each board have to be configured in `Modbus Example Configuration` menu according to the communication table of example.
|
||||
One ESP8266 development board should be flashed with modbus_master example and connected to the same network. All the boards require configuration of network settings as described in `examples/common_components/protocol_examples_common`.
|
||||
|
||||
## How to setup and use an example:
|
||||
|
||||
### Configure the application
|
||||
Start the command below to setup configuration:
|
||||
```
|
||||
idf.py menuconfig
|
||||
make menuconfig
|
||||
```
|
||||
|
||||
The communication parameters of Modbus stack allow to configure it appropriately but usually it is enough to use default settings.
|
||||
@ -88,56 +88,57 @@ Option 1:
|
||||
Configure the external Modbus master software according to port configuration parameters used in the example. The Modbus Slave application can be used with this example to emulate slave devices with its parameters. Use official documentation for software to setup emulation of slave devices.
|
||||
|
||||
Option 2:
|
||||
Other option is to have the modbus_slave example application flashed into ESP32 WROVER KIT board and connect boards together as showed on the Modbus connection schematic above. See the Modbus slave API documentation to configure communication parameters and slave addresses as defined in "Example parameters definition" table above.
|
||||
Other option is to have the modbus_slave example application flashed into ESP8266 WROVER KIT board and connect boards together as showed on the Modbus connection schematic above. See the Modbus slave API documentation to configure communication parameters and slave addresses as defined in "Example parameters definition" table above.
|
||||
|
||||
### Build and flash software of master device
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
make flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
See the Getting Started Guide for full steps to configure and use ESP8266 RTOS to build projects.
|
||||
|
||||
## Example Output
|
||||
Example output of the application:
|
||||
```
|
||||
I (4644) esp_netif_handlers: example_connect: sta ip: 192.168.1.39, mask: 255.255.255.0, gw: 192.168.1.1
|
||||
I (4644) example_connect: Got IPv4 event: Interface "example_connect: sta" address: 192.168.1.39
|
||||
I (5644) example_connect: Got IPv6 event: Interface "example_connect: sta" address: fe80:0000:0000:0000:bedd:c2ff:fed1:b210, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
I (5644) example_connect: Connected to example_connect: sta
|
||||
I (5654) example_connect: - IPv4 address: 192.168.1.39
|
||||
I (5664) example_connect: - IPv6 address: fe80:0000:0000:0000:bedd:c2ff:fed1:b210, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
I (5674) uart: ESP_INTR_FLAG_IRAM flag not set while CONFIG_UART_ISR_IN_IRAM is enabled, flag updated
|
||||
I (5684) MASTER_TEST: Leave IP(0) = [192.168.1.21] set by user.
|
||||
I (5694) MASTER_TEST: IP(1) is not set in the table.
|
||||
I (5694) MASTER_TEST: Configured 1 IP addresse(s).
|
||||
I (5704) MASTER_TEST: Modbus master stack initialized...
|
||||
I (5704) MB_TCP_MASTER_PORT: TCP master stack initialized.
|
||||
I (5724) MB_TCP_MASTER_PORT: Host[IP]: "192.168.1.21"[192.168.1.21]
|
||||
I (5724) MB_TCP_MASTER_PORT: Add slave IP: 192.168.1.21
|
||||
I (5734) MB_TCP_MASTER_PORT: Connecting to slaves...
|
||||
-.-.-.I (5844) MB_TCP_MASTER_PORT: Connected 1 slaves, start polling...
|
||||
I (6004) MASTER_TEST: Start modbus test...
|
||||
I (6044) MASTER_TEST: Characteristic #0 Data_channel_0 (Volts) value = 1.120000 (0x3f8f5c29) read successful.
|
||||
I (6054) MASTER_TEST: Characteristic #1 Humidity_1 (%rH) value = 1.340000 (0x3fab851f) read successful.
|
||||
I (6074) MASTER_TEST: Characteristic #2 Temperature_1 (C) value = 2.340000 (0x4015c28f) read successful.
|
||||
I (6084) MASTER_TEST: Characteristic #3 Humidity_2 (%rH) value = 2.560000 (0x4023d70a) read successful.
|
||||
I (6094) MASTER_TEST: Characteristic #4 Temperature_2 (C) value = 3.560000 (0x4063d70a) read successful.
|
||||
I (6104) MASTER_TEST: Characteristic #5 Humidity_3 (%rH) value = 3.780000 (0x4071eb85) read successful.
|
||||
I (6124) MASTER_TEST: Characteristic #6 RelayP1 (on/off) value = OFF (0x55) read successful.
|
||||
I (6134) MASTER_TEST: Characteristic #7 RelayP2 (on/off) value = OFF (0xaa) read successful.
|
||||
I (6854) MASTER_TEST: Characteristic #0 Data_channel_0 (Volts) value = 1.120000 (0x3f8f5c29) read successful.
|
||||
I (7064) MASTER_TEST: Characteristic #1 Humidity_1 (%rH) value = 1.740000 (0x3fdeb852) read successful.
|
||||
I (7264) MASTER_TEST: Characteristic #2 Temperature_1 (C) value = 2.340000 (0x4015c28f) read successful.
|
||||
...
|
||||
I (45974) MASTER_TEST: Characteristic #4 Temperature_2 (C) value = 3.560000 (0x4063d70a) read successful.
|
||||
I (46174) MASTER_TEST: Characteristic #5 Humidity_3 (%rH) value = 3.780000 (0x4071eb85) read successful.
|
||||
I (46384) MASTER_TEST: Characteristic #6 RelayP1 (on/off) value = OFF (0x55) read successful.
|
||||
I (46584) MASTER_TEST: Characteristic #7 RelayP2 (on/off) value = ON (0xff) read successful.
|
||||
I (47094) MASTER_TEST: Alarm triggered by cid #7.
|
||||
I (47094) MASTER_TEST: Destroy master...
|
||||
I (2893) tcpip_adapter: sta ip: 192.168.3.21, mask: 255.255.255.0, gw: 192.168.3.1
|
||||
I (2899) example_connect: Connected to tm_20#
|
||||
I (2900) example_connect: IPv4 address: 192.168.3.21
|
||||
I (2909) wifi: pm stop
|
||||
I (2913) MASTER_TEST: Query PTR: _modbus._tcp.local
|
||||
PTR : mb_slave_tcp_01
|
||||
TXT : [3] mb_id=33221100; mac=A4CF12E8733B; board=esp32;
|
||||
I (6003) MASTER_TEST: Resolved slave mb_slave_tcp_01[192.168.3.20]:502
|
||||
I (6008) MASTER_TEST: Index: 0, sl_addr: 1, name:mb_slave_tcp_01, resolve to IP: [192.168.3.20]
|
||||
I (6023) MASTER_TEST: Index: 1, sl_addr: 1, name:mb_slave_tcp_01, set to IP: [192.168.3.20]
|
||||
I (6036) MASTER_TEST: Index: 2, sl_addr: 1, name:mb_slave_tcp_01, set to IP: [192.168.3.20]
|
||||
I (6050) MASTER_TEST: Index: 3, sl_addr: 1, name:mb_slave_tcp_01, set to IP: [192.168.3.20]
|
||||
I (6064) MASTER_TEST: Index: 4, sl_addr: 1, name:mb_slave_tcp_01, set to IP: [192.168.3.20]
|
||||
I (6078) MASTER_TEST: Index: 5, sl_addr: 1, name:mb_slave_tcp_01, set to IP: [192.168.3.20]
|
||||
I (6092) MASTER_TEST: Index: 6, sl_addr: 1, name:mb_slave_tcp_01, set to IP: [192.168.3.20]
|
||||
I (6106) MASTER_TEST: Index: 7, sl_addr: 1, name:mb_slave_tcp_01, set to IP: [192.168.3.20]
|
||||
I (6125) MASTER_TEST: Modbus master stack initialized...
|
||||
I (6129) MB_TCP_MASTER_PORT: TCP master stack initialized.
|
||||
I (6139) MB_TCP_MASTER_PORT: Host[IP]: "192.168.3.20"[192.168.3.20]
|
||||
I (6149) MB_TCP_MASTER_PORT: Add slave IP: 192.168.3.20
|
||||
I (6158) MB_TCP_MASTER_PORT: Connecting to slaves...
|
||||
-.tcp_connect: can only connect from state CLOSED
|
||||
I (6186) MB_TCP_MASTER_PORT: Connected 1 slaves, start polling...
|
||||
I (6332) MASTER_TEST: Start modbus test...
|
||||
I (6363) MASTER_TEST: Characteristic #0 Data_channel_0 (Volts) value = 1.120000 (0x3f8f5c29) read successful.
|
||||
I (6393) MASTER_TEST: Characteristic #1 Humidity_1 (%rH) value = 1.340000 (0x3fab851f) read successful.
|
||||
I (6423) MASTER_TEST: Characteristic #2 Temperature_1 (C) value = 2.340000 (0x4015c28f) read successful.
|
||||
I (6463) MASTER_TEST: Characteristic #3 Humidity_2 (%rH) value = 2.560000 (0x4023d70a) read successful.
|
||||
......
|
||||
I (11273) MASTER_TEST: Characteristic #4 Temperature_2 (C) value = 3.560000 (0x4063d70a) read successful.
|
||||
I (11324) MASTER_TEST: Characteristic #5 Humidity_3 (%rH) value = 3.780000 (0x4071eb85) read successful.
|
||||
I (11374) MASTER_TEST: Characteristic #6 RelayP1 (on/off) value = OFF (0x55) read successful.
|
||||
I (11424) MASTER_TEST: Characteristic #7 RelayP2 (on/off) value = ON (0xff) read successful.
|
||||
I (11923) MASTER_TEST: Alarm triggered by cid #7.
|
||||
I (11925) MASTER_TEST: Destroy master...
|
||||
|
||||
```
|
||||
The example reads the characteristics from slave device(s), while alarm is not triggered in the slave device (See the "Example parameters definition"). The output line describes Timestamp, Cid of characteristic, Characteristic name (Units), Characteristic value (Hex data).
|
||||
|
||||
|
@ -8,6 +8,7 @@ menu "Modbus TCP Example Configuration"
|
||||
|
||||
config MB_MDNS_IP_RESOLVER
|
||||
bool "Resolve Modbus slave addresses using mDNS service."
|
||||
select ENABLE_MDNS
|
||||
|
||||
config MB_SLAVE_IP_FROM_STDIN
|
||||
bool "Configure Modbus slave addresses from stdin"
|
||||
|
@ -20,7 +20,9 @@
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_netif.h"
|
||||
#ifdef CONFIG_MB_MDNS_IP_RESOLVER
|
||||
#include "mdns.h"
|
||||
#endif
|
||||
#include "protocol_examples_common.h"
|
||||
|
||||
#include "modbus_params.h" // for modbus parameters structures
|
||||
@ -264,11 +266,11 @@ static char* master_get_slave_ip_str(mdns_ip_addr_t* address, mb_tcp_addr_type_t
|
||||
char* slave_ip_str = NULL;
|
||||
|
||||
while (a) {
|
||||
if ((a->addr.type == ESP_IPADDR_TYPE_V6) && (addr_type == MB_IPV6)) {
|
||||
if ((a->addr.type == IPADDR_TYPE_V6) && (addr_type == MB_IPV6)) {
|
||||
if (-1 == asprintf(&slave_ip_str, IPV6STR, IPV62STR(a->addr.u_addr.ip6))) {
|
||||
abort();
|
||||
}
|
||||
} else if ((a->addr.type == ESP_IPADDR_TYPE_V4) && (addr_type == MB_IPV4)) {
|
||||
} else if ((a->addr.type == IPADDR_TYPE_V4) && (addr_type == MB_IPV4)) {
|
||||
if (-1 == asprintf(&slave_ip_str, IPSTR, IP2STR(&(a->addr.u_addr.ip4)))) {
|
||||
abort();
|
||||
}
|
||||
@ -450,7 +452,6 @@ static void master_operation_func(void *arg)
|
||||
err = mbc_master_get_parameter(cid, (char*)param_descriptor->param_key,
|
||||
(uint8_t*)&value, &type);
|
||||
if (err == ESP_OK) {
|
||||
*(float*)temp_data_ptr = value;
|
||||
if ((param_descriptor->mb_param_type == MB_PARAM_HOLDING) ||
|
||||
(param_descriptor->mb_param_type == MB_PARAM_INPUT)) {
|
||||
ESP_LOGI(MASTER_TAG, "Characteristic #%d %s (%s) value = %f (0x%x) read successful.",
|
||||
@ -458,21 +459,21 @@ static void master_operation_func(void *arg)
|
||||
(char*)param_descriptor->param_key,
|
||||
(char*)param_descriptor->param_units,
|
||||
value,
|
||||
*(uint32_t*)temp_data_ptr);
|
||||
*(uint32_t*)&value);
|
||||
if (((value > param_descriptor->param_opts.max) ||
|
||||
(value < param_descriptor->param_opts.min))) {
|
||||
alarm_state = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
uint16_t state = *(uint16_t*)temp_data_ptr;
|
||||
uint16_t state = *(uint16_t*)&value;
|
||||
const char* rw_str = (state & param_descriptor->param_opts.opt1) ? "ON" : "OFF";
|
||||
ESP_LOGI(MASTER_TAG, "Characteristic #%d %s (%s) value = %s (0x%x) read successful.",
|
||||
param_descriptor->cid,
|
||||
(char*)param_descriptor->param_key,
|
||||
(char*)param_descriptor->param_units,
|
||||
(const char*)rw_str,
|
||||
*(uint16_t*)temp_data_ptr);
|
||||
*(uint16_t*)&value);
|
||||
if (state & param_descriptor->param_opts.opt1) {
|
||||
alarm_state = true;
|
||||
break;
|
||||
@ -507,7 +508,7 @@ static void master_operation_func(void *arg)
|
||||
static esp_err_t master_init(void)
|
||||
{
|
||||
esp_err_t result = nvs_flash_init();
|
||||
if (result == ESP_ERR_NVS_NO_FREE_PAGES || result == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
if (result == ESP_ERR_NVS_NO_FREE_PAGES) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
result = nvs_flash_init();
|
||||
}
|
||||
@ -536,7 +537,9 @@ static esp_err_t master_init(void)
|
||||
#endif
|
||||
comm_info.ip_mode = MB_MODE_TCP;
|
||||
comm_info.ip_addr = (void*)slave_ip_address_table;
|
||||
comm_info.ip_netif_ptr = (void*)get_example_netif();
|
||||
void * nif = NULL;
|
||||
ESP_ERROR_CHECK(tcpip_adapter_get_netif(TCPIP_ADAPTER_IF_STA, &nif));
|
||||
comm_info.ip_netif_ptr = nif;
|
||||
|
||||
#if CONFIG_MB_MDNS_IP_RESOLVER
|
||||
int res = 0;
|
||||
|
@ -6,14 +6,7 @@ CONFIG_FMB_TCP_PORT_DEFAULT=502
|
||||
CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20
|
||||
CONFIG_FMB_PORT_TASK_STACK_SIZE=4096
|
||||
CONFIG_FMB_PORT_TASK_PRIO=10
|
||||
CONFIG_FMB_COMM_MODE_RTU_EN=n
|
||||
CONFIG_FMB_COMM_MODE_ASCII_EN=n
|
||||
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=2000
|
||||
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300
|
||||
CONFIG_FMB_TIMER_PORT_ENABLED=y
|
||||
CONFIG_FMB_TIMER_GROUP=0
|
||||
CONFIG_FMB_TIMER_INDEX=0
|
||||
CONFIG_FMB_TIMER_ISR_IN_IRAM=y
|
||||
CONFIG_MB_MDNS_IP_RESOLVER=n
|
||||
CONFIG_MB_SLAVE_IP_FROM_STDIN=y
|
||||
CONFIG_MB_MDNS_IP_RESOLVER=y
|
||||
CONFIG_EXAMPLE_CONNECT_IPV6=n
|
@ -1,17 +1,17 @@
|
||||
# Modbus Slave Example
|
||||
|
||||
This example demonstrates using of FreeModbus TCP slave stack port implementation for ESP32(-S2). The external Modbus host is able to read/write device parameters using Modbus protocol transport. The parameters accessible thorough Modbus are located in `mb_example_common/modbus_params.h\c` files and can be updated by user.
|
||||
This example demonstrates using of FreeModbus TCP slave stack port implementation for ESP8266. The external Modbus host is able to read/write device parameters using Modbus protocol transport. The parameters accessible thorough Modbus are located in `mb_example_common/modbus_params.h\c` files and can be updated by user.
|
||||
These are represented in structures holding_reg_params, input_reg_params, coil_reg_params, discrete_reg_params for holding registers, input parameters, coils and discrete inputs accordingly. The app_main application demonstrates how to setup Modbus stack and use notifications about parameters change from host system.
|
||||
The FreeModbus stack located in `components/freemodbus` folder and contain `/port` folder inside which contains FreeModbus stack port for ESP32. There are some parameters that can be configured in KConfig file to start stack correctly (See description below for more information).
|
||||
The FreeModbus stack located in `components/freemodbus` folder and contain `/port` folder inside which contains FreeModbus stack port for ESP8266. There are some parameters that can be configured in KConfig file to start stack correctly (See description below for more information).
|
||||
|
||||
The slave example uses shared parameter structures defined in ```examples/protocols/modbus/mb_example_common``` folder.
|
||||
|
||||
## Hardware required :
|
||||
Option 1:
|
||||
The ESP32(-S2) development board flashed with modbus_tcp_slave example + external Modbus master host software.
|
||||
The ESP8266 development board flashed with modbus_tcp_slave example + external Modbus master host software.
|
||||
|
||||
Option 2:
|
||||
The modbus_tcp_master example application configured as described in its README.md file and flashed into ESP32(-S2) board.
|
||||
The modbus_tcp_master example application configured as described in its README.md file and flashed into ESP8266 board.
|
||||
Note: The ```Example Data (Object) Dictionary``` in the modbus_tcp_master example can be edited to address parameters from other slaves connected into Modbus segment.
|
||||
|
||||
## How to setup and use an example:
|
||||
@ -19,10 +19,9 @@ Note: The ```Example Data (Object) Dictionary``` in the modbus_tcp_master exampl
|
||||
### Configure the application
|
||||
Start the command below to show the configuration menu:
|
||||
```
|
||||
idf.py menuconfig
|
||||
make menuconfig
|
||||
```
|
||||
|
||||
To configure the example to use Wi-Fi or Ethernet connection, open the project configuration menu and navigate to "Example Connection Configuration" menu. Select either "Wi-Fi" or "Ethernet" in the "Connect using" choice.
|
||||
Follow the instructions in `examples/common_components/protocol_examples_common` for further configuration.
|
||||
|
||||
The communication parameters of freemodbus stack (Component config->Modbus configuration) allow to configure it appropriately but usually it is enough to use default settings.
|
||||
@ -33,51 +32,87 @@ Option 1:
|
||||
Configure the external Modbus master software according to port configuration parameters used in application.
|
||||
As an example the Modbus Poll application can be used with this example.
|
||||
Option 2:
|
||||
Setup ESP32(-S2) development board and set modbus_tcp_master example configuration as described in its README.md file.
|
||||
Setup ESP8266 development board and set modbus_tcp_master example configuration as described in its README.md file.
|
||||
Setup one or more slave boards and connect them into the same Modbus segment (See configuration above).
|
||||
|
||||
### Build and flash software
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
make flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
See the Getting Started Guide for full steps to configure and use ESP8266 RTOS to build projects.
|
||||
|
||||
## Example Output
|
||||
Example output of the application:
|
||||
```
|
||||
I (4235) esp_netif_handlers: example_connect: sta ip: 192.168.1.21, mask: 255.255.255.0, gw: 192.168.1.1
|
||||
I (4235) example_connect: Got IPv4 event: Interface "example_connect: sta" address: 192.168.1.21
|
||||
I (4465) example_connect: Got IPv6 event: Interface "example_connect: sta" address: fe80:0000:0000:0000:7edf:a1ff:fe00:4039, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
I (4465) example_connect: Connected to example_connect: sta
|
||||
I (4475) example_connect: - IPv4 address: 192.168.1.21
|
||||
I (4475) example_connect: - IPv6 address: fe80:0000:0000:0000:7edf:a1ff:fe00:4039, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
I (4495) MB_TCP_SLAVE_PORT: Socket (#54), listener on port: 502, errno=0
|
||||
I (4495) MB_TCP_SLAVE_PORT: Protocol stack initialized.
|
||||
I (4505) SLAVE_TEST: Modbus slave stack initialized.
|
||||
I (4505) SLAVE_TEST: Start modbus test...
|
||||
I (41035) MB_TCP_SLAVE_PORT: Socket (#55), accept client connection from address: 192.168.1.39
|
||||
I (41225) SLAVE_TEST: INPUT READ (41704766 us), ADDR:1, TYPE:8, INST_ADDR:0x3ffcb878, SIZE:2
|
||||
I (41235) SLAVE_TEST: HOLDING READ (41719746 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffcb9b4, SIZE:2
|
||||
I (41255) SLAVE_TEST: INPUT READ (41732965 us), ADDR:3, TYPE:8, INST_ADDR:0x3ffcb87c, SIZE:2
|
||||
I (41265) SLAVE_TEST: HOLDING READ (41745923 us), ADDR:3, TYPE:2, INST_ADDR:0x3ffcb9b8, SIZE:2
|
||||
I (41275) SLAVE_TEST: INPUT READ (41759563 us), ADDR:5, TYPE:8, INST_ADDR:0x3ffcb880, SIZE:2
|
||||
I (41295) SLAVE_TEST: HOLDING READ (41772568 us), ADDR:5, TYPE:2, INST_ADDR:0x3ffcb9bc, SIZE:2
|
||||
I (41305) SLAVE_TEST: COILS WRITE (41785889 us), ADDR:0, TYPE:16, INST_ADDR:0x3ffcb874, SIZE:8
|
||||
I (41315) SLAVE_TEST: COILS WRITE (41799175 us), ADDR:8, TYPE:16, INST_ADDR:0x3ffcb875, SIZE:8
|
||||
I (41945) SLAVE_TEST: INPUT READ (42421629 us), ADDR:1, TYPE:8, INST_ADDR:0x3ffcb878, SIZE:2
|
||||
I (42145) SLAVE_TEST: HOLDING READ (42626497 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffcb9b4, SIZE:2
|
||||
I (42345) SLAVE_TEST: INPUT READ (42831315 us), ADDR:3, TYPE:8, INST_ADDR:0x3ffcb87c, SIZE:2
|
||||
I (42555) SLAVE_TEST: HOLDING READ (43036111 us), ADDR:3, TYPE:2, INST_ADDR:0x3ffcb9b8, SIZE:2
|
||||
I (42755) SLAVE_TEST: INPUT READ (43240950 us), ADDR:5, TYPE:8, INST_ADDR:0x3ffcb880, SIZE:2
|
||||
I (42865) SLAVE_TEST: HOLDING READ (43343204 us), ADDR:5, TYPE:2, INST_ADDR:0x3ffcb9bc, SIZE:2
|
||||
......
|
||||
I (81265) SLAVE_TEST: HOLDING READ (81743698 us), ADDR:5, TYPE:2, INST_ADDR:0x3ffcb9bc, SIZE:2
|
||||
I (81465) SLAVE_TEST: COILS WRITE (81948482 us), ADDR:0, TYPE:16, INST_ADDR:0x3ffcb874, SIZE:8
|
||||
I (81465) SLAVE_TEST: Modbus controller destroyed.
|
||||
I (2881) tcpip_adapter: sta ip: 192.168.3.20, mask: 255.255.255.0, gw: 192.168.3.1
|
||||
I (2888) example_connect: Connected to tm_20#
|
||||
I (2891) example_connect: IPv4 address: 192.168.3.20
|
||||
I (2896) wifi: pm stop
|
||||
I (2904) MB_TCP_SLAVE_PORT: Socket (#54), listener on port: 502, errno=0
|
||||
I (2913) MB_TCP_SLAVE_PORT: Protocol stack initialized.
|
||||
I (2922) SLAVE_TEST: Modbus slave stack initialized.
|
||||
I (2930) SLAVE_TEST: Start modbus test...
|
||||
I (19106) MB_TCP_SLAVE_PORT: Socket (#55), accept client connection from address: 192.168.3.21
|
||||
I (19265) SLAVE_TEST: INPUT READ (18911980 us), ADDR:1, TYPE:8, INST_ADDR:0x3ffeaba8, SIZE:2
|
||||
I (19296) SLAVE_TEST: HOLDING READ (18943192 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffeace4, SIZE:2
|
||||
I (19326) SLAVE_TEST: INPUT READ (18973448 us), ADDR:3, TYPE:8, INST_ADDR:0x3ffeabac, SIZE:2
|
||||
I (19358) SLAVE_TEST: HOLDING READ (19004891 us), ADDR:3, TYPE:2, INST_ADDR:0x3ffeace8, SIZE:2
|
||||
I (19397) SLAVE_TEST: INPUT READ (19044277 us), ADDR:5, TYPE:8, INST_ADDR:0x3ffeabb0, SIZE:2
|
||||
I (19426) SLAVE_TEST: HOLDING READ (19073113 us), ADDR:5, TYPE:2, INST_ADDR:0x3ffeacec, SIZE:2
|
||||
I (19460) SLAVE_TEST: COILS READ (19107124 us), ADDR:0, TYPE:32, INST_ADDR:0x3ffeaba4, SIZE:8
|
||||
I (19498) SLAVE_TEST: COILS READ (19145299 us), ADDR:8, TYPE:32, INST_ADDR:0x3ffeaba5, SIZE:8
|
||||
I (20034) SLAVE_TEST: INPUT READ (19681079 us), ADDR:1, TYPE:8, INST_ADDR:0x3ffeaba8, SIZE:2
|
||||
I (20076) SLAVE_TEST: HOLDING READ (19723188 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffeace4, SIZE:2
|
||||
I (20108) SLAVE_TEST: INPUT READ (19754952 us), ADDR:3, TYPE:8, INST_ADDR:0x3ffeabac, SIZE:2
|
||||
I (20136) SLAVE_TEST: HOLDING READ (19783314 us), ADDR:3, TYPE:2, INST_ADDR:0x3ffeace8, SIZE:2
|
||||
I (20177) SLAVE_TEST: INPUT READ (19822984 us), ADDR:5, TYPE:8, INST_ADDR:0x3ffeabb0, SIZE:2
|
||||
I (20211) SLAVE_TEST: HOLDING READ (19857553 us), ADDR:5, TYPE:2, INST_ADDR:0x3ffeacec, SIZE:2
|
||||
I (20247) SLAVE_TEST: COILS READ (19893160 us), ADDR:0, TYPE:32, INST_ADDR:0x3ffeaba4, SIZE:8
|
||||
I (20279) SLAVE_TEST: COILS READ (19925017 us), ADDR:8, TYPE:32, INST_ADDR:0x3ffeaba5, SIZE:8
|
||||
I (20813) SLAVE_TEST: INPUT READ (20459320 us), ADDR:1, TYPE:8, INST_ADDR:0x3ffeaba8, SIZE:2
|
||||
I (20848) SLAVE_TEST: HOLDING READ (20494363 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffeace4, SIZE:2
|
||||
I (20879) SLAVE_TEST: INPUT READ (20524902 us), ADDR:3, TYPE:8, INST_ADDR:0x3ffeabac, SIZE:2
|
||||
I (20908) SLAVE_TEST: HOLDING READ (20554668 us), ADDR:3, TYPE:2, INST_ADDR:0x3ffeace8, SIZE:2
|
||||
I (20975) SLAVE_TEST: INPUT READ (20621131 us), ADDR:5, TYPE:8, INST_ADDR:0x3ffeabb0, SIZE:2
|
||||
I (21018) SLAVE_TEST: HOLDING READ (20664381 us), ADDR:5, TYPE:2, INST_ADDR:0x3ffeacec, SIZE:2
|
||||
I (21049) SLAVE_TEST: COILS READ (20695402 us), ADDR:0, TYPE:32, INST_ADDR:0x3ffeaba4, SIZE:8
|
||||
I (21077) SLAVE_TEST: COILS READ (20723316 us), ADDR:8, TYPE:32, INST_ADDR:0x3ffeaba5, SIZE:8
|
||||
I (21614) SLAVE_TEST: INPUT READ (21259932 us), ADDR:1, TYPE:8, INST_ADDR:0x3ffeaba8, SIZE:2
|
||||
I (21647) SLAVE_TEST: HOLDING READ (21293443 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffeace4, SIZE:2
|
||||
I (21692) SLAVE_TEST: INPUT READ (21338194 us), ADDR:3, TYPE:8, INST_ADDR:0x3ffeabac, SIZE:2
|
||||
I (21739) SLAVE_TEST: HOLDING READ (21385227 us), ADDR:3, TYPE:2, INST_ADDR:0x3ffeace8, SIZE:2
|
||||
I (21771) SLAVE_TEST: INPUT READ (21417793 us), ADDR:5, TYPE:8, INST_ADDR:0x3ffeabb0, SIZE:2
|
||||
I (21809) SLAVE_TEST: HOLDING READ (21455368 us), ADDR:5, TYPE:2, INST_ADDR:0x3ffeacec, SIZE:2
|
||||
I (21842) SLAVE_TEST: COILS READ (21487628 us), ADDR:0, TYPE:32, INST_ADDR:0x3ffeaba4, SIZE:8
|
||||
I (21877) SLAVE_TEST: COILS READ (21522833 us), ADDR:8, TYPE:32, INST_ADDR:0x3ffeaba5, SIZE:8
|
||||
I (22431) SLAVE_TEST: INPUT READ (22077259 us), ADDR:1, TYPE:8, INST_ADDR:0x3ffeaba8, SIZE:2
|
||||
I (22470) SLAVE_TEST: HOLDING READ (22116077 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffeace4, SIZE:2
|
||||
I (22497) SLAVE_TEST: INPUT READ (22143396 us), ADDR:3, TYPE:8, INST_ADDR:0x3ffeabac, SIZE:2
|
||||
I (22531) SLAVE_TEST: HOLDING READ (22177761 us), ADDR:3, TYPE:2, INST_ADDR:0x3ffeace8, SIZE:2
|
||||
I (22567) SLAVE_TEST: INPUT READ (22213536 us), ADDR:5, TYPE:8, INST_ADDR:0x3ffeabb0, SIZE:2
|
||||
I (22599) SLAVE_TEST: HOLDING READ (22245560 us), ADDR:5, TYPE:2, INST_ADDR:0x3ffeacec, SIZE:2
|
||||
I (22628) SLAVE_TEST: COILS READ (22274374 us), ADDR:0, TYPE:32, INST_ADDR:0x3ffeaba4, SIZE:8
|
||||
I (22660) SLAVE_TEST: COILS READ (22306273 us), ADDR:8, TYPE:32, INST_ADDR:0x3ffeaba5, SIZE:8
|
||||
I (23216) SLAVE_TEST: INPUT READ (22862829 us), ADDR:1, TYPE:8, INST_ADDR:0x3ffeaba8, SIZE:2
|
||||
I (23289) SLAVE_TEST: HOLDING READ (22935222 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffeace4, SIZE:2
|
||||
I (23327) SLAVE_TEST: INPUT READ (22973935 us), ADDR:3, TYPE:8, INST_ADDR:0x3ffeabac, SIZE:2
|
||||
I (23359) SLAVE_TEST: HOLDING READ (23006060 us), ADDR:3, TYPE:2, INST_ADDR:0x3ffeace8, SIZE:2
|
||||
I (23388) SLAVE_TEST: INPUT READ (23034291 us), ADDR:5, TYPE:8, INST_ADDR:0x3ffeabb0, SIZE:2
|
||||
I (23428) SLAVE_TEST: HOLDING READ (23074133 us), ADDR:5, TYPE:2, INST_ADDR:0x3ffeacec, SIZE:2
|
||||
I (23458) SLAVE_TEST: COILS READ (23103983 us), ADDR:0, TYPE:32, INST_ADDR:0x3ffeaba4, SIZE:8
|
||||
I (23487) SLAVE_TEST: COILS READ (23132996 us), ADDR:8, TYPE:32, INST_ADDR:0x3ffeaba5, SIZE:8
|
||||
I (24012) SLAVE_TEST: INPUT READ (23659114 us), ADDR:1, TYPE:8, INST_ADDR:0x3ffeaba8, SIZE:2
|
||||
I (24048) SLAVE_TEST: HOLDING READ (23694832 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffeace4, SIZE:2
|
||||
I (24077) SLAVE_TEST: INPUT READ (23723499 us), ADDR:3, TYPE:8, INST_ADDR:0x3ffeabac, SIZE:2
|
||||
I (24122) SLAVE_TEST: HOLDING READ (23768128 us), ADDR:3, TYPE:2, INST_ADDR:0x3ffeace8, SIZE:2
|
||||
I (24169) SLAVE_TEST: INPUT READ (23816157 us), ADDR:5, TYPE:8, INST_ADDR:0x3ffeabb0, SIZE:2
|
||||
I (24210) SLAVE_TEST: HOLDING READ (23856338 us), ADDR:5, TYPE:2, INST_ADDR:0x3ffeacec, SIZE:2
|
||||
I (24272) SLAVE_TEST: COILS READ (23918867 us), ADDR:0, TYPE:32, INST_ADDR:0x3ffeaba4, SIZE:8
|
||||
I (24276) SLAVE_TEST: Modbus controller destroyed.
|
||||
```
|
||||
The output lines describe type of operation, its timestamp, modbus address, access type, storage address in parameter structure and number of registers accordingly.
|
||||
|
||||
|
@ -11,6 +11,7 @@ menu "Modbus Example Configuration"
|
||||
config MB_MDNS_IP_RESOLVER
|
||||
bool "Resolve slave addresses using mDNS service"
|
||||
default y
|
||||
select ENABLE_MDNS
|
||||
help
|
||||
This option allows to use mDNS service to resolve IP addresses of the Modbus slaves.
|
||||
If the option is disabled the ip addresses of slaves are defined in static table.
|
||||
|
@ -14,8 +14,9 @@
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
#ifdef CONFIG_MB_MDNS_IP_RESOLVER
|
||||
#include "mdns.h"
|
||||
#endif
|
||||
#include "esp_netif.h"
|
||||
#include "protocol_examples_common.h"
|
||||
|
||||
@ -31,7 +32,7 @@
|
||||
#define MB_REG_HOLDING_START (0x0000)
|
||||
#define MB_REG_COILS_START (0x0000)
|
||||
|
||||
#define MB_PAR_INFO_GET_TOUT (10) // Timeout for get parameter info
|
||||
#define MB_PAR_INFO_GET_TOUT (50) // Timeout for get parameter info
|
||||
#define MB_CHAN_DATA_MAX_VAL (10)
|
||||
#define MB_CHAN_DATA_OFFSET (1.1f)
|
||||
|
||||
@ -45,8 +46,6 @@
|
||||
|
||||
#define SLAVE_TAG "SLAVE_TEST"
|
||||
|
||||
static portMUX_TYPE param_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
#if CONFIG_MB_MDNS_IP_RESOLVER
|
||||
|
||||
#define MB_ID_BYTE0(id) ((uint8_t)(id))
|
||||
@ -143,7 +142,7 @@ static void setup_reg_data(void)
|
||||
void app_main(void)
|
||||
{
|
||||
esp_err_t result = nvs_flash_init();
|
||||
if (result == ESP_ERR_NVS_NO_FREE_PAGES || result == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
if (result == ESP_ERR_NVS_NO_FREE_PAGES) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
result = nvs_flash_init();
|
||||
}
|
||||
@ -181,8 +180,10 @@ void app_main(void)
|
||||
#endif
|
||||
comm_info.ip_mode = MB_MODE_TCP;
|
||||
comm_info.ip_addr = NULL;
|
||||
comm_info.ip_netif_ptr = (void*)get_example_netif();
|
||||
// Setup communication parameters and start stack
|
||||
void * nif = NULL;
|
||||
ESP_ERROR_CHECK(tcpip_adapter_get_netif(TCPIP_ADAPTER_IF_STA, &nif));
|
||||
comm_info.ip_netif_ptr = nif;
|
||||
// Setup communication parameters and start stack
|
||||
ESP_ERROR_CHECK(mbc_slave_setup((void*)&comm_info));
|
||||
|
||||
// The code below initializes Modbus register area descriptors
|
||||
@ -244,12 +245,12 @@ void app_main(void)
|
||||
(uint32_t)reg_info.size);
|
||||
if (reg_info.address == (uint8_t*)&holding_reg_params.holding_data0)
|
||||
{
|
||||
portENTER_CRITICAL(¶m_lock);
|
||||
portENTER_CRITICAL();
|
||||
holding_reg_params.holding_data0 += MB_CHAN_DATA_OFFSET;
|
||||
if (holding_reg_params.holding_data0 >= (MB_CHAN_DATA_MAX_VAL - MB_CHAN_DATA_OFFSET)) {
|
||||
coil_reg_params.coils_port1 = 0xFF;
|
||||
}
|
||||
portEXIT_CRITICAL(¶m_lock);
|
||||
portEXIT_CRITICAL();
|
||||
}
|
||||
} else if (event & MB_EVENT_INPUT_REG_RD) {
|
||||
ESP_ERROR_CHECK(mbc_slave_get_param_info(®_info, MB_PAR_INFO_GET_TOUT));
|
||||
|
@ -6,16 +6,7 @@ CONFIG_FMB_TCP_PORT_DEFAULT=502
|
||||
CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20
|
||||
CONFIG_FMB_PORT_TASK_STACK_SIZE=4096
|
||||
CONFIG_FMB_PORT_TASK_PRIO=10
|
||||
CONFIG_FMB_COMM_MODE_RTU_EN=n
|
||||
CONFIG_FMB_COMM_MODE_ASCII_EN=n
|
||||
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=1000
|
||||
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300
|
||||
CONFIG_FMB_TIMER_PORT_ENABLED=y
|
||||
CONFIG_FMB_TIMER_GROUP=0
|
||||
CONFIG_FMB_TIMER_INDEX=0
|
||||
CONFIG_FMB_TIMER_ISR_IN_IRAM=y
|
||||
CONFIG_MB_MDNS_IP_RESOLVER=n
|
||||
CONFIG_MB_SLAVE_IP_FROM_STDIN=y
|
||||
CONFIG_MB_SLAVE_ADDR=1
|
||||
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y
|
||||
CONFIG_EXAMPLE_CONNECT_IPV6=n
|
||||
CCONFIG_EXAMPLE_CONNECT_IPV6=n
|
@ -166,7 +166,7 @@ build_example () {
|
||||
|
||||
EXAMPLE_NUM=0
|
||||
|
||||
EXAMPLE_PATHS=$( find ${IDF_PATH}/examples/ -type f -name Makefile | grep -v "/components/" | grep -v "/common_components/" | grep -v "/main/" | grep -v "/build_system/cmake/" | sort )
|
||||
EXAMPLE_PATHS=$( find ${IDF_PATH}/examples/ -type f -name Makefile | grep -v "/components/" | grep -v "/common_components/" | grep -v "/main/" | grep -v "/build_system/cmake/" | grep -v "/mb_example_common/" | sort )
|
||||
for FN in ${EXAMPLE_PATHS}
|
||||
do
|
||||
if [[ $EXAMPLE_NUM -lt $START_NUM || $EXAMPLE_NUM -ge $END_NUM ]]
|
||||
|
@ -73,7 +73,7 @@ LOG_SUSPECTED=${LOG_PATH}/common_log.txt
|
||||
touch ${LOG_SUSPECTED}
|
||||
SDKCONFIG_DEFAULTS_CI=sdkconfig.ci
|
||||
|
||||
EXAMPLE_PATHS=$( find ${IDF_PATH}/examples/ -type f -name CMakeLists.txt | grep -v "/components/" | grep -v "/common_components/" | grep -v "/main/" | grep -v "/build_system/cmake/" | sort )
|
||||
EXAMPLE_PATHS=$( find ${IDF_PATH}/examples/ -type f -name CMakeLists.txt | grep -v "/components/" | grep -v "/common_components/" | grep -v "/main/" | grep -v "/build_system/cmake/" | grep -v "/mb_example_common/" | sort )
|
||||
if [ $# -eq 0 ]
|
||||
then
|
||||
START_NUM=0
|
||||
|
Reference in New Issue
Block a user