Tutorial¶
This tutorial covers the major API methods with real examples and commands.
Links to the API reference will be provided all along so you can get more details.
Before going further on this tutorial, make sure to have a suitable python environment (see Installation).
Connecting to the device¶
First you need to identify your device, its name depends on your platform! You can ask pyserial to list all available console devices on your system:
$ python -m serial.tools.list_ports
/dev/ttyS0
/dev/ttyS1
/dev/ttyUSB0
3 ports found
Here I use an USB serial adapter on /dev/ttyUSB0, which is confirmed by the system logs.
On windows this command most likely returns devices with name in COM*.
For this tutorial I will use /dev/ttyUSB0, replace with yours when applicable.
The following code simply opens the device:
1#!/usr/bin/env python
2
3import asyncio
4from aio_ld2410 import LD2410
5
6async def main():
7 async with LD2410('/dev/ttyUSB0') as device:
8 ...
9
10if __name__ == '__main__':
11 asyncio.run(main())
See also
LD2410.__init__() if you ever need to change the baud rate (defaults to 256000).
Entering the configuration mode¶
Entering configuration mode is also implemented as an asynchronous context. You cannot call configuration commands outside of this context! This context is a requirement before any other command can be issued.
See also
LD2410.configure() for more details.
In the following example the configuration context spreads over the emphasized lines:
1#!/usr/bin/env python
2
3import asyncio
4from aio_ld2410 import LD2410
5
6async def main():
7 async with LD2410('/dev/ttyUSB0') as device:
8 async with device.configure():
9 ver = await device.get_firmware_version()
10
11 print(f'[+] Running with firmware v{ver}')
12
13if __name__ == '__main__':
14 asyncio.run(main())
Debug steps if this code does not work
If you ever have an issue with this example, perform the following checks:
Check that the device you provided is correct (and is a
LD2410)Check that your device does not expect a different baud rate
Notice that on LD2410B and LD2410C, some features may not be available
if the firmware printed by this code is too old.
Reading configuration¶
Standard parameters¶
The following example used LD2410.get_parameters() to read all the standard
parameters from the device (returning a ParametersStatus):
1#!/usr/bin/env python
2
3import asyncio
4from aio_ld2410 import LD2410
5from collections.abc import Iterable
6
7def format_values(values: Iterable[int]) -> str:
8 return ' | '.join(map('{:3d}'.format, values))
9
10async def main():
11 async with LD2410('/dev/ttyUSB0') as device:
12 async with device.configure():
13 cfg = await device.get_parameters()
14
15 print(f'Max distance gate {cfg.max_distance_gate}')
16 print(f'Max motion detection gate {cfg.moving_max_distance_gate}')
17 print(f'Max static detection gate {cfg.static_max_distance_gate}')
18 print(f'Presence timeout {cfg.presence_timeout}')
19 print('Detection thresholds:')
20 print(' Gate ' + format_values(range(cfg.max_distance_gate + 1)))
21 print(' Moving ' + format_values(cfg.moving_threshold))
22 print(' Static ' + format_values(cfg.static_threshold))
23
24if __name__ == '__main__':
25 asyncio.run(main())
This code produces the following output:
$ ./examples/read_simple_configuration.py
Max distance gate 8
Max motion detection gate 8
Max static detection gate 8
Presence timeout 5
Detection thresholds:
Gate 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8
Moving 50 | 50 | 40 | 30 | 20 | 15 | 15 | 15 | 15
Static 0 | 0 | 40 | 40 | 30 | 30 | 20 | 20 | 20
Distance resolution¶
Not available on all models / firmwares
This command seems to only be available for LD2410B and LD2410C devices
with a (quite) recent firmware.
Using LD2410.get_distance_resolution() we can read the range covered by gates:
1#!/usr/bin/env python
2
3import asyncio
4from aio_ld2410 import LD2410
5
6async def main():
7 async with LD2410('/dev/ttyUSB0') as device:
8 async with device.configure():
9 res = await device.get_distance_resolution()
10
11 print(f'Each gate covers {res} centimeters')
12
13if __name__ == '__main__':
14 asyncio.run(main())
Writing configuration¶
Gate configuration comes with two methods that are when combined analogous
to the LD2410.get_parameters() seen above.
General configuration¶
The following example simply sets the maximum detection gate for moving targets to 4 (3 meters) and the maximum detection gate for static targets to 6 (4.5 meters).
It means that detected targets are either close to the sensor and moving, or can be farther away but standing still.
1#!/usr/bin/env python
2
3import asyncio
4from aio_ld2410 import LD2410
5
6async def main():
7 async with LD2410('/dev/ttyUSB0') as device:
8 async with device.configure():
9 await device.set_parameters(
10 moving_max_distance_gate=4,
11 static_max_distance_gate=6,
12 presence_timeout=10,
13 )
14
15if __name__ == '__main__':
16 asyncio.run(main())
Gate sensitivity configuration¶
To be perfectly complementary to read_simple_configuration, we have to use
LD2410.set_gate_sensitivity(), as shown in the following example:
1#!/usr/bin/env python
2
3import asyncio
4from aio_ld2410 import LD2410
5
6async def main():
7 # We want it less sensitive for moving people.
8 MOVING_CONFIG = [50, 50, 40, 40, 35, 30]
9 # But a little bit more for people standing in front of the sensor.
10 STATIC_CONFIG = [0, 0, 40, 35, 30, 25, 20]
11
12 async with LD2410('/dev/ttyUSB0') as device:
13 async with device.configure():
14 for i in range(len(MOVING_CONFIG)):
15 await device.set_gate_sensitivity(
16 distance_gate=i,
17 moving_threshold=MOVING_CONFIG[i],
18 static_threshold=STATIC_CONFIG[i],
19 )
20
21if __name__ == '__main__':
22 asyncio.run(main())
The result of both scripts can be read again with read_simple_configuration:
$ ./examples/read_simple_configuration.py
Max distance gate 8
Max motion detection gate 4
Max static detection gate 6
Presence timeout 10
Detection thresholds:
Gate 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8
Moving 50 | 50 | 40 | 40 | 35 | 30 | 15 | 15 | 15
Static 0 | 0 | 40 | 35 | 30 | 25 | 20 | 20 | 20
Set distance resolution¶
Distance resolution can easily be set through LD2410.set_distance_resolution()
but it requires a device restart to be effective on the sensor itself.
Module restart can be performed through LD2410.restart_module().
Your device lies to you
If you forget to restart, a call to LD2410.get_distance_resolution() will
return value you just configured and not the value currently applied.
1#!/usr/bin/env python
2
3import asyncio
4from aio_ld2410 import LD2410
5
6async def main():
7 async with LD2410('/dev/ttyUSB0') as device:
8 async with device.configure():
9 await device.set_distance_resolution(20)
10 await device.restart_module()
11
12 print('....DEVICE IS RESTARTING....')
13 await asyncio.sleep(2.0)
14
15 async with device.configure():
16 res = await device.get_distance_resolution()
17
18 print(f'Each gate covers {res} centimeters')
19
20if __name__ == '__main__':
21 asyncio.run(main())
Notice the emphasized line, we have to wait for a little bit before we can send new commands to the device (as it is restarting).
Reading reports¶
Reports are pushed by the device to the serial link regularly and contain detection results. Advanced reports cat be requested using the engineering mode, which will not be covered in this tutorial
See also
LD2410.set_engineering_mode() to toggle the engineering mode.
ReportEngineeringStatus to get the content of advanced reports.
Three methods are provided to get reports:
- LD2410.get_last_report() to get the latest received report immediately (if any)
- LD2410.get_next_report() to wait and get the next available report
- LD2410.get_reports() to get reports as they arrive with an asynchronous iterator
Configuration mode conflict
Note that reports are not generated while the configuration mode is active.
The following example runs with the already configured value and reports static and moving targets as they arrive. Run this and hang around in front of the sensor to see changes.
1#!/usr/bin/env python
2
3import asyncio
4from aio_ld2410 import LD2410, ReportBasicStatus, TargetStatus
5
6def format_basic_report(rep: ReportBasicStatus) -> str:
7 items = []
8
9 if rep.target_status & TargetStatus.STATIC:
10 items.append(
11 f'STATIC > dist {rep.static_distance:3d}'
12 f' (energy {rep.static_energy:3d})'
13 )
14 else:
15 items.append(30 * ' ')
16
17 if rep.target_status & TargetStatus.MOVING:
18 items.append(
19 f'MOVING > dist {rep.moving_distance:3d}'
20 f' (energy {rep.moving_energy:3d})'
21 )
22 else:
23 items.append(30 * ' ')
24
25 if rep.target_status:
26 items.append(f'DETECT > dist {rep.detection_distance:3d}')
27 else:
28 items.append('')
29
30 return ' | '.join(items)
31
32
33async def main():
34 async with LD2410('/dev/ttyUSB0') as device:
35 async for report in device.get_reports():
36 print(' ' + format_basic_report(report.basic))
37
38if __name__ == '__main__':
39 asyncio.run(main())
Here is a sample output of this command:
$ ./examples/read_basic_reports.py
STATIC > dist 37 (energy 56) | MOVING > dist 27 (energy 71) | DETECT > dist 33
STATIC > dist 27 (energy 56) | MOVING > dist 34 (energy 45) | DETECT > dist 32
STATIC > dist 34 (energy 54) | | DETECT > dist 32
STATIC > dist 34 (energy 54) | MOVING > dist 38 (energy 41) | DETECT > dist 31
STATIC > dist 38 (energy 53) | MOVING > dist 41 (energy 41) | DETECT > dist 32
STATIC > dist 41 (energy 55) | MOVING > dist 47 (energy 71) | DETECT > dist 32
STATIC > dist 47 (energy 56) | | DETECT > dist 33
STATIC > dist 47 (energy 56) | | DETECT > dist 34
STATIC > dist 47 (energy 57) | | DETECT > dist 36
STATIC > dist 47 (energy 57) | | DETECT > dist 36
Depending on your use case, you might want to change the configuration parameters and run this script again to finely tune the configuration values.
This tutorial is now complete, to go further consider taking a look at the API reference.