AP_RangeFinder_LightWareI2C.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  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. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. GNU General Public License for more details.
  10. You should have received a copy of the GNU General Public License
  11. along with this program. If not, see <http://www.gnu.org/licenses/>.
  12. */
  13. #include "AP_RangeFinder_LightWareI2C.h"
  14. #include <AP_HAL/AP_HAL.h>
  15. #include <AP_HAL/utility/sparse-endian.h>
  16. extern const AP_HAL::HAL& hal;
  17. #define LIGHTWARE_DISTANCE_READ_REG 0
  18. #define LIGHTWARE_LOST_SIGNAL_TIMEOUT_READ_REG 22
  19. #define LIGHTWARE_LOST_SIGNAL_TIMEOUT_WRITE_REG 23
  20. #define LIGHTWARE_TIMEOUT_REG_DESIRED_VALUE 5
  21. static const size_t lx20_max_reply_len_bytes = 32;
  22. static const size_t lx20_max_expected_stream_reply_len_bytes = 14;
  23. #define stream_the_median_distance_to_the_first_return "ldf,0"
  24. #define stream_the_raw_distance_to_the_first_return "ldf,1"
  25. #define stream_the_signal_strength_first_return "lhf"
  26. #define stream_the_raw_distance_to_the_last_return "ldl,1"
  27. #define stream_the_signal_strength_last_return "lhl"
  28. #define stream_the_level_of_background_noise "ln"
  29. /* Data streams from the LiDAR can include any sf20 LiDAR measurement.
  30. * A request to stream the desired measurement is made on a 20Hz basis and
  31. * on the next 20Hz service 50ms later, the result is read and a streaming
  32. * request is made for the next desired measurement in the sequence.
  33. * Results are generally available from the LiDAR within 10mS of request.
  34. */
  35. #define STREAM1_VAL stream_the_raw_distance_to_the_last_return
  36. #define STREAM2_VAL stream_the_signal_strength_last_return
  37. #define STREAM3_VAL stream_the_raw_distance_to_the_first_return
  38. #define STREAM4_VAL stream_the_signal_strength_first_return
  39. #define STREAM5_VAL stream_the_level_of_background_noise
  40. static const char *parse_stream_id[NUM_SF20_DATA_STREAMS] = { // List of stream identifier strings. Must match init_stream_id[].
  41. STREAM1_VAL ":"
  42. };
  43. static const char *init_stream_id[NUM_SF20_DATA_STREAMS] = {// List of stream initialization strings. Must match parse_stream_id[].
  44. "$1" STREAM1_VAL "\r\n"
  45. };
  46. static const uint8_t streamSequence[] = { 0 }; // List of 0 based stream Ids that determine the LiDAR values collected.
  47. static const uint8_t numStreamSequenceIndexes = sizeof(streamSequence)/sizeof(streamSequence[0]);
  48. /*
  49. The constructor also initializes the rangefinder. Note that this
  50. constructor is not called until detect() returns true, so we
  51. already know that we should setup the rangefinder
  52. */
  53. AP_RangeFinder_LightWareI2C::AP_RangeFinder_LightWareI2C(RangeFinder::RangeFinder_State &_state,
  54. AP_RangeFinder_Params &_params,
  55. AP_HAL::OwnPtr<AP_HAL::I2CDevice> dev)
  56. : AP_RangeFinder_Backend(_state, _params)
  57. , _dev(std::move(dev)) {}
  58. /*
  59. Detects if a Lightware rangefinder is connected. We'll detect by
  60. trying to take a reading on I2C. If we get a result the sensor is
  61. there.
  62. */
  63. AP_RangeFinder_Backend *AP_RangeFinder_LightWareI2C::detect(RangeFinder::RangeFinder_State &_state,
  64. AP_RangeFinder_Params &_params,
  65. AP_HAL::OwnPtr<AP_HAL::I2CDevice> dev)
  66. {
  67. if (!dev) {
  68. return nullptr;
  69. }
  70. AP_RangeFinder_LightWareI2C *sensor
  71. = new AP_RangeFinder_LightWareI2C(_state, _params, std::move(dev));
  72. if (!sensor) {
  73. return nullptr;
  74. }
  75. WITH_SEMAPHORE(sensor->_dev->get_semaphore());
  76. if (!sensor->init()) {
  77. delete sensor;
  78. return nullptr;
  79. }
  80. return sensor;
  81. }
  82. /**
  83. * Wrapper function over #transfer() to write a sequence of bytes to
  84. * device. No values are read.
  85. */
  86. bool AP_RangeFinder_LightWareI2C::write_bytes(uint8_t *write_buf_u8, uint32_t len_u8)
  87. {
  88. return _dev->transfer(write_buf_u8, len_u8, NULL, 0);
  89. }
  90. /**
  91. * Disables "address tagging" in the sf20 response packets.
  92. */
  93. void AP_RangeFinder_LightWareI2C::sf20_disable_address_tagging()
  94. {
  95. sf20_send_and_expect("#CT,0\r\n", "ct:0");
  96. }
  97. /*
  98. send a native command and check for an expected reply
  99. */
  100. bool AP_RangeFinder_LightWareI2C::sf20_send_and_expect(const char* send_msg, const char* expected_reply)
  101. {
  102. const size_t expected_reply_len = strlen(expected_reply);
  103. uint8_t rx_bytes[expected_reply_len + 1];
  104. memset(rx_bytes, 0, sizeof(rx_bytes));
  105. if ((expected_reply_len > lx20_max_reply_len_bytes) ||
  106. (expected_reply_len < 2)) {
  107. return false;
  108. }
  109. if (!write_bytes((uint8_t*)send_msg,
  110. strlen(send_msg))) {
  111. return false;
  112. }
  113. if (!sf20_wait_on_reply(rx_bytes)) {
  114. return false;
  115. }
  116. if ((rx_bytes[0] != expected_reply[0]) ||
  117. (rx_bytes[1] != expected_reply[1]) ) {
  118. return false;
  119. }
  120. for (uint8_t i=0; i<10; i++) {
  121. if (_dev->read(rx_bytes, expected_reply_len)) {
  122. break;
  123. }
  124. // give a bit of time for the remaining bytes to be available
  125. hal.scheduler->delay(1);
  126. }
  127. return memcmp(rx_bytes, expected_reply, expected_reply_len) == 0;
  128. }
  129. /*
  130. send a native command and fill a reply into a buffer. Used for
  131. version string
  132. */
  133. void AP_RangeFinder_LightWareI2C::sf20_get_version(const char* send_msg, const char *reply_prefix, char reply[15])
  134. {
  135. const size_t expected_reply_len = 16;
  136. uint8_t rx_bytes[expected_reply_len + 1];
  137. memset(rx_bytes, 0, sizeof(rx_bytes));
  138. if (!write_bytes((uint8_t*)send_msg, strlen(send_msg))) {
  139. return;
  140. }
  141. if (!sf20_wait_on_reply(rx_bytes)) {
  142. return;
  143. }
  144. if ((rx_bytes[0] != uint8_t(reply_prefix[0])) ||
  145. (rx_bytes[1] != uint8_t(reply_prefix[1])) ) {
  146. return;
  147. }
  148. for (uint8_t i=0; i<10; i++) {
  149. if (_dev->read(rx_bytes, expected_reply_len)) {
  150. break;
  151. }
  152. // give a bit of time for the remaining bytes to be available
  153. hal.scheduler->delay(1);
  154. }
  155. memcpy(reply, &rx_bytes[2], 14);
  156. }
  157. /* Driver first attempts to initialize the sf20.
  158. * If for any reason this fails, the driver attempts to initialize the legacy LightWare LiDAR.
  159. * If this fails, the driver returns false indicating no LightWare LiDAR is present.
  160. */
  161. bool AP_RangeFinder_LightWareI2C::init()
  162. {
  163. if (sf20_init()) {
  164. hal.console->printf("Found SF20 native Lidar\n");
  165. return true;
  166. }
  167. if (legacy_init()) {
  168. hal.console->printf("Found SF20 legacy Lidar\n");
  169. return true;
  170. }
  171. hal.console->printf("SF20 not found\n");
  172. return false;
  173. }
  174. /*
  175. initialise lidar using legacy 16 bit protocol
  176. */
  177. bool AP_RangeFinder_LightWareI2C::legacy_init()
  178. {
  179. union {
  180. be16_t be16_val;
  181. uint8_t bytes[2];
  182. } timeout;
  183. // Retrieve lost signal timeout register
  184. const uint8_t read_reg = LIGHTWARE_LOST_SIGNAL_TIMEOUT_READ_REG;
  185. if (!_dev->transfer(&read_reg, 1, timeout.bytes, 2)) {
  186. return false;
  187. }
  188. // Check lost signal timeout register against desired value and write it if it does not match
  189. if (be16toh(timeout.be16_val) != LIGHTWARE_TIMEOUT_REG_DESIRED_VALUE) {
  190. timeout.be16_val = htobe16(LIGHTWARE_TIMEOUT_REG_DESIRED_VALUE);
  191. const uint8_t send_buf[3] = {LIGHTWARE_LOST_SIGNAL_TIMEOUT_WRITE_REG, timeout.bytes[0], timeout.bytes[1]};
  192. if (!_dev->transfer(send_buf, sizeof(send_buf), nullptr, 0)) {
  193. return false;
  194. }
  195. }
  196. // call timer() at 20Hz
  197. _dev->register_periodic_callback(50000,
  198. FUNCTOR_BIND_MEMBER(&AP_RangeFinder_LightWareI2C::legacy_timer, void));
  199. return true;
  200. }
  201. /*
  202. initialise using newer text based protocol
  203. */
  204. bool AP_RangeFinder_LightWareI2C::sf20_init()
  205. {
  206. // version strings for reporting
  207. char version[15] {};
  208. sf20_get_version("?P\r\n", "p:", version);
  209. if (version[0]) {
  210. hal.console->printf("SF20 Lidar version %s\n", version);
  211. }
  212. // Makes sure that "address tagging" is turned off.
  213. // Address tagging starts every response with "0x66".
  214. // Turns off Address Tagging just in case it was previously left on in the non-volatile configuration.
  215. sf20_disable_address_tagging();
  216. // Disconnect the servo (if applicable)
  217. sf20_send_and_expect("#SC,0\r\n", "sc:0");
  218. // Change the power consumption:
  219. // 0 = power off
  220. // 1 = power on
  221. // As of 7/10/17 sw and fw version 1.0 the "#E,1" command does not seem to be supported.
  222. // When it is supported the expected response would be "e:1".
  223. // Changes the number of lost signal confirmations: 1 [1..250].
  224. if (!sf20_send_and_expect("#LC,1\r\n", "lc:1")) {
  225. return false;
  226. }
  227. #if 0
  228. // This location in the code may be uncommented to do a one time change of the devices address.
  229. // It should be commented out again and immediately reloaded to the pixhawk after the device has
  230. // been modified by this initialization process.
  231. // Address change to 0x65 = 101
  232. write_bytes((uint8_t*)"#CI,0x65\r\n",10);
  233. _dev->set_address(0x65);
  234. uint8_t rx_bytes[lx20_max_reply_len_bytes + 1];
  235. sf20_wait_on_reply(rx_bytes);
  236. // Save the comm settings
  237. if (!sf20_send_and_expect("%C\r\n", "%c:")) {
  238. return false;
  239. }
  240. #endif
  241. #if 0
  242. /*
  243. this can be used to change the laser encoding pattern
  244. Changes the laser encoding pattern: 3 (Random A) [0..4].
  245. */
  246. if (!sf20_send_and_expect("#LE,3\r\n", "le:3")) {
  247. return false;
  248. }
  249. #endif
  250. // Sets datum offset [-10.00 ... 10.00].
  251. if (!sf20_send_and_expect("#LO,0.00\r\n", "lo:0.00")) {
  252. return false;
  253. }
  254. // Changes to a new measuring mode (update rate):
  255. // 1 = 388 readings per second
  256. // 2 = 194 readings per second
  257. // 3 = 129 readings per second
  258. // 4 = 97 readings per second
  259. // 5 = 78 readings per second
  260. // 6 = 65 readings per second
  261. // 7 = 55 readings per second
  262. // 8 = 48 readings per second
  263. if (!sf20_send_and_expect("#LM,7\r\n", "lm:7")) {
  264. return false;
  265. }
  266. // Changes the gain boost value:
  267. // Adjustment range = -20.00 ... 5.00
  268. if (!sf20_send_and_expect("#LB,0.00\r\n", "lb:0.00")) {
  269. return false;
  270. }
  271. // Switches distance streaming on or off:
  272. // 0 = off
  273. // 1 = on
  274. if (!sf20_send_and_expect("#SU,1\r\n", "su:1")) {
  275. return false;
  276. }
  277. // Changes the laser state:
  278. // 0 = laser is off
  279. // 1 = laser is running
  280. if (!sf20_send_and_expect("#LF,1\r\n", "lf:1")) {
  281. return false;
  282. }
  283. // Requests the measurement specified in the first stream.
  284. write_bytes((uint8_t*)init_stream_id[0], strlen(init_stream_id[0]));
  285. // call timer() at 20Hz
  286. _dev->register_periodic_callback(50000,
  287. FUNCTOR_BIND_MEMBER(&AP_RangeFinder_LightWareI2C::sf20_timer, void));
  288. return true;
  289. }
  290. // read - return last value measured by sensor
  291. bool AP_RangeFinder_LightWareI2C::legacy_get_reading(uint16_t &reading_cm)
  292. {
  293. be16_t val;
  294. const uint8_t read_reg = LIGHTWARE_DISTANCE_READ_REG;
  295. // read the high and low byte distance registers
  296. if (_dev->transfer(&read_reg, 1, (uint8_t *)&val, sizeof(val))) {
  297. // combine results into distance
  298. reading_cm = be16toh(val);
  299. return true;
  300. }
  301. return false;
  302. }
  303. // read - return last value measured by sf20 sensor
  304. bool AP_RangeFinder_LightWareI2C::sf20_get_reading(uint16_t &reading_cm)
  305. {
  306. // Parses up to 5 ASCII streams for LiDAR data.
  307. // If a parse fails, the stream measurement is not updated until it is successfully read in the future.
  308. uint8_t stream[lx20_max_expected_stream_reply_len_bytes+1]; // Maximum response length for a stream ie "ldf,0:40.99" is 11 characters
  309. uint8_t i = streamSequence[currentStreamSequenceIndex];
  310. size_t num_processed_chars = 0;
  311. /* Reads the LiDAR value requested during the last interrupt. */
  312. if (!_dev->read(stream, sizeof(stream))) {
  313. return false;
  314. }
  315. stream[lx20_max_expected_stream_reply_len_bytes] = 0;
  316. if (!sf20_parse_stream(stream, &num_processed_chars, parse_stream_id[i], sf20_stream_val[i])) {
  317. return false;
  318. }
  319. if (i==0) {
  320. reading_cm = sf20_stream_val[0];
  321. }
  322. // Increment the stream sequence
  323. currentStreamSequenceIndex++;
  324. if (currentStreamSequenceIndex >= numStreamSequenceIndexes) {
  325. currentStreamSequenceIndex = 0;
  326. }
  327. i = streamSequence[currentStreamSequenceIndex];
  328. // Request the next stream in the sequence from the SF20
  329. write_bytes((uint8_t*)init_stream_id[i], strlen(init_stream_id[i]));
  330. return true;
  331. }
  332. bool AP_RangeFinder_LightWareI2C::sf20_parse_stream(uint8_t *stream_buf,
  333. size_t *p_num_processed_chars,
  334. const char *string_identifier,
  335. uint16_t &val)
  336. {
  337. size_t string_identifier_len = strlen(string_identifier);
  338. for (uint32_t i = 0 ; i < string_identifier_len ; i++) {
  339. if (stream_buf[*p_num_processed_chars] != string_identifier[i]) {
  340. return false;
  341. }
  342. (*p_num_processed_chars)++;
  343. }
  344. /* Number is always returned in hundredths. So 6.33 is returned as 633. 6.3 is returned as 630.
  345. * 6 is returned as 600.
  346. * Extract number in format 6.33 or 123.99 (meters to be converted to centimeters).
  347. * Percentages such as 100 (percent), are returned as 10000.
  348. */
  349. uint32_t final_multiplier = 100;
  350. bool decrement_multiplier = false;
  351. bool number_found = false;
  352. uint16_t accumulator = 0;
  353. uint16_t digit_u16 = (uint16_t)stream_buf[*p_num_processed_chars];
  354. while ((((digit_u16 <= '9') &&
  355. (digit_u16 >= '0')) ||
  356. (digit_u16 == '.')) &&
  357. (*p_num_processed_chars < lx20_max_reply_len_bytes)) {
  358. (*p_num_processed_chars)++;
  359. if (decrement_multiplier) {
  360. final_multiplier /=10;
  361. }
  362. if (digit_u16 == '.') {
  363. decrement_multiplier = true;
  364. digit_u16 = (uint16_t)stream_buf[*p_num_processed_chars];
  365. continue;
  366. }
  367. number_found = true;
  368. accumulator *= 10;
  369. accumulator += digit_u16 - '0';
  370. digit_u16 = (uint16_t)stream_buf[*p_num_processed_chars];
  371. }
  372. accumulator *= final_multiplier;
  373. val = accumulator;
  374. return number_found;
  375. }
  376. /*
  377. update the state of the sensor
  378. */
  379. void AP_RangeFinder_LightWareI2C::update(void)
  380. {
  381. // nothing to do - its all done in the timer()
  382. }
  383. void AP_RangeFinder_LightWareI2C::legacy_timer(void)
  384. {
  385. if (legacy_get_reading(state.distance_cm)) {
  386. // update range_valid state based on distance measured
  387. update_status();
  388. } else {
  389. set_status(RangeFinder::RangeFinder_NoData);
  390. }
  391. }
  392. void AP_RangeFinder_LightWareI2C::sf20_timer(void)
  393. {
  394. if (sf20_get_reading(state.distance_cm)) {
  395. // update range_valid state based on distance measured
  396. update_status();
  397. } else {
  398. set_status(RangeFinder::RangeFinder_NoData);
  399. }
  400. }
  401. // Only for use during init as this blocks while waiting for the SF20 to be ready.
  402. bool AP_RangeFinder_LightWareI2C::sf20_wait_on_reply(uint8_t *rx_two_byte)
  403. {
  404. // Waits for a non-zero first byte while repeatedly reading 16 bits.
  405. // This is used after a read command to allow the sf20 time to provide the result.
  406. uint32_t start_time_ms = AP_HAL::millis();
  407. const uint32_t max_wait_time_ms = 50;
  408. while (AP_HAL::millis() - start_time_ms < max_wait_time_ms) {
  409. if (!_dev->read(rx_two_byte, 2)) {
  410. hal.scheduler->delay(1);
  411. continue;
  412. }
  413. if (rx_two_byte[0] != 0) {
  414. // normal exit
  415. return true;
  416. }
  417. }
  418. return false;
  419. }