mmWave Wars: one sensor (module) to rule them all

Sensor Code

HLK-LD1115J

YAML

esphome:
  name: tinypico-mmwave-ld1115h
  platform: ESP32
  board: esp32dev
  includes:
    - uart_read_line_sensor_ld1115h.h # https://esphome.io/cookbook/uart_text_sensor.html

# Enable logging
logger:
  level: INFO

# Enable Home Assistant API
api:
  reboot_timeout: 6h
  services:
      # Service to send a command directly to the display. Useful for testing
    - service: send_command
      variables:
        cmd: string
      then:
        - uart.write: !lambda
            std::string command = to_string(cmd) +"\n";
            return std::vector<uint8_t>(command.begin(), command.end());

ota:
  password: !secret ota

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: !secret ap_ssid
    password: !secret ap_password

substitutions:
  device_name: tinypico_mmwave_ld1115h

web_server:
  port: 80
  version: 2
  include_internal: true
  ota: false

captive_portal:

uart:
  id: uart_bus
  tx_pin: GPIO5
  rx_pin: GPIO18
  baud_rate: 115200

binary_sensor:
  - platform: gpio
    name: mmwave_presence_ld1115h
    id: mmwave_presence_ld1115h
    device_class: motion
    pin:
      number: GPIO33
      mode: INPUT_PULLDOWN
    on_state:
      then:
        - if:
            condition:
              - binary_sensor.is_off: mmwave_presence_ld1115h
            then:
              - lambda: |-
                  id(mov_SNR).publish_state(0.0);
                  id(occ_SNR).publish_state(0.0);

text_sensor:
  - platform: template
    name: uptime_human_readable
    id: uptime_human_readable
    icon: mdi:clock-start
    update_interval: 60s

sensor:
  - platform: uptime
    name: uptime_sensor
    id: uptime_sensor
    update_interval: 60s
    internal: true
    on_raw_value:
      then:
        - text_sensor.template.publish:
            id: uptime_human_readable
            state: !lambda |-
                      int seconds = round(id(uptime_sensor).raw_state);
                      int days = seconds / (24 * 3600);
                      seconds = seconds % (24 * 3600);
                      int hours = seconds / 3600;
                      seconds = seconds % 3600;
                      int minutes = seconds /  60;
                      seconds = seconds % 60;
                      return (
                        (days ? to_string(days)+":" : "00:") +
                        (hours ? to_string(hours)+":" : "00:") +
                        (minutes ? to_string(minutes)+":" : "00:") +
                        (to_string(seconds))
                      ).c_str();

  - platform: custom
    lambda: |-
      auto s = new hilink(id(uart_bus));
      App.register_component(s);
      //return {};
      return {s->mov_SNR, s->occ_SNR};
    sensors:
      - name: mov_SNR
        id: mov_SNR
        internal: true
      
      - name: occ_SNR
        id: occ_SNR
        internal: true
      
switch:
  - platform: safe_mode
    name: use_safe_mode
    
  - platform: template
    name: show_SNR
    id: show_SNR
    internal: true
    optimistic: true
    turn_off_action:
      - lambda: |-
          id(mov_SNR).publish_state(0.0);
          id(occ_SNR).publish_state(0.0);

number:
  - platform: template
    name: dtime
    id: dtime # do not change
    entity_category: config
    min_value: 1
    max_value: 600
    lambda: |-
      hilink(id(uart_bus)).getmmwConf("get_all");
      return {};
    step: 1
    unit_of_measurement: s
    mode: box
    set_action:
      - uart.write: !lambda
          std::string setdtime = "dtime=" + to_string((int)x);
          return std::vector<unsigned char>(setdtime.begin(), setdtime.end());
      - delay: 250ms
      - uart.write: "save\n"

  - platform: template
    name: mov_SNR_target
    id: mov_SNR_target # do not change
    entity_category: config
    min_value: 0
    max_value: 65536
    mode: box
    optimistic: true
    step: 1
    set_action:
      - uart.write: !lambda
          std::string setth1 = "th1=" + to_string((int)x);
          return std::vector<unsigned char>(setth1.begin(), setth1.end());
      - delay: 250ms
      - uart.write: "save\n"

  - platform: template
    name: th3_SNR_target_long_dist
    id: th3_SNR_target # do not change
    entity_category: config
    min_value: 0
    max_value: 65536
    mode: box
    optimistic: true
    step: 1
    set_action:
      - uart.write: !lambda
          std::string setth3 = "th3=" + to_string((int)x);
          return std::vector<unsigned char>(setth3.begin(), setth3.end());
      - delay: 250ms
      - uart.write: "save\n"

  - platform: template
    name: occ_SNR_target
    id: occ_SNR_target # do not change
    entity_category: config
    min_value: 0
    max_value: 65536
    mode: box
    optimistic: true
    step: 1
    set_action:
      - uart.write: !lambda
          std::string setth2 = "th2=" + to_string((int)x);
          return std::vector<unsigned char>(setth2.begin(), setth2.end());
      - delay: 250ms
      - uart.write: "save\n"
      
  - platform: template
    name: ind_min
    id: ind_min # do not change
    entity_category: config
    min_value: 0
    max_value: 32
    mode: box 
    optimistic: true
    step: 1
    set_action:
      - uart.write: !lambda
          std::string setind_min = "ind_min=" + to_string((int)x);
          return std::vector<unsigned char>(setind_min.begin(), setind_min.end());
      - delay: 250ms
      - uart.write: "save\n"

  - platform: template
    name: ind_max
    id: ind_max # do not change
    entity_category: config
    min_value: 0
    max_value: 32
    mode: box 
    optimistic: true
    step: 1
    set_action:
      - uart.write: !lambda
          std::string setind_max = "ind_max=" + to_string((int)x);
          return std::vector<unsigned char>(setind_max.begin(), setind_max.end());
      - delay: 250ms
      - uart.write: "save\n"

  - platform: template
    name: mov_sn
    id: mov_sn # do not change
    entity_category: config
    min_value: 0
    max_value: 4
    mode: box 
    optimistic: true
    step: 1
    set_action:
      - uart.write: !lambda
          std::string setmov_sn = "mov_sn=" + to_string((int)x);
          return std::vector<unsigned char>(setmov_sn.begin(), setmov_sn.end());
      - delay: 250ms
      - uart.write: "save\n"

  - platform: template
    name: occ_sn
    id: occ_sn # do not change
    entity_category: config
    min_value: 0
    max_value: 16
    mode: box 
    optimistic: true
    step: 1
    set_action:
      - uart.write: !lambda
          std::string setocc_sn = "occ_sn=" + to_string((int)x);
          return std::vector<unsigned char>(setocc_sn.begin(), setocc_sn.end());
      - delay: 250ms
      - uart.write: "save\n"

button:
  - platform: restart
    name: Restart_ESP_$device_name
    entity_category: diagnostic

uart_read_line_sensor_ld1115h.h

#include "esphome.h"
#include <string>

class hilink : public Component, public UARTDevice {
 public:
  hilink(UARTComponent *parent) : UARTDevice(parent) {}
  Sensor *mov_SNR = new Sensor();
  Sensor *occ_SNR = new Sensor();
  void setup() override {
    //
  }

  void getmmwConf(std::string mmwparam) {
    mmwparam = mmwparam + "\n";
    write_array(std::vector<unsigned char>(mmwparam.begin(), mmwparam.end()));
  }

  int readline(int readch, char *buffer, int len)
  {
    static int pos = 0;
    int rpos;

    if (readch > 0) {
      switch (readch) {
        case '\n': // Ignore new-lines
          break;
        case '\r': // Return on new-lines
          rpos = pos;
          pos = 0;  // Reset position index ready for next time
          return rpos;
        default:
          if (pos < len-1) {
            buffer[pos++] = readch;
            buffer[pos] = 0;
          }
      }
    }
    // No end of line has been found, so return -1.
    return -1;
  }

  void loop() override {
    const int max_line_length = 80;
    static char buffer[max_line_length];

    while (available()) {
      if(readline(read(), buffer, max_line_length) > 0) {
        std::string line = buffer;

        //ESP_LOGD("custom", "Line is: %s", line.c_str());
        if (line.substr(0,3) == "th1") {
          ESP_LOGD("custom", "Found th1: %s", line.c_str());
          id(mov_SNR_target).publish_state(parse_number<int>(line.substr(7)).value());
        }
        else if (line.substr(0,3) == "th2") {
          ESP_LOGD("custom", "Found th2: %s", line.c_str());
          id(occ_SNR_target).publish_state(parse_number<int>(line.substr(7)).value());
        }
        else if (line.substr(0,3) == "th3") {
          ESP_LOGD("custom", "Found th3: %s", line.c_str());
          id(th3_SNR_target).publish_state(parse_number<int>(line.substr(7)).value());
        }
        else if (line.substr(0,7) == "ind_min") {
          ESP_LOGD("custom", "Found ind_min: %s", line.c_str());
          id(ind_min).publish_state(parse_number<int>(line.substr(11)).value());
        }
        else if (line.substr(0,7) == "ind_max") {
          ESP_LOGD("custom", "Found ind_max: %s", line.c_str());
          id(ind_max).publish_state(parse_number<int>(line.substr(11)).value());
        }
        else if (line.substr(0,6) == "mov_sn") {
          ESP_LOGD("custom", "Found mov_sn: %s", line.c_str());
          id(mov_sn).publish_state(parse_number<int>(line.substr(10)).value());
        }
        else if (line.substr(0,6) == "occ_sn") {
          ESP_LOGD("custom", "Found occ_sn: %s", line.c_str());
          id(occ_sn).publish_state(parse_number<int>(line.substr(10)).value());
        }
        else if (line.substr(0,5) == "dtime") {
          ESP_LOGD("custom", "Found dtime: '%s'", line.c_str());
          std::string dtime_str = line.substr(9,(line.length() - 12));
          int dtime_ms = parse_number<int>(dtime_str).value();
          ESP_LOGD("custom", "Found dtime_ms: %i", dtime_ms);
          id(dtime).publish_state(dtime_ms/1000);
        }
        if (line.substr(0,4) == "mov," && id(show_SNR).state) {
          // ESP_LOGD("custom", "Found occ_sn: %s", line.c_str());
          mov_SNR->publish_state(parse_number<int>(line.substr(7)).value());
        }
        else if (line.substr(0,4) == "occ," && id(show_SNR).state) {
          // ESP_LOGD("custom", "Found occ_sn: %s", line.c_str());
          occ_SNR->publish_state(parse_number<int>(line.substr(7)).value());
        }
      }
    }
  }
};

HLK-LD2410

YAML

esphome:
  name: "tinypico-dev-ld2410"
  platform: ESP32
  board: esp32dev
  includes:
    - uart_read_line_sensor_ld2410v3.h
  on_boot:
    priority: -100
    then:
      - script.execute: get_config

# Enable logging
logger:
  baud_rate: 0
  logs:
    sensor: INFO # DEBUG level with uart_target_output = overload!
    binary_sensor: INFO
    text_sensor: INFO

# Enable Home Assistant API
api:

ota:
  password: !secret ota

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: !secret ap_ssid
    password: !secret ap_password

substitutions:
  device_name: dev-sensor

web_server:
  port: 80
  version: 2
  include_internal: true
  ota: false

captive_portal:

uart:
  id: uart_bus
  tx_pin:
    number: GPIO18
  rx_pin: 
    number: GPIO23
  baud_rate: 256000
  parity: NONE
  stop_bits: 1

switch:
  - platform: safe_mode
    name: use_safe_mode
    
  - platform: template
    name: configmode
    id: configmode
    optimistic: true
    # assumed_state: false
    turn_on_action:
      # - switch.turn_off: engineering_mode
      - lambda: 'static_cast<LD2410 *>(ld2410)->setConfigMode(true);'
      - delay: 100ms
      - script.execute: clear_targets
    turn_off_action:
      - lambda: 'static_cast<LD2410 *>(ld2410)->setConfigMode(false);'

  - platform: template
    name: show_target_stats
    id: show_stats
    optimistic: true
    internal: true
    turn_off_action:
      - script.execute: clear_targets

text_sensor:
  - platform: template
    name: uptime_human_readable
    id: uptime_human_readable
    icon: mdi:clock-start
    update_interval: 60s

sensor:
  - platform: uptime
    name: uptime_sensor
    id: uptime_sensor
    update_interval: 60s
    internal: true
    on_raw_value:
      then:
        - text_sensor.template.publish:
            id: uptime_human_readable
            state: !lambda |-
                      int seconds = round(id(uptime_sensor).raw_state);
                      int days = seconds / (24 * 3600);
                      seconds = seconds % (24 * 3600);
                      int hours = seconds / 3600;
                      seconds = seconds % 3600;
                      int minutes = seconds /  60;
                      seconds = seconds % 60;
                      return (
                        (days ? to_string(days)+":" : "00:") +
                        (hours ? to_string(hours)+":" : "00:") +
                        (minutes ? to_string(minutes)+":" : "00:") +
                        (to_string(seconds))
                      ).c_str();

  - platform: custom # currently crashes ESP32
    lambda: |-
      auto uart_component = static_cast<LD2410 *>(ld2410);
      //return {uart_component->movingTargetDistance,uart_component->movingTargetEnergy,uart_component->stillTargetDistance,uart_component->stillTargetEnergy,uart_component->detectDistance};
      return {};
    sensors:
    
  - platform: template
    name: movingTargetDistance
    id: movingTargetDistance
    unit_of_measurement: "cm"
    accuracy_decimals: 0
    internal: true
    
  - platform: template
    name: movingTargetEnergy
    id: movingTargetEnergy
    unit_of_measurement: "%"
    accuracy_decimals: 0
    internal: true
    
  - platform: template
    name: stillTargetDistance
    id: stillTargetDistance
    unit_of_measurement: "cm"
    accuracy_decimals: 0
    internal: true
    
  - platform: template
    name: stillTargetEnergy
    id: stillTargetEnergy
    unit_of_measurement: "%"
    accuracy_decimals: 0
    internal: true
    
  - platform: template
    name: detectDistance
    id: detectDistance
    unit_of_measurement: "cm"
    accuracy_decimals: 0
    internal: true
    
custom_component:
  - lambda: |-
      return {new LD2410(id(uart_bus))};
    components:
      - id: ld2410
      
binary_sensor:
  - platform: gpio
    name: mmwave_presence_ld2410
    id: mmwave_presence_ld2410
    pin: GPIO5
    device_class: motion
    on_state:
      then:
        - if: 
            condition: 
              - binary_sensor.is_off: mmwave_presence_ld2410
            then: 
              - delay: 150ms
              - script.execute: clear_targets

number:  
  - platform: template
    name: configMaxDistance
    id: maxconfigDistance
    unit_of_measurement: "M"
    min_value: 0.75
    max_value: 6
    step: 0.75
    update_interval: never
    optimistic: true
    set_action:
      - switch.turn_on: configmode
      - delay: 50ms
      - lambda: |-
          auto uart_component = static_cast<LD2410 *>(ld2410);
          uart_component->setMaxDistancesAndNoneDuration(x/0.75,x/0.75,id(noneDuration).state);
      - delay: 50ms
      - lambda: 'static_cast<LD2410 *>(ld2410)->queryParameters();'
      - delay: 50ms
      - switch.turn_off: configmode

  - platform: template
    name: "sensitivity_threshold_(%)"
    id: allSensitivity
    min_value: 10
    max_value: 100
    step: 5
    mode: box
    update_interval: never
    optimistic: true
    set_action:
      - switch.turn_on: configmode
      - delay: 50ms
      - lambda: |-
          auto uart_component = static_cast<LD2410 *>(ld2410);
          uart_component->setAllSensitivity(x);
      - delay: 50ms
      - lambda: 'static_cast<LD2410 *>(ld2410)->queryParameters();'
      - delay: 50ms
      - switch.turn_off: configmode
      
  - platform: template
    name: "motion_hold_(sec)"
    id: noneDuration
    min_value: 0
    # max_value: 32767
    max_value: 900
    step: 1
    mode: box
    update_interval: never
    optimistic: true
    set_action:
      - switch.turn_on: configmode
      - delay: 50ms
      - lambda: |-
          auto uart_component = static_cast<LD2410 *>(ld2410);
          uart_component->setMaxDistancesAndNoneDuration(id(maxconfigDistance).state, id(maxconfigDistance).state, x);
      - delay: 50ms
      - lambda: 'static_cast<LD2410 *>(ld2410)->queryParameters();'
      - delay: 50ms
      - switch.turn_off: configmode
button:
  - platform: restart
    name: "reset/restart_ESP/MCU"
    entity_category: diagnostic
    on_press:
      - switch.turn_on: configmode
      - delay: 50ms
      - lambda: 'static_cast<LD2410 *>(ld2410)->factoryReset();'
      - delay: 150ms
      - lambda: 'static_cast<LD2410 *>(ld2410)->reboot();'
      - delay: 150ms

script:
  - id: get_config
    then:
      - switch.turn_on: configmode
      - delay: 500ms
      - lambda: 'static_cast<LD2410 *>(ld2410)->queryParameters();'
      - delay: 500ms
      - switch.turn_off: configmode
      
  - id: clear_targets
    then:
      - lambda: |-
          //id(hasTarget).publish_state(0);
          //id(hasMovingTarget).publish_state(0);
          //id(hasStillTarget).publish_state(0);
          id(movingTargetDistance).publish_state(0);
          id(movingTargetEnergy).publish_state(0);
          id(stillTargetDistance).publish_state(0);
          id(stillTargetEnergy).publish_state(0);
          id(detectDistance).publish_state(0);
          

uart_read_line_sensor_ld2410v3.h

#include "esphome.h"

class LD2410 : public Component, public UARTDevice
{
public:
  LD2410(UARTComponent *parent) : UARTDevice(parent) {}
  std::vector<uint8_t> bytes;
  const std::vector<uint8_t> config_header = {0xFD, 0xFC, 0xFB, 0xFA, 0x1C, 0x00};
  const std::vector<uint8_t> target_header = {0xF4, 0xF3, 0xF2, 0xF1, 0x0D, 0x00};
  const std::vector<uint8_t> ld2410_end_conf = {0x04, 0x03, 0x02, 0x01};

  void ESP_LOGD_HEX(std::vector<uint8_t> bytes, uint8_t separator) {
    std::string res;
    size_t len = bytes.size();
    char buf[5];
    for (size_t i = 0; i < len; i++) {
      if (i > 0) {
        res += separator;
      }
      sprintf(buf, "%02X", bytes[i]);
      res += buf;
    }
    ESP_LOGD("custom", "%s", res.c_str());
  }

  void sendCommand(char *commandStr, char *commandValue, int commandValueLen)
  {
    uint16_t len = 2;
    if (commandValue != nullptr) {
      len += commandValueLen;
    }
    std::vector<uint8_t> ld2410_conf = {0xFD, 0xFC, 0xFB, 0xFA, lowByte(len), highByte(len), commandStr[0], commandStr[1]};
    if (commandValue != nullptr)
    {
      for (int i = 0; i < commandValueLen; i++)
      {
        ld2410_conf.push_back(commandValue[i]);
      }
    }
    for (int i = 0; i < ld2410_end_conf.size(); i++) 
    {
      ld2410_conf.push_back(ld2410_end_conf[i]);
    }
    // ESP_LOGD_HEX(ld2410_conf,':');
    write_array(std::vector<uint8_t>(ld2410_conf.begin(), ld2410_conf.end()));
  }

  int twoByteToInt(char firstByte, char secondByte)
  {
    return (int16_t)(secondByte << 8) + firstByte;
  }

  void handleTargetData(std::vector<uint8_t> buffer)
  {
    TARGETUnion targetUnion;
    std::copy(buffer.begin(), buffer.end(), targetUnion.bytes);
    if (id(show_stats).state == 1 && targetUnion.target.type == 0x02 && targetUnion.target.state != 0x00)
    {
      int movdist = twoByteToInt(targetUnion.target.movdist, targetUnion.target.movdist2);
      if (id(movingTargetDistance).state != movdist)
      {
        id(movingTargetDistance).publish_state(movdist);
      }
      if (id(movingTargetEnergy).state != targetUnion.target.movval)
      {
        id(movingTargetEnergy).publish_state(targetUnion.target.movval);
      }
      int stadist = twoByteToInt(targetUnion.target.stadist, targetUnion.target.stadist2);
      if (id(stillTargetDistance).state != stadist)
      {
        id(stillTargetDistance).publish_state(stadist);
      }
      if (id(stillTargetEnergy).state != targetUnion.target.staval)
      {
        id(stillTargetEnergy).publish_state(targetUnion.target.staval);
      }
      int decdist = twoByteToInt(targetUnion.target.decdist, targetUnion.target.decdist2);
      if (id(detectDistance).state != decdist)
      {
        id(detectDistance).publish_state(decdist);
      }
    }
    else 
    {
      return; 
    }
    // Engineering data - datasheet is horrible
    // if (targetUnion.target.type == 0x01)
    // }
  }

  void handleConfData(std::vector<uint8_t> buffer)
  {
    CONFUnion confUnion;
    std::copy(buffer.begin(), buffer.end(), confUnion.bytes);
    if (confUnion.conf.cmd == 0x61 && confUnion.conf.cmd_val == 0x01 && confUnion.conf.ack_stat == 0x00 && confUnion.conf.head == 0xAA)
    {
      id(maxconfigDistance).publish_state(float(confUnion.conf.max_sta_dist * 0.75));
      id(allSensitivity).publish_state(confUnion.conf.mov0sen);
      id(noneDuration).publish_state(confUnion.conf.none);
    }
  }

  void setConfigMode(bool confenable)
  {
    char cmd[2] = {confenable ? 0xFF : 0xFE,0x00};
    char value[2] = {0x01, 0x00};
    sendCommand(cmd, confenable ? value : nullptr, 2);
  }

  void queryParameters()
  {
    char cmd_query[2] = {0x61, 0x00};
    sendCommand(cmd_query, nullptr, 0);
  }

  void setup() override
  {  }

  void loop() override
  {
    while (available())
    {
      bytes.push_back(read());
      if (bytes.size() < 6)
      {
        continue;
      }
      if (doesHeaderMatch(bytes, config_header))
      {
        if (bytes.size() < sizeof(CONF))
        {
          continue;
        }
        handleConfData(bytes);
        bytes.clear();
      }
      else if (doesHeaderMatch(bytes, target_header)) {
        if (bytes.size() < sizeof(TARGET))
        {
          continue;
        }
        handleTargetData(bytes);
        bytes.clear();
      }
      else
      {
        bytes.erase(bytes.begin());
        continue;
      }
    }
  }

  void setEngineeringMode(bool engenable)
  {
    char cmd[2] = {engenable ? 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, sizeof(value));
  }

  void setAllSensitivity(int senval)
  {
    //  64 00  00 00  FF FF 00 00 01 00  28 00 00 00 02 00 28 00 00 00 04 03 02 01
    // {cmd  }{dword}{   dgate  }{mword} {   mval   }{sword}{   sval   }{    MFR  }
    char cmd[2] = {0x64, 0x00};
    char value[18] = {0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x01, 0x00, lowByte(senval), highByte(senval), 0x00, 0x00, 0x02, 0x00, lowByte(senval), highByte(senval), 0x00, 0x00};
    sendCommand(cmd, value, sizeof(value));
  }

  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, sizeof(value));
  }

  bool doesHeaderMatch(std::vector<uint8_t> bytes, std::vector<uint8_t> header)
  {
    bool is_equal = std::equal(header.begin(), header.end(), bytes.begin());
    return (is_equal == true ? true : false);
  }

/*
TARGET EXAMPLE DATA
{F4:F3:F2:F1}:{0D:00}:{02}:{AA}: 02  : 4B:00:  4F  : 00:00 : 64   :  29:00 :{55}: {00} :{F8:F7:F6:F5}
{  header   }  {len}  {typ}{hd}{state}{mdist}{mval}{stadis}{staval}{decdis} {tl} {chck} {    MFR    }
*/
  typedef struct
  {
    uint32_t MFR;
    uint16_t len;
    uint8_t type;                 // target or engineering
    uint8_t head;                // fixed head
    uint8_t state;              // state
    uint8_t movdist;          // movement distance
    uint8_t movdist2;          // movement distance
    uint8_t movval;           // movement energy value
    uint8_t stadist;        // stationary distance
    uint8_t stadist2;        // stationary distance
    uint8_t staval;         // stationary energy value
    uint8_t decdist;      // detection distance
    uint8_t decdist2;      // detection distance
    uint8_t tail;         // tail
    uint8_t chk;         // unused
    uint32_t MFR_end ;  // end
  } TARGET;

  typedef union
  {
    TARGET target;
    uint8_t bytes[sizeof(TARGET)];
  } TARGETUnion;

/*
CONF EXAMPLE DATA
FD:FC:FB:FA MFR[0-3]
1C:00 len[4-5]
61:01 CMD[6-7]
00:00 ACKstat[8-9]
AA Head [10]
08 maxDist [11]
06 maxMovDist[12]
06 maxStaDist[13]
1E:1E:1E:1E:1E:1E:1E:1E:1E:1E:1E:1E:1E:1E:1E:1E:1E:1E (9mov & 9sta sensitivities)
5A:00 none[32-33]
04:03:02:01[34-37]
*/
  typedef struct
  {
    uint32_t MFR;
    uint16_t len;
    uint8_t cmd;
    uint8_t cmd_val;
    uint8_t ack_stat;
    uint8_t ack_stathigh;
    uint8_t head;
    uint8_t max_dist;
    uint8_t max_mov_dist;
    uint8_t max_sta_dist;
    uint8_t mov0sen;
    uint8_t mov1sen;
    uint8_t mov2sen;
    uint8_t mov3sen;
    uint8_t mov4sen;
    uint8_t mov5sen;
    uint8_t mov6sen;
    uint8_t mov7sen;
    uint8_t mov8sen;
    uint8_t sta0sen;
    uint8_t sta1sen;
    uint8_t sta2sen;
    uint8_t sta3sen;
    uint8_t sta4sen;
    uint8_t sta5sen;
    uint8_t sta6sen;
    uint8_t sta7sen;
    uint8_t sta8sen;
    uint16_t none;
    uint32_t MFR_end;
  } CONF;

  typedef union
  {
    CONF conf;
    uint8_t bytes[sizeof(CONF)];
  } CONFUnion;
};

18 Likes