LCOV - code coverage report
Current view: top level - AP_RangeFinder - AP_RangeFinder_Benewake_TFMiniPlus.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 84 95 88.4 %
Date: 2026-09-09 11:02:08 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2019  Lucas De Marchi <[email protected]>
       3             :  *
       4             :  * This file is free software: you can redistribute it and/or modify it
       5             :  * under the terms of the GNU General Public License as published by the
       6             :  * Free Software Foundation, either version 3 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * This file is distributed in the hope that it will be useful, but
      10             :  * WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      12             :  * See the GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License along
      15             :  * with this program.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : #include "AP_RangeFinder_Benewake_TFMiniPlus.h"
      18             : 
      19             : #if AP_RANGEFINDER_BENEWAKE_TFMINIPLUS_ENABLED
      20             : 
      21             : #include <GCS_MAVLink/GCS.h>
      22             : #include <AP_HAL/AP_HAL.h>
      23             : 
      24             : extern const AP_HAL::HAL& hal;
      25             : 
      26             : #define DRIVER "TFMiniPlus"
      27             : #define BENEWAKE_OUT_OF_RANGE_ADD_CM 100
      28             : 
      29             : /*
      30             :  * Command format:
      31             :  *
      32             :  * uint8_t header;
      33             :  * uint8_t len;
      34             :  * uint8_t id;
      35             :  * uint8_t data[];
      36             :  * uint8_t checksum;
      37             :  */
      38             : 
      39             : #define CMD_SET_DATA_OUT 0x5A, 0x05, 0x07
      40             : #define CMD_ENABLE_DATA_OUT CMD_SET_DATA_OUT, 0x01, 0x67
      41             : #define CMD_DISABLE_DATA_OUT CMD_SET_DATA_OUT, 0x00, 0x66
      42             : 
      43           6 : bool AP_RangeFinder_Benewake_TFMiniPlus::init()
      44             : {
      45           6 :     const uint8_t CMD_FW_VERSION[] =         { 0x5A, 0x04, 0x01, 0x5F };
      46           6 :     const uint8_t CMD_SYSTEM_RESET[] =       { 0x5A, 0x04, 0x04, 0x62 };
      47           6 :     const uint8_t CMD_OUTPUT_FORMAT_CM[] =   { 0x5A, 0x05, 0x05, 0x01, 0x65 };
      48           6 :     const uint8_t CMD_ENABLE_DATA_OUTPUT[] = { CMD_ENABLE_DATA_OUT };
      49           6 :     const uint8_t CMD_FRAME_RATE_250HZ[] =   { 0x5A, 0x06, 0x03, 0xFA, 0x00, 0x5D };
      50           6 :     const uint8_t CMD_SAVE_SETTINGS[] =      { 0x5A, 0x04, 0x11, 0x6F };
      51             :     const uint8_t *cmds[] = {
      52             :         CMD_OUTPUT_FORMAT_CM,
      53             :         CMD_FRAME_RATE_250HZ,
      54             :         CMD_ENABLE_DATA_OUTPUT,
      55             :         CMD_SAVE_SETTINGS,
      56           6 :     };
      57             :     uint8_t val[12], i;
      58             :     bool ret;
      59             : 
      60           6 :     WITH_SEMAPHORE(dev.get_semaphore());
      61             : 
      62           6 :     dev.set_retries(0);
      63             : 
      64             :     /*
      65             :      * Check we get a response for firmware version to detect if sensor is there
      66             :      */
      67           6 :     ret = dev.transfer(CMD_FW_VERSION, sizeof(CMD_FW_VERSION), nullptr, 0);
      68           6 :     if (!ret) {
      69           4 :         return false;
      70             :     }
      71             : 
      72           2 :     hal.scheduler->delay(100);
      73             : 
      74           2 :     ret = dev.transfer(nullptr, 0, val, 7);
      75           4 :     if (!ret || val[0] != 0x5A || val[1] != 0x07 || val[2] != 0x01 ||
      76           2 :         !check_checksum(val, 7)) {
      77           0 :         return false;
      78             :     }
      79             : 
      80           2 :     if (val[5] * 10000 + val[4] * 100 + val[3] < 20003) {
      81           0 :         GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "TFMini: FW ver %u.%u.%u (need>=2.0.3)",
      82             :                             (unsigned)val[5],(unsigned)val[4],(unsigned)val[3]);
      83           0 :         return false;
      84             :     }
      85             : 
      86           2 :     GCS_SEND_TEXT(MAV_SEVERITY_INFO, "TFMiniPlus: found fw version %u.%u.%u",
      87             :                         val[5], val[4], val[3]);
      88             : 
      89          10 :     for (i = 0; i < ARRAY_SIZE(cmds); i++) {
      90           8 :         ret = dev.transfer(cmds[i], cmds[i][1], nullptr, 0);
      91           8 :         if (!ret) {
      92           0 :             GCS_SEND_TEXT(MAV_SEVERITY_INFO, "TFMiniPlus: Unable to set configuration register %u",
      93             :                                 cmds[i][2]);
      94           0 :             return false;
      95             :         }
      96           8 :         hal.scheduler->delay(100);
      97             :     }
      98             : 
      99           2 :     dev.transfer(CMD_SYSTEM_RESET, sizeof(CMD_SYSTEM_RESET), nullptr, 0);
     100             : 
     101           2 :     hal.scheduler->delay(100);
     102             : 
     103           2 :     dev.register_periodic_callback(20000,
     104             :                                      FUNCTOR_BIND_MEMBER(&AP_RangeFinder_Benewake_TFMiniPlus::timer, void));
     105             : 
     106           2 :     return true;
     107           6 : }
     108             : 
     109        3172 : void AP_RangeFinder_Benewake_TFMiniPlus::update()
     110             : {
     111        3172 :     WITH_SEMAPHORE(_sem);
     112             : 
     113             :     // Power down if commanded
     114        3172 :     power_state.commanded_power_down = should_power_down();
     115        3172 :     if (power_state.commanded_power_down || power_state.current_power_down) {
     116         147 :         set_status(RangeFinder::Status::PoweredDown);
     117             :         // Clear accumulation
     118         147 :         accum.sum = 0;
     119         147 :         accum.count = 0;
     120         147 :         return;
     121             :     }
     122             : 
     123             :     // Average samples
     124        3025 :     if (accum.count > 0) {
     125        3025 :         state.distance_m = (accum.sum * 0.01f) / accum.count;
     126        3025 :         state.last_reading_ms = AP_HAL::millis();
     127        3025 :         accum.sum = 0;
     128        3025 :         accum.count = 0;
     129        3025 :         update_status();
     130           0 :     } else if (AP_HAL::millis() - state.last_reading_ms > 200) {
     131             :         // Timeout for bad data
     132           0 :         set_status(RangeFinder::Status::NoData);
     133             :     }
     134        3172 : }
     135             : 
     136        7751 : void AP_RangeFinder_Benewake_TFMiniPlus::process_raw_measure(le16_t distance_raw, le16_t strength_raw,
     137             :                                                              uint16_t &output_distance_cm)
     138             : {
     139        7751 :     uint16_t strength = le16toh(strength_raw);
     140        7751 :     const uint16_t MAX_DIST_CM = 1200;
     141        7751 :     const uint16_t MIN_DIST_CM = 10;
     142             : 
     143        7751 :     output_distance_cm = le16toh(distance_raw);
     144             : 
     145        7751 :     if (strength < 100 || strength == 0xFFFF || output_distance_cm > MAX_DIST_CM) {
     146             :         /*
     147             :          * From manual: "when the signal strength is lower than 100 or equal to
     148             :          * 65535, the detection is unreliable, TFmini Plus will set distance
     149             :          * value to 0." - force it to the max distance so status is set to OutOfRangeHigh
     150             :          * rather than NoData.
     151             :          */
     152           0 :         output_distance_cm = MAX(MAX_DIST_CM, max_distance()*100 + BENEWAKE_OUT_OF_RANGE_ADD_CM);
     153             :     } else {
     154        7751 :         output_distance_cm = constrain_int16(output_distance_cm, MIN_DIST_CM, MAX_DIST_CM);
     155             :     }
     156        7751 : }
     157             : 
     158        7753 : bool AP_RangeFinder_Benewake_TFMiniPlus::check_checksum(uint8_t *arr, int pkt_len)
     159             : {
     160        7753 :     uint8_t checksum = 0;
     161             :     int i;
     162             : 
     163             :     /* sum them all except the last (the checksum) */
     164       85275 :     for (i = 0; i < pkt_len - 1; i++) {
     165       77522 :         checksum += arr[i];
     166             :     }
     167             : 
     168        7753 :     return checksum == arr[pkt_len - 1];
     169             : }
     170             : 
     171        8117 : void AP_RangeFinder_Benewake_TFMiniPlus::timer()
     172             : {
     173             :     bool change_power_state;
     174             :     bool power_down;
     175             :     {
     176             :         // Update power flags with semaphore
     177        8117 :         WITH_SEMAPHORE(_sem);
     178        8117 :         change_power_state = power_state.current_power_down != power_state.commanded_power_down;
     179        8117 :         power_down = power_state.commanded_power_down;
     180        8117 :     }
     181             : 
     182        8117 :     if (change_power_state) {
     183             :         // Change in power state
     184             :         bool success;
     185           2 :         if (power_down) {
     186             :             // Send power down command
     187           1 :             const uint8_t CMD_DISABLE[] = { CMD_DISABLE_DATA_OUT };
     188           1 :             success = dev.transfer(CMD_DISABLE, sizeof(CMD_DISABLE), nullptr, 0);
     189             : 
     190             :         } else {
     191             :             // Send power up command
     192           1 :             const uint8_t CMD_ENABLE[] = { CMD_ENABLE_DATA_OUT };
     193           1 :             success = dev.transfer(CMD_ENABLE, sizeof(CMD_ENABLE), nullptr, 0);
     194             :         }
     195             : 
     196             :         // Mark state as changed if command was written
     197           2 :         if (success) {
     198           2 :             WITH_SEMAPHORE(_sem);
     199           2 :             power_state.current_power_down = power_down;
     200           2 :         }
     201             :         // Wait until next call before trying to read data
     202         366 :         return;
     203             :     }
     204             :     
     205        8115 :     if (power_down) {
     206             :         // Powered down, nothing to do
     207         364 :         return;
     208             :     }
     209             : 
     210        7751 :     uint8_t CMD_READ_MEASUREMENT[] = { 0x5A, 0x05, 0x00, 0x07, 0x66 };
     211             :     union {
     212             :         struct PACKED {
     213             :             uint8_t header1;
     214             :             uint8_t header2;
     215             :             le16_t distance;
     216             :             le16_t strength;
     217             :             le32_t timestamp;
     218             :             uint8_t checksum;
     219             :         } val;
     220             :         uint8_t arr[11];
     221             :     } u;
     222             :     uint16_t distance;
     223             : 
     224        7751 :     if (!dev.transfer(CMD_READ_MEASUREMENT, sizeof(CMD_READ_MEASUREMENT), nullptr, 0)) {
     225             :         // Failed write
     226           0 :         return;
     227             :     }
     228             : 
     229             :     // Short delay helps avoid bad data, despite datasheet saying its not needed
     230        7751 :     hal.scheduler->delay_microseconds(10);
     231             : 
     232        7751 :     if (!dev.transfer(nullptr, 0, (uint8_t *)&u, sizeof(u))) {
     233             :         // Failed read
     234           0 :         return;
     235             :     }
     236             : 
     237        7751 :     if (u.val.header1 != 0x59 || u.val.header2 != 0x59 || !check_checksum(u.arr, sizeof(u))) {
     238           0 :         return;
     239             :     }
     240             : 
     241        7751 :     process_raw_measure(u.val.distance, u.val.strength, distance);
     242             : 
     243             :     {
     244        7751 :         WITH_SEMAPHORE(_sem);
     245        7751 :         accum.sum += distance;
     246        7751 :         accum.count++;
     247        7751 :     }
     248             : }
     249             : 
     250             : #endif  // AP_RANGEFINDER_BENEWAKE_TFMINIPLUS_ENABLED

Generated by: LCOV version 1.14