Custom components, libraries etc

New to HA and ESPHome, so bear with me.

I’ve already made a few custom components to get the feel of it, as I have tons of stuff that will need customization and/or are not supported.
All works fine so far, but now, I’m getting bolder and wanted to do something with a DPS310 sensor.
There is two libraries for that in the “regular” PlatformIO, but these libraries does not seem to exist in ESPHome.
Question is, HOW do I get these in there, if possible at all.

I ended up with, as a quick fix, writing my own class to handle a DSP310 sensor over I2C.
That worked fine, but, I had to do it the ugly Arduino way (actually even uglier). I had to write ALL the code in the mycustomclass.h file.

Is there any way this can be split up in a decent way, so I can have separate h (and cpp?) files for the sensor class and a basic, minimalistic mycustomclass.h file, that just implements the actual esphome/hassio interface?
I tried a few variants, but as the mycustomclass.h file (and only that) is moved from the custom_component folder (or where-ever you specify in the YAML), to the /cofig/esphome//src, it didn’t work.

All tips and ideas are much appreciated.

Did you figure this one out? I’m also looking use a DPS310 with ESPHome as well.

I cannot really remember. I haven’t been working much with Homeassistant since then.
I don’t think I went any further with this, as the “dirty” solution worked.
I have attached the code I was using (I think). It is located in /config/esphome/custom_components/mycusensor.h

Good luck.

#include "esphome.h"



// status code
#define DPS__SUCCEEDED 0
#define DPS__FAIL_UNKNOWN -1
#define DPS__FAIL_INIT_FAILED -2
#define DPS__FAIL_TOOBUSY -3
#define DPS__FAIL_UNFINISHED -4

// constants
#define DPS__NUM_OF_SCAL_FACTS 8
#define DPS__RESULT_BLOCK_LENGTH 3


typedef struct
{
    uint8_t regAddress;
    uint8_t length;
} RegBlock_t;

enum RegisterBlocks_e
{
    PRS = 0, // pressure value
    TEMP,    // temperature value
};

const RegBlock_t registerBlocks[2] = {
    {0x00, 3},
    {0x03, 3},
};

const RegBlock_t coeffBlock = {0x10, 18};


class myclass {
public:
    myclass() { _local_address = 0x76; };

    void getTwosComplement(int32_t *raw, uint8_t length) {
    	if (*raw & ((uint32_t)1 << (length - 1)))
    		*raw -= (uint32_t)1 << length;
    }

    byte i2c_read(byte reg) {
        Wire.beginTransmission(_local_address);
        Wire.write(reg);
        Wire.endTransmission(false);
        Wire.requestFrom((byte) _local_address, (byte) 1, (byte) true);
        if (Wire.available() == 1)
            return (byte) Wire.read();
        return 0;
    }

    void i2c_write(byte reg, byte val) {
        Wire.beginTransmission(_local_address);
        Wire.write(reg);
        Wire.write(val);
        Wire.endTransmission();
    }

    int16_t readBlock(RegBlock_t regBlock, uint8_t *buffer) {
    	//do not read if there is no buffer
    	if (buffer == NULL)
    		return 0; //0 bytes read successfully

    	Wire.beginTransmission((byte) _local_address);
    	Wire.write(regBlock.regAddress);
    	Wire.endTransmission(false);

    	//request length bytes from slave
    	int16_t ret = Wire.requestFrom((byte) _local_address, regBlock.length, 1U);
    	//read all received bytes to buffer
    	for (int16_t count = 0; count < ret; count++)
    		buffer[count] = Wire.read();
    	return ret;
    }

    int16_t getRawResult(int32_t *raw, RegBlock_t reg)
    {
    	uint8_t buffer[DPS__RESULT_BLOCK_LENGTH] = {0};
    	if (readBlock(reg, buffer) != DPS__RESULT_BLOCK_LENGTH)
    		return DPS__FAIL_UNKNOWN;

    	*raw = (uint32_t)buffer[0] << 16 | (uint32_t)buffer[1] << 8 | (uint32_t)buffer[2];
    	getTwosComplement(raw, 24);
    	return DPS__SUCCEEDED;
    }

    int16_t readcoeffs(void) {
    	// TODO: remove magic number
    	uint8_t buffer[18];

    	//read COEF registers to buffer
    	int16_t ret = readBlock(coeffBlock, buffer);

    	//compose coefficients from buffer content
    	m_c0Half = ((uint32_t)buffer[0] << 4) | (((uint32_t)buffer[1] >> 4) & 0x0F);
    	getTwosComplement(&m_c0Half, 12);
    	//c0 is only used as c0*0.5, so c0_half is calculated immediately
    	m_c0Half = m_c0Half / 2U;

    	//now do the same thing for all other coefficients
    	m_c1 = (((uint32_t)buffer[1] & 0x0F) << 8) | (uint32_t)buffer[2];
    	getTwosComplement(&m_c1, 12);
    	m_c00 = ((uint32_t)buffer[3] << 12) | ((uint32_t)buffer[4] << 4) | (((uint32_t)buffer[5] >> 4) & 0x0F);
    	getTwosComplement(&m_c00, 20);
    	m_c10 = (((uint32_t)buffer[5] & 0x0F) << 16) | ((uint32_t)buffer[6] << 8) | (uint32_t)buffer[7];
    	getTwosComplement(&m_c10, 20);

    	m_c01 = ((uint32_t)buffer[8] << 8) | (uint32_t)buffer[9];
    	getTwosComplement(&m_c01, 16);

    	m_c11 = ((uint32_t)buffer[10] << 8) | (uint32_t)buffer[11];
    	getTwosComplement(&m_c11, 16);
    	m_c20 = ((uint32_t)buffer[12] << 8) | (uint32_t)buffer[13];
    	getTwosComplement(&m_c20, 16);
    	m_c21 = ((uint32_t)buffer[14] << 8) | (uint32_t)buffer[15];
    	getTwosComplement(&m_c21, 16);
    	m_c30 = ((uint32_t)buffer[16] << 8) | (uint32_t)buffer[17];
    	getTwosComplement(&m_c30, 16);
    	return DPS__SUCCEEDED;
    }

    void init() {
        readcoeffs();

        i2c_write(0x06, 0b00110011);
        i2c_write(0x07, 0x80 | 0b00110011);

        // patch some error
        i2c_write(0x0E, 0xA5);
        i2c_write(0x0F, 0x96);
        i2c_write(0x62, 0x02);
        i2c_write(0x0E, 0x00);
        i2c_write(0x0F, 0x00);

        i2c_write(0x08, 0b00100111);
    }

    float getTemp() {
        int32_t raw_val;

        getRawResult(&raw_val, registerBlocks[TEMP]);

        float temp = raw_val;

    	//scale temperature according to scaling table and oversampling
    	temp /= scaling_facts[3];

    	//update last measured temperature
    	//it will be used for pressure compensation
    	m_lastTempScal = temp;

    	//Calculate compensated temperature
    	temp = m_c0Half + m_c1 * temp;

    	return temp;
    }

    float getPressure() {
        int32_t raw_val;

        getRawResult(&raw_val, registerBlocks[PRS]);

        float prs = raw_val;

    	//scale pressure according to scaling table and oversampling
    	prs /= scaling_facts[3];

    	//Calculate compensated pressure
    	prs = m_c00 + prs * (m_c10 + prs * (m_c20 + prs * m_c30)) + m_lastTempScal * (m_c01 + prs * (m_c11 + prs * m_c21));

    	//return pressure
    	return prs;
    }


protected:
    uint8_t _local_address;

    //scaling factor table
	static const int32_t scaling_facts[DPS__NUM_OF_SCAL_FACTS];

  	// compensation coefficients
    int32_t m_c0Half;
    int32_t m_c1;
	int32_t m_c00;
	int32_t m_c10;
	int32_t m_c01;
	int32_t m_c11;
	int32_t m_c20;
	int32_t m_c21;
	int32_t m_c30;

    // last measured scaled temperature (necessary for pressure compensation)
	float m_lastTempScal;
};


// Over sampling rate:                                             1        2       4       8        16     32       64      128
const int32_t myclass::scaling_facts[DPS__NUM_OF_SCAL_FACTS] = {524288, 1572864, 3670016, 7864320, 253952, 516096, 1040384, 2088960};



/******************************************************************************/
/******************************************************************************/
/******************************************************************************/

class MyCustomSensor : public PollingComponent {
    public:

        Sensor *temperature_sensor = new Sensor();
        Sensor *pressure_sensor = new Sensor();

    // constructor
    MyCustomSensor() : PollingComponent(15000) {}

  //float get_setup_priority() const override { return esphome::setup_priority::BUS; }

    void setup() override {
        // This will be called by App.setup()
        myc = new myclass();
        myc->init();
    }

    void update() override {
        // This will be called every "update_interval" milliseconds.
        temperature_sensor->publish_state(myc->getTemp());
        pressure_sensor->publish_state(myc->getPressure() / 100);
    }

    protected:
        myclass *myc;
};

1 Like

Thanks! I’ll give this a try once I get my hands on a few of the pressure sensors.