Does ESPHome support one-wire addressable switches such as the DS2413 board?

Looking to add some physical light switches to HA using a NodeMCU (ESP32) and ESPHome and a good way looks to be using addressable switches such as the DS2413 to reduce wiring, does any one have any experience of these and if ESPHome supports this type of hardware (can’t find it in the docs) or will do soon? I see ESPHome supports Dallas, but can’t find anything more…

Background, bascially I looking at using NodeMCU’s to control all my lights (12v system) on a boat, and to reduce the amount of cabling and GPIO pins used I thought about using boards like the ones linked below in the light switches. The DS2413 could be quite a distance away from the NodeMCU (15meters max) - is that looking possible? I would like to keep the physical switches and lights themselves on the NodeMCU’s, (at the mo I just use the binary_sensor for the switches) so connecting the switches to another system like the Pi I would like to avoid.


Docs are very complete so if it’s not there it is not supported.
Please fill in a feature request if it doesn’t exist yet.

Cheers, looks like a request already been made… https://github.com/esphome/feature-requests/issues/391

I posted similarly in the github issue but thought I’d give it a go here as well to maximize the potential help I can get on this. I picked up a few of the DS2413 chips in very tiny tsoc6 form. I “dead-bug” soldered it to some headers so I can prototype on a breadboard and verified it’s functionality using Adafruit’s example code here:

So now I’ve moved onto attempting a custom component to use these things. Admittedly, this is my first custom component and my coding is limited mostly to bash scripting and 101 level C so this is going to be a learning experience no doubt. i.e. constructors and classes are very new to me.

So that said, would be helpful if y’all had some advice to get me going. I’ve read the custom output and custom sensor pages and have started trying to understand some of the example custom components I’ve found.

edit: This device supports being an input or output – working on output first as that is my immediate use case.

First question, are the esp_one_wire and/or dallas libraries that esphome usable in a custom component? i.e. can I leverage the functions that scan the bus for devices?

Here’s what I have so far, and no it doesn’t work.

in custom_component_DS2413.h:

#include "esphome.h"
#include "esp_one_wire.h"

namespace esphome {
namespace dallas {

#define DS2413_FAMILY_ID 0x3A
#define DS2413_ACCESS_READ 0xF5
#define DS2413_ACCESS_WRITE 0x5A
#define DS2413_ACK_SUCCESS 0xAA
#define DS2413_ACK_ERROR 0xFF

  // static const uint8_t DALLAS_MODEL_DS2413 =

class DS2413CustomComponent : public Component,  public BinaryOutput  {

  public:
    ESPOneWire oneWire(GPIOPin *pin);
    uint8_t address[8] = {0, 0, 0, 0, 0, 0, 0, 0};
    
    void setup() override {
      ESP_LOGD("custom", "Looking for a DS2413 on the bus");
      OneWire.reset_search();
    }
    void write_state(bool state) override {}
  };
} // namespace dallas
} // namespace esphome

and in the relevant yaml:

esphome:
  <<: !include templates/devices/wemos_d1_mini_core.yaml
  includes:
    - custom_component_DS2413.h
  libraries:
    - "esp_one_wire.h"

dallas:
  - pin: D3

output:
  - platform: custom
    type: binary
    lambda: |-
      auto my_custom_binary_output = new DS2413CustomComponent();
      App.register_component(my_custom_binary_output);
      return {my_custom_binary_output};
    outputs:
      - id: sw_01  # note that the DS2413 has two GPIO so I also need to figure out multiple outputs here

TTIA!

-J

Quick update. This now compiles but I still need to work out actually writing the output (which controls the state of the two outputs). I bailed on using the built in Dallas component because I couldn’t figure it out. The last two bits written to the bus define the state of the switch. i.e.
0x0 = both off
0x1 = sw 2 on, sw 1 off
0x2 = sw 1 on, sw 2 off
0x3 = both on

My question is how do I do this? The way I think it works is:

  1. pass the desired states into write_state function (how do I get current state?)
  2. write the states and publish results

Any ideas? Thanks!

-J

#include "esphome.h"
// #include "esp_one_wire.h"
#include <OneWire.h>

namespace esphome {
// namespace dallas {

#define DS2413_ONEWIRE_PIN  (0)

#define DS2413_FAMILY_ID    0x3A
#define DS2413_ACCESS_READ  0xF5
#define DS2413_ACCESS_WRITE 0x5A
#define DS2413_ACK_SUCCESS  0xAA
#define DS2413_ACK_ERROR    0xFF

OneWire oneWire(DS2413_ONEWIRE_PIN);
uint8_t address[8] = {0, 0, 0, 0, 0, 0, 0, 0};

// static const uint8_t DALLAS_MODEL_DS2413 =

class DS2413CustomComponent : public Component, public BinaryOutput
  {

  public:
 
      void setup() override {
      Serial.begin(9600);
      ESP_LOGD("custom", "Looking for a DS2413 on the bus");
      oneWire.reset_search();
      delay(250);
      if (!oneWire.search(address))
      {
        printBytes(address, 8);
        Serial.println(F("No device found on the bus!"));
        oneWire.reset_search();
        while (1)
          ;
      }

      /* Check the CRC in the device address */
      if (OneWire::crc8(address, 7) != address[7])
      {
        Serial.println(F("Invalid CRC!"));
        while (1)
          ;
      }

      if (address[0] != DS2413_FAMILY_ID)
      {
        printBytes(address, 8);
        Serial.println(F(" is not a DS2413!"));
        while (1)
          ;
      }

      Serial.print(F("Found a DS2413: "));
      printBytes(address, 8);
      Serial.println(F(""));
    
    }
  private:
    void printBytes(uint8_t *addr, uint8_t count, bool newline = 0)
    {
      for (uint8_t i = 0; i < count; i++)
      {
        Serial.print(addr[i] >> 4, HEX);
        Serial.print(addr[i] & 0x0f, HEX);
        Serial.print(" ");
      }
      if (newline)
      {
        Serial.println();
      }
    }

    byte read(void)
    {
      bool ok = false;
      uint8_t results;

      oneWire.reset();
      oneWire.select(address);
      oneWire.write(DS2413_ACCESS_READ);

      results = oneWire.read();                 /* Get the register results   */
      ok = (!results & 0x0F) == (results >> 4); /* Compare nibbles            */
      results &= 0x0F;                          /* Clear inverted values      */

      oneWire.reset();

      // return ok ? results : -1;
      return results;
    }
    bool write(uint8_t state)
    {
      uint8_t ack = 0;

      /* Top six bits must '1' */
      state |= 0xFC;

      oneWire.reset();
      oneWire.select(address);
      oneWire.write(DS2413_ACCESS_WRITE);
      oneWire.write(state);
      oneWire.write(~state); /* Invert data and resend     */
      ack = oneWire.read();  /* 0xAA=success, 0xFF=failure */
      if (ack == DS2413_ACK_SUCCESS)
      {
        oneWire.read(); /* Read the status byte      */
      }
      oneWire.reset();

      return (ack == DS2413_ACK_SUCCESS ? true : false);
    }

   
  
  
  void write_state(bool state01) override {
    bool ok = false;

    // if state01 {
    //   ok = write(0x1);
    // } else {
    //   ok = write(0x0);
    // }

   
    // if (!ok)
    //   ESP_LOGD("custom", "Wire failed");
  }
  };

// } // namespace dallas
} // namespace esphome

and the relevant yaml:

esphome:
  <<: !include templates/devices/wemos_d1_mini_core.yaml
  includes:
    - custom_component_DS2413.h

output:
  - platform: custom
    type: binary
    lambda: |-
      auto my_custom_binary_output = new DS2413CustomComponent();
      App.register_component(my_custom_binary_output);
      return {my_custom_binary_output};
    outputs:
      - id: sw_01

Bump. Finished some other projects and want to re-engage. Need some guidance on building custom one-wire devices. I can’t even compile yet.

TIA,

-J

Hey, your idea sounds very interesting, have you made any progress here?