Control digital potentiometer through home assistant via esphome?

According to the esphome docs for mqtt (pointed to above)

All devices defined through ESPHome should show up automatically in the entities section of Home Assistant.

ESPHome still not supporting digipots, or even one common type? Thought this would be an obvious addition back in '19. It would be so easy to bring old stuff into the digital era with it, and million other applications.
Is it because digipots do not usually come in premade/breakout format or?
Please!

If this should be something like a feature request(?) you might wanna think to address it in the correct communication channel which should be the esphome githubs

Yet you didn’t add it :slight_smile:

Seriously this is an open source project. People scratch their own itches.

1 Like

Hello Tom,
I’m also trying to control an MCP41010 with your information. Unfortunately, I have no idea about C++ and can’t get any further. I connected the MCP41010 to a Wemos D1 mini and copied your code to the Wemos. Unfortunately, the resistance does not change. I have defined the following pins in the yaml file clk_pin:GPIO14
mosi_pin:GPIO13
miso_pin:GPIO12

What I’m still missing is the CS connector on the MCP41010. To which pin on the Wemos do I have to connect it and where do I define the CS_pin?
Unfortunately my English is not very good and I hope you understand me and can help me.
Thanks
greeting
Dieter

I did it :slight_smile: !
The archive contains everything you need to control the MCP410 ** via ESPHome.
Suitable for any ESP8266 with 4Mb ROM.


DigiPot.zip

4 Likes

Thank you…

A couple of notes from my experience with the AD5204 (4 pots in a single chip) which has some pins you need to be aware of.
For it to work properly you must connect PR and SHDN to Vdd, and Vss to GND.
Drawing below
The other pins CS, CLK, SDI work the same way as any SPI device.

spi.transfer() will always send a byte regardless of what you put in there.
For the AD5204 you need to send 11 bits (MSBFIRST) - that’s 3 bits for the pot address, and 8 bits for pot value.
So you enable CS (active low)
Then you send a normal byte with decimal value 0-3 for the pot address.
And finally you send the second byte with the pot value 0-255, and the chip will discard the excess 5 bits from the first byte.
Disable CS to end communication and the chip will change the pot value accordingly.

I managed to get a simplified version of the code to work with MCP41XXX (digital potentiometer). The code below functions without SoftSPI.h and DigitalPin.h

MCP41100.h

#include "esphome.h"

using namespace esphome;

class MCP41100 : public Component, public FloatOutput {
 public:
  /*
  MCP41xxx Digital Pot Control
  
  The MCP41xxx is SPI-compatible. The hardware is managed by sending two bytes:
  BYTE 1 - is a command byte (NOP, WRITE, SHUTDOWN)
  BYTE 2 - is the data byte (new setpoint value 0-255)
  */

  // Define the MCP41100 OP command bits (only one POT)
  // Note: command byte format xxCCxxPP, CC command, PP pot number (01 if selected) 
  #define MCP_NOP 0b00000000
  #define MCP_WRITE 0b00010001
  #define MCP_SHTDWN 0b00100001

  // Define the CS (chip select) for the digital pot
  int CS_PIN = 5;

  void setup() override {
    SPI.begin();
    pinMode(CS_PIN, OUTPUT);
    digitalWrite(CS_PIN, HIGH);
  }

  void write_state(float state) override {
    // state is the amount this output should be on, from 0.0 to 1.0
    // we need to convert it to an integer first
    int value = state * 255;

    SPI.beginTransaction(SPISettings(14000000, MSBFIRST, SPI_MODE0));
    digitalWrite(CS_PIN, LOW); // CS pin low to select chip
    delay(50);
    SPI.transfer(MCP_WRITE);   // Send command code
    SPI.transfer(value);       // Send associated value
    delay(50);
    digitalWrite(CS_PIN, HIGH);// CS pin high to de-select chip
    SPI.endTransaction();
  }
};

YAML

esphome:
  name: pool-pump-controller
  friendly_name: Pool Pump Controller
  includes:
    MCP41100.h

spi:
  clk_pin: GPIO18
  mosi_pin: GPIO23

number:
  - platform: template
    name: "Speed"
    min_value: 0
    max_value: 100
    step: 1
    optimistic: true
    unit_of_measurement: "%"
    mode: slider
    set_action:
      - output.set_level:
          id: pot_out
          level: !lambda "return x / 100.0;"

output:
- platform: custom
  type: float
  lambda: |-
    auto mcp41100 = new MCP41100();
    App.register_component(mcp41100);
    return {mcp41100};

  outputs:
    id: pot_out
4 Likes

Can you help with it? I want connect AD5292. Can you wtite to more info for yaml file?

Hi! I used the examples for AD5292.

Make AD5292.h

#include "esphome.h"

using namespace esphome;

class AD5292 : public Component, public FloatOutput {
 public:
  /*
  AD5292 Digital Pot Control
  
  The AD5292 is SPI-compatible. The hardware is managed by sending two bytes:
  Command Bits[DB13:DB10]
  Data Bits[DB9:DB0]
  */

  // Define the MCP41100 OP command bits (only one POT)
  // Note: command byte format xxCCxxPP, CC command, PP pot number (01 if selected) 
  #define AD5292_NOP 0b0000000000000000 // NOP command: do nothing. Command 0.
  #define AD5292_WRITE 0b000001 // Write contents of serial data to RDAC. Command 1.
  #define AD5292_SHTDWN 0b0010000000000001 // Software shutdown. D0 = 0 (normal mode). D0 = 1 (device placed in shutdown mode). Command 8.
  //                                     ^D0 bit
  #define AD5292_READ_RDAC 0b0000100000000000 // Read RDAC wiper setting from the SDO output in the next frame. Command 2.
  #define AD5292_SAVE_20TP 0b0000110000000000 // Store wiper setting: store RDAC setting to 20-TP memory. Save 20-TP memory settings in RDAC. Command 3.
  #define AD5292_RESET_20TP 0b0001000000000000 // Reset: refresh RDAC with 20-TP stored value. Command 4.
  #define AD5292_READ_20TP 0b00010000000 // Read contents of 20-TP memory, or status of 20-TP memory, from the SDO output in the next frame. Command 5.
  #define AD5292_WRITE_CR 0b000110000000 // Write contents of serial data to control register. Command 6.
  #define AD5292_READ_CR 0b0001110000000000 // Read control register from the SDO output in the next frame.. Command 7.
  
  // Define the CS (chip select) for the digital pot
  int CS_PIN = 5;

  void setup() override {
    SPI.begin();
    pinMode(CS_PIN, OUTPUT);
    digitalWrite(CS_PIN, HIGH);
  }

  void write_state(float state) override {
    // state is the amount this output should be on, from 0.0 to 1.0
    // we need to convert it to an integer first
    int value = state * 255;

    SPI.beginTransaction(SPISettings(14000000, MSBFIRST, SPI_MODE0));
    digitalWrite(CS_PIN, LOW); // CS pin low to select chip
    delay(50);
    SPI.transfer(AD5292_WRITE);   // Send command code
    SPI.transfer(value);       // Send associated value
    delay(50);
    digitalWrite(CS_PIN, HIGH);// CS pin high to de-select chip
    SPI.endTransaction();
  }
};

Can you check it? It will work?
In datasheets i can see:

Datasheets:

In yaml:

esphome:
  name: pool-pump-controller
  friendly_name: Pool Pump Controller
  includes:
    AD5292/AD5292.h

spi:
  clk_pin: GPIO18
  mosi_pin: GPIO23

number:
  - platform: template
    name: "Speed"
    min_value: 0
    max_value: 100
    step: 1
    optimistic: true
    unit_of_measurement: "%"
    mode: slider
    set_action:
      - output.set_level:
          id: pot_out
          level: !lambda "return x / 100.0;"

output:
- platform: custom
  type: float
  lambda: |-
    auto AD5292= new AD5292();
    App.register_component(AD5292);
    return {AD5292};

  outputs:
    id: pot_out

The configs has the errors:

INFO ESPHome 2023.12.9
INFO Reading configuration /config/esphome/hv-control.yaml...
INFO Generating C++ source...
INFO Compiling app...
Processing hv-control (board: esp32dev; framework: arduino; platform: platformio/[email protected])
--------------------------------------------------------------------------------
HARDWARE: ESP32 240MHz, 320KB RAM, 4MB Flash
 - toolchain-xtensa-esp32 @ 8.4.0+2021r2-patch5
Dependency Graph
|-- AsyncTCP-esphome @ 2.0.1
|-- WiFi @ 2.0.0
|-- FS @ 2.0.0
|-- Update @ 2.0.0
|-- ESPAsyncWebServer-esphome @ 3.1.0
|-- DNSServer @ 2.0.0
|-- ESPmDNS @ 2.0.0
|-- noise-c @ 0.1.4
|-- SPI @ 2.0.0
Compiling .pioenvs/hv-control/src/main.cpp.o
Compiling .pioenvs/hv-control/lib64d/WiFi/WiFiUdp.cpp.o
Archiving .pioenvs/hv-control/lib64d/libWiFi.a
Compiling .pioenvs/hv-control/lib01c/FS/FS.cpp.o
<unicode string>: In lambda function:
<unicode string>:57:25: error: expected type-specifier before 'AD5292'
<unicode string>:59:21: error: could not convert '{AD5292}' from '<brace-enclosed initializer list>' to 'std::vector<esphome::output::FloatOutput*>'
*** [.pioenvs/hv-control/src/main.cpp.o] Error 1
========================== [FAILED] Took 7.41 seconds ==========================