Skip to content

Commit ed3b139

Browse files
AP_RangeFinder: add DTS6012M driver support
1 parent 2d99a5e commit ed3b139

File tree

6 files changed

+278
-0
lines changed

6 files changed

+278
-0
lines changed

libraries/AP_RangeFinder/AP_RangeFinder.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
#include "AP_RangeFinder_Ainstein_LR_D1.h"
6565
#include "AP_RangeFinder_RDS02UF.h"
6666
#include "AP_RangeFinder_LightWare_GRF.h"
67+
#include "AP_RangeFinder_DTS6012M.h"
6768

6869
#include <AP_BoardConfig/AP_BoardConfig.h>
6970
#include <AP_Logger/AP_Logger.h>
@@ -628,6 +629,12 @@ __INITFUNC__ void RangeFinder::detect_instance(uint8_t instance, uint8_t& serial
628629
break;
629630
#endif // AP_RANGEFINDER_LIGHTWARE_GRF_ENABLED
630631

632+
#if AP_RANGEFINDER_DTS6012M_ENABLED
633+
case Type::DTS6012M:
634+
serial_create_fn = AP_RangeFinder_DTS6012M::create;
635+
break;
636+
#endif
637+
631638
case Type::NONE:
632639
break;
633640
}

libraries/AP_RangeFinder/AP_RangeFinder.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@ class RangeFinder
191191
#if AP_RANGEFINDER_BENEWAKE_TFS20L_ENABLED
192192
BenewakeTFS20L = 46,
193193
#endif // AP_RANGEFINDER_BENEWAKE_TFS20L_ENABLED
194+
#if AP_RANGEFINDER_DTS6012M_ENABLED
195+
DTS6012M = 47,
196+
#endif
194197
#if AP_RANGEFINDER_SIM_ENABLED
195198
SIM = 100,
196199
#endif
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
/*
2+
This program is free software: you can redistribute it and/or modify
3+
it under the terms of the GNU General Public License as published by
4+
the Free Software Foundation, either version 3 of the License, or
5+
(at your option) any later version.
6+
7+
This program is distributed in the hope that it will be useful,
8+
but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
GNU General Public License for more details.
11+
12+
You should have received a copy of the GNU General Public License
13+
along with this program. If not, see <http://www.gnu.org/licenses/>.
14+
*/
15+
16+
#include "AP_RangeFinder_DTS6012M.h"
17+
18+
#if AP_RANGEFINDER_DTS6012M_ENABLED
19+
20+
#include <AP_HAL/AP_HAL.h>
21+
22+
extern const AP_HAL::HAL& hal;
23+
24+
/*
25+
CRC-16/MODBUS calculation
26+
Polynomial: 0x8005, Init: 0xFFFF, RefIn: true, RefOut: true, XorOut: 0x0000
27+
*/
28+
uint16_t AP_RangeFinder_DTS6012M::crc16_modbus(const uint8_t *data, uint16_t len)
29+
{
30+
uint16_t crc = 0xFFFF;
31+
for (uint16_t i = 0; i < len; i++) {
32+
crc ^= data[i];
33+
for (uint8_t j = 0; j < 8; j++) {
34+
if (crc & 0x0001) {
35+
crc = (crc >> 1) ^ 0xA001; // 0xA001 is the bit-reversed form of 0x8005
36+
} else {
37+
crc >>= 1;
38+
}
39+
}
40+
}
41+
return crc;
42+
}
43+
44+
/*
45+
send the start stream command (0x01) to begin periodic measurement output
46+
Frame: A5 03 20 01 00 00 00 CRC16_H CRC16_L
47+
*/
48+
void AP_RangeFinder_DTS6012M::send_start_command()
49+
{
50+
const uint8_t cmd[] = {
51+
DTS6012M_FRAME_HEADER, // 0xA5
52+
DTS6012M_DEVICE_ID, // 0x03
53+
DTS6012M_DEVICE_TYPE, // 0x20
54+
DTS6012M_CMD_START_STREAM, // 0x01
55+
0x00, // reserved
56+
0x00, 0x00 // length = 0 (no data)
57+
};
58+
59+
// calculate CRC over the command bytes
60+
const uint16_t crc = crc16_modbus(cmd, sizeof(cmd));
61+
62+
uart->write(cmd, sizeof(cmd));
63+
// CRC is sent high byte first per protocol spec
64+
uart->write(uint8_t(crc >> 8));
65+
uart->write(uint8_t(crc & 0xFF));
66+
}
67+
68+
/*
69+
read from the sensor and return distance in meters
70+
Protocol response frame (23 bytes total):
71+
Header: A5 03 20 01 00 00 0E (7 bytes)
72+
Data (14 bytes):
73+
[0-1] Secondary target distance (LE, mm) - 0xFFFF if invalid
74+
[2-3] Secondary target correction
75+
[4-5] Secondary target intensity
76+
[6-7] Primary target distance (LE, mm)
77+
[8-9] Primary target correction
78+
[10-11] Primary target intensity
79+
[12-13] Sunlight base
80+
CRC16: 2 bytes (big-endian)
81+
*/
82+
bool AP_RangeFinder_DTS6012M::get_reading(float &reading_m)
83+
{
84+
if (uart == nullptr) {
85+
return false;
86+
}
87+
88+
// send start command on first call
89+
if (!start_cmd_sent) {
90+
send_start_command();
91+
start_cmd_sent = true;
92+
}
93+
94+
float sum_mm = 0;
95+
uint16_t count = 0;
96+
uint16_t count_out_of_range = 0;
97+
int32_t latest_intensity = -1;
98+
99+
// read available bytes from the sensor
100+
for (auto i = 0; i < 8192; i++) {
101+
uint8_t c;
102+
if (!uart->read(c)) {
103+
break;
104+
}
105+
106+
// waiting for frame header
107+
if (linebuf_len == 0) {
108+
if (c == DTS6012M_FRAME_HEADER) {
109+
linebuf[linebuf_len++] = c;
110+
}
111+
continue;
112+
}
113+
114+
// accumulate bytes into buffer
115+
linebuf[linebuf_len++] = c;
116+
117+
// validate header fields as they arrive
118+
if (linebuf_len == 2 && c != DTS6012M_DEVICE_ID) {
119+
linebuf_len = 0;
120+
continue;
121+
}
122+
if (linebuf_len == 3 && c != DTS6012M_DEVICE_TYPE) {
123+
linebuf_len = 0;
124+
continue;
125+
}
126+
if (linebuf_len == 4 && c != DTS6012M_CMD_START_STREAM) {
127+
// not a measurement frame, discard
128+
linebuf_len = 0;
129+
continue;
130+
}
131+
132+
// wait for complete frame
133+
if (linebuf_len < DTS6012M_FRAME_LEN) {
134+
continue;
135+
}
136+
137+
// frame complete - verify length field
138+
const uint16_t data_len = (uint16_t(linebuf[5]) << 8) | linebuf[6];
139+
if (data_len != DTS6012M_DATA_LEN) {
140+
linebuf_len = 0;
141+
continue;
142+
}
143+
144+
// verify CRC-16 over header + data (all bytes except last 2)
145+
const uint16_t crc_calc = crc16_modbus(linebuf, DTS6012M_FRAME_LEN - DTS6012M_CRC_LEN);
146+
const uint16_t crc_recv = (uint16_t(linebuf[DTS6012M_FRAME_LEN - 2]) << 8) | linebuf[DTS6012M_FRAME_LEN - 1];
147+
148+
if (crc_calc != crc_recv) {
149+
linebuf_len = 0;
150+
continue;
151+
}
152+
153+
// extract primary target distance (data bytes 6-7, little-endian, in mm)
154+
const uint8_t *data = &linebuf[DTS6012M_HEADER_LEN];
155+
const uint16_t dist_mm = uint16_t(data[7]) << 8 | data[6];
156+
157+
if (dist_mm == DTS6012M_DIST_INVALID) {
158+
count_out_of_range++;
159+
} else if (dist_mm > DTS6012M_DIST_MAX_MM) {
160+
count_out_of_range++;
161+
} else {
162+
sum_mm += dist_mm;
163+
count++;
164+
}
165+
166+
// capture primary target intensity for signal quality (data bytes 10-11, LE)
167+
latest_intensity = int32_t(uint16_t(data[11]) << 8 | data[10]);
168+
169+
// reset for next frame
170+
linebuf_len = 0;
171+
}
172+
173+
// update signal quality from latest intensity reading
174+
if (latest_intensity >= 0) {
175+
// map intensity to 0-100%, clamping at 10000 as practical maximum
176+
_signal_quality_pct = int8_t(constrain_int32(latest_intensity * 100 / 10000, 0, 100));
177+
}
178+
179+
if (count > 0) {
180+
reading_m = (sum_mm * 0.001f) / count;
181+
return true;
182+
}
183+
184+
if (count_out_of_range > 0) {
185+
reading_m = max_distance() + 1.0f;
186+
return true;
187+
}
188+
189+
return false;
190+
}
191+
192+
#endif // AP_RANGEFINDER_DTS6012M_ENABLED
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
This program is free software: you can redistribute it and/or modify
3+
it under the terms of the GNU General Public License as published by
4+
the Free Software Foundation, either version 3 of the License, or
5+
(at your option) any later version.
6+
7+
This program is distributed in the hope that it will be useful,
8+
but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
GNU General Public License for more details.
11+
12+
You should have received a copy of the GNU General Public License
13+
along with this program. If not, see <http://www.gnu.org/licenses/>.
14+
*/
15+
16+
#pragma once
17+
18+
#include "AP_RangeFinder_config.h"
19+
20+
#if AP_RANGEFINDER_DTS6012M_ENABLED
21+
22+
#include "AP_RangeFinder_Backend_Serial.h"
23+
24+
// DTS6012M protocol constants
25+
#define DTS6012M_FRAME_HEADER 0xA5
26+
#define DTS6012M_DEVICE_ID 0x03
27+
#define DTS6012M_DEVICE_TYPE 0x20
28+
#define DTS6012M_CMD_START_STREAM 0x01
29+
#define DTS6012M_CMD_STOP_STREAM 0x02
30+
#define DTS6012M_HEADER_LEN 7 // header(1) + devid(1) + devtype(1) + cmd(1) + reserved(1) + length(2)
31+
#define DTS6012M_DATA_LEN 14 // measurement data length
32+
#define DTS6012M_CRC_LEN 2
33+
#define DTS6012M_FRAME_LEN (DTS6012M_HEADER_LEN + DTS6012M_DATA_LEN + DTS6012M_CRC_LEN) // 23 bytes
34+
#define DTS6012M_DIST_MAX_MM 20000 // 20m max range
35+
#define DTS6012M_DIST_INVALID 0xFFFF
36+
37+
class AP_RangeFinder_DTS6012M : public AP_RangeFinder_Backend_Serial
38+
{
39+
public:
40+
static AP_RangeFinder_Backend_Serial *create(
41+
RangeFinder::RangeFinder_State &_state,
42+
AP_RangeFinder_Params &_params) {
43+
return NEW_NOTHROW AP_RangeFinder_DTS6012M(_state, _params);
44+
}
45+
46+
protected:
47+
using AP_RangeFinder_Backend_Serial::AP_RangeFinder_Backend_Serial;
48+
49+
MAV_DISTANCE_SENSOR _get_mav_distance_sensor_type() const override {
50+
return MAV_DISTANCE_SENSOR_LASER;
51+
}
52+
53+
private:
54+
bool get_reading(float &reading_m) override;
55+
uint32_t initial_baudrate(uint8_t serial_instance) const override { return 921600; }
56+
int8_t get_signal_quality_pct() const override { return _signal_quality_pct; }
57+
58+
// protocol parsing state
59+
uint8_t linebuf[DTS6012M_FRAME_LEN];
60+
uint8_t linebuf_len = 0;
61+
bool start_cmd_sent = false;
62+
int8_t _signal_quality_pct = RangeFinder::SIGNAL_QUALITY_UNKNOWN;
63+
64+
// send start stream command to sensor
65+
void send_start_command();
66+
67+
// CRC-16/MODBUS calculation
68+
static uint16_t crc16_modbus(const uint8_t *data, uint16_t len);
69+
};
70+
71+
#endif // AP_RANGEFINDER_DTS6012M_ENABLED

libraries/AP_RangeFinder/AP_RangeFinder_Params.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ const AP_Param::GroupInfo AP_RangeFinder_Params::var_info[] = {
6161
// @Values: 44:HexsoonRadar
6262
// @Values: 45:LightWare-GRF
6363
// @Values: 46:BenewakeTFS20L
64+
// @Values: 47:DTS6012M
6465
// @Values: 100:SITL
6566
// @User: Standard
6667
AP_GROUPINFO_FLAGS("TYPE", 1, AP_RangeFinder_Params, type, 0, AP_PARAM_FLAG_ENABLE),

libraries/AP_RangeFinder/AP_RangeFinder_config.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,4 +199,8 @@
199199
#define AP_RANGEFINDER_HEXSOONRADAR_ENABLED (HAL_MAX_CAN_PROTOCOL_DRIVERS && AP_RANGEFINDER_BACKEND_DEFAULT_ENABLED)
200200
#endif
201201

202+
#ifndef AP_RANGEFINDER_DTS6012M_ENABLED
203+
#define AP_RANGEFINDER_DTS6012M_ENABLED AP_RANGEFINDER_BACKEND_DEFAULT_ENABLED
204+
#endif
205+
202206
#define AP_RANGEFINDER_NRA24_CAN_DRIVER_ENABLED (AP_RANGEFINDER_HEXSOONRADAR_ENABLED || AP_RANGEFINDER_NRA24_CAN_ENABLED)

0 commit comments

Comments
 (0)