presence_sensor.h
#include "esphome.h"
#include "Radar.h"
using namespace esphome;
static const char* TAG = "PresenceSensor";
class PresenceSensor : public Component, public UARTDevice {
public:
Radar* radar = new Radar(&Serial);
Sensor* presence_sensor = new Sensor();
PresenceSensor(UARTComponent *parent) : UARTDevice(parent) {}
void setup() {
// delay(1000);
// radar->send_write_SystemParameter_ThresholdGear_1();
// delay(1000);
// radar->send_write_SystemParameter_SceneSetting_LivingTopMounted();
}
std::vector<int> bytes;
void loop() override {
while(available() > 0) {
int incomingByte = read();
//hit next message
if(incomingByte == 85) {
//could check if checksum matches if you feel like it (last 2 bytes)
processPacket();
bytes.clear();
bytes.push_back(85);
} else {
bytes.push_back(incomingByte);
}
}
}
std::vector<float> lastLevels = std::vector<float>(4, 0.0f);
void processPacket() {
//the array could look like 85 7 0 4 3 0 1 1 1 137 92
//cut off the first 3 header bytes and the last 2 checksum bytes
bytes.erase(bytes.begin(), bytes.begin() + 3);
bytes.resize(bytes.size() - 2); //remove last 2 checksum bytes
std::string str = "";
//NOW YOU HAVE THE DATA PACKET BYTES. DO WHATEVER YOU WANT
//this is the one you really only care about
//to get the movement amount
if((bytes[0] == 0x03 || bytes[0] == 0x04) && (bytes[1] == 0x03) && (bytes[2] == 0x06))
{
str = "Proactive Radar Signs: " + esphome::to_string(getFloat(bytes[3], bytes[4], bytes[5], bytes[6]));
ESP_LOGI(TAG, str.c_str());
float newSensorValue = getFloat(bytes[3], bytes[4], bytes[5], bytes[6]);
lastLevels.insert(lastLevels.begin(), newSensorValue);
//sometimes when movement starts, the sensor jumps down to zero for one or two readings
//this fixes that behaviour
if(newSensorValue < 0.1f && (lastLevels[0] > 5 || lastLevels[1] > 5 || lastLevels[2] > 5 || lastLevels[3] > 5)) {
//don't publish
} else {
presence_sensor->publish_state(newSensorValue);
}
return;
}
if((bytes[0] == 0x04) && (bytes[1] == 0x03) && (bytes[2] == 0x07))
{
str = "Proactive Radar Approaching: " + hexStr(bytes[5]);
ESP_LOGI(TAG, str.c_str());
return;
}
if((bytes[0] == 0x03 || bytes[0] == 0x04) && (bytes[1] == 0x03) && (bytes[2] == 0x05)) //env
{
str += "Proactive Radar Env:";
if(bytes[3] == 0x00 && bytes[4] == 0xFF && bytes[5] == 0xFF)
{
str += " Unoccupied";
}
else if(bytes[3] == 0x01 && bytes[4] == 0x00 && bytes[5] == 0xFF)
{
str += " Stationary";
}
else if(bytes[3] == 0x01 && bytes[4] == 0x01 && bytes[5] == 0x01)
{
str += " Exercise";
}
else
{
str += " Other?";
}
ESP_LOGI(TAG, str.c_str());
return;
}
//toss away most of the data (headers, length byte, checksum)
for (int i = 0; i < bytes.size(); i++) {
int code = bytes[i];
if(i == 0) {
if(code == 0x03)
{
str += "Passive ";
}
else if (code == 0x04)
{
str += "Proactive ";
}
} else if(i == 1) {
if(code == 0x03)
{
str += "Radar ";
}
} else if(i == 2) {
if(code == 0x05)
{
str += "Env ";
}
else if (code == 0x06)
{
str += "Signs ";
}
} else {
str += hexStr(bytes[i]) + " ";
}
}
ESP_LOGI(TAG, str.c_str());
}
char hexmap[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
std::string hexStr(int data)
{
std::string s = " ";
s[0] = '0';
s[1] = 'x';
s[2] = hexmap[(data & 0xF0) >> 4];
s[3] = hexmap[data & 0x0F];
//s = "0x" + s;
return s;
}
typedef union
{
unsigned char Byte[4];
float Float;
}FloatByte;
float getFloat(int b1, int b2, int b3, int b4)
{
FloatByte fb;
fb.Byte[0] = b1;
fb.Byte[1] = b2;
fb.Byte[2] = b3;
fb.Byte[3] = b4;
return fb.Float;
}
};
Radar.cpp
#include "Arduino.h"
#include "Radar.h"
#define MESSAGE_HEAD 0x55
#define ACTIVE_REPORT 0x04
#define FALL_REPORT 0x06
#define REPORT_RADAR 0x03
#define REPORT_OTHER 0x05
#define HEARTBEAT 0x01
#define ABNOEMAL 0x02
#define ENVIRONMENT 0x05
#define BODYSIGN 0x06
#define CLOSE_AWAY 0x07
#define CA_BE 0x01
#define CA_CLOSE 0x02
#define CA_AWAY 0x03
#define SOMEBODY_BE 0x01
#define SOMEBODY_MOVE 0x01
#define SOMEBODY_STOP 0x00
#define NOBODY 0x00
Radar::Radar(Stream *s)
{
_s = s;
}
//CHECKSUM CALCULATION
const unsigned char cuc_CRCHi[256]= {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40
};
const unsigned char cuc_CRCLo[256]= {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9,
0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,
0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF,
0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB,
0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA,
0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97,
0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E,
0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89,
0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83,
0x41, 0x81, 0x80, 0x40
};
unsigned short int Radar::us_CalculateCrc16(unsigned char *lpuc_Frame, unsigned short int lus_Len){
unsigned char luc_CRCHi = 0xFF;
unsigned char luc_CRCLo = 0xFF;
int li_Index=0;
while(lus_Len--){
li_Index = luc_CRCLo ^ *( lpuc_Frame++);
luc_CRCLo = (unsigned char)( luc_CRCHi ^ cuc_CRCHi[li_Index]);
luc_CRCHi = cuc_CRCLo[li_Index];
}
return (unsigned short int )(luc_CRCLo << 8 | luc_CRCHi);
}
char Radar::CRC(char ad1, char ad2, char ad3, char ad4, char ad5, char ad6, char ad7){
unsigned char data[] = {ad1, ad2, ad3, ad4, ad5, ad6, ad7};
unsigned short int crc_data = 0x0000;
unsigned int lenth = sizeof(data)/sizeof(unsigned char);
crc_data = us_CalculateCrc16(data, lenth);
return crc_data;
}
void Radar::sendMessage(unsigned char partialData[], int length)
{
length += 3;
unsigned char data[length];
unsigned char dataWithChecksum[length + 2];
data[0] = 0x55;
data[1] = length + 1; //
data[2] = 0x00;
for (int n = 0; n < length - 3; n++)data[n + 3] = partialData[n];
for (int n = 0; n < length; n++)dataWithChecksum[n] = data[n];
unsigned short int crc_data = us_CalculateCrc16(data, length);
dataWithChecksum[length] = (crc_data & 0xff00) >> 8;
dataWithChecksum[length+1] = crc_data & 0xff;
for (int n = 0; n < length + 2; n++){
_s->write(dataWithChecksum[n]);
}
}
unsigned char read_RadarInfo_EnvironmentalStatus[3] = {0x01, 0x03, 0x05};
unsigned char read_RadarInfo_MotorSigns[3] = {0x01, 0x03, 0x06};
unsigned char read_SystemParameter_ThresholdGear[3] = {0x01, 0x04, 0x0C}; //tested
unsigned char read_SystemParameter_SceneSetting[3] = {0x01, 0x04, 0x10};
unsigned char write_SystemParameter_ThresholdGear_1[4] = {0x02, 0x04, 0x0C, 0x01}; //least sensitive
unsigned char write_SystemParameter_ThresholdGear_2[4] = {0x02, 0x04, 0x0C, 0x02};
unsigned char write_SystemParameter_ThresholdGear_3[4] = {0x02, 0x04, 0x0C, 0x03};
unsigned char write_SystemParameter_ThresholdGear_4[4] = {0x02, 0x04, 0x0C, 0x04};
unsigned char write_SystemParameter_ThresholdGear_5[4] = {0x02, 0x04, 0x0C, 0x05};
unsigned char write_SystemParameter_ThresholdGear_6[4] = {0x02, 0x04, 0x0C, 0x06};
unsigned char write_SystemParameter_ThresholdGear_7[4] = {0x02, 0x04, 0x0C, 0x07}; //default
unsigned char write_SystemParameter_ThresholdGear_8[4] = {0x02, 0x04, 0x0C, 0x08};
unsigned char write_SystemParameter_ThresholdGear_9[4] = {0x02, 0x04, 0x0C, 0x09};
unsigned char write_SystemParameter_ThresholdGear_10[4] = {0x02, 0x04, 0x0C, 0x0A}; //most sensitive
unsigned char write_SystemParameter_SceneSetting_Default[4] = {0x02, 0x04, 0x10, 0x00}; //roughly a distance setting?
unsigned char write_SystemParameter_SceneSetting_AreaTopLoading[4] = {0x02, 0x04, 0x10, 0x01};
unsigned char write_SystemParameter_SceneSetting_BathroomTopMounted[4] = {0x02, 0x04, 0x10, 0x02};
unsigned char write_SystemParameter_SceneSetting_BathroomTopLoading[4] = {0x02, 0x04, 0x10, 0x03};
unsigned char write_SystemParameter_SceneSetting_LivingTopMounted[4] = {0x02, 0x04, 0x10, 0x04};
unsigned char write_SystemParameter_SceneSetting_OfficeTopLoading[4] = {0x02, 0x04, 0x10, 0x05};
unsigned char write_SystemParameter_SceneSetting_HotelTopLoading[4] = {0x02, 0x04, 0x10, 0x06}; //hotel is the largest?
void Radar::send_read_RadarInfo_EnvironmentalStatus() { sendMessage(read_RadarInfo_EnvironmentalStatus, 3); };
void Radar::send_read_RadarInfo_MotorSigns() { sendMessage(read_RadarInfo_MotorSigns, 3); };
void Radar::send_read_SystemParameter_ThresholdGear() { sendMessage(read_SystemParameter_ThresholdGear, 3); };
void Radar::send_read_SystemParameter_SceneSetting() { sendMessage(read_SystemParameter_SceneSetting, 3); };
void Radar::send_write_SystemParameter_ThresholdGear_1() { sendMessage(write_SystemParameter_ThresholdGear_1, 4); };
void Radar::send_write_SystemParameter_ThresholdGear_2() { sendMessage(write_SystemParameter_ThresholdGear_2, 4); };
void Radar::send_write_SystemParameter_ThresholdGear_3() { sendMessage(write_SystemParameter_ThresholdGear_3, 4); };
void Radar::send_write_SystemParameter_ThresholdGear_4() { sendMessage(write_SystemParameter_ThresholdGear_4, 4); };
void Radar::send_write_SystemParameter_ThresholdGear_5() { sendMessage(write_SystemParameter_ThresholdGear_5, 4); };
void Radar::send_write_SystemParameter_ThresholdGear_6() { sendMessage(write_SystemParameter_ThresholdGear_6, 4); };
void Radar::send_write_SystemParameter_ThresholdGear_7() { sendMessage(write_SystemParameter_ThresholdGear_7, 4); };
void Radar::send_write_SystemParameter_ThresholdGear_8() { sendMessage(write_SystemParameter_ThresholdGear_8, 4); };
void Radar::send_write_SystemParameter_ThresholdGear_9() { sendMessage(write_SystemParameter_ThresholdGear_9, 4); };
void Radar::send_write_SystemParameter_ThresholdGear_10() { sendMessage(write_SystemParameter_ThresholdGear_10, 4); };
void Radar::send_write_SystemParameter_SceneSetting_Default() { sendMessage(write_SystemParameter_SceneSetting_Default, 4); };
void Radar::send_write_SystemParameter_SceneSetting_AreaTopLoading() { sendMessage(write_SystemParameter_SceneSetting_AreaTopLoading, 4); };
void Radar::send_write_SystemParameter_SceneSetting_BathroomTopMounted() { sendMessage(write_SystemParameter_SceneSetting_BathroomTopMounted, 4); };
void Radar::send_write_SystemParameter_SceneSetting_BathroomTopLoading() { sendMessage(write_SystemParameter_SceneSetting_BathroomTopLoading, 4); };
void Radar::send_write_SystemParameter_SceneSetting_LivingTopMounted() { sendMessage(write_SystemParameter_SceneSetting_LivingTopMounted, 4); };
void Radar::send_write_SystemParameter_SceneSetting_OfficeTopLoading() { sendMessage(write_SystemParameter_SceneSetting_OfficeTopLoading, 4); };
void Radar::send_write_SystemParameter_SceneSetting_HotelTopLoading() { sendMessage(write_SystemParameter_SceneSetting_HotelTopLoading, 4); };
Radar.h
#ifndef _RADAR_H__
#define _RADAR_H__
#include <Arduino.h>
class Radar
{
private:
Stream *_s;
public:
Radar(Stream *s);
char CRC(char ad1, char ad2, char ad3, char ad4, char ad5, char ad6, char ad7);
void sendMessage(unsigned char partialData[], int length);
unsigned short int us_CalculateCrc16(unsigned char *lpuc_Frame, unsigned short int lus_Len);
void send_read_RadarInfo_EnvironmentalStatus();
void send_read_RadarInfo_MotorSigns();
void send_read_SystemParameter_ThresholdGear(); //tested
void send_read_SystemParameter_SceneSetting();
void send_write_SystemParameter_ThresholdGear_1(); //least sensitive
void send_write_SystemParameter_ThresholdGear_2();
void send_write_SystemParameter_ThresholdGear_3();
void send_write_SystemParameter_ThresholdGear_4();
void send_write_SystemParameter_ThresholdGear_5();
void send_write_SystemParameter_ThresholdGear_6();
void send_write_SystemParameter_ThresholdGear_7(); //default
void send_write_SystemParameter_ThresholdGear_8();
void send_write_SystemParameter_ThresholdGear_9();
void send_write_SystemParameter_ThresholdGear_10(); //most sensitive
void send_write_SystemParameter_SceneSetting_Default(); //roughly a distance setting?
void send_write_SystemParameter_SceneSetting_AreaTopLoading();
void send_write_SystemParameter_SceneSetting_BathroomTopMounted();
void send_write_SystemParameter_SceneSetting_BathroomTopLoading();
void send_write_SystemParameter_SceneSetting_LivingTopMounted();
void send_write_SystemParameter_SceneSetting_OfficeTopLoading();
void send_write_SystemParameter_SceneSetting_HotelTopLoading(); //hotel is the largest?
};
#endif
presence-sensor.yaml
esphome:
name: presence-sensor
platform: ESP32
board: esp32dev
includes:
- presence_sensor.h
- libraries/Radar.h
- libraries/Radar.cpp
logger:
#level: VERBOSE #makes uart stream available in esphome logstream
baud_rate: 0 #disable logging over uart
api:
ota:
password: "password"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
power_save_mode: none
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Presence-Sensor"
password: "password"
captive_portal:
#9600 baud is mandatory, GPIOs are to your preference
uart:
id: uart_bus
tx_pin: GPIO26
rx_pin: GPIO27
baud_rate: 9600
sensor:
- platform: custom
lambda: |-
auto my_sensor = new PresenceSensor(id(uart_bus));
App.register_component(my_sensor);
return {my_sensor->presence_sensor};
sensors:
- name: "Presence Level"
accuracy_decimals: 1
state_class: measurement
Need 5V, GND, RX, TX. That’s All!