devices.esphome.io

Pentek Intellidrive PID10

Pentek Intellidrive PID10

Device Type: sensor
Electrical Standard: global
Board: esp32

image

Data can be gathered from your Pentair Pentek Intellidrive PID Variable Frequency Pump Controller over Modbus. Tested with a PID10 Device.

The Intellidrive provides a 2-wire RS485 interface. Wire up a an RS485 transceiver like MAX485 to an ESP32 to interface with the device using Esphome's Modbus Component

Modbus Interface Notes

The Intellidrive Modbus interface is poorly documented. Most interesting registers should be provided below, but there may be more to discover. See the Unofficial Modbus Docs for more information on known or suspected registers.

Configuration example

esphome:
name: well-pump-controller
friendly_name: "Well Pump Controller"
comment: "Pentek Intellidrive PID10"
esp32:
board: nodemcu-32s
wifi:
ssid: YOUR_SSID
password: YOUR_PW
power_save_mode: none
ota:
api:
web_server:
port: 80
time:
- platform: sntp
id: sntp_time
timezone: America/Los_Angeles
# Example UART configuration
uart:
id: mod_bus
tx_pin: GPIO16
rx_pin: GPIO17
baud_rate: 19200
data_bits: 8
parity: NONE
stop_bits: 1
modbus:
id: modbus1
modbus_controller:
- id: modctrl1
address: 0x1
modbus_id: modbus1
setup_priority: -10
update_interval: 10s
- id: modctrl2
address: 0x2
modbus_id: modbus1
setup_priority: -10
update_interval: 10s
sensor:
# SETTINGS PARAMS --------------
# software version is a string, low-byte first
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x0000
name: "AOC Software Version"
register_type: holding
value_type: RAW
register_count: 10
accuracy_decimals: 2
lambda: |-
if (data.size() < item->offset + 20) return NAN;
std::string temp;
std::string result;
bool found_dot = false;
for (int i = 0; i < 20; i+=2) {
char chars[] = {
static_cast<char>(data[item->offset + i]), // low byte
static_cast<char>(data[item->offset + i + 1]), // high byte
};
for (char c : chars) {
temp += c;
}
}
for (auto it = temp.rbegin(); it != temp.rend(); ++it) {
char c = *it;
if (c == '.' && !found_dot) {
result += c; found_dot = true;
} else if (c >= '0' && c <= '9') {
result += c;
}
}
return std::stof(result);
# PID control settings
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00D0
name: "PID P-Gain"
register_type: holding
value_type: U_WORD
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00D1
name: "PID I-Time"
device_class: duration
unit_of_measurement: "ms"
register_type: holding
value_type: U_WORD
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00D2
name: "PID D-Time"
device_class: duration
unit_of_measurement: "ms"
register_type: holding
value_type: U_WORD
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00D3
name: "PID D-Limit"
register_type: holding
value_type: U_WORD
# SLEEP settings
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00FC
name: "Sleep Boost Differential"
unit_of_measurement: "psi"
device_class: "pressure"
register_type: holding
value_type: U_WORD
filters:
- multiply: 0.1450
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00FB
name: "Sleep Boost Delay"
unit_of_measurement: "s"
device_class: duration
register_type: holding
value_type: U_WORD
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00F9
name: "Sleep Wake Up Differential"
unit_of_measurement: "psi"
device_class: "pressure"
register_type: holding
value_type: U_WORD
filters:
- multiply: 0.1450
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00FA
name: "Sleep Wake Delay"
unit_of_measurement: "s"
device_class: duration
register_type: holding
value_type: U_WORD
# PASSWORD settings
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00EE
name: "Password Lock Time"
unit_of_measurement: "min"
device_class: duration
register_type: holding
value_type: U_WORD
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00EC
name: "App Password"
register_type: holding
value_type: U_WORD
# SET POINTS
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00DA
name: "Setpoint Internal"
unit_of_measurement: "psi"
device_class: "pressure"
register_type: holding
value_type: U_WORD
filters:
- multiply: 0.1450
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00E5
name: "Setpoint External"
unit_of_measurement: "psi"
device_class: "pressure"
register_type: holding
value_type: U_WORD
filters:
- multiply: 0.1450
# MOTOR settings
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x1452
name: "3PH Motor Type ID"
register_type: holding
value_type: U_WORD
internal: true
on_value:
then:
- lambda: |-
if (x == 0) {
id(ph3_motor_type).publish_state("Submersible");
} else if (x == 1) {
id(ph3_motor_type).publish_state("Above-Ground");
} else {
std::string mode_str = "Unknown Motor Type: " + std::to_string(x);
id(ph3_motor_type).publish_state(mode_str.c_str());
}
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x1450
name: "Motor Service Factor Amps"
unit_of_measurement: "A"
device_class: current
register_type: holding
value_type: FP32
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00E9
name: "Motor Max Frequency"
register_type: holding
value_type: U_WORD
unit_of_measurement: "Hz"
device_class: "frequency"
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00EA
name: "Motor Min Frequency"
register_type: holding
value_type: U_WORD
unit_of_measurement: "Hz"
device_class: "frequency"
# SENSOR settings
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00DC
name: "Sensor Max Pressure"
unit_of_measurement: "psi"
device_class: "pressure"
register_type: holding
value_type: U_WORD
filters:
- multiply: 0.1450
# EX RUNTIME SETTINGS
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00EF
name: "Excessive Runtime Hours"
unit_of_measurement: "h"
device_class: "duration"
register_type: holding
value_type: U_WORD
# DRY RUN settings
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00E1
name: "Dry Run Auto Restart Delay"
unit_of_measurement: "s"
device_class: "duration"
register_type: holding
value_type: U_WORD
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00E0
name: "Dry Run Number of Resets"
register_type: holding
value_type: U_WORD
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00DF
name: "Dry Run Detection Time"
register_type: holding
value_type: U_WORD
unit_of_measurement: "s"
device_class: "duration"
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00E4
name: "Dry Run Fill Time"
register_type: holding
value_type: U_WORD
unit_of_measurement: "s"
device_class: "duration"
# I/O Settings
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00E6
name: "I/O Input 1 Mode ID"
register_type: holding
value_type: U_WORD
internal: true
on_value:
then:
- lambda: |-
if (x == 0) {
id(io_input_1_mode).publish_state("Unused");
} else if (x == 1) {
id(io_input_1_mode).publish_state("Run Enable");
} else if (x == 2) {
id(io_input_1_mode).publish_state("Ext Fault");
} else if (x == 3) {
id(io_input_1_mode).publish_state("Ext Setpoint");
} else {
std::string mode_str = "Unknown Input Mode: " + std::to_string(x);
id(io_input_1_mode).publish_state(mode_str.c_str());
}
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00E7
name: "I/O Input 2 Mode ID"
register_type: holding
value_type: U_WORD
internal: true
on_value:
then:
- lambda: |-
if (x == 0) {
id(io_input_2_mode).publish_state("Unused");
} else if (x == 1) {
id(io_input_2_mode).publish_state("Run Enable");
} else if (x == 2) {
id(io_input_2_mode).publish_state("Ext Fault");
} else if (x == 3) {
id(io_input_2_mode).publish_state("Ext Setpoint");
} else {
std::string mode_str = "Unknown Input Mode: " + std::to_string(x);
id(io_input_2_mode).publish_state(mode_str.c_str());
}
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00FE
name: "I/O Output Mode ID"
register_type: holding
value_type: U_WORD
internal: true
on_value:
then:
- lambda: |-
if (x == 0) {
id(io_output_mode).publish_state("Unused");
} else if (x == 1) {
id(io_output_mode).publish_state("Run");
} else if (x == 2) {
id(io_output_mode).publish_state("Fault");
} else {
std::string mode_str = "Unknown Output Mode: " + std::to_string(x);
id(io_output_mode).publish_state(mode_str.c_str());
}
# Over Pressure
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x0101
name: "Over Pressure Setpoint"
unit_of_measurement: "psi"
device_class: "pressure"
register_type: holding
value_type: U_WORD
filters:
- multiply: 0.1450
# MONITORING PARAMS --------------
# software version is a string, low-byte first
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x13EC
name: "MOC Software Version"
register_type: holding
value_type: RAW
register_count: 10
accuracy_decimals: 2
lambda: |-
if (data.size() < item->offset + 20) return NAN;
std::string temp;
std::string result;
bool found_dot = false;
for (int i = 0; i < 20; i+=2) {
char chars[] = {
static_cast<char>(data[item->offset + i]), // low byte
static_cast<char>(data[item->offset + i + 1]), // high byte
};
for (char c : chars) {
temp += c;
}
}
for (auto it = temp.rbegin(); it != temp.rend(); ++it) {
char c = *it;
if (c == '.' && !found_dot) {
result += c; found_dot = true;
} else if (c >= '0' && c <= '9') {
result += c;
}
}
return std::stof(result);
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x141D
name: "Motor Power Consumption"
id: "motor_power_consumption"
unit_of_measurement: "W"
state_class: measurement
device_class: power
register_type: holding
value_type: U_DWORD
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x141F
name: "Motor Phase A Current"
id: "phase_a_current"
unit_of_measurement: "A"
state_class: measurement
device_class: current
register_type: holding
value_type: FP32
filters:
- clamp:
min_value: 0
max_value: 20
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x1421
name: "Motor Phase B Current"
id: "phase_b_current"
unit_of_measurement: "A"
state_class: measurement
device_class: current
register_type: holding
value_type: FP32
filters:
- clamp:
min_value: 0
max_value: 20
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x1423
name: "Motor Phase C Current"
id: "phase_c_current"
unit_of_measurement: "A"
state_class: measurement
device_class: current
register_type: holding
value_type: FP32
filters:
- clamp:
min_value: 0
max_value: 20
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x1425
name: "Motor Phase Voltage"
id: "motor_phase_voltage"
unit_of_measurement: "V"
state_class: measurement
device_class: voltage
register_type: holding
value_type: FP32
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x1453
name: "Supply Voltage"
id: "supply_voltage"
unit_of_measurement: "V"
state_class: measurement
device_class: voltage
register_type: holding
value_type: U_DWORD
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x1416
name: "Motor Speed Actual"
unit_of_measurement: "Hz"
device_class: "frequency"
state_class: measurement
register_type: holding
value_type: FP32
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x001E
name: "Fault Code"
register_type: holding
value_type: U_DWORD
internal: true
on_value:
then:
- lambda: |-
if (x == 0 || x == 1) {
id(fault_state).publish_state("None");
} else if (x == 8192) {
id(fault_state).publish_state("Open Transducer");
} else if (x == 16384) {
id(fault_state).publish_state("Short Transducer");
} else if (x == 65536) {
id(fault_state).publish_state("Under Voltage");
} else if (x == 64) {
id(fault_state).publish_state("Can Not Start Motor");
} else if (x == 32) {
id(fault_state).publish_state("Dry Run");
} else if (x == 2048) {
id(fault_state).publish_state("Ground Fault");
} else if (x == 4096) {
id(fault_state).publish_state("System Not Grounded");
} else if (x == 256) {
id(fault_state).publish_state("Over Current");
} else if (x == 32768) {
id(fault_state).publish_state("Low Amps");
} else {
std::string mode_str = "Unknown Fault: " + std::to_string(x);
id(fault_state).publish_state(mode_str.c_str());
}
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x0029
name: "Run Mode ID"
register_type: holding
value_type: U_WORD
internal: true
on_value:
then:
- lambda: |-
if (x == 0) {
id(run_mode).publish_state("Stop");
} else if (x == 1) {
id(run_mode).publish_state("Pump Out");
} else if (x == 2) {
id(run_mode).publish_state("Auto Start");
} else {
std::string mode_str = "Unknown Mode: " + std::to_string(x);
id(run_mode).publish_state(mode_str.c_str());
}
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x1452
name: "Motor Type ID"
register_type: holding
value_type: U_WORD
internal: true
on_value:
then:
- lambda: |-
if (x == 0) {
id(motor_type).publish_state("3-Phase");
} else if (x == 1) {
id(motor_type).publish_state("1-Phase, 2-Wire");
} else if (x == 2) {
id(motor_type).publish_state("1-Phase, 3-Wire");
} else {
std::string mode_str = "Unknown Motor Type: " + std::to_string(x);
id(motor_type).publish_state(mode_str.c_str());
}
# MONITORING PARAMS (UNDOCUMENTED) --------------
- platform: modbus_controller
modbus_controller_id: modctrl1
address: 0x1419
name: "Motor Speed"
unit_of_measurement: "Hz"
device_class: "frequency"
state_class: measurement
register_type: holding
value_type: FP32
- platform: modbus_controller
modbus_controller_id: modctrl1
address: 0x1455
name: "IGBT Temp"
unit_of_measurement: "°C"
state_class: measurement
register_type: holding
value_type: FP32
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00DD
name: "Current Pressure"
unit_of_measurement: "psi"
device_class: "pressure"
state_class: measurement
register_type: holding
value_type: U_WORD
filters:
- multiply: 0.1450
# DERIVED SENSORS --------------
- platform: integration
name: "Motor Energy Consumption"
sensor: motor_power_consumption
time_unit: h
unit_of_measurement: "kWh"
device_class: energy
state_class: total_increasing
binary_sensor:
# MOTOR settings
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x157B
name: "Motor Above Ground"
register_type: holding
# EX RUNTIME settings
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x00F0
name: "Excessive Runtime Detection"
register_type: holding
# No GROUND settings
- platform: modbus_controller
modbus_controller_id: modctrl2
address: 0x15E1
name: "No Ground Detection"
register_type: holding
text_sensor:
- platform: template
name: "Run Mode"
id: run_mode
- platform: template
name: "Fault"
id: fault_state
- platform: template
name: "Motor Type"
id: motor_type
- platform: template
name: "3PH Motor Type"
id: ph3_motor_type
- platform: template
name: "I/O Input 1 Mode"
id: io_input_1_mode
- platform: template
name: "I/O Input 2 Mode"
id: io_input_2_mode
- platform: template
name: "I/O Output Mode"
id: io_output_mode
Edit this page on GitHub