Hi
Old post but it is never to late.
I needed to use this DS1603L sensor for a project and the only option was to create an esphome external component but my knowledge in coding is zero.
After some digging in GitHub - jesserockz/esphome-external-component-examples to figure out the files structure and with the use of Chat GTP I managed to create the external component for DS1603L-V1 ultrasonic sensor.
Below the codes for .h, .cpp, .py and yaml.
DS1603L.h
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/uart/uart.h"
namespace esphome {
namespace DS1603L {
class DS1603L : public sensor::Sensor, public PollingComponent, public uart::UARTDevice {
public:
DS1603L() = default;
void setup() override; // Called during setup
void update() override; // Called periodically
void loop() override; // Called frequently to process data
void dump_config() override; // Prints configuration info
private:
uint8_t rx_buffer_[4]; // Buffer for incoming data
void parse_data_(); // Parse received data
};
} // namespace DS1603L
} // namespace esphome
DS1603L.cpp
#include "esphome/core/log.h"
#include "DS1603L.h"
namespace esphome {
namespace DS1603L {
static const char *TAG = "DS1603L.sensor";
void DS1603L::setup() {
ESP_LOGCONFIG(TAG, "Setting up DS1603L sensor...");
}
void DS1603L::update() {
// Updates are primarily handled in the loop
}
void DS1603L::loop() {
// Check if enough bytes are available
while (this->available() >= 4) {
// Read 4 bytes of data
this->read_array(this->rx_buffer_, 4);
ESP_LOGD(TAG, "Raw Data: %02X %02X %02X %02X",
this->rx_buffer_[0], this->rx_buffer_[1],
this->rx_buffer_[2], this->rx_buffer_[3]);
// Verify the header byte
if (this->rx_buffer_[0] != 0xFF) {
ESP_LOGW(TAG, "Invalid header received");
continue;
}
// Parse the received data
this->parse_data_();
}
}
void DS1603L::dump_config() {
ESP_LOGCONFIG(TAG, "DS1603L Sensor:");
LOG_SENSOR("", "Liquid Level", this);
}
void DS1603L::parse_data_() {
uint8_t header = this->rx_buffer_[0];
uint8_t data_h = this->rx_buffer_[1];
uint8_t data_l = this->rx_buffer_[2];
uint8_t checksum = this->rx_buffer_[3];
// Validate header
if (header != 0xFF) {
ESP_LOGW(TAG, "Invalid header: Received 0x%02X, expected 0xFF", header);
return;
}
// Compute checksum
uint8_t computed_checksum = (header + data_h + data_l) & 0xFF;
ESP_LOGD(TAG, "Data: Header=0x%02X, Data_H=0x%02X, Data_L=0x%02X, Checksum=0x%02X",
header, data_h, data_l, checksum);
ESP_LOGD(TAG, "Checksum: Computed=0x%02X, Received=0x%02X", computed_checksum, checksum);
if (checksum != computed_checksum) {
ESP_LOGW(TAG, "Checksum mismatch: Received 0x%02X, expected 0x%02X", checksum, computed_checksum);
return;
}
// Calculate liquid level
int liquid_level = (data_h << 8) | data_l; // Combine high and low bytes
ESP_LOGI(TAG, "Liquid Level: %d cm", liquid_level);
// Publish the state
this->publish_state(liquid_level);
}
} // namespace DS1603L
} // namespace esphome
sensor.py
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import uart, sensor
from esphome.const import CONF_ID, CONF_NAME, UNIT_EMPTY, ICON_EMPTY
# Define the namespace for DS1603L
DEPENDENCIES = ["uart"]
DS1603L_ns = cg.esphome_ns.namespace("DS1603L")
DS1603L = DS1603L_ns.class_("DS1603L", cg.PollingComponent, uart.UARTDevice)
# Configuration schema for DS1603L
CONFIG_SCHEMA = (
sensor.sensor_schema(
unit_of_measurement=UNIT_EMPTY, # Unit is customizable in YAML
icon=ICON_EMPTY, # Icon is customizable in YAML
accuracy_decimals=1, # Default to 1 decimal place
)
.extend(cv.polling_component_schema("60s")) # Default update interval is 60s
.extend(uart.UART_DEVICE_SCHEMA) # Include UART configuration options
.extend({
cv.GenerateID(): cv.declare_id(DS1603L), # Declare sensor ID
cv.Optional(CONF_NAME, default="DS1603L Sensor"): cv.string, # Default name
})
)
# Generate the C++ code from YAML
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) # Create a new DS1603L instance
cg.add(var.set_name(config[CONF_NAME])) # Set the sensor name
await cg.register_component(var, config) # Register the component
await sensor.register_sensor(var, config) # Register it as a sensor
await uart.register_uart_device(var, config) # Register UART communication
Inside the component folder you also need to put an empty __init__.py
file
YAML
esphome:
name: heating-oil
friendly_name: Heating OIL
esp32:
board: esp32dev
framework:
type: arduino
external_components:
- source:
type: local
path: my_components
# Enable logging
logger:
level: INFO # Set the default log level
# Enable Home Assistant API
api:
encryption:
key: # Your api key here
ota:
- platform: esphome
password: # Your ota password here
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
manual_ip:
static_ip: 192.168.30.15
gateway: 192.168.30.1
subnet: 255.255.255.0
# Enable hotspot if case wifi connection fails
ap:
ssid: "heating-oil fail hotspot"
password: !secret wifi_password
# Sync time with Home Assistant.
time:
- platform: homeassistant
id: homeassistant_time
captive_portal:
web_server:
port: 80
uart:
id: uart_bus
baud_rate: 9600
tx_pin: 17
rx_pin: 16
sensor:
- platform: DS1603L
name: "Heating OIL Level"
id: ds1603l_sensor_id
unit_of_measurement: "mm"
icon: "mdi:water"
accuracy_decimals: 1
update_interval: 5s
button:
- platform: restart
icon: mdi:power-cycle
name: "ESP Reboot"
As i mention i have zero knowledge in coding so PLEASE check the code before use it.
PS. Sorry for my poor English.