How to convert my code to esphome sensor / external component?

Hi all, I’m not a developer but I made this code that works for my PM uart sensor. Now I would like to convert this to an esphome sensor but how can I split the code into .cpp and .h? Or is there another way to integrate custom code into esphome sensors?
And yes, I could buy one off the standard already integrated sensors but I didn’t… :wink:

#include <SoftwareSerial.h>

SoftwareSerial mySerial(D1, D2); // RX, TX

void setup() {
  Serial.begin(9600);
  mySerial.begin(9600);
}

void printValues(byte data[]) {
  // Print the values to the terminal
  int pm1_standard = (data[4] << 8) | data[5];
  int pm25_standard = (data[6] << 8) | data[7];
  int pm10_standard = (data[8] << 8) | data[9];

  Serial.print("PM1.0: ");
  Serial.print(pm1_standard);
  Serial.print(" ug/m3, PM2.5: ");
  Serial.print(pm25_standard);
  Serial.print(" ug/m3, PM10: ");
  Serial.print(pm10_standard);
  Serial.println(" ug/m3");
}

uint16_t calculateChecksum(byte data[]) {
  uint16_t checksum = 0;

  // Sum the bytes from index 0 to 29
  for (int i = 0; i < 30; i++) {
    checksum += data[i];
  }

  return checksum;
}

void loop() {
  if (mySerial.available() >= 32) {
    // Wait for the reception of at least 32 bytes (complete data frame)
    if (mySerial.read() == 0x42 && mySerial.read() == 0x4D) {
      // Check for the beginning of a data frame
      byte data[32];
      for (int i = 0; i < 32; i++) {
        data[i] = mySerial.read();
      }

      // Check the received checksum
      uint16_t receivedChecksum = (data[30] << 8) | data[31];
      uint16_t calculatedChecksum = calculateChecksum(data);

      if (receivedChecksum == 0xFFFF || receivedChecksum == calculatedChecksum) {
        // Print the values
        printValues(data);
      }
    }
  }
}

Theres a guide in the esphome docs…

I have read the documents a thousand times, but there is a discrepancy between the maturity of the documentation and my knowledge of the subject. I need some real developers view and knowledge at this point.
I have now created a folder structure, a .h, .cpp and a yaml. But I have an error message in the editor that d3_sensor cannot be found. The compiler can find the files but returns an error message.
When I compile I get the message;

/config/esphome/finedust-1.yaml: In lambda function:
/config/esphome/finestof-1.yaml:33:28: error: expected type-specifier before 'D3Sensor'
    33 | auto d3_sensor = new D3Sensor();
       | ^~~~~~~~

In the editor I already see this error;

/homeassistant/esphome/components/d3_sensor/d3_sensor.h

#ifndef D3_SENSOR_H
#define D3_SENSOR_H

#include <Arduino.h>

class D3Sensor {
  public:
    void setup();
    void loop();

  private:
    void printValues(byte data[]);
    uint16_t calculateChecksum(byte data[]);
};

#endif  // D3_SENSOR_H

/homeassistant/esphome/components/d3_sensor/d3_sensor.cpp

#include "d3_sensor.h"
#include <SoftwareSerial.h>

namespace esphome {

D3Sensor::D3Sensor() {
  // Constructor implementation
}

void D3Sensor::setup() {
  mySerial.begin(9600);
}

void D3Sensor::printValues(byte data[]) {
  int pm1_standard = (data[4] << 8) | data[5];
  int pm25_standard = (data[6] << 8) | data[7];
  int pm10_standard = (data[8] << 8) | data[9];
}

uint16_t D3Sensor::calculateChecksum(byte data[]) {
  uint16_t checksum = 0;

  // Sum the bytes from index 0 to 29
  for (int i = 0; i < 30; i++) {
    checksum += data[i];
  }

  return checksum;
}

void D3Sensor::loop() {
  if (mySerial.available() >= 32) {
    // Wait for the reception of at least 32 bytes (complete data frame)
    if (mySerial.read() == 0x42 && mySerial.read() == 0x4D) {
      // Check for the beginning of a data frame
      byte data[32];
      for (int i = 0; i < 32; i++) {
        data[i] = mySerial.read();
      }

      // Check the received checksum
      uint16_t receivedChecksum = (data[30] << 8) | data[31];
      uint16_t calculatedChecksum = calculateChecksum(data);

      if (receivedChecksum == 0xFFFF || receivedChecksum == calculatedChecksum) {
        // Declare variables for the sensors
        int pm1_standard = (data[4] << 8) | data[5];
        int pm25_standard = (data[6] << 8) | data[7];
        int pm10_standard = (data[8] << 8) | data[9];
        
        // Publish data to Home Assistant as sensors
        id(pm1_sensor).publish_state(pm1_standard);
        id(pm25_sensor).publish_state(pm25_standard);
        id(pm10_sensor).publish_state(pm10_standard);
        id(checksum_status_sensor).publish_state("Okay");
      } else {
        id(checksum_status_sensor).publish_state("Not Okay");
      }
    }
  }
}

}  // namespace esphome

external_components:
  - source:
      type: local
      path: components/d3_sensor

sensor:
  - platform: custom
    lambda: |-
      auto d3_sensor = new D3Sensor();
      App.register_component(d3_sensor);
      std::vector<esphome::sensor::Sensor*> sensors;
      sensors.push_back(d3_sensor->pm1);
      sensors.push_back(d3_sensor->pm2_5);
      sensors.push_back(d3_sensor->pm10);
      return sensors;
    sensors:
      - name: PM1.0
        unit_of_measurement: µg/m³
        accuracy_decimals: 0
      - name: PM2.5
        unit_of_measurement: µg/m³
        accuracy_decimals: 0
      - name: PM10
        unit_of_measurement: µg/m³
        accuracy_decimals: 0
1 Like