periodic push

This commit is contained in:
2023-06-29 16:14:36 +02:00
parent c948b95622
commit 831f676068
267 changed files with 17705 additions and 10864 deletions

352
esphome/include/bcm500ds.h Executable file
View File

@@ -0,0 +1,352 @@
#include "esphome.h"
// protocol
#define TUYA_COVER_MAX_LEN 640 // Max length of message value
// #define TUYA_COVER_MAX_LEN 256 // Max length of message value
#define TUYA_COVER_BUFFER_LEN 6 // Length of serial buffer for header + type + length
#define TUYA_COVER_HEADER_LEN 2 // Length of fixed header
// enable/disable reversed motor direction
// Normal = header (55AA) + (00060005) + 050100010011 "(55AA00060005050100010011)
// Reversed = header (55AA) + (00060005) + 050100010112 "(55AA00060005050100010112)"
#define TUYA_COVER_DISABLE_REVERSING { 0x69, 0x01, 0x00, 0x01, 0x00 } //dpid = 105, type = bool, len = 1, value = disable
#define TUYA_COVER_ENABLE_REVERSING { 0x69, 0x01, 0x00, 0x01, 0x01 } //dpid = 105, type = bool, len = 1, value = enable
// Curtain commands
// Open = header (55AA) + (00060005) + 6604000100 "(55aa000600056604000100)"
// Close = header (55AA) + (00060005) + 6604000101 "(55aa000600056604000101)"
// Stop = header (55AA) + (00060005) + 6604000102 "(55AA000600056604000102)"
#define TUYA_COVER_OPEN { 0x66, 0x04, 0x00, 0x01, 0x00 } //dpid = 101, type = enum, len = 1, value = OPEN
#define TUYA_COVER_CLOSE { 0x66, 0x04, 0x00, 0x01, 0x01 } //dpid = 101, type = enum, len = 1, value = CLOSE
#define TUYA_COVER_STOP { 0x66, 0x04, 0x00, 0x01, 0x02 } //dpid = 101, type = enum, len = 1, value = STOP
#define TUYA_COVER_SET_POSITION { 0x65, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00 } //"65020004000000" //dpid = 2, type = value, len = 4, value = 0x000000 + 1 byte (0x00-0x64)
static const char *TAG = "TUYACOVER";
static const uint16_t TUYA_COVER_HEADER = 0x55AA;
//static const uint16_t TUYA_COVER_VERSION = 0x03;
static const uint16_t TUYA_COVER_VERSION = 0x00;
const uint8_t tuya_cover_enable_reversing[] = TUYA_COVER_ENABLE_REVERSING;
const uint8_t tuya_cover_disable_reversing[] = TUYA_COVER_DISABLE_REVERSING;
const uint8_t tuya_cover_open[] = TUYA_COVER_OPEN;
const uint8_t tuya_cover_close[] = TUYA_COVER_CLOSE;
const uint8_t tuya_cover_stop[] = TUYA_COVER_STOP;
static uint8_t tuya_cover_pos[] = TUYA_COVER_SET_POSITION;
#define HEARTBEAT_INTERVAL_MS 10000
unsigned long previousHeartbeatMillis = 0;
struct TUYACOVERCommand
{
uint16_t header;
uint8_t version;
uint8_t command;
uint16_t length;
uint8_t value[TUYA_COVER_MAX_LEN];
uint8_t checksum;
};
struct TUYACOVERMessage
{
uint8_t dpid;
uint8_t type;
uint16_t len;
uint8_t value[TUYA_COVER_MAX_LEN - 4]; //Subtract dpid, type, len
};
enum TUYACOVERCommandType
{
TUYA_COVER_HEARTBEAT = 0x00,
TUYA_COVER_COMMAND = 0x06,
TUYA_COVER_RESPONSE = 0x07,
TUYA_COVER_QUERY_STATUS = 0x08
};
enum TUYACOVERdpidType
{
TUYA_COVER_DPID_POSITION= 0x65,
TUYA_COVER_DPID_DIRECTION = 0x64,
TUYA_COVER_DPID_UNKNOWN = 0x67,
TUYA_COVER_DPID_ERROR = 0x6E
};
// Variables
TUYACOVERCommand command_{TUYA_COVER_HEADER, TUYA_COVER_VERSION, 0, 0, {}, 0};
uint8_t uart_buffer_[TUYA_COVER_BUFFER_LEN]{0};
// Forward declarations
bool read_command();
void write_command(TUYACOVERCommandType command, const uint8_t *value, uint16_t length);
uint8_t checksum();
/*
* Attempt to read an entire command from the serial UART into the command struct.
* Will fail early if unable to find the two-byte header in the current
* data stream. If the header is found, it will contine to read the complete
* TLV+checksum sequence off the port. If the entire sequence can be read
* and the checksum is valid, it will return true.
*/
bool read_command()
{
// Shift bytes through until we find a valid header
bool valid_header = false;
while (Serial.available() >= 1)
{
uart_buffer_[0] = uart_buffer_[1];
uart_buffer_[1] = Serial.read();
command_.header = (uart_buffer_[0] << 8) + uart_buffer_[1];
if (command_.header == TUYA_COVER_HEADER)
{
valid_header = true;
break;
}
}
// Read the next 4 bytes (Version, Command, Data length)
// Read n bytes (Data length)
// Read the checksum byte
if (valid_header)
{
Serial.readBytes(uart_buffer_ + TUYA_COVER_HEADER_LEN, TUYA_COVER_BUFFER_LEN - TUYA_COVER_HEADER_LEN);
command_.version = uart_buffer_[2];
command_.command = uart_buffer_[3];
command_.length = (uart_buffer_[4] << 8) + uart_buffer_[5];
ESP_LOGV(TAG, "RX: Header = 0x%04X, Version = 0x%02X, Command = 0x%02X, Data length = 0x%04X", command_.header, command_.version, command_.command, command_.length);
if (command_.length < TUYA_COVER_MAX_LEN)
{
Serial.readBytes(command_.value, command_.length);
ESP_LOGV(TAG, "RX_RAW:");
for (size_t i = 0; i < command_.length; i++)
{
ESP_LOGV(TAG, "%02d: 0x%02X", i, command_.value[i]);
}
while (Serial.available() == 0) // Dirty
{
//Wait
}
command_.checksum = Serial.read();
ESP_LOGV(TAG, "RX_CHK: 0x%02X", command_.checksum);
uint8_t calc_checksum = checksum();
if (calc_checksum == command_.checksum)
{
// Clear buffer contents to start with beginning of next command
memset(uart_buffer_, 0, TUYA_COVER_BUFFER_LEN);
return true;
}
else
{
memset(uart_buffer_, 0, TUYA_COVER_BUFFER_LEN);
ESP_LOGE(TAG, "Checksum error: Read = 0x%02X != Calculated = 0x%02X", command_.checksum, calc_checksum);
}
}
else
{
memset(uart_buffer_, 0, TUYA_COVER_BUFFER_LEN);
ESP_LOGE(TAG, "Command length exceeds limit: %d >= %d", command_.length, TUYA_COVER_MAX_LEN);
}
}
// Do not clear buffer to allow for resume in case of reading partway through header RX
return false;
}
/*
* Store the given type, value, and length into the command struct and send
* it out the serial port. Automatically calculates the checksum as well.
*/
void write_command(TUYACOVERCommandType command, const uint8_t *value, uint16_t length)
{
// Copy params into command struct
command_.header = TUYA_COVER_HEADER;
command_.version = TUYA_COVER_VERSION;
command_.command = command;
command_.length = length;
ESP_LOGV(TAG, "TX: Header = 0x%04X, Version = 0x%02X, Command = 0x%02X, Data length = 0x%04X", command_.header, command_.version, command_.command, command_.length);
memcpy(&command_.value, value, length);
ESP_LOGV(TAG, "TX_RAW");
for (size_t i = 0; i < command_.length; i++)
{
ESP_LOGV(TAG, "%02d: 0x%02X", i, command_.value[i]);
}
// Copy struct values into buffer, converting longs to big-endian
uart_buffer_[0] = command_.header >> 8;
uart_buffer_[1] = command_.header & 0xFF;
uart_buffer_[2] = command_.version;
uart_buffer_[3] = command_.command;
uart_buffer_[4] = command_.length >> 8;
uart_buffer_[5] = command_.length & 0xFF;
command_.checksum = checksum();
ESP_LOGV(TAG, "TX_CHK: 0x%02X", command_.checksum);
// Send buffer out via UART
Serial.write(uart_buffer_, TUYA_COVER_BUFFER_LEN);
Serial.write(command_.value, command_.length);
Serial.write(command_.checksum);
// Clear buffer contents to avoid re-reading our own payload
memset(uart_buffer_, 0, TUYA_COVER_BUFFER_LEN);
}
/*
* Calculate checksum from current UART buffer (header+type+length) plus command value.
*/
uint8_t checksum()
{
uint8_t checksum = 0;
for (size_t i = 0; i < TUYA_COVER_BUFFER_LEN; i++)
{
checksum += uart_buffer_[i];
}
for (size_t i = 0; i < command_.length; i++)
{
checksum += command_.value[i];
}
return checksum;
}
class CustomCurtain : public Component, public Cover
{
protected:
public:
void setup() override
{
ESP_LOGI(TAG, "TUYA_COVER_COMMAND = QUERY_STATUS");
write_command(TUYA_COVER_QUERY_STATUS, 0, 0);
}
CoverTraits get_traits() override
{
auto traits = CoverTraits();
traits.set_is_assumed_state(false);
traits.set_supports_position(true);
traits.set_supports_tilt(false);
return traits;
}
void control(const CoverCall &call) override
{
if (call.get_position().has_value())
{
// Write pos (range 0-1) to cover
// Cover pos (range 0x00-0x64) (closed - open)
uint8_t pos = (100-(*call.get_position() * 100));
ESP_LOGV(TAG, "POS = %d", pos);
switch (pos)
{
case 0:
ESP_LOGI(TAG, "TUYA_COVER_COMMAND = CLOSE");
write_command(TUYA_COVER_COMMAND, tuya_cover_close, sizeof(tuya_cover_close));
break;
case 100:
ESP_LOGI(TAG, "TUYA_COVER_COMMAND = OPEN");
write_command(TUYA_COVER_COMMAND, tuya_cover_open, sizeof(tuya_cover_open));
break;
default:
tuya_cover_pos[7] = pos;
ESP_LOGI(TAG, "TUYA_COVER_COMMAND = POS = %d%%", pos);
write_command(TUYA_COVER_COMMAND, tuya_cover_pos, sizeof(tuya_cover_pos));
break;
}
// publish_state only when position is confirmed in loop()
}
if (call.get_stop())
{
// User requested cover stop
ESP_LOGI(TAG, "TUYA_COVER_COMMAND = STOP");
write_command(TUYA_COVER_COMMAND, tuya_cover_stop, sizeof(tuya_cover_stop));
}
}
void loop() override
{
unsigned long currentHeartbeatMillis = millis();
if (currentHeartbeatMillis - previousHeartbeatMillis >= HEARTBEAT_INTERVAL_MS)
{
previousHeartbeatMillis += HEARTBEAT_INTERVAL_MS;
ESP_LOGI(TAG, "TUYA_COVER_COMMAND = HEARTBEAT");
write_command(TUYA_COVER_HEARTBEAT, 0, 0);
}
bool have_message = read_command();
if(have_message && command_.command == TUYA_COVER_HEARTBEAT)
{
ESP_LOGI(TAG, "TUYA_COVER_RESPONSE = %s", (command_.value[0] == 0) ? "FIRST_HEARTBEAT" : "HEARTBEAT");
}
else if (have_message && command_.command == TUYA_COVER_RESPONSE)
{
switch (command_.value[0])
{
case TUYA_COVER_DPID_POSITION:
ESP_LOGI(TAG, "TUYA_COVER_DPID_POSITION = %d%%", command_.value[7]);
this->position = (1-((command_.value[7]) / 100.0f));
this->publish_state();
break;
case TUYA_COVER_DPID_DIRECTION:
ESP_LOGI(TAG, "TUYA_COVER_DPID_DIRECTION = 0x%02X", command_.value[4]);
break;
case TUYA_COVER_DPID_UNKNOWN:
ESP_LOGI(TAG, "TUYA_COVER_DPID_UNKNOWN ENUM = 0x%02X", command_.value[4]);
break;
case TUYA_COVER_DPID_ERROR:
ESP_LOGI(TAG, "TUYA_COVER_DPID_ERROR BITMAP = 0x%02X", command_.value[4]);
break;
default:
break;
}
}
}
};
class CustomAPI : public Component, public CustomAPIDevice
{
public:
void setup() override
{
register_service(&CustomAPI::setStatusReport, "get_status_report");
register_service(&CustomAPI::setMotorNormal, "set_motor_normal");
register_service(&CustomAPI::setMotorReversed, "set_motor_reversed");
register_service(&CustomAPI::sendCommand, "send_command", {"data"});
}
void setStatusReport()
{
ESP_LOGI(TAG, "TUYA_COVER_COMMAND = QUERY_STATUS");
write_command(TUYA_COVER_QUERY_STATUS, 0, 0);
}
void setMotorNormal()
{
ESP_LOGI(TAG, "TUYA_COVER_COMMAND = ENABLE_REVERSING");
write_command(TUYA_COVER_COMMAND, tuya_cover_enable_reversing, sizeof(tuya_cover_enable_reversing));
}
void setMotorReversed()
{
ESP_LOGI(TAG, "TUYA_COVER_COMMAND = ENABLE_REVERSING");
write_command(TUYA_COVER_COMMAND, tuya_cover_disable_reversing, sizeof(tuya_cover_disable_reversing));
}
void sendCommand(std::string data)
{
int i = 0;
uint8_t sum = 0;
while (i < data.length())
{
const char hex[2] = {data[i++], data[i++]};
uint8_t d = strtoul(hex, NULL, 16);
sum += d;
writeByte(d);
}
writeByte(sum);
}
void writeByte(uint8_t data)
{
Serial.write(data);
}
};

145
esphome/include/epaper75.h Executable file
View File

@@ -0,0 +1,145 @@
#include <string>
#include <iostream>
#define ICON_stop "\U000F04DB"
#define ICON_play "\U000F040A"
#define ICON_pause "\U000F03E4"
std::string playbackStatusToIcon(bool playing, bool paused) {
if (playing) return ICON_play;
else if (paused) return ICON_pause;
else return ICON_stop;
}
#define ICON_moon_first_quarter "\U000F0F61"
#define ICON_moon_full "\U000F0F62"
#define ICON_moon_last_quarter "\U000F0F63"
#define ICON_moon_new "\U000F0F64"
#define ICON_moon_waning_crescent "\U000F0F65"
#define ICON_moon_waning_gibbous "\U000F0F66"
#define ICON_moon_waxing_crescent "\U000F0F67"
#define ICON_moon_waxing_gibbous "\U000F0F68"
std::string moonToIcon(std::string moonPhase)
{
if (moonPhase == "new_moon") return ICON_moon_new;
if (moonPhase == "waxing_crescent") return ICON_moon_waxing_crescent;
if (moonPhase == "first_quarter") return ICON_moon_first_quarter;
if (moonPhase == "waxing_gibbous") return ICON_moon_waxing_gibbous;
if (moonPhase == "full_moon") return ICON_moon_full;
if (moonPhase == "waning_gibbous") return ICON_moon_waning_gibbous;
if (moonPhase == "last_quarter") return ICON_moon_last_quarter;
if (moonPhase == "waning_crescent") return ICON_moon_waning_crescent;
return "";
}
// Map weather states to MDI characters.
std::map<std::string, std::string> weather_icon_map
{
{"cloudy", "\U000F0590"},
{"cloudy-alert", "\U000F0F2F"},
{"cloudy-arrow-right", "\U000F0E6E"},
{"fog", "\U000F0591"},
{"hail", "\U000F0592"},
{"hazy", "\U000F0F30"},
{"hurricane", "\U000F0898"},
{"lightning", "\U000F0593"},
{"lightning-rainy", "\U000F067E"},
{"night", "\U000F0594"},
{"night-partly-cloudy", "\U000F0F31"},
{"partlycloudy", "\U000F0595"},
{"partly-lightning", "\U000F0F32"},
{"partly-rainy", "\U000F0F33"},
{"partly-snowy", "\U000F0F34"},
{"partly-snowy-rainy", "\U000F0F35"},
{"pouring", "\U000F0596"},
{"rainy", "\U000F0597"},
{"snowy", "\U000F0598"},
{"snowy-heavy", "\U000F0F36"},
{"snowy-rainy", "\U000F067F"},
{"sunny", "\U000F0599"},
{"sunny-alert", "\U000F0F37"},
{"sunny-off", "\U000F14E4"},
{"sunset", "\U000F059A"},
{"sunset-down", "\U000F059B"},
{"sunset-up", "\U000F059C"},
{"tornado", "\U000F0F38"},
{"windy", "\U000F059D"},
{"windy-variant", "\U000F059E"},
{"car", "\U000f010b"},
{"trash", "\U000F0819"},
};
std::map<std::string, std::string> moon_icon_map
{
{ "mdi:moon-waxing-crescent", "\U000f0f67" },
{ "mdi:moon-first-quarter", "\U000F0F61" },
{ "mdi:moon-waxing-gibbous", "\U000F0F68" },
{ "mdi:moon-full", "\U000F0F62" },
{ "mdi:moon-waning-gibbous", "\U000F0F66" },
{ "mdi:moon-last-quarter", "\U000F0F63" },
{ "mdi:moon-waning-crescent", "\U000F0F65" },
};
#define ICON_w_clear_night "\U000F0594"
#define ICON_w_cloudy "\U000F0590"
#define ICON_w_fog "\U000F0591"
#define ICON_w_hail "\U000F0592"
#define ICON_w_lightning "\U000F0593"
#define ICON_w_lightning_rainy "\U000F067E"
#define ICON_w_night_partly_cloudy "\U000F0F31"
#define ICON_w_partly_cloudy "\U000F0595"
#define ICON_w_pouring "\U000F0596"
#define ICON_w_rainy "\U000F0597"
#define ICON_w_snowy "\U000F0F36"
#define ICON_w_snowy_rainy "\U000F067F"
#define ICON_w_sunny "\U000F0599"
#define ICON_w_windy "\U000F059D"
#define ICON_w_windy_variant "\U000F059E"
#define ICON_w_exceptional "\U000F0F38"
std::string conditionToIcon(std::string condition, bool daytime)
{
if (condition == "clear") return ICON_w_clear_night;
if (condition == "clear-night") return ICON_w_clear_night;
if (condition == "cloudy") return ICON_w_cloudy;
if (condition == "dust") return ICON_w_fog;
if (condition == "dusty") return ICON_w_fog;
if (condition == "fog") return ICON_w_fog;
if (condition == "frost") return ICON_w_snowy;
if (condition == "hail") return ICON_w_hail;
if (condition == "haze") return ICON_w_fog;
if (condition == "hazy") return ICON_w_fog;
if (condition == "heavy_shower") return ICON_w_rainy;
if (condition == "heavy_showers") return ICON_w_rainy;
if (condition == "light_rain") return ICON_w_rainy;
if (condition == "light_showers") return ICON_w_rainy;
if (condition == "light_shower") return ICON_w_rainy;
if (condition == "lightning") return ICON_w_lightning;
if (condition == "lightning-rainy") return ICON_w_lightning_rainy;
if (condition == "mostly_sunny") return ICON_w_sunny;
if (condition == "night") return ICON_w_clear_night;
if (condition == "partlycloudy" && !daytime) return ICON_w_night_partly_cloudy;
if (condition == "partlycloudy" && daytime) return ICON_w_partly_cloudy;
if (condition == "partly_cloudy" && !daytime) return ICON_w_night_partly_cloudy;
if (condition == "partly_cloudy" && daytime) return ICON_w_partly_cloudy;
if (condition == "pouring") return ICON_w_pouring;
if (condition == "rain") return ICON_w_rainy;
if (condition == "rainy") return ICON_w_rainy;
if (condition == "shower") return ICON_w_rainy;
if (condition == "showers") return ICON_w_rainy;
if (condition == "snow") return ICON_w_snowy;
if (condition == "snowy") return ICON_w_snowy;
if (condition == "snowy-rainy") return ICON_w_snowy_rainy;
if (condition == "storm") return ICON_w_lightning_rainy;
if (condition == "storms") return ICON_w_lightning_rainy;
if (condition == "sunny") return ICON_w_sunny;
if (condition == "wind") return ICON_w_windy;
if (condition == "windy") return ICON_w_windy;
if (condition == "windy-variant") return ICON_w_windy_variant;
if (condition == "exceptional") return ICON_w_exceptional;
return "";
}

280
esphome/include/ld2410_uart.h Executable file
View File

@@ -0,0 +1,280 @@
#include "esphome.h"
#define CHECK_BIT(var, pos) (((var) >> (pos)) & 1)
class LD2410 : public PollingComponent, public UARTDevice
{
public:
LD2410(UARTComponent *parent) : UARTDevice(parent) {}
BinarySensor *hasTarget = new BinarySensor();
BinarySensor *hasMovingTarget = new BinarySensor();
BinarySensor *hasStillTarget = new BinarySensor();
BinarySensor *lastCommandSuccess = new BinarySensor();
Sensor *movingTargetDistance = new Sensor();
Sensor *movingTargetEnergy = new Sensor();
Sensor *stillTargetDistance = new Sensor();
Sensor *stillTargetEnergy = new Sensor();
Sensor *detectDistance = new Sensor();
Number *maxMovingDistanceRange;
Number *maxStillDistanceRange;
int movingSensitivities[9] = {0};
int stillSensitivities[9] = {0};
Number *noneDuration;
long lastPeriodicMillis = millis();
void setNumbers(Number *maxMovingDistanceRange_, Number *maxStillDistanceRange_, Number *noneDuration_){
maxMovingDistanceRange = maxMovingDistanceRange_;
maxStillDistanceRange = maxStillDistanceRange_;
noneDuration = noneDuration_;
}
void sendCommand(char *commandStr, char *commandValue, int commandValueLen)
{
lastCommandSuccess->publish_state(false);
// frame start bytes
write_byte(0xFD);
write_byte(0xFC);
write_byte(0xFB);
write_byte(0xFA);
// length bytes
int len = 2;
if (commandValue != nullptr)
len += commandValueLen;
write_byte(lowByte(len));
write_byte(highByte(len));
// command string bytes
write_byte(commandStr[0]);
write_byte(commandStr[1]);
// command value bytes
if (commandValue != nullptr)
{
for (int i = 0; i < commandValueLen; i++)
{
write_byte(commandValue[i]);
}
}
// frame end bytes
write_byte(0x04);
write_byte(0x03);
write_byte(0x02);
write_byte(0x01);
delay(50);
}
int twoByteToInt(char firstByte, char secondByte)
{
return (int16_t)(secondByte << 8) + firstByte;
}
void handlePeriodicData(char *buffer, int len)
{
if (len < 12)
return; // 4 frame start bytes + 2 length bytes + 1 data end byte + 1 crc byte + 4 frame end bytes
if (buffer[0] != 0xF4 || buffer[1] != 0xF3 || buffer[2] != 0xF2 || buffer[3] != 0xF1)
return; // check 4 frame start bytes
if (buffer[7] != 0xAA || buffer[len - 6] != 0x55 || buffer[len - 5] != 0x00)
return; // data head=0xAA, data end=0x55, crc=0x00
/*
Data Type: 6th byte
0x01: Engineering mode
0x02: Normal mode
*/
char dataType = buffer[5];
/*
Target states: 9th byte
0x00 = No target
0x01 = Moving targets
0x02 = Still targets
0x03 = Moving+Still targets
*/
char stateByte = buffer[8];
hasTarget->publish_state(stateByte != 0x00);
/*
Reduce data update rate to prevent home assistant database size glow fast
*/
long currentMillis = millis();
if (currentMillis - lastPeriodicMillis < 1000)
return;
lastPeriodicMillis = currentMillis;
hasMovingTarget->publish_state(CHECK_BIT(stateByte, 0));
hasStillTarget->publish_state(CHECK_BIT(stateByte, 1));
/*
Moving target distance: 10~11th bytes
Moving target energy: 12th byte
Still target distance: 13~14th bytes
Still target energy: 15th byte
Detect distance: 16~17th bytes
*/
int newMovingTargetDistance = twoByteToInt(buffer[9], buffer[10]);
if (movingTargetDistance->get_state() != newMovingTargetDistance)
movingTargetDistance->publish_state(newMovingTargetDistance);
int newMovingTargetEnergy = buffer[11];
if (movingTargetEnergy->get_state() != newMovingTargetEnergy)
movingTargetEnergy->publish_state(newMovingTargetEnergy);
int newStillTargetDistance = twoByteToInt(buffer[12], buffer[13]);
if (stillTargetDistance->get_state() != newStillTargetDistance)
stillTargetDistance->publish_state(newStillTargetDistance);
int newStillTargetEnergy = buffer[14];
if (stillTargetEnergy->get_state() != newStillTargetEnergy)
stillTargetEnergy->publish_state(buffer[14]);
int newDetectDistance = twoByteToInt(buffer[15], buffer[16]);
if (detectDistance->get_state() != newDetectDistance)
detectDistance->publish_state(newDetectDistance);
if (dataType == 0x01)
{ // engineering mode
// todo: support engineering mode data
}
}
void handleACKData(char *buffer, int len)
{
if (len < 10)
return;
if (buffer[0] != 0xFD || buffer[1] != 0xFC || buffer[2] != 0xFB || buffer[3] != 0xFA)
return; // check 4 frame start bytes
if (buffer[7] != 0x01)
return;
if (twoByteToInt(buffer[8], buffer[9]) != 0x00)
{
lastCommandSuccess->publish_state(false);
return;
}
lastCommandSuccess->publish_state(true);
switch (buffer[6])
{
case 0x61: // Query parameters response
{
if (buffer[10] != 0xAA)
return; // value head=0xAA
/*
Moving distance range: 13th byte
Still distance range: 14th byte
*/
maxMovingDistanceRange->publish_state(buffer[12]);
maxStillDistanceRange->publish_state(buffer[13]);
/*
Moving Sensitivities: 15~23th bytes
Still Sensitivities: 24~32th bytes
*/
for (int i = 0; i < 9; i++)
{
movingSensitivities[i] = buffer[14 + i];
}
for (int i = 0; i < 9; i++)
{
stillSensitivities[i] = buffer[23 + i];
}
/*
None Duration: 33~34th bytes
*/
noneDuration->publish_state(twoByteToInt(buffer[32], buffer[33]));
}
break;
default:
break;
}
}
void readline(int readch, char *buffer, int len)
{
static int pos = 0;
if (readch >= 0)
{
if (pos < len - 1)
{
buffer[pos++] = readch;
buffer[pos] = 0;
}
else
{
pos = 0;
}
if (pos >= 4)
{
if (buffer[pos - 4] == 0xF8 && buffer[pos - 3] == 0xF7 && buffer[pos - 2] == 0xF6 && buffer[pos - 1] == 0xF5)
{
handlePeriodicData(buffer, pos);
pos = 0; // Reset position index ready for next time
}
else if (buffer[pos - 4] == 0x04 && buffer[pos - 3] == 0x03 && buffer[pos - 2] == 0x02 && buffer[pos - 1] == 0x01)
{
handleACKData(buffer, pos);
pos = 0; // Reset position index ready for next time
}
}
}
return;
}
void setConfigMode(bool enable)
{
char cmd[2] = {enable ? 0xFF : 0xFE, 0x00};
char value[2] = {0x01, 0x00};
sendCommand(cmd, enable ? value : nullptr, 2);
}
void queryParameters()
{
char cmd_query[2] = {0x61, 0x00};
sendCommand(cmd_query, nullptr, 0);
}
void setup() override
{
set_update_interval(15000);
}
void loop() override
{
const int max_line_length = 80;
static char buffer[max_line_length];
while (available())
{
readline(read(), buffer, max_line_length);
}
}
void setEngineeringMode(bool enable)
{
char cmd[2] = {enable ? 0x62 : 0x63, 0x00};
sendCommand(cmd, nullptr, 0);
}
void setMaxDistancesAndNoneDuration(int maxMovingDistanceRange, int maxStillDistanceRange, int noneDuration)
{
char cmd[2] = {0x60, 0x00};
char value[18] = {0x00, 0x00, lowByte(maxMovingDistanceRange), highByte(maxMovingDistanceRange), 0x00, 0x00, 0x01, 0x00, lowByte(maxStillDistanceRange), highByte(maxStillDistanceRange), 0x00, 0x00, 0x02, 0x00, lowByte(noneDuration), highByte(noneDuration), 0x00, 0x00};
sendCommand(cmd, value, 18);
queryParameters();
}
void factoryReset()
{
char cmd[2] = {0xA2, 0x00};
sendCommand(cmd, nullptr, 0);
}
void reboot()
{
char cmd[2] = {0xA3, 0x00};
sendCommand(cmd, nullptr, 0);
// not need to exit config mode because the ld2410 will reboot automatically
}
void setBaudrate(int index)
{
char cmd[2] = {0xA1, 0x00};
char value[2] = {index, 0x00};
sendCommand(cmd, value, 2);
}
void update()
{
}
};