LCOV - code coverage report
Current view: top level - AC_Sprayer - AC_Sprayer.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 55 63 87.3 %
Date: 2026-09-09 11:02:08 Functions: 6 6 100.0 %

          Line data    Source code
       1             : #include "AC_Sprayer_config.h"
       2             : 
       3             : #if HAL_SPRAYER_ENABLED
       4             : 
       5             : #include "AC_Sprayer.h"
       6             : #include <AP_AHRS/AP_AHRS.h>
       7             : #include <AP_HAL/AP_HAL.h>
       8             : #include <AP_Math/AP_Math.h>
       9             : #include <SRV_Channel/SRV_Channel.h>
      10             : 
      11             : extern const AP_HAL::HAL& hal;
      12             : 
      13             : // ------------------------------
      14             : 
      15             : const AP_Param::GroupInfo AC_Sprayer::var_info[] = {
      16             :     // @Param: ENABLE
      17             :     // @DisplayName: Sprayer enable/disable
      18             :     // @Description: Allows you to enable (1) or disable (0) the sprayer
      19             :     // @Values: 0:Disabled,1:Enabled
      20             :     // @User: Standard
      21             :     AP_GROUPINFO_FLAGS("ENABLE", 0, AC_Sprayer, _enabled, 0, AP_PARAM_FLAG_ENABLE),
      22             : 
      23             :     // @Param: PUMP_RATE
      24             :     // @DisplayName: Pump speed
      25             :     // @Description: Desired pump speed when travelling 1m/s expressed as a percentage
      26             :     // @Units: %
      27             :     // @Range: 0 100
      28             :     // @User: Standard
      29             :     AP_GROUPINFO("PUMP_RATE",   1, AC_Sprayer, _pump_pct_1ms, AC_SPRAYER_DEFAULT_PUMP_RATE),
      30             : 
      31             :     // @Param: SPINNER
      32             :     // @DisplayName: Spinner rotation speed
      33             :     // @Description: Spinner's rotation speed in PWM (a higher rate will disperse the spray over a wider area horizontally)
      34             :     // @Units: ms
      35             :     // @Range: 1000 2000
      36             :     // @User: Standard
      37             :     AP_GROUPINFO("SPINNER",     2, AC_Sprayer, _spinner_pwm, AC_SPRAYER_DEFAULT_SPINNER_PWM),
      38             : 
      39             :     // @Param: SPEED_MIN
      40             :     // @DisplayName: Speed minimum
      41             :     // @Description: Speed minimum at which we will begin spraying
      42             :     // @Units: cm/s
      43             :     // @Range: 0 1000
      44             :     // @User: Standard
      45             :     AP_GROUPINFO("SPEED_MIN",   3, AC_Sprayer, _speed_min, AC_SPRAYER_DEFAULT_SPEED_MIN),
      46             : 
      47             :     // @Param: PUMP_MIN
      48             :     // @DisplayName: Pump speed minimum
      49             :     // @Description: Minimum pump speed expressed as a percentage
      50             :     // @Units: %
      51             :     // @Range: 0 100
      52             :     // @User: Standard
      53             :     AP_GROUPINFO("PUMP_MIN",   4, AC_Sprayer, _pump_min_pct, AC_SPRAYER_DEFAULT_PUMP_MIN),
      54             : 
      55             :     AP_GROUPEND
      56             : };
      57             : 
      58        1286 : AC_Sprayer::AC_Sprayer()
      59             : {
      60        1286 :     if (_singleton) {
      61             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
      62           0 :         AP_HAL::panic("Too many sprayers");
      63             : #endif
      64           0 :         return;
      65             :     }
      66        1286 :     _singleton = this;
      67             : 
      68        1286 :     AP_Param::setup_object_defaults(this, var_info);
      69             : 
      70             :     // check for silly parameter values
      71        1286 :     if (_pump_pct_1ms < 0.0f || _pump_pct_1ms > 100.0f) {
      72           0 :         _pump_pct_1ms.set_and_save(AC_SPRAYER_DEFAULT_PUMP_RATE);
      73             :     }
      74        1286 :     if (_spinner_pwm < 0) {
      75           0 :         _spinner_pwm.set_and_save(AC_SPRAYER_DEFAULT_SPINNER_PWM);
      76             :     }
      77             : 
      78             :     // To-Do: ensure that the pump and spinner servo channels are enabled
      79             : }
      80             : 
      81             : /*
      82             :  * Get the AP_Sprayer singleton
      83             :  */
      84             : AC_Sprayer *AC_Sprayer::_singleton;
      85          19 : AC_Sprayer *AC_Sprayer::get_singleton()
      86             : {
      87          19 :     return _singleton;
      88             : }
      89             : 
      90      267048 : void AC_Sprayer::run(const bool activate)
      91             : {
      92             :     // return immediately if no change
      93      267048 :     if (_flags.running == activate) {
      94      267036 :         return;
      95             :     }
      96             : 
      97             :     // set flag indicate whether spraying is permitted:
      98             :     // do not allow running to be set to true if we are currently not enabled
      99          12 :     _flags.running = _enabled && activate;
     100             : 
     101             :     // turn off the pump and spinner servos if necessary
     102          12 :     if (!_flags.running) {
     103           6 :         stop_spraying();
     104             :     }
     105             : }
     106             : 
     107         173 : void AC_Sprayer::stop_spraying()
     108             : {
     109         173 :     SRV_Channels::set_output_limit(SRV_Channel::k_sprayer_pump, SRV_Channel::Limit::MIN);
     110         173 :     SRV_Channels::set_output_limit(SRV_Channel::k_sprayer_spinner, SRV_Channel::Limit::MIN);
     111             : 
     112         173 :     _flags.spraying = false;
     113         173 : }
     114             : 
     115             : /// update - adjust pwm of servo controlling pump speed according to the desired quantity and our horizontal speed
     116      267333 : void AC_Sprayer::update()
     117             : {
     118             :     // exit immediately if we are disabled or shouldn't be running
     119      267333 :     if (!_enabled || !running()) {
     120      267029 :         run(false);
     121      267029 :         return;
     122             :     }
     123             : 
     124             :     // exit immediately if the pump function has not been set-up for any servo
     125         304 :     if (!SRV_Channels::function_assigned(SRV_Channel::k_sprayer_pump)) {
     126           0 :         return;
     127             :     }
     128             : 
     129             :     // get horizontal velocity
     130         304 :     Vector3f velocity;
     131         304 :     if (!AP::ahrs().get_velocity_NED(velocity)) {
     132             :         // treat unknown velocity as zero which should lead to pump stopping
     133             :         // velocity will already be zero but this avoids a coverity warning
     134           0 :         velocity.zero();
     135             :     }
     136             : 
     137         304 :     float ground_speed = velocity.xy().length() * 100.0;
     138             : 
     139             :     // get the current time
     140         304 :     const uint32_t now = AP_HAL::millis();
     141             : 
     142         304 :     bool should_be_spraying = _flags.spraying;
     143             :     // check our speed vs the minimum
     144         304 :     if (ground_speed >= _speed_min) {
     145             :         // if we are not already spraying
     146         138 :         if (!_flags.spraying) {
     147             :             // set the timer if this is the first time we've surpassed the min speed
     148          10 :             if (_speed_over_min_time == 0) {
     149           5 :                 _speed_over_min_time = now;
     150             :             }else{
     151             :                 // check if we've been over the speed long enough to engage the sprayer
     152           5 :                 if((now - _speed_over_min_time) > AC_SPRAYER_DEFAULT_TURN_ON_DELAY) {
     153           5 :                     should_be_spraying = true;
     154           5 :                     _speed_over_min_time = 0;
     155             :                 }
     156             :             }
     157             :         }
     158             :         // reset the speed under timer
     159         138 :         _speed_under_min_time = 0;
     160             :     } else {
     161             :         // we are under the min speed.
     162         166 :         if (_flags.spraying) {
     163             :             // set the timer if this is the first time we've dropped below the min speed
     164           5 :             if (_speed_under_min_time == 0) {
     165           1 :                 _speed_under_min_time = now;
     166             :             }else{
     167             :                 // check if we've been over the speed long enough to engage the sprayer
     168           4 :                 if((now - _speed_under_min_time) > AC_SPRAYER_DEFAULT_SHUT_OFF_DELAY) {
     169           1 :                     should_be_spraying = false;
     170           1 :                     _speed_under_min_time = 0;
     171             :                 }
     172             :             }
     173             :         }
     174             :         // reset the speed over timer
     175         166 :         _speed_over_min_time = 0;
     176             :     }
     177             : 
     178             :     // if testing pump output speed as if travelling at 1m/s
     179         304 :     if (_flags.testing) {
     180           0 :         ground_speed = 100.0f;
     181           0 :         should_be_spraying = true;
     182             :     }
     183             : 
     184             :     // if spraying or testing update the pump servo position
     185         304 :     if (should_be_spraying) {
     186         137 :         float pos = ground_speed * _pump_pct_1ms;
     187         137 :         pos = MAX(pos, 100 *_pump_min_pct); // ensure min pump speed
     188         137 :         pos = MIN(pos,10000); // clamp to range
     189         137 :         SRV_Channels::move_servo(SRV_Channel::k_sprayer_pump, pos, 0, 10000);
     190         137 :         SRV_Channels::set_output_pwm(SRV_Channel::k_sprayer_spinner, _spinner_pwm);
     191         137 :         _flags.spraying = true;
     192             :     } else {
     193         167 :         stop_spraying();
     194             :     }
     195             : }
     196             : 
     197             : namespace AP {
     198             : 
     199          19 : AC_Sprayer *sprayer()
     200             : {
     201          19 :     return AC_Sprayer::get_singleton();
     202             : }
     203             : 
     204             : };
     205             : #endif // HAL_SPRAYER_ENABLED

Generated by: LCOV version 1.14