|
|
|
@ -3,12 +3,44 @@ |
|
|
|
|
#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 g_modbus; |
|
|
|
|
static volatile uint16_t register2 = 0; |
|
|
|
|
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 |
|
|
|
@ -21,7 +53,7 @@ static esp_err_t read_fn(Tcpd_t serv, TcpdClient_t client, int sockfd) |
|
|
|
|
if (nbytes <= 0) return ESP_FAIL; |
|
|
|
|
|
|
|
|
|
size_t resp_len = 0; |
|
|
|
|
ModbusError_t e = mb_handleRequest(&g_modbus, buf, nbytes, buf2, 1024, &resp_len); |
|
|
|
|
ModbusError_t e = mb_handleRequest(&gModbus, buf, nbytes, buf2, 1024, &resp_len); |
|
|
|
|
if (e == 0) { |
|
|
|
|
tcpd_send(client, buf2, (ssize_t) resp_len); |
|
|
|
|
} else { |
|
|
|
@ -40,41 +72,230 @@ 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); |
|
|
|
|
|
|
|
|
|
if (ref == 1) { |
|
|
|
|
*pValue = 1042; // device ID
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
if (ref == 2) { |
|
|
|
|
*pValue = register2; |
|
|
|
|
return 0; |
|
|
|
|
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_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); |
|
|
|
|
|
|
|
|
|
if (ref == 2) { |
|
|
|
|
register2 = value; |
|
|
|
|
return 0; |
|
|
|
|
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_ILLEGAL_DATA_ADDRESS; |
|
|
|
|
return MB_EXCEPTION_OK; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void mbiface_setup() |
|
|
|
|
{ |
|
|
|
|
ESP_LOGI(TAG, "initing MB iface"); |
|
|
|
|
g_modbus.proto = MB_PROTO_TCP; |
|
|
|
|
g_modbus.addr = 1; |
|
|
|
|
g_modbus.startOfAccess = startOfAccess; |
|
|
|
|
g_modbus.endOfAccess = endOfAccess; |
|
|
|
|
g_modbus.readHolding = rh; |
|
|
|
|
g_modbus.writeHolding = wh; |
|
|
|
|
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; |
|
|
|
|