Custom Sensor with UART for TFMini TOF Sensor

Hi All,

I’m trying to write a custom sensor for a TFMini. Link to the sensor.

Following the guide here https://esphome.io/components/sensor/custom.html , i have been able to create a test sensor.

Main question, How do i incorporate the arduino code into the custom sensor? Or any pointers to where to look?

I have been able to get the sensor working on an ESP32 using this code:

// This code is wrriten to read data from Benewake mini-S LiDAR through UART physical interface.
// The code is tested with ESP32. It can also be used with ESP8266 by making slight modifications
// by Ibrahim Technical Support Engineer: [email protected]


// Note the format for setting a serial port is as follows: Serial2.begin(baud-rate, protocol, RX pin, TX pin);
#define RXD2 16
#define TXD2 17
int dist; /*----actual distance measurements of LiDAR---*/
int strength; /*----signal strength of LiDAR----------------*/
float temprature;
unsigned char check;        /*----save check value------------------------*/
int i;
unsigned char uart[9];  /*----save data measured by LiDAR-------------*/
const int HEADER=0x59; /*----frame header of data package------------*/
int rec_debug_state = 0x01;//receive state for frame


void setup() {
	delay(2000);
	Serial.begin(115200);
	Serial.println("\nBenewake TFmini-S UART LiDAR Program");
	Serial2.begin(115200);
}



void Get_Lidar_data(){
if (Serial2.available()) //check if serial port has data input
    {
    if(rec_debug_state == 0x01)
        {  //the first byte
          uart[0]=Serial2.read();
          if(uart[0] == 0x59)
              {
                check = uart[0];
                rec_debug_state = 0x02;
              }
        }
else if(rec_debug_state == 0x02)
     {//the second byte
      uart[1]=Serial2.read();
      if(uart[1] == 0x59)
          {
            check += uart[1];
            rec_debug_state = 0x03;
          }
      else{
            rec_debug_state = 0x01;
          }
      }

else if(rec_debug_state == 0x03)
        {
          uart[2]=Serial2.read();
          check += uart[2];
          rec_debug_state = 0x04;
        }
else if(rec_debug_state == 0x04)
        {
          uart[3]=Serial2.read();
          check += uart[3];
          rec_debug_state = 0x05;
        }
else if(rec_debug_state == 0x05)
        {
          uart[4]=Serial2.read();
          check += uart[4];
          rec_debug_state = 0x06;
        }
else if(rec_debug_state == 0x06)
        {
          uart[5]=Serial2.read();
          check += uart[5];
          rec_debug_state = 0x07;
        }
else if(rec_debug_state == 0x07)
        {
          uart[6]=Serial2.read();
          check += uart[6];
          rec_debug_state = 0x08;
        }
else if(rec_debug_state == 0x08)
        {
          uart[7]=Serial2.read();
          check += uart[7];
          rec_debug_state = 0x09;
        }
else if(rec_debug_state == 0x09)
        {
          uart[8]=Serial2.read();
          if(uart[8] == check)
            {
              
              dist = uart[2] + uart[3]*256;//the distance
              strength = uart[4] + uart[5]*256;//the strength
              temprature = uart[6] + uart[7] *256;//calculate chip temprature
              temprature = temprature/8 - 256;                              
              Serial.print("dist = ");
              Serial.print(dist); //output measure distance value of LiDAR
              Serial.print('\n');
              Serial.print("strength = ");
              Serial.print(strength); //output signal strength value
              Serial.print('\n');
              Serial.print("\t Chip Temprature = ");
              Serial.print(temprature);
              Serial.println(" celcius degree"); //output chip temperature of Lidar                                                       
              while(Serial2.available()){Serial2.read();} // This part is added becuase some previous packets are there in the buffer so to clear serial buffer and get fresh data.
              delay(100);
              


             }
          rec_debug_state = 0x01;
        }
    }
}

void loop() {
	Get_Lidar_data();
}

However, where i am stuck, is how to incorporate this into my custom sensor?

Code so far:

Custom Sensor:

#include "esphome.h"
#include "DFRobot_TFMini.h"

class DFRobotTFminicustom : public PollingComponent, public Sensor {
 public:
   DFRobot_TFmini tof;
    // constructor
   DFRobotTFminicustom() : PollingComponent(1000) {}
  
   float get_setup_priority() const override { return esphome::setup_priority::IO; }

  void setup() override {
    // This will be called by App.setup()
    tof.begin();
  }
  void update() override {
    publish_state(42.0);
  }
};

and my ESPhome yaml:

esphome:
  name: tof-sensor-test-esp32
  platform: ESP32
  board: esp32dev
  includes: 
    - esp32toftest.h

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:
  password: "##########################"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_passphrase

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Tof-Sensor-Test-Esp32"
    password: "############"

captive_portal:

sensor:
- platform: custom
  lambda: |-
    auto tof_distance = new DFRobotTFminicustom();
    App.register_component(tof_distance);
    return {tof_distance};

  sensors:
    name: "Distance"  

Anybody?? Even just some pointers.

Maybe my config files can help you.

You need theese 2 files.

TFMiniPlus.yaml

esphome:
  name: tfminiplus
  platform: ESP32
  board: esp32dev 

  includes:
    - TFMiniPlus.h
  
  libraries:
    - "TFMPlus"
    
# Enable logging
logger:

# Enable Home Assistant API
api:

ota:
  password: "OTA Password"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "TFMiniPlus"
    password: "password"

captive_portal:

sensor:
- platform: custom
  lambda: |-
    auto my_sensor = new MyDistanceSensor();
    App.register_component(my_sensor);
    return {my_sensor};

  sensors:
  - name: "Distance Sensor"
    unit_of_measurement: "cm"
    accuracy_decimals: 0

Custom Component:
TFMiniPlus.h

#include "esphome.h"
#include "TFMPlus.h"
// ...

// Initialize variables
int16_t tfDist = 0;    // Distance to object in centimeters
int16_t tfFlux = 0;    // Strength or quality of return signal
int16_t tfTemp = 0;    // Internal temperature of Lidar sensor chip

// Define some variables we need later
float distance;

class MyDistanceSensor : public PollingComponent, public Sensor {
 public:
    
    //TFmini tfmini;
    TFMPlus tfmP;
    
    MyDistanceSensor() : PollingComponent(1000) { }

    void setup() override {
    
        Serial2.begin( 115200);  // Initialize TFMPLus device serial port.
        delay(20);               // Give port time to initalize
        tfmP.begin( &Serial2);   // Initialize device library object and...
    
        delay(1000);  // added to allow the System Rest enough time to complete
        
        // Send some example commands to the TFMini-Plus
        // - - Perform a system reset - - - - - - - - - - -
        Serial.printf( "===== TFMiniPlus ===== \r\n");
        Serial.printf( "Soft reset: ");
        if( tfmP.sendCommand( SOFT_RESET, 0))
        {
            Serial.printf( "passed.\r\n");
        }
        else tfmP.printReply();
      
        delay(500);  // added to allow the System Rest enough time to complete
    
        // - - Display the firmware version - - - - - - - - -
        Serial.printf( "Firmware version: ");
        if( tfmP.sendCommand( GET_FIRMWARE_VERSION, 0))
        {
            Serial.printf( "%1u.", tfmP.version[ 0]); // print three single numbers
            Serial.printf( "%1u.", tfmP.version[ 1]); // each separated by a dot
            Serial.printf( "%1u\r\n", tfmP.version[ 2]);
        }
        else tfmP.printReply();
        // - - Set the data frame-rate to 20Hz - - - - - - - -
        Serial.printf( "Data-Frame rate: ");
        if( tfmP.sendCommand( SET_FRAME_RATE, FRAME_20))
        {
            Serial.printf( "%2uHz.\r\n", FRAME_20);
        }
        else tfmP.printReply();
        // - - - - - - - - - - - - - - - - - - - - - - - -
    
        delay(500);            // And wait for half a second.

    }

    void update() override {
        
        if( tfmP.getData( tfDist, tfFlux, tfTemp)) // Get data from the device.
        {
            Serial.println("==== Medindo Distância ====");   // display distance in log
            Serial.print( "Dist: ");   // display distance in log
            Serial.print(tfDist);   // display distance in log
            Serial.println(" cm");   // display distance in log
            Serial.println("============ // ===========");
        }
        else                  // If the command fails...
        {
            tfmP.printFrame();  // display the error and HEX dataa
        }
    
        distance = tfDist;

        if (isnan(distance)) {
            distance = 0;
        }
  
    publish_state(distance);
    }
};

// ...

@lincolnchamps I am looking into creating a custom sensor as well (but for an energy meter which should be read via an IR eye using the DLMS protocol)…

As far as I understand, the files needs to be placed somewhere in the ESPhome folder?
You also refer to TFMPlus.h in your example - what does this look like ?

Would you share the (complete) code for your custom sensor and how the files are placed (in order to be able to create the device in ESPhome) ?

I guess this part about custom sensors is unfortunately not what is best described on the documentation pages…

2 Likes