Skip to content

Commit f0e4dd9

Browse files
authored
Merge pull request wled#4013 from wesleygas/0_15
Add LD2410 sensor usermod
2 parents 57b01c2 + c540ec5 commit f0e4dd9

File tree

4 files changed

+282
-0
lines changed

4 files changed

+282
-0
lines changed

usermods/LD2410_v2/readme.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# BH1750 usermod
2+
3+
> This usermod requires a second UART and was only tested on the ESP32
4+
5+
6+
This usermod will read from a LD2410 movement/presence sensor.
7+
8+
The movement and presence state are displayed in both the Info section of the web UI, as well as published to the `/movement` and `/stationary` MQTT topics respectively.
9+
10+
## Dependencies
11+
- Libraries
12+
- `ncmreynolds/ld2410@^0.1.3`
13+
- This must be added under `lib_deps` in your `platformio.ini` (or `platformio_override.ini`).
14+
- Data is published over MQTT - make sure you've enabled the MQTT sync interface.
15+
16+
## Compilation
17+
18+
To enable, compile with `USERMOD_LD2410` defined (e.g. in `platformio_override.ini`)
19+
```ini
20+
[env:usermod_USERMOD_LD2410_esp32dev]
21+
extends = env:esp32dev
22+
build_flags =
23+
${common.build_flags_esp32}
24+
-D USERMOD_LD2410
25+
lib_deps =
26+
${esp32.lib_deps}
27+
ncmreynolds/ld2410@^0.1.3
28+
```
29+
30+
### Configuration Options
31+
The Usermod screen allows you to:
32+
- enable/disable the usermod
33+
- Configure the RX/TX pins
34+
35+
## Change log
36+
- 2024-06 Created by @wesleygas (https://github.com/wesleygas/)
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
#warning **** Included USERMOD_LD2410 ****
2+
3+
#ifndef WLED_ENABLE_MQTT
4+
#error "This user mod requires MQTT to be enabled."
5+
#endif
6+
7+
#pragma once
8+
9+
#include "wled.h"
10+
#include <ld2410.h>
11+
12+
class LD2410Usermod : public Usermod {
13+
14+
private:
15+
16+
bool enabled = true;
17+
bool initDone = false;
18+
bool sensorFound = false;
19+
unsigned long lastTime = 0;
20+
unsigned long last_mqtt_sent = 0;
21+
22+
int8_t default_uart_rx = 19;
23+
int8_t default_uart_tx = 18;
24+
25+
26+
String mqttMovementTopic = F("");
27+
String mqttStationaryTopic = F("");
28+
bool mqttInitialized = false;
29+
bool HomeAssistantDiscovery = true; // Publish Home Assistant Discovery messages
30+
31+
32+
ld2410 radar;
33+
bool stationary_detected = false;
34+
bool last_stationary_state = false;
35+
bool movement_detected = false;
36+
bool last_movement_state = false;
37+
38+
// These config variables have defaults set inside readFromConfig()
39+
int8_t uart_rx_pin;
40+
int8_t uart_tx_pin;
41+
42+
// string that are used multiple time (this will save some flash memory)
43+
static const char _name[];
44+
static const char _enabled[];
45+
46+
void publishMqtt(const char* topic, const char* state, bool retain); // example for publishing MQTT message
47+
48+
void _mqttInitialize()
49+
{
50+
mqttMovementTopic = String(mqttDeviceTopic) + F("/ld2410/movement");
51+
mqttStationaryTopic = String(mqttDeviceTopic) + F("/ld2410/stationary");
52+
if (HomeAssistantDiscovery){
53+
_createMqttSensor(F("Movement"), mqttMovementTopic, F("motion"), F(""));
54+
_createMqttSensor(F("Stationary"), mqttStationaryTopic, F("occupancy"), F(""));
55+
}
56+
}
57+
58+
// Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop.
59+
void _createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement)
60+
{
61+
String t = String(F("homeassistant/binary_sensor/")) + mqttClientID + F("/") + name + F("/config");
62+
63+
StaticJsonDocument<600> doc;
64+
65+
doc[F("name")] = String(serverDescription) + F(" Module");
66+
doc[F("state_topic")] = topic;
67+
doc[F("unique_id")] = String(mqttClientID) + name;
68+
if (unitOfMeasurement != "")
69+
doc[F("unit_of_measurement")] = unitOfMeasurement;
70+
if (deviceClass != "")
71+
doc[F("device_class")] = deviceClass;
72+
doc[F("expire_after")] = 1800;
73+
doc[F("payload_off")] = "OFF";
74+
doc[F("payload_on")] = "ON";
75+
76+
JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device
77+
device[F("name")] = serverDescription;
78+
device[F("identifiers")] = "wled-sensor-" + String(mqttClientID);
79+
device[F("manufacturer")] = F("WLED");
80+
device[F("model")] = F("FOSS");
81+
device[F("sw_version")] = versionString;
82+
83+
String temp;
84+
serializeJson(doc, temp);
85+
DEBUG_PRINTLN(t);
86+
DEBUG_PRINTLN(temp);
87+
88+
mqtt->publish(t.c_str(), 0, true, temp.c_str());
89+
}
90+
91+
public:
92+
93+
inline bool isEnabled() { return enabled; }
94+
95+
void setup() {
96+
Serial1.begin(256000, SERIAL_8N1, uart_rx_pin, uart_tx_pin);
97+
Serial.print(F("\nLD2410 radar sensor initialising: "));
98+
if(radar.begin(Serial1)){
99+
Serial.println(F("OK"));
100+
} else {
101+
Serial.println(F("not connected"));
102+
}
103+
initDone = true;
104+
}
105+
106+
107+
void loop() {
108+
// NOTE: on very long strips strip.isUpdating() may always return true so update accordingly
109+
if (!enabled || strip.isUpdating()) return;
110+
radar.read();
111+
unsigned long curr_time = millis();
112+
if(curr_time - lastTime > 1000) //Try to Report every 1000ms
113+
{
114+
lastTime = curr_time;
115+
sensorFound = radar.isConnected();
116+
if(!sensorFound) return;
117+
stationary_detected = radar.presenceDetected();
118+
if(stationary_detected != last_stationary_state){
119+
if (WLED_MQTT_CONNECTED){
120+
publishMqtt("/ld2410/stationary", stationary_detected ? "ON":"OFF", false);
121+
last_stationary_state = stationary_detected;
122+
}
123+
}
124+
movement_detected = radar.movingTargetDetected();
125+
if(movement_detected != last_movement_state){
126+
if (WLED_MQTT_CONNECTED){
127+
publishMqtt("/ld2410/movement", movement_detected ? "ON":"OFF", false);
128+
last_movement_state = movement_detected;
129+
}
130+
}
131+
// If there hasn't been any activity, send current state to confirm sensor is alive
132+
if(curr_time - last_mqtt_sent > 1000*60*5 && WLED_MQTT_CONNECTED){
133+
publishMqtt("/ld2410/stationary", stationary_detected ? "ON":"OFF", false);
134+
publishMqtt("/ld2410/movement", movement_detected ? "ON":"OFF", false);
135+
}
136+
}
137+
}
138+
139+
140+
void addToJsonInfo(JsonObject& root)
141+
{
142+
// if "u" object does not exist yet wee need to create it
143+
JsonObject user = root[F("u")];
144+
if (user.isNull()) user = root.createNestedObject(F("u"));
145+
146+
JsonArray ld2410_sta_json = user.createNestedArray(F("LD2410 Stationary"));
147+
JsonArray ld2410_mov_json = user.createNestedArray(F("LD2410 Movement"));
148+
if (!enabled){
149+
ld2410_sta_json.add(F("disabled"));
150+
ld2410_mov_json.add(F("disabled"));
151+
} else if(!sensorFound){
152+
ld2410_sta_json.add(F("LD2410"));
153+
ld2410_sta_json.add(" Not Found");
154+
} else {
155+
ld2410_sta_json.add("Sta ");
156+
ld2410_sta_json.add(stationary_detected ? "ON":"OFF");
157+
ld2410_mov_json.add("Mov ");
158+
ld2410_mov_json.add(movement_detected ? "ON":"OFF");
159+
}
160+
}
161+
162+
void addToConfig(JsonObject& root)
163+
{
164+
JsonObject top = root.createNestedObject(FPSTR(_name));
165+
top[FPSTR(_enabled)] = enabled;
166+
//save these vars persistently whenever settings are saved
167+
top["uart_rx_pin"] = default_uart_rx;
168+
top["uart_tx_pin"] = default_uart_tx;
169+
}
170+
171+
172+
bool readFromConfig(JsonObject& root)
173+
{
174+
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
175+
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
176+
177+
JsonObject top = root[FPSTR(_name)];
178+
179+
bool configComplete = !top.isNull();
180+
if (!configComplete)
181+
{
182+
DEBUG_PRINT(FPSTR(_name));
183+
DEBUG_PRINT(F("LD2410"));
184+
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
185+
return false;
186+
}
187+
188+
configComplete &= getJsonValue(top["uart_rx_pin"], uart_rx_pin, default_uart_rx);
189+
configComplete &= getJsonValue(top["uart_tx_pin"], uart_tx_pin, default_uart_tx);
190+
191+
return configComplete;
192+
}
193+
194+
195+
#ifndef WLED_DISABLE_MQTT
196+
/**
197+
* onMqttConnect() is called when MQTT connection is established
198+
*/
199+
void onMqttConnect(bool sessionPresent) {
200+
// do any MQTT related initialisation here
201+
if(!radar.isConnected()) return;
202+
publishMqtt("/ld2410/status", "I am alive!", false);
203+
if (!mqttInitialized)
204+
{
205+
_mqttInitialize();
206+
mqttInitialized = true;
207+
}
208+
}
209+
#endif
210+
211+
uint16_t getId()
212+
{
213+
return USERMOD_ID_LD2410;
214+
}
215+
};
216+
217+
218+
// add more strings here to reduce flash memory usage
219+
const char LD2410Usermod::_name[] PROGMEM = "LD2410Usermod";
220+
const char LD2410Usermod::_enabled[] PROGMEM = "enabled";
221+
222+
223+
// implementation of non-inline member methods
224+
225+
void LD2410Usermod::publishMqtt(const char* topic, const char* state, bool retain)
226+
{
227+
#ifndef WLED_DISABLE_MQTT
228+
//Check if MQTT Connected, otherwise it will crash
229+
if (WLED_MQTT_CONNECTED) {
230+
last_mqtt_sent = millis();
231+
char subuf[64];
232+
strcpy(subuf, mqttDeviceTopic);
233+
strcat(subuf, topic);
234+
mqtt->publish(subuf, 0, retain, state);
235+
}
236+
#endif
237+
}

wled00/const.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@
177177
#define USERMOD_ID_BME68X 49 //Usermod "usermod_bme68x.h
178178
#define USERMOD_ID_INA226 50 //Usermod "usermod_ina226.h"
179179
#define USERMOD_ID_AHT10 51 //Usermod "usermod_aht10.h"
180+
#define USERMOD_ID_LD2410 52 //Usermod "usermod_ld2410.h"
180181

181182
//Access point behavior
182183
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot

wled00/usermods_list.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@
230230
#include "../usermods/INA226_v2/usermod_ina226.h"
231231
#endif
232232

233+
#ifdef USERMOD_LD2410
234+
#include "../usermods/LD2410_v2/usermod_ld2410.h"
235+
#endif
236+
233237
void registerUsermods()
234238
{
235239
/*
@@ -446,4 +450,8 @@ void registerUsermods()
446450
#ifdef USERMOD_INA226
447451
usermods.add(new UsermodINA226());
448452
#endif
453+
454+
#ifdef USERMOD_LD2410
455+
usermods.add(new LD2410Usermod());
456+
#endif
449457
}

0 commit comments

Comments
 (0)