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;
};