/** * @Author: 李建 * @Date: 2025/4/27 15:35 * Description: 实现485主站 * Copyright: Copyright (©) 2025 永续绿建. All rights reserved. */ #include #include "modbus_master.h" #include "mbcontroller.h" #define OPTS(min_val, max_val, step_val) { .opt1 = min_val, .opt2 = max_val, .opt3 = step_val } static const char *TAG = "MODBUS_MASTER"; enum { // 分风箱主板从站号 MB_DEVICE_XFJS_ADDR = 10, // 分控一从站号 MB_DEVICE_FK_1_ADDR = 2, // 分控二从站号 MB_DEVICE_FK_2_ADDR = 3, // 分控三从站号 MB_DEVICE_FK_3_ADDR = 4, // 分控四从站号 MB_DEVICE_FK_4_ADDR = 5, }; static void *master_handle = NULL; // 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 (10) // Timeout to update cid over Modbus #define UPDATE_CIDS_TIMEOUT_MS (500) #define UPDATE_CIDS_TIMEOUT_TICS (UPDATE_CIDS_TIMEOUT_MS / portTICK_PERIOD_MS) // Timeout between polls #define POLL_TIMEOUT_MS (1) #define POLL_TIMEOUT_TICS (POLL_TIMEOUT_MS / portTICK_PERIOD_MS) // 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)) // Discrete offset macro #define STR(fieldname) ((const char *)( fieldname )) 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_POWER, STR("power"), STR(""), MB_DEVICE_XFJS_ADDR, MB_PARAM_HOLDING, POWER_ADDRESS_REGISTER, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE}, {CID_MODE, STR("mode"), STR(""), MB_DEVICE_XFJS_ADDR, MB_PARAM_HOLDING, MODE_ADDRESS_REGISTER, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE}, {CID_FAN_SPEED, STR("fan_speed"), STR(""), MB_DEVICE_XFJS_ADDR, MB_PARAM_HOLDING, FAN_SPEED_ADDRESS_REGISTER, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE}, {CID_EXTERNAL_TEMPERATURE, STR("external_temperature"), STR(""), MB_DEVICE_XFJS_ADDR, MB_PARAM_HOLDING, EXTERNAL_TEMPERATURE_ADDRESS_REGISTER, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE}, {CID_EXTERNAL_HUMIDITY, STR("external_humidity"), STR(""), MB_DEVICE_XFJS_ADDR, MB_PARAM_HOLDING, EXTERNAL_HUMIDITY_ADDRESS_REGISTER, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE}, {CID_RETURN_AIR_TEMPERATURE, STR("return_air_temperature"), STR(""), MB_DEVICE_XFJS_ADDR, MB_PARAM_HOLDING, RETURN_AIR_TEMPERATURE_ADDRESS_REGISTER, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE}, {CID_RETURN_AIR_HUMIDITY, STR("return_air_humidity"), STR(""), MB_DEVICE_XFJS_ADDR, MB_PARAM_HOLDING, RETURN_AIR_HUMIDITY_ADDRESS_REGISTER, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE}, {CID_RETURN_AIR_PM25, STR("return_air_pm25"), STR(""), MB_DEVICE_XFJS_ADDR, MB_PARAM_HOLDING, RETURN_AIR_PM25_ADDRESS_REGISTER, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE}, {CID_RETURN_AIR_CO2, STR("return_air_co2"), STR(""), MB_DEVICE_XFJS_ADDR, MB_PARAM_HOLDING, RETURN_AIR_CO2_ADDRESS_REGISTER, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE}, {CID_SELECT_POSITION, STR("select_position"), STR(""), MB_DEVICE_XFJS_ADDR, MB_PARAM_HOLDING, SELECT_POSITION_ADDRESS_REGISTER, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE}, {CID_MIN_HUMIDITY, STR("min_humidity"), STR(""), MB_DEVICE_XFJS_ADDR, MB_PARAM_HOLDING, MIN_HUMIDITY_ADDRESS_REGISTER, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE}, {CID_MAX_HUMIDITY, STR("max_humidity"), STR(""), MB_DEVICE_XFJS_ADDR, MB_PARAM_HOLDING, MAX_HUMIDITY_ADDRESS_REGISTER, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE}, {CID_AIR_EXHAUST_FIRST_VOL, STR("air_exhaust_first_vol"), STR(""), MB_DEVICE_XFJS_ADDR, MB_PARAM_HOLDING, AIR_EXHAUST_FIRST_VOL_ADDRESS_REGISTER, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE}, {CID_AIR_EXHAUST_SECOND_VOL, STR("air_exhaust_second_vol"), STR(""), MB_DEVICE_XFJS_ADDR, MB_PARAM_HOLDING, AIR_EXHAUST_SECOND_VOL_ADDRESS_REGISTER, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE}, {CID_AIR_EXHAUST_THIRD_VOL, STR("air_exhaust_third_vol"), STR(""), MB_DEVICE_XFJS_ADDR, MB_PARAM_HOLDING, AIR_EXHAUST_THIRD_VOL_ADDRESS_REGISTER, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE}, {CID_AIR_EXHAUST_FOURTH_VOL, STR("air_exhaust_fourth_vol"), STR(""), MB_DEVICE_XFJS_ADDR, MB_PARAM_HOLDING, AIR_EXHAUST_FOURTH_VOL_ADDRESS_REGISTER, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE}, {CID_AIR_EXHAUST_FIFTH_VOL, STR("air_exhaust_fifth_vol"), STR(""), MB_DEVICE_XFJS_ADDR, MB_PARAM_HOLDING, AIR_EXHAUST_FIFTH_VOL_ADDRESS_REGISTER, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE}, {CID_AIR_SUPPLY_FIRST_VOL, STR("air_supply_first_vol"), STR(""), MB_DEVICE_XFJS_ADDR, MB_PARAM_HOLDING, AIR_SUPPLY_FIRST_VOL_ADDRESS_REGISTER, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE}, {CID_AIR_SUPPLY_SECOND_VOL, STR("air_supply_second_vol"), STR(""), MB_DEVICE_XFJS_ADDR, MB_PARAM_HOLDING, AIR_SUPPLY_SECOND_VOL_ADDRESS_REGISTER, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE}, {CID_AIR_SUPPLY_THIRD_VOL, STR("air_supply_third_vol"), STR(""), MB_DEVICE_XFJS_ADDR, MB_PARAM_HOLDING, AIR_SUPPLY_THIRD_VOL_ADDRESS_REGISTER, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE}, {CID_AIR_SUPPLY_FOURTH_VOL, STR("air_supply_fourth_vol"), STR(""), MB_DEVICE_XFJS_ADDR, MB_PARAM_HOLDING, AIR_SUPPLY_FOURTH_VOL_ADDRESS_REGISTER, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE}, {CID_AIR_SUPPLY_FIFTH_VOL, STR("air_supply_fifth_vol"), STR(""), MB_DEVICE_XFJS_ADDR, MB_PARAM_HOLDING, AIR_SUPPLY_FIFTH_VOL_ADDRESS_REGISTER, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE}, {CID_ERROR_CODE, STR("error_code"), STR(""), MB_DEVICE_XFJS_ADDR, MB_PARAM_HOLDING, ERROR_CODE_ADDRESS_REGISTER, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE}, }; // Calculate number of parameters in the table const uint16_t num_device_parameters = (sizeof(device_parameters) / sizeof(device_parameters[0])); IRAM_ATTR static esp_err_t master_init() { // Initialize Modbus controller mb_communication_info_t comm = { .ser_opts.port = MB_PORT_NUM, .ser_opts.mode = MB_RTU, .ser_opts.baudrate = MB_DEV_SPEED, .ser_opts.parity = MB_PARITY_NONE, .ser_opts.response_tout_ms = 400, .ser_opts.data_bits = UART_DATA_8_BITS, .ser_opts.stop_bits = UART_STOP_BITS_1 }; ESP_LOGI(TAG, "Modbus controller initialized..."); esp_err_t err = mbc_master_create_serial(&comm, &master_handle); MB_RETURN_ON_FALSE((master_handle != NULL), ESP_ERR_INVALID_STATE, TAG, "mb controller initialization fail."); MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, "mb controller initialization fail, returns(0x%x).", (int) err); // Set UART pin numbers err = uart_set_pin(MB_PORT_NUM, MB_GPIO_TX, MB_GPIO_RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, "mb serial set pin failure, uart_set_pin() returned (0x%x).", (int) err); err = mbc_master_start(master_handle); MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, "mb controller start fail, returned (0x%x).", (int) err); // Set driver mode to Half Duplex err = uart_set_mode(MB_PORT_NUM, UART_MODE_UART); MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, "mb serial set mode failure, uart_set_mode() returned (0x%x).", (int) err); vTaskDelay(5); err = mbc_master_set_descriptor(master_handle, &device_parameters[0], num_device_parameters); MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, "mb controller set descriptor fail, returns(0x%x).", (int) err); ESP_LOGI(TAG, "Modbus master stack initialized..."); return err; } //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; // default: // instance_ptr = NULL; // break; // } // } else { // ESP_LOGE(TAG, "Wrong parameter offset for CID #%u", (unsigned)param_descriptor->cid); // assert(instance_ptr != NULL); // } // return instance_ptr; //} void modbus_master_task(void *arg) { vTaskDelay(5000 / portTICK_PERIOD_MS); ESP_LOGI(TAG, "Modbus master task started..."); esp_err_t err = ESP_OK; uint8_t temp_data[2] = {0}; // temporary buffer to hold maximum CID size uint8_t type = 0; const mb_parameter_descriptor_t *param_descriptor = NULL; for (;;) { for (uint16_t cid = 0; (err != ESP_ERR_NOT_FOUND) && cid < 1; cid++) { err = mbc_master_get_cid_info(master_handle, cid, ¶m_descriptor); if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) { err = mbc_master_get_parameter(master_handle, param_descriptor->cid, (uint8_t *) temp_data, &type); if (err == ESP_OK) { ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %d read successful.", param_descriptor->cid, (char *) param_descriptor->param_key, (char *) param_descriptor->param_units, *(uint16_t *) temp_data); } else { ESP_LOGE(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(1000 / portTICK_PERIOD_MS); } vTaskDelay(5000 / portTICK_PERIOD_MS); } } void modbus_master_init() { ESP_LOGI(TAG, "Modbus master init..."); ESP_ERROR_CHECK(master_init()); vTaskDelay(10); //xTaskCreatePinnedToCore(modbus_master_task, "modbus_master_task", 4 * 1024, NULL, 5, NULL, 1); } esp_err_t mm_set_param(uint16_t cid, uint8_t *value) { uint8_t type = PARAM_TYPE_U16; return mbc_master_set_parameter(master_handle, cid, value, &type); } esp_err_t mm_get_param(uint16_t cid, uint8_t *value) { uint8_t type = PARAM_TYPE_U16; return mbc_master_get_parameter(master_handle, cid, value, &type); } void modbus_master_destroy() { mbc_master_delete(master_handle); }