2016-07-21 22:04:24 -03:00
/*
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "AP_Button.h"
2021-08-12 00:38:18 -03:00
# if HAL_BUTTON_ENABLED
2016-07-21 22:04:24 -03:00
# include <GCS_MAVLink/GCS_MAVLink.h>
# include <GCS_MAVLink/GCS.h>
2022-02-25 01:06:27 -04:00
# include <RC_Channel/RC_Channel.h>
2016-07-21 22:04:24 -03:00
2019-12-23 19:22:29 -04:00
// very crude debounce method
# define DEBOUNCE_MS 50
2016-07-21 22:04:24 -03:00
extern const AP_HAL : : HAL & hal ;
2019-11-25 09:14:52 -04:00
AP_Button * AP_Button : : _singleton ;
2016-07-21 22:04:24 -03:00
const AP_Param : : GroupInfo AP_Button : : var_info [ ] = {
// @Param: ENABLE
// @DisplayName: Enable button reporting
// @Description: This enables the button checking module. When this is disabled the parameters for setting button inputs are not visible
// @Values: 0:Disabled, 1:Enabled
// @User: Advanced
AP_GROUPINFO_FLAGS ( " ENABLE " , 0 , AP_Button , enable , 0 , AP_PARAM_FLAG_ENABLE ) ,
// @Param: PIN1
// @DisplayName: First button Pin
2022-04-22 10:14:44 -03:00
// @Description: Digital pin number for first button input. Some common values are given, but see the Wiki's "GPIOs" page for how to determine the pin number for a given autopilot.
2016-07-21 22:04:24 -03:00
// @User: Standard
2019-02-25 01:13:46 -04:00
// @Values: -1:Disabled,50:AUXOUT1,51:AUXOUT2,52:AUXOUT3,53:AUXOUT4,54:AUXOUT5,55:AUXOUT6
2016-07-21 22:04:24 -03:00
AP_GROUPINFO ( " PIN1 " , 1 , AP_Button , pin [ 0 ] , - 1 ) ,
// @Param: PIN2
// @DisplayName: Second button Pin
2022-04-22 10:14:44 -03:00
// @Description: Digital pin number for second button input. Some common values are given, but see the Wiki's "GPIOs" page for how to determine the pin number for a given autopilot.
2016-07-21 22:04:24 -03:00
// @User: Standard
2019-02-25 01:13:46 -04:00
// @Values: -1:Disabled,50:AUXOUT1,51:AUXOUT2,52:AUXOUT3,53:AUXOUT4,54:AUXOUT5,55:AUXOUT6
2016-07-21 22:04:24 -03:00
AP_GROUPINFO ( " PIN2 " , 2 , AP_Button , pin [ 1 ] , - 1 ) ,
// @Param: PIN3
// @DisplayName: Third button Pin
2022-04-22 10:14:44 -03:00
// @Description: Digital pin number for third button input. Some common values are given, but see the Wiki's "GPIOs" page for how to determine the pin number for a given autopilot.
2016-07-21 22:04:24 -03:00
// @User: Standard
2019-02-25 01:13:46 -04:00
// @Values: -1:Disabled,50:AUXOUT1,51:AUXOUT2,52:AUXOUT3,53:AUXOUT4,54:AUXOUT5,55:AUXOUT6
2016-07-21 22:04:24 -03:00
AP_GROUPINFO ( " PIN3 " , 3 , AP_Button , pin [ 2 ] , - 1 ) ,
// @Param: PIN4
// @DisplayName: Fourth button Pin
2022-04-22 10:14:44 -03:00
// @Description: Digital pin number for fourth button input. Some common values are given, but see the Wiki's "GPIOs" page for how to determine the pin number for a given autopilot.
2016-07-21 22:04:24 -03:00
// @User: Standard
2019-02-25 01:13:46 -04:00
// @Values: -1:Disabled,50:AUXOUT1,51:AUXOUT2,52:AUXOUT3,53:AUXOUT4,54:AUXOUT5,55:AUXOUT6
2016-07-21 22:04:24 -03:00
AP_GROUPINFO ( " PIN4 " , 4 , AP_Button , pin [ 3 ] , - 1 ) ,
// @Param: REPORT_SEND
// @DisplayName: Report send time
// @Description: The duration in seconds that a BUTTON_CHANGE report is repeatedly sent to the GCS regarding a button changing state. Note that the BUTTON_CHANGE message is MAVLink2 only.
// @User: Standard
// @Range: 0 3600
AP_GROUPINFO ( " REPORT_SEND " , 5 , AP_Button , report_send_time , 10 ) ,
2020-10-01 01:13:47 -03:00
// @Param: OPTIONS1
// @DisplayName: Button Pin 1 Options
2022-02-08 20:32:46 -04:00
// @Description: Options for Pin 1. PWM input detects PWM above or below 1800/1200us instead of logic level. If PWM is not detected or is less than 800us or above 2200us the button will interpreted as low. Invert changes HIGH state to be logic low voltage on pin, or below 1200us, if PWM input.
2020-10-01 01:13:47 -03:00
// @User: Standard
// @Bitmask: 0:PWM Input,1:InvertInput
AP_GROUPINFO ( " OPTIONS1 " , 6 , AP_Button , options [ 0 ] , 0 ) ,
// @Param: OPTIONS2
// @DisplayName: Button Pin 2 Options
2022-02-08 20:32:46 -04:00
// @Description: Options for Pin 2. PWM input detects PWM above or below 1800/1200us instead of logic level. If PWM is not detected or is less than 800us or above 2200us the button will interpreted as low. Invert changes HIGH state to be logic low voltage on pin, or below 1200us, if PWM input.
2020-10-01 01:13:47 -03:00
// @User: Standard
// @Bitmask: 0:PWM Input,1:InvertInput
AP_GROUPINFO ( " OPTIONS2 " , 7 , AP_Button , options [ 1 ] , 0 ) ,
// @Param: OPTIONS3
// @DisplayName: Button Pin 3 Options
2022-02-08 20:32:46 -04:00
// @Description: Options for Pin 3. PWM input detects PWM above or below 1800/1200us instead of logic level. If PWM is not detected or is less than 800us or above 2200us the button will interpreted as low. Invert changes HIGH state to be logic low voltage on pin, or below 1200us, if PWM input.
2020-10-01 01:13:47 -03:00
// @Bitmask: 0:PWM Input,1:InvertInput
AP_GROUPINFO ( " OPTIONS3 " , 8 , AP_Button , options [ 2 ] , 0 ) ,
// @Param: OPTIONS4
// @DisplayName: Button Pin 4 Options
2022-02-08 20:32:46 -04:00
// @Description: Options for Pin 4. PWM input detects PWM above or below 1800/1200us instead of logic level. If PWM is not detected or is less than 800us or above 2200us the button will interpreted as low. Invert changes HIGH state to be logic low voltage on pin, or below 1200us, if PWM input.
2020-10-01 01:13:47 -03:00
// @User: Standard
// @Bitmask: 0:PWM Input,1:InvertInput
AP_GROUPINFO ( " OPTIONS4 " , 9 , AP_Button , options [ 3 ] , 0 ) ,
2020-10-01 02:04:16 -03:00
// @Param: FUNC1
2022-12-29 22:10:57 -04:00
// @CopyFieldsFrom: RC1_OPTION
2020-10-01 02:04:16 -03:00
// @DisplayName: Button Pin 1 RC Channel function
2020-12-05 09:40:19 -04:00
// @Description: Auxiliary RC Options function executed on pin change
2020-10-01 02:04:16 -03:00
// @User: Standard
AP_GROUPINFO ( " FUNC1 " , 10 , AP_Button , pin_func [ 0 ] , ( uint16_t ) RC_Channel : : AUX_FUNC : : DO_NOTHING ) ,
// @Param: FUNC2
2022-12-29 22:10:57 -04:00
// @CopyFieldsFrom: BTN_FUNC1
2020-10-01 02:04:16 -03:00
// @DisplayName: Button Pin 2 RC Channel function
AP_GROUPINFO ( " FUNC2 " , 11 , AP_Button , pin_func [ 1 ] , ( uint16_t ) RC_Channel : : AUX_FUNC : : DO_NOTHING ) ,
// @Param: FUNC3
2022-12-29 22:10:57 -04:00
// @CopyFieldsFrom: BTN_FUNC1
2020-10-01 02:04:16 -03:00
// @DisplayName: Button Pin 3 RC Channel function
AP_GROUPINFO ( " FUNC3 " , 12 , AP_Button , pin_func [ 2 ] , ( uint16_t ) RC_Channel : : AUX_FUNC : : DO_NOTHING ) ,
// @Param: FUNC4
2022-12-29 22:10:57 -04:00
// @CopyFieldsFrom: BTN_FUNC1
2020-10-01 02:04:16 -03:00
// @DisplayName: Button Pin 4 RC Channel function
AP_GROUPINFO ( " FUNC4 " , 13 , AP_Button , pin_func [ 3 ] , ( uint16_t ) RC_Channel : : AUX_FUNC : : DO_NOTHING ) ,
2016-07-21 22:04:24 -03:00
AP_GROUPEND
} ;
// constructor
AP_Button : : AP_Button ( void )
{
AP_Param : : setup_object_defaults ( this , var_info ) ;
2019-11-25 09:14:52 -04:00
if ( _singleton ! = nullptr ) {
AP_HAL : : panic ( " AP_Button must be singleton " ) ;
}
_singleton = this ;
2016-07-21 22:04:24 -03:00
}
/*
update and report , called from main loop
*/
void AP_Button : : update ( void )
{
if ( ! enable ) {
return ;
}
// call setup pins at update rate (5Hz) to allow for runtime parameter change of pins
setup_pins ( ) ;
if ( ! initialised ) {
initialised = true ;
// get initial mask
last_mask = get_mask ( ) ;
2019-12-23 19:22:29 -04:00
debounce_mask = last_mask ;
2016-07-21 22:04:24 -03:00
// register 1kHz timer callback
hal . scheduler - > register_timer_process ( FUNCTOR_BIND_MEMBER ( & AP_Button : : timer_update , void ) ) ;
}
2020-10-01 01:13:47 -03:00
// act on any changes in state
{
WITH_SEMAPHORE ( last_debounced_change_ms_sem ) ;
if ( last_debounced_change_ms > last_debounce_ms ) {
last_debounce_ms = last_debounced_change_ms ;
}
}
// update the PWM state:
2020-10-01 02:04:16 -03:00
uint8_t new_pwm_state = pwm_state ;
2020-10-01 01:13:47 -03:00
for ( uint8_t i = 0 ; i < AP_BUTTON_NUM_PINS ; i + + ) {
2020-10-01 02:04:16 -03:00
const uint8_t mask = ( 1U < < i ) ;
2020-10-01 01:13:47 -03:00
if ( ! is_pwm_input ( i ) ) {
// not a PWM input
2020-10-01 02:04:16 -03:00
new_pwm_state & = ~ mask ;
2020-10-01 01:13:47 -03:00
continue ;
}
const uint16_t pwm_us = pwm_pin_source [ i ] . get_pwm_us ( ) ;
2022-02-08 20:32:46 -04:00
if ( pwm_us < RC_Channel : : RC_MIN_LIMIT_PWM | | pwm_us > RC_Channel : : RC_MAX_LIMIT_PWM ) {
// invalid pulse width, trigger low
if ( pwm_state & mask ) {
new_pwm_state & = ~ mask ;
}
continue ;
}
2020-10-01 01:13:47 -03:00
// these values are the same as used in RC_Channel:
2020-10-01 02:04:16 -03:00
if ( pwm_state & mask ) {
// currently asserted; check to see if we should de-assert
2021-02-03 13:21:28 -04:00
if ( pwm_us < RC_Channel : : AUX_SWITCH_PWM_TRIGGER_LOW ) {
2020-10-01 02:04:16 -03:00
new_pwm_state & = ~ mask ;
}
} else {
// currently not asserted; check to see if we should assert
2021-02-03 13:21:28 -04:00
if ( pwm_us > RC_Channel : : AUX_SWITCH_PWM_TRIGGER_HIGH ) {
2020-10-01 02:04:16 -03:00
new_pwm_state | = mask ;
}
}
2020-10-01 01:13:47 -03:00
}
2021-08-13 02:48:43 -03:00
const uint64_t now_ms = AP_HAL : : millis64 ( ) ;
2020-10-01 01:13:47 -03:00
if ( new_pwm_state ! = pwm_state ) {
2021-08-13 02:48:43 -03:00
if ( new_pwm_state ! = tentative_pwm_state ) {
tentative_pwm_state = new_pwm_state ;
pwm_start_debounce_ms = now_ms ;
} else if ( now_ms - pwm_start_debounce_ms > DEBOUNCE_MS ) {
pwm_state = new_pwm_state ;
last_debounce_ms = now_ms ;
}
} else {
tentative_pwm_state = pwm_state ;
pwm_start_debounce_ms = now_ms ;
2020-10-01 01:13:47 -03:00
}
2023-09-02 02:21:34 -03:00
# if HAL_GCS_ENABLED
2020-10-01 02:04:16 -03:00
if ( last_debounce_ms ! = 0 & &
2016-07-21 22:04:24 -03:00
( AP_HAL : : millis ( ) - last_report_ms ) > AP_BUTTON_REPORT_PERIOD_MS & &
2020-10-01 02:04:16 -03:00
( AP_HAL : : millis64 ( ) - last_debounce_ms ) < report_send_time * 1000ULL ) {
2016-07-21 22:04:24 -03:00
// send a change report
last_report_ms = AP_HAL : : millis ( ) ;
// send a report to GCS
send_report ( ) ;
}
2023-09-02 02:21:34 -03:00
# endif
2020-10-01 02:04:16 -03:00
if ( ! aux_functions_initialised ) {
run_aux_functions ( true ) ;
aux_functions_initialised = true ;
}
if ( last_debounce_ms ! = 0 & &
last_debounce_ms ! = last_action_time_ms ) {
last_action_time_ms = last_debounce_ms ;
run_aux_functions ( false ) ;
}
}
void AP_Button : : run_aux_functions ( bool force )
{
RC_Channel * rc_channel = rc ( ) . channel ( 1 ) ;
if ( rc_channel = = nullptr ) {
return ;
}
for ( uint8_t i = 0 ; i < AP_BUTTON_NUM_PINS ; i + + ) {
const RC_Channel : : AUX_FUNC func = RC_Channel : : AUX_FUNC ( pin_func [ i ] . get ( ) ) ;
if ( func = = RC_Channel : : AUX_FUNC : : DO_NOTHING ) {
continue ;
}
const uint8_t value_mask = ( 1U < < i ) ;
bool value ;
if ( is_pwm_input ( i ) ) {
value = ( pwm_state & value_mask ) ! = 0 ;
} else {
value = ( debounce_mask & value_mask ) ! = 0 ;
}
if ( is_input_inverted ( i ) ) {
value = ! value ;
}
const bool actioned = ( ( state_actioned_mask & value_mask ) ! = 0 ) ;
if ( ! force & & value = = actioned ) {
// no change on this pin
continue ;
}
// mark action as done:
if ( value ) {
state_actioned_mask | = value_mask ;
} else {
state_actioned_mask & = ~ value_mask ;
}
const RC_Channel : : AuxSwitchPos pos = value ? RC_Channel : : AuxSwitchPos : : HIGH : RC_Channel : : AuxSwitchPos : : LOW ;
// I wonder if we can do better here:
2022-10-06 08:00:15 -03:00
# if AP_RC_CHANNEL_AUX_FUNCTION_STRINGS_ENABLED
2020-10-01 02:04:16 -03:00
const char * str = rc_channel - > string_for_aux_function ( func ) ;
if ( str ! = nullptr ) {
2023-09-02 02:21:34 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_INFO , " Button %i: executing (%s %s) " , i + 1 , str , rc_channel - > string_for_aux_pos ( pos ) ) ;
2020-10-01 02:04:16 -03:00
}
# endif
2021-04-28 06:13:51 -03:00
rc_channel - > run_aux_function ( func , pos , RC_Channel : : AuxFuncTriggerSource : : BUTTON ) ;
2020-10-01 02:04:16 -03:00
}
2016-07-21 22:04:24 -03:00
}
2019-12-23 19:22:29 -04:00
// get state of a button
// used by scripting
bool AP_Button : : get_button_state ( uint8_t number )
{
// pins params are 1 indexed not zero
if ( number = = 0 | | number > AP_BUTTON_NUM_PINS ) {
return false ;
}
2020-10-01 01:13:47 -03:00
if ( is_pwm_input ( number - 1 ) ) {
return ( pwm_state & ( 1U < < ( number - 1 ) ) ) ;
}
2019-12-23 19:22:29 -04:00
return ( ( ( 1 < < ( number - 1 ) ) & debounce_mask ) ! = 0 ) ;
} ;
2016-07-21 22:04:24 -03:00
/*
get current mask
*/
uint8_t AP_Button : : get_mask ( void )
{
uint8_t mask = 0 ;
for ( uint8_t i = 0 ; i < AP_BUTTON_NUM_PINS ; i + + ) {
if ( pin [ i ] = = - 1 ) {
continue ;
}
2020-10-01 01:13:47 -03:00
if ( is_pwm_input ( i ) ) {
continue ;
}
2016-07-21 22:04:24 -03:00
mask | = hal . gpio - > read ( pin [ i ] ) < < i ;
}
2020-10-01 01:13:47 -03:00
2016-07-21 22:04:24 -03:00
return mask ;
}
/*
called at 1 kHz to check for button state change
*/
void AP_Button : : timer_update ( void )
{
if ( ! enable ) {
return ;
}
uint8_t mask = get_mask ( ) ;
2019-12-23 19:22:29 -04:00
uint64_t now = AP_HAL : : millis64 ( ) ;
2016-07-21 22:04:24 -03:00
if ( mask ! = last_mask ) {
last_mask = mask ;
2019-12-23 19:22:29 -04:00
last_change_time_ms = now ;
}
2020-10-01 01:13:47 -03:00
if ( debounce_mask ! = last_mask & &
( now - last_change_time_ms ) > DEBOUNCE_MS ) {
2019-12-23 19:22:29 -04:00
// crude de-bouncing, debounces all buttons as one, not individually
debounce_mask = last_mask ;
2020-10-01 02:04:16 -03:00
WITH_SEMAPHORE ( last_debounced_change_ms_sem ) ;
2020-10-01 01:13:47 -03:00
last_debounced_change_ms = now ;
2016-07-21 22:04:24 -03:00
}
}
2023-09-02 02:21:34 -03:00
# if HAL_GCS_ENABLED
2016-07-21 22:04:24 -03:00
/*
send a BUTTON_CHANGE report to the GCS
*/
2021-02-01 12:26:26 -04:00
void AP_Button : : send_report ( void ) const
2016-07-21 22:04:24 -03:00
{
2020-10-01 01:13:47 -03:00
const uint8_t mask = last_mask | pwm_state ;
2018-11-06 22:11:36 -04:00
const mavlink_button_change_t packet {
time_boot_ms : AP_HAL : : millis ( ) ,
2020-10-01 01:13:47 -03:00
last_change_ms : uint32_t ( last_debounce_ms ) ,
state : mask ,
2018-11-06 22:11:36 -04:00
} ;
gcs ( ) . send_to_active_channels ( MAVLINK_MSG_ID_BUTTON_CHANGE ,
( const char * ) & packet ) ;
2016-07-21 22:04:24 -03:00
}
2023-09-02 02:21:34 -03:00
# endif
2016-07-21 22:04:24 -03:00
/*
setup the pins as input with pullup . We need pullup to give reliable
input with a pulldown button
*/
void AP_Button : : setup_pins ( void )
{
for ( uint8_t i = 0 ; i < AP_BUTTON_NUM_PINS ; i + + ) {
2020-10-01 01:13:47 -03:00
if ( is_pwm_input ( i ) ) {
pwm_pin_source [ i ] . set_pin ( pin [ i ] , " Button " ) ;
continue ;
}
2016-07-21 22:04:24 -03:00
if ( pin [ i ] = = - 1 ) {
continue ;
}
2020-10-01 01:13:47 -03:00
2016-07-21 22:04:24 -03:00
hal . gpio - > pinMode ( pin [ i ] , HAL_GPIO_INPUT ) ;
// setup pullup
hal . gpio - > write ( pin [ i ] , 1 ) ;
}
}
2019-11-25 09:14:52 -04:00
2021-07-24 04:07:13 -03:00
// check settings are valid
bool AP_Button : : arming_checks ( size_t buflen , char * buffer ) const
{
if ( ! enable ) {
return true ;
}
for ( uint8_t i = 0 ; i < AP_BUTTON_NUM_PINS ; i + + ) {
if ( pin [ i ] ! = - 1 & & ! hal . gpio - > valid_pin ( pin [ i ] ) ) {
2022-04-19 01:24:28 -03:00
uint8_t servo_ch ;
if ( hal . gpio - > pin_to_servo_channel ( pin [ i ] , servo_ch ) ) {
hal . util - > snprintf ( buffer , buflen , " BTN_PIN%u=%d, set SERVO%u_FUNCTION=-1 " , unsigned ( i + 1 ) , int ( pin [ i ] . get ( ) ) , unsigned ( servo_ch + 1 ) ) ;
} else {
hal . util - > snprintf ( buffer , buflen , " BTN_PIN%u=%d invalid " , unsigned ( i + 1 ) , int ( pin [ i ] . get ( ) ) ) ;
}
2021-07-24 04:07:13 -03:00
return false ;
}
}
return true ;
}
2019-11-25 09:14:52 -04:00
namespace AP {
AP_Button & button ( )
{
return * AP_Button : : get_singleton ( ) ;
}
}
2021-08-12 00:38:18 -03:00
# endif