#include #include "mbiface.h" #include "socket_server.h" #include "tasks.h" #include "modbus.h" #include "fancontrol.h" #include "settings.h" static const char * TAG = "mb"; Tcpd_t g_mbifc_server = NULL; ModbusSlave_t gModbus; enum HoldingRegisters { H_MODE = 1, H_POWER, H_SUMMER, H_INITIAL_MODE, H_INITIAL_POWER, H_RECUP_MODE, H_RECUP_TIME, H_RECUP_TIME_MIN, H_RECUP_TIME_MAX, H_RECUP_FACTOR, H_RAMP_TIME, H_BLIND_TIME, H_MIN_POWER, }; enum InputRegisters { I_RECUP_TIME_IN = 1, I_RECUP_TIME_OUT, I_T_VALIDITY, I_T_IN_INST, I_T_OUT_INST, I_T_INDOOR, I_T_OUTDOOR, I_T_INFLOW, I_T_EXHAUST, I_MODE_INST, I_MOTOR_RAMP, I_BLIND_RAMP, }; /** * Socket read handler */ static esp_err_t read_fn(Tcpd_t serv, TcpdClient_t client, int sockfd) { uint8_t buf[1024]; uint8_t buf2[1024]; int nbytes = read(sockfd, buf, sizeof(buf)); if (nbytes <= 0) return ESP_FAIL; size_t resp_len = 0; ModbusError_t e = mb_handleRequest(&gModbus, buf, nbytes, buf2, 1024, &resp_len); if (e == 0) { tcpd_send(client, buf2, (ssize_t) resp_len); } else { ESP_LOGE(TAG, "Error %d, closing socket", e); tcpd_kick(client); } return ESP_OK; } ModbusException_t startOfAccess(ModbusSlave_t *pSlave, ModbusFunction_t function, uint8_t i) { return 0; } void endOfAccess(ModbusSlave_t *ms) { // } ModbusException_t ri(ModbusSlave_t *pSlave, uint16_t ref, uint16_t *pValue) { ESP_LOGD(TAG, "Read input %d", ref); uint16_t scratch = 0; switch (ref) { case I_RECUP_TIME_IN: *pValue = gState.real_recup_time_in; break; case I_RECUP_TIME_OUT: *pValue = gState.real_recup_time_out; break; case I_T_VALIDITY: scratch |= (int)gState.valid_t_actual_in; scratch |= (int)gState.valid_t_actual_out << 1; scratch |= (int)gState.valid_t_indoor << 2; scratch |= (int)gState.valid_t_outdoor << 3; scratch |= (int)gState.valid_t_inflow << 4; scratch |= (int)gState.valid_t_exhaust << 5; *pValue = scratch; break; case I_T_IN_INST: *pValue = gState.t_actual_in; break; case I_T_OUT_INST: *pValue = gState.t_actual_out; break; case I_T_INDOOR: *pValue = gState.t_indoor; break; case I_T_OUTDOOR: *pValue = gState.t_outdoor; break; case I_T_INFLOW: *pValue = gState.t_inflow; break; case I_T_EXHAUST: *pValue = gState.t_exhaust; break; case I_MODE_INST: *pValue = gState.instantaneous_vent_mode; break; case I_MOTOR_RAMP: *pValue = gState.ramp; break; case I_BLIND_RAMP: *pValue = gState.blind_position; break; default: return MB_EXCEPTION_ILLEGAL_DATA_ADDRESS; } return MB_EXCEPTION_OK; } ModbusException_t rh(ModbusSlave_t *pSlave, uint16_t ref, uint16_t *pValue) { ESP_LOGD(TAG, "Read holding %d", ref); switch (ref) { case H_MODE: *pValue = (int) gState.set_vent_mode; break; case H_POWER: *pValue = gState.set_power; break; case H_SUMMER: *pValue = (int) gSettings.summer_mode; break; case H_INITIAL_MODE: *pValue = (int) gSettings.initial_mode; break; case H_INITIAL_POWER: *pValue = gSettings.initial_power; break; case H_RECUP_MODE: *pValue = gSettings.recup_mode; break; case H_RECUP_TIME: *pValue = gSettings.recup_time; break; case H_RECUP_TIME_MIN: *pValue = gSettings.min_recup_time; break; case H_RECUP_TIME_MAX: *pValue = gSettings.max_recup_time; break; case H_RECUP_FACTOR: *pValue = gSettings.recup_factor; break; case H_RAMP_TIME: *pValue = gSettings.ramp_time; break; case H_BLIND_TIME: *pValue = gSettings.blind_time; break; case H_MIN_POWER: *pValue = gSettings.min_power; break; default: // inputs are mapped to the holding address space if (ref >= 100) { return ri(pSlave, ref - 100, pValue); } return MB_EXCEPTION_ILLEGAL_DATA_ADDRESS; } return MB_EXCEPTION_OK; } static bool is_valid_vent_mode(int value) { switch (value) { case VENT_MODE_OFF: case VENT_MODE_FREE: case VENT_MODE_OUT: case VENT_MODE_IN: case VENT_MODE_RECUP: return true; default: return false; } } ModbusException_t wh(ModbusSlave_t *pSlave, uint16_t ref, uint16_t value) { ESP_LOGD(TAG, "Write holding %d := %02x", ref, value); switch (ref) { case H_MODE: if (!is_valid_vent_mode(value)) { return MB_EXCEPTION_ILLEGAL_DATA_VALUE; } fan_set_vent_mode(value); break; case H_POWER: if (value > 100) { return MB_EXCEPTION_ILLEGAL_DATA_VALUE; } fan_set_power(value); break; case H_SUMMER: gSettings.summer_mode = (value != 0); settings_persist(SETTINGS_summer_mode); break; case H_INITIAL_MODE: if (!is_valid_vent_mode(value)) { return MB_EXCEPTION_ILLEGAL_DATA_VALUE; } gSettings.initial_mode = value; settings_persist(SETTINGS_initial_mode); break; case H_INITIAL_POWER: if (value > 100) { return MB_EXCEPTION_ILLEGAL_DATA_VALUE; } gSettings.initial_power = value; settings_persist(SETTINGS_initial_power); break; case H_RECUP_MODE: if (value == RECUP_MODE_TEMP || value == RECUP_MODE_TIME) { gSettings.recup_mode = value; } else { return MB_EXCEPTION_ILLEGAL_DATA_VALUE; } break; case H_RECUP_TIME: if (value == 0) { return MB_EXCEPTION_ILLEGAL_DATA_VALUE; } gSettings.recup_time = value; settings_persist(SETTINGS_recup_time); break; case H_RECUP_TIME_MIN: if (value == 0) { return MB_EXCEPTION_ILLEGAL_DATA_VALUE; } gSettings.min_recup_time = value; settings_persist(SETTINGS_min_recup_time); break; case H_RECUP_TIME_MAX: if (value == 0) { return MB_EXCEPTION_ILLEGAL_DATA_VALUE; } gSettings.max_recup_time = value; settings_persist(SETTINGS_max_recup_time); break; case H_RECUP_FACTOR: if (value > 100) { return MB_EXCEPTION_ILLEGAL_DATA_VALUE; } gSettings.recup_factor = value; settings_persist(SETTINGS_recup_factor); break; case H_RAMP_TIME: gSettings.ramp_time = value; settings_persist(SETTINGS_ramp_time); break; case H_BLIND_TIME: gSettings.blind_time = value; settings_persist(SETTINGS_blind_time); break; case H_MIN_POWER: if (value > 100) { return MB_EXCEPTION_ILLEGAL_DATA_VALUE; } gSettings.min_power = value; settings_persist(SETTINGS_min_power); break; default: return MB_EXCEPTION_ILLEGAL_DATA_ADDRESS; } return MB_EXCEPTION_OK; } void mbiface_setup() { ESP_LOGI(TAG, "initing MB iface"); gModbus.proto = MB_PROTO_TCP; gModbus.addr = 1; gModbus.startOfAccess = startOfAccess; gModbus.endOfAccess = endOfAccess; gModbus.readHolding = rh; gModbus.writeHolding = wh; gModbus.readInput = ri; tcpd_config_t server_config = TCPD_INIT_DEFAULT(); server_config.max_clients = 1; server_config.task_prio = MBIFC_TASK_PRIO; server_config.task_stack = MBIFC_TASK_STACK; server_config.task_name = "MBIFC"; server_config.port = 502; server_config.read_fn = read_fn; ESP_ERROR_CHECK(tcpd_init(&server_config, &g_mbifc_server)); }