DS2413 Custom Component Help! [update: working as a switch]

I’m starting a new thread as the other one is marked as having a solution even though the solution was simply to put in a feature request. Anywho, I’m trying to create a custom component using DS2413 which is 2 GPIO controlled via the OneWire protocol. I have to admit that my coding is arduino-grade and I’m not too familiar with the concepts of classes and constructors so a custom component like this is a bit over my head. The adafruit example code works so I’ve tackled the “easy” part of ensuring the hardware is good. Can anyone help me get started on the custom component? For starts, I don’t care if I can’t use it as an input, my initial use case is to have 2 outputs/switches controlled via OneWire. (The OCD in me will want to massage the code afterwards to support both). In any event, the link above has working arduino code and I’ve included where I am below. Can one of you experts give me some tips to make some progress? I’m spinning my wheels here.

-J

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

/* Notes 
use mcp23008.h as model? 
https://esphome.io/api/mcp23008_8h_source.html
use 4ch lcrelay as model?
https://github.com/nekromant/esphome-lctech-4chanel-modules/blob/0d73a55a865092039fc2a3c29c2268e6e561d7dd/lcrelay.h
*/
#define TAG "dallas"

static const uint8_t DS2413_ONEWIRE_PIN = 0;  //D3
static const uint8_t DS2413_FAMILY_ID = 0x3A;
static const uint8_t DS2413_ACCESS_READ = 0xF5;
static const uint8_t DS2413_ACCESS_WRITE = 0x5A;
static const uint8_t DS2413_ACK_SUCCESS = 0xAA;
static const uint8_t DS2413_ACK_ERROR = 0xFF;

using namespace esphome;


class DS2413 : public Component, public Switch  {
  public:
    int ctlID;

    DS2413(ESPOneWire *parent, int control_id) : ESPOneWire(DS2413_ONEWIRE_PIN)
    {
      ctlID = control_id;
    }
   
    void setup() override  {
      ESP_LOGD("custom", "Looking for a DS2413 on the bus");
      for (j = 0; j < 1; j++) // num switches -1
      {
        this->write_state(false);
      }
    }

void write_state(bool state01) override
{
  bool sw_1 = 0;
  bool sw_2 = 0;

}; // class
esphome:
  <<: !include templates/devices/wemos_d1_mini_core.yaml
  includes:
    - custom_component_DS2413.h
  libraries:
    - "OneWire"

switch:
  - platform: custom
    lambda: |-
      auto my_custom_binary_output = new DS2413();
      App.register_component(my_custom_binary_output);
      return {my_custom_binary_output->sw_1, my_custom_binary_output->sw_2};
    switches:
      - id: sw_01
        name: "Switch 1"
      - id: sw_02
        name: "Switch 2"

Let me first say that I’m not coding is not my day job so I could use some help cleaning this up. However, the code is working as is for using the DS2413 as a dual switch. However, it’s kludgy because I couldn’t figure out how to create a custom component with multiple switches which is all kinds of inefficient. It scans the bus twice on startup, I have duplicative but otherwise equal functions, etc. I’ll post what I have and if anyone wants to help me clean it up I would be much appreciative. The limitations of the current state of code as I can think of are:

  • only supports a single DS2413 device on the oneWire bus
  • no restore on boot, you set default boot states with bool variables near the top
  • scans the bus twice on setup()
  • switch output mode only, no support yet for using as an input
  • on bus scan, the address is printed incorrectly

To use, create a file called “custom_component_DS2413.h” with the contents shown below and copy it to your esphome yaml location. Also grab the OneWire.cpp and OneWire.h files and put them in the same directory. Finally, create an esphome project using the yaml code below. Adjust the top part to your board, names can be whatever you want.

OneWire files

With that all out of the way, here it is working:

relevant yaml:

esphome:
  name: ${project_name}
  platform: ESP8266
  board: d1_mini
  includes:
    - custom_component_DS2413.h
  libraries:
    - "OneWire"

switch:
  - platform: custom
    lambda: |-
      auto sw01 = new DS2413_1();
      App.register_component(sw01);
      return {sw01};
    switches:
      - id: switch_01
        name: "Switch 1"
  - platform: custom
    lambda: |-
      auto sw02 = new DS2413_2();
      App.register_component(sw02);
      return {sw02};
    switches:
      - id: switch_02
        name: "Switch 2"

custom_component_DS2413.h:

#pragma once
#include "esphome.h"
#include "OneWire.h"

bool inverted = true; // invert output
bool sw1    = false;  // set default boot state 
bool sw2    = false;  // set default boot state

#define DS2413_ONEWIRE_PIN  0 // gpio0 = d3

#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

#define TAG "dallas"

class DS2413_1 : public Component, public Switch  {
  public:
    OneWire oneWire;  // initiate component
    uint8_t address[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
    
    DS2413_1():oneWire(DS2413_ONEWIRE_PIN) {} // constructor

    void printBytes(uint8_t* addr, uint8_t count) {
      for (uint8_t i = 0; i < count; i++) 
      {
        ESP_LOGD(TAG, "%i:", addr[i]); // working but needs conversion to hex and a string buffer
      }
    }
    
    void setup() override  {
      ESP_LOGD(TAG, "Looking for a DS2413 on the bus...");
      oneWire.reset_search();
      delay(250);
      if (!oneWire.search(address)) 
      {
        printBytes(address, 8);
        ESP_LOGD(TAG, "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]) 
      {
        ESP_LOGD(TAG, "Invalid CRC!");
        while(1);
      }
      
      /* Make sure we have a DS2413 */
      if (address[0] != DS2413_FAMILY_ID) 
      {
        printBytes(address, 8);
        ESP_LOGD(TAG, " is not a DS2413!");
        while(1);
      }
      
      ESP_LOGD(TAG, "Found a DS2413: ");
      printBytes(address, 8);

      write_state(sw1);
    }
      

bool write(uint8_t state)
{
  uint8_t ack = 0;
  state |= 0xFC; /* Top six bits must '1' */

  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 state) override
{
  sw1 = state;
  bool ok = false;

  ESP_LOGD(TAG, "Writing state of: %i", sw1);

  if (sw2 && sw1) {
    ok = (inverted) ? write (0x00) : write(0x03);
  } else if (sw2 && !sw1) {
    ok = (inverted) ? write (0x01) : write(0x02);
  } else if (!sw2 && sw1) {
    ok = (inverted) ? write (0x02) : write(0x01);
  } else {
    ok = (inverted) ? write (0x03) : write(0x00);
  }

  (ok) ? publish_state(sw1) : ESP_LOGD("custom", "Wire failed");  
  
  } // write_state
  
}; // class


class DS2413_2 : public Component, public Switch  {
  public:
    OneWire oneWire;  // initiate component
    uint8_t address[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
    
    DS2413_2():oneWire(DS2413_ONEWIRE_PIN) {} // constructor

    void printBytes(uint8_t* addr, uint8_t count) {
      for (uint8_t i = 0; i < count; i++) 
      {
        ESP_LOGD(TAG, "%i:", addr[i]); // working but needs conversion to hex and a buffer
      }
    }
    
    void setup() override  {
      ESP_LOGD(TAG, "Looking for a DS2413 on the bus...");
      oneWire.reset_search();
      delay(250);
      if (!oneWire.search(address)) 
      {
        printBytes(address, 8);
        ESP_LOGD(TAG, "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]) 
      {
        ESP_LOGD(TAG, "Invalid CRC!");
        while(1);
      }
      
      /* Make sure we have a DS2413 */
      if (address[0] != DS2413_FAMILY_ID) 
      {
        printBytes(address, 8);
        ESP_LOGD(TAG, " is not a DS2413!");
        while(1);
      }
      
      ESP_LOGD(TAG, "Found a DS2413: ");
      printBytes(address, 8);

      write_state(sw2);
    }

bool write(uint8_t state)
{
  uint8_t ack = 0;
  state |= 0xFC;  /* Top six bits must '1' */

  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 state) override
{
  sw2 = state;
  bool ok = false;

  ESP_LOGD(TAG, "Writing state of: %i", sw2);

  if (sw2 && sw1) {
    ok = (inverted) ? write (0x00) : write(0x03);
  } else if (sw2 && !sw1) {
    ok = (inverted) ? write (0x01) : write(0x02);
  } else if (!sw2 && sw1) {
    ok = (inverted) ? write (0x02) : write(0x01);
  } else {
    ok = (inverted) ? write (0x03) : write(0x00);
  }

  (ok) ? publish_state(sw2) : ESP_LOGD("custom", "Wire failed");  
  
  } // write_state
  
}; // class

All the best,

-J