drivers/tone_alarm and tune_control small improvements/cleanup

- drivers/tone_alarm: move to ModuleBase and purge CDev (/dev/tone_alarm0)
 - drivers/tone_alarm: only run on tune_control publication (or scheduled note) rather than continuously
 - drivers/tone_alarm: use HRT to schedule tone stop (prevents potential disruption)
 - msg/tune_control: add tune_id numbering
 - systemcmds/tune_control: add "error" special case tune_id
 - move all tune_control publication to new uORB::PublicationQueued<>
 - start tone_alarm immediately after board defaults are loaded to fix potential startup issues
 - for SITL (or other boards with no TONE output) print common messages (startup, error, etc)
This commit is contained in:
Daniel Agar
2020-10-05 21:39:26 -04:00
committed by GitHub
parent c7072b61a3
commit 08bf71b73d
39 changed files with 429 additions and 577 deletions
+4 -51
View File
@@ -34,64 +34,16 @@
/**
* @file drv_tone_alarm.h
*
* Driver for the PX4 audio alarm port, /dev/tone_alarm.
* Driver for the PX4 audio alarm port.
*
* The tone_alarm driver supports a set of predefined "alarm"
* patterns and one user-supplied pattern. Patterns are ordered by
* priority, with a higher-priority pattern interrupting any
* lower-priority pattern that might be playing.
*
* The TONE_SET_ALARM ioctl can be used to select a predefined
* alarm pattern, from 1 - <TBD>. Selecting pattern zero silences
* the alarm.
*
* To supply a custom pattern, write an array of 1 - <TBD> tone_note
* structures to /dev/tone_alarm. The custom pattern has a priority
* of zero.
*
* Patterns will normally play once and then silence (if a pattern
* was overridden it will not resume). A pattern may be made to
* repeat by inserting a note with the duration set to
* DURATION_REPEAT. This pattern will loop until either a
* higher-priority pattern is started or pattern zero is requested
* via the ioctl.
*/
#pragma once
#include "drv_hrt.h"
#include <lib/tunes/tune_definition.h>
#include <uORB/topics/tune_control.h>
#define CBRK_BUZZER_KEY 782097
#define CBRK_OFF 0
#define CBRK_ON 1
#define CBRK_UNINIT 2
#define _TONE_ALARM_BASE 0x7400
#define TONE_SET_ALARM _PX4_IOC(_TONE_ALARM_BASE, 1)
#define TONE_ALARM0_DEVICE_PATH "/dev/tone_alarm0"
// TODO: remove this once the tone_alarm driver is changed to the new tunelib
enum {
TONE_STOP_TUNE = 0,
TONE_STARTUP_TUNE,
TONE_ERROR_TUNE,
TONE_NOTIFY_POSITIVE_TUNE,
TONE_NOTIFY_NEUTRAL_TUNE,
TONE_NOTIFY_NEGATIVE_TUNE,
TONE_ARMING_WARNING_TUNE,
TONE_BATTERY_WARNING_SLOW_TUNE,
TONE_BATTERY_WARNING_FAST_TUNE,
TONE_GPS_WARNING_TUNE,
TONE_ARMING_FAILURE_TUNE,
TONE_PARACHUTE_RELEASE_TUNE,
TONE_EKF_WARNING_TUNE,
TONE_BARO_WARNING_TUNE,
TONE_SINGLE_BEEP_TUNE,
TONE_HOME_SET,
TONE_NUMBER_OF_TUNES
};
namespace ToneAlarmInterface
{
@@ -102,8 +54,9 @@ void init();
/**
* @brief Starts playing the note.
* @return the time the note started played.
*/
void start_note(unsigned frequency);
hrt_abstime start_note(unsigned frequency);
/**
* @brief Stops playing the current note and makes the player 'safe'.
+5 -10
View File
@@ -74,6 +74,7 @@
#include <systemlib/err.h>
#include <lib/mixer/MixerGroup.hpp>
#include <uORB/Publication.hpp>
#include <uORB/topics/actuator_controls.h>
#include <uORB/topics/actuator_outputs.h>
#include <uORB/topics/actuator_armed.h>
@@ -161,7 +162,7 @@ private:
char _device[20];
orb_advert_t _t_outputs;
orb_advert_t _t_esc_status;
orb_advert_t _tune_control_sub;
uORB::PublicationQueued<tune_control_s> _tune_control_pub{ORB_ID(tune_control)};
unsigned int _num_outputs;
bool _primary_pwm_device;
bool _motortest;
@@ -448,14 +449,14 @@ MK::scaling(float val, float inMin, float inMax, float outMin, float outMax)
void
MK::play_beep(int count)
{
tune_control_s tune = {};
tune_control_s tune{};
tune.tune_id = static_cast<int>(TuneID::SINGLE_BEEP);
for (int i = 0; i < count; i++) {
orb_publish(ORB_ID(tune_control), _tune_control_sub, &tune);
tune.timestamp = hrt_absolute_time();
_tune_control_pub.publish(tune);
usleep(300000);
}
}
int
@@ -490,12 +491,6 @@ MK::task_main()
memset(&esc, 0, sizeof(esc));
_t_esc_status = orb_advertise(ORB_ID(esc_status), &esc);
/*
* advertise the tune_control.
*/
tune_control_s tune = {};
_tune_control_sub = orb_advertise_queue(ORB_ID(tune_control), &tune, tune_control_s::ORB_QUEUE_LENGTH);
pollfd fds[2];
fds[0].fd = _t_actuators;
fds[0].events = POLLIN;
+2 -6
View File
@@ -129,14 +129,11 @@ SafetyButton::CheckPairingRequest(bool button_pressed)
++_pairing_button_counter;
}
if (_pairing_button_counter == 3) {
vehicle_command_s vcmd{};
vcmd.command = vehicle_command_s::VEHICLE_CMD_START_RX_PAIR;
vcmd.timestamp = now;
vcmd.param1 = 10.f; // GCS pairing request handled by a companion.
vcmd.timestamp = hrt_absolute_time();
_to_command.publish(vcmd);
PX4_DEBUG("Sending GCS pairing request");
@@ -150,9 +147,8 @@ SafetyButton::CheckPairingRequest(bool button_pressed)
_to_led_control.publish(led_control);
tune_control_s tune_control{};
tune_control.tune_id = TONE_NOTIFY_POSITIVE_TUNE;
tune_control.tune_id = tune_control_s::TUNE_ID_NOTIFY_POSITIVE;
tune_control.volume = tune_control_s::VOLUME_LEVEL_DEFAULT;
tune_control.tune_override = 0;
tune_control.timestamp = hrt_absolute_time();
_to_tune_control.publish(tune_control);
@@ -77,7 +77,6 @@ private:
void FlashButton();
void CheckPairingRequest(bool button_pressed);
uORB::Subscription _armed_sub{ORB_ID(actuator_armed)};
uORB::Publication<safety_s> _to_safety{ORB_ID(safety)};
uORB::PublicationQueued<vehicle_command_s> _to_command{ORB_ID(vehicle_command)};
+1 -1
View File
@@ -7,7 +7,7 @@ serial_config:
then
mavlink start -d /dev/iridium -m iridium -b 115200
else
tune_control play -t 20
tune_control play error
fi
port_config_param:
name: ISBD_CONFIG
+4 -1
View File
@@ -1,6 +1,6 @@
############################################################################
#
# Copyright (C) 2015-2019 PX4 Development Team. All rights reserved.
# Copyright (C) 2015-2020 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@@ -34,8 +34,11 @@
px4_add_module(
MODULE drivers__tone_alarm
MAIN tone_alarm
COMPILE_FLAGS
#-DDEBUG_BUILD
SRCS
ToneAlarm.cpp
ToneAlarm.h
DEPENDS
circuit_breaker
arch_tone_alarm
+178 -145
View File
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (C) 2013-2019 PX4 Development Team. All rights reserved.
* Copyright (C) 2013-2020 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -38,195 +38,228 @@
#include "ToneAlarm.h"
#include <px4_platform_common/time.h>
#include <uORB/Publication.hpp>
using namespace time_literals;
ToneAlarm::ToneAlarm() :
CDev(TONE_ALARM0_DEVICE_PATH),
ScheduledWorkItem(MODULE_NAME, px4::wq_configurations::hp_default)
{
// ensure ORB_ID(tune_control) is advertised with correct queue depth
orb_advertise_queue(ORB_ID(tune_control), nullptr, tune_control_s::ORB_QUEUE_LENGTH);
}
ToneAlarm::~ToneAlarm()
{
_should_run = false;
int counter = 0;
while (_running && counter < 10) {
px4_usleep(100000);
counter++;
}
ToneAlarmInterface::stop_note();
}
int ToneAlarm::init()
bool ToneAlarm::Init()
{
if (CDev::init() != OK) {
return PX4_ERROR;
}
// NOTE: Implement hardware specific detail in the ToneAlarmInterface class implementation.
ToneAlarmInterface::init();
_running = true;
_tune_control_sub.set_interval_us(10_ms);
if (!_tune_control_sub.registerCallback()) {
PX4_ERR("tune_control callback registration failed!");
return PX4_ERROR;
}
ScheduleNow();
return OK;
return true;
}
void ToneAlarm::next_note()
void ToneAlarm::InterruptStopNote(void *arg)
{
if (!_should_run) {
_running = false;
return;
}
// Check for updates
orb_update();
unsigned int frequency = 0;
unsigned int duration = 0;
// Does an inter-note silence occur?
if (_silence_length > 0) {
stop_note();
duration = _silence_length;
_silence_length = 0;
} else if (_play_tone) {
int parse_ret_val = _tunes.get_next_note(frequency, duration, _silence_length);
if (parse_ret_val > 0) {
// Continue playing.
_play_tone = true;
// A frequency of 0 corresponds to stop_note();
if (frequency > 0) {
// Start playing the note.
start_note(frequency);
}
} else {
_play_tone = false;
stop_note();
}
} else {
// Schedule a callback with the tunes max interval.
duration = _tunes.get_maximum_update_interval();
stop_note();
}
// Schedule a callback when the note should stop.
ScheduleDelayed(duration);
ToneAlarmInterface::stop_note();
}
void ToneAlarm::Run()
{
next_note();
}
void ToneAlarm::orb_update()
{
// Check for updates
if (_tune_control_sub.updated()) {
_tune_control_sub.copy(&_tune);
if (_tune.timestamp > 0) {
_play_tone = _tunes.set_control(_tune) == 0;
}
}
}
void ToneAlarm::status()
{
if (_running) {
PX4_INFO("running");
} else {
PX4_INFO("stopped");
}
}
void ToneAlarm::start_note(unsigned frequency)
{
// Check if circuit breaker is enabled.
if (_cbrk == CBRK_UNINIT) {
_cbrk = circuit_breaker_enabled("CBRK_BUZZER", CBRK_BUZZER_KEY);
if (!_circuit_break_initialized) {
if (circuit_breaker_enabled("CBRK_BUZZER", CBRK_BUZZER_KEY)) {
request_stop();
}
_circuit_break_initialized = true;
}
if (_cbrk != CBRK_OFF) {
if (should_exit()) {
_tune_control_sub.unregisterCallback();
exit_and_cleanup();
return;
}
// NOTE: Implement hardware specific detail in the ToneAlarmInterface class implementation.
ToneAlarmInterface::start_note(frequency);
}
// Check for next tune_control when not currently playing
if (_tune_control_sub.updated()) {
tune_control_s tune_control;
void ToneAlarm::stop_note()
{
// NOTE: Implement hardware specific detail in the ToneAlarmInterface class implementation.
ToneAlarmInterface::stop_note();
}
if (_tune_control_sub.copy(&tune_control)) {
if (tune_control.timestamp > 0) {
if (!_play_tone || (_play_tone && tune_control.tune_override)) {
PX4_DEBUG("new tune %d", tune_control.tune_id);
/**
* Local functions in support of the shell command.
*/
namespace
{
if (_tunes.set_control(tune_control) == PX4_OK) {
if (tune_control.tune_override) {
// clear existing
ToneAlarmInterface::stop_note();
_next_note_time = 0;
hrt_cancel(&_hrt_call);
}
ToneAlarm *g_dev;
_play_tone = true;
} // namespace
#if (!defined(TONE_ALARM_TIMER) && !defined(GPIO_TONE_ALARM_GPIO)) || defined(DEBUG_BUILD)
/**
* Tone alarm Driver 'main' command.
* Entry point for the tone_alarm driver module.
*/
extern "C" __EXPORT int tone_alarm_main(int argc, char *argv[])
{
if (argc > 1) {
const char *argv1 = argv[1];
switch (tune_control.tune_id) {
case tune_control_s::TUNE_ID_STARTUP:
PX4_INFO("startup tune");
break;
if (!strcmp(argv1, "start")) {
if (g_dev == nullptr) {
g_dev = new ToneAlarm();
case tune_control_s::TUNE_ID_ERROR:
PX4_ERR("error tune");
break;
if (g_dev == nullptr) {
PX4_ERR("could not allocate the driver.");
case tune_control_s::TUNE_ID_NOTIFY_POSITIVE:
PX4_INFO("notify positive");
break;
case tune_control_s::TUNE_ID_NOTIFY_NEUTRAL:
PX4_INFO("notify neutral");
break;
case tune_control_s::TUNE_ID_NOTIFY_NEGATIVE:
PX4_ERR("notify negative");
break;
case tune_control_s::TUNE_ID_ARMING_WARNING:
PX4_WARN("arming warning");
break;
case tune_control_s::TUNE_ID_BATTERY_WARNING_SLOW:
PX4_WARN("battery warning (slow)");
break;
case tune_control_s::TUNE_ID_BATTERY_WARNING_FAST:
PX4_WARN("battery warning (fast)");
break;
case tune_control_s::TUNE_ID_ARMING_FAILURE:
PX4_ERR("arming failure");
break;
case tune_control_s::TUNE_ID_SINGLE_BEEP:
PX4_WARN("beep");
break;
case tune_control_s::TUNE_ID_HOME_SET:
PX4_INFO("home set");
break;
}
#endif // (!TONE_ALARM_TIMER && !GPIO_TONE_ALARM_GPIO) || DEBUG_BUILD
}
} else if (_play_tone && !tune_control.tune_override) {
// otherwise re-publish tune to process next
PX4_DEBUG("tune already playing, requeing tune: %d", tune_control.tune_id);
uORB::PublicationQueued<tune_control_s> tune_control_pub{ORB_ID(tune_control)};
tune_control.timestamp = hrt_absolute_time();
tune_control_pub.publish(tune_control);
}
}
}
}
if (g_dev->init() != OK) {
delete g_dev;
g_dev = nullptr;
PX4_ERR("driver init failed.");
}
unsigned int frequency = 0;
unsigned int duration = 0;
unsigned int silence_length = 0;
} else {
PX4_INFO("already started");
// Does an inter-note silence occur?
if ((_next_note_time != 0) && (hrt_absolute_time() < _next_note_time)) {
PX4_DEBUG("inter-note silence");
ScheduleAt(_next_note_time);
} else if (_play_tone && (_tunes.get_next_note(frequency, duration, silence_length) == Tunes::Status::Continue)) {
PX4_DEBUG("Play frequency: %d, duration: %d us, silence: %d us", frequency, duration, silence_length);
if (frequency > 0) {
// Start playing the note.
const hrt_abstime time_started = ToneAlarmInterface::start_note(frequency);
if (time_started > 0) {
// schedule stop with HRT
hrt_call_at(&_hrt_call, time_started + duration, (hrt_callout)&InterruptStopNote, this);
_next_note_time = time_started + duration + silence_length;
// schedule next note
ScheduleAt(_next_note_time);
}
return 0;
}
if (!strcmp(argv1, "stop")) {
delete g_dev;
g_dev = nullptr;
return 0;
}
if (!strcmp(argv1, "status")) {
if (g_dev != nullptr) {
g_dev->status();
} else {
PX4_INFO("driver stopped");
}
return 0;
} else {
// A frequency of 0 corresponds to ToneAlarmInterface::stop_note()
_next_note_time = hrt_absolute_time() + duration + silence_length;
ToneAlarmInterface::stop_note();
ScheduleAt(_next_note_time);
}
} else {
PX4_INFO("missing command, try 'start', status, 'stop'");
PX4_DEBUG("stopping");
ToneAlarmInterface::stop_note();
_play_tone = false;
_next_note_time = 0;
}
// if done playing and a new tune_control is still available re-schedule
if (!Scheduled() && _tune_control_sub.updated()) {
ScheduleDelayed(_tunes.get_maximum_update_interval());
}
}
int ToneAlarm::task_spawn(int argc, char *argv[])
{
ToneAlarm *instance = new ToneAlarm();
if (!instance) {
PX4_ERR("alloc failed");
return -1;
}
if (!instance->Init()) {
delete instance;
return PX4_ERROR;
}
_object.store(instance);
_task_id = task_id_is_work_queue;
return PX4_OK;
}
int ToneAlarm::print_usage(const char *reason)
{
if (reason) {
PX4_WARN("%s\n", reason);
}
PRINT_MODULE_DESCRIPTION(
R"DESCR_STR(
### Description
This module is responsible for the tone alarm.
)DESCR_STR");
PRINT_MODULE_USAGE_NAME("tone_alarm", "driver");
PRINT_MODULE_USAGE_COMMAND("start");
PRINT_MODULE_USAGE_DEFAULT_COMMANDS();
return 0;
};
}
extern "C" __EXPORT int tone_alarm_main(int argc, char *argv[])
{
return ToneAlarm::main(argc, argv);
}
+29 -58
View File
@@ -40,79 +40,50 @@
#pragma once
#include <circuit_breaker/circuit_breaker.h>
#include <drivers/device/device.h>
#include <drivers/drv_tone_alarm.h>
#include <lib/tunes/tunes.h>
#include <px4_platform_common/defines.h>
#include <px4_platform_common/px4_config.h>
#include <px4_platform_common/getopt.h>
#include <px4_platform_common/log.h>
#include <px4_platform_common/module.h>
#include <px4_platform_common/px4_work_queue/ScheduledWorkItem.hpp>
#include <lib/circuit_breaker/circuit_breaker.h>
#include <lib/tunes/tunes.h>
#include <uORB/Subscription.hpp>
#include <uORB/SubscriptionCallback.hpp>
#include <uORB/topics/tune_control.h>
#include <drivers/drv_tone_alarm.h>
#include <string.h>
#if !defined(UNUSED)
# define UNUSED(a) ((void)(a))
#endif
class ToneAlarm : public cdev::CDev, public px4::ScheduledWorkItem
class ToneAlarm : public ModuleBase<ToneAlarm>, public px4::ScheduledWorkItem
{
public:
ToneAlarm();
~ToneAlarm();
~ToneAlarm() override;
/**
* @brief Initializes the character device and hardware registers.
*/
int init() override;
/** @see ModuleBase */
static int task_spawn(int argc, char *argv[]);
/**
* @brief Prints the driver status to the console.
*/
void status();
/** @see ModuleBase */
static int custom_command(int argc, char *argv[]) { return 0; };
protected:
/**
* @brief Parses the next note out of the string and plays it.
*/
void next_note();
/**
* @brief Trampoline for the work queue.
*/
void Run() override;
/**
* @brief Updates the uORB topics for local subscribers.
*/
void orb_update();
/**
* @brief Starts playing the note.
*/
void start_note(unsigned frequency);
/**
* @brief Stops playing the current note and makes the player 'safe'.
*/
void stop_note();
volatile bool _running{false}; ///< Flag to indicate the current driver status.
int _cbrk{CBRK_UNINIT}; ///< If true, no audio output.
/** @see ModuleBase */
static int print_usage(const char *reason = nullptr);
private:
static void InterruptStopNote(void *arg);
volatile bool _should_run{true};
bool Init();
void Run() override;
hrt_call _hrt_call{};
Tunes _tunes;
hrt_abstime _next_note_time{0};
uORB::SubscriptionCallbackWorkItem _tune_control_sub{this, ORB_ID(tune_control)};
bool _circuit_break_initialized{false};
bool _play_tone{false};
unsigned int _silence_length{0}; ///< If nonzero, silence before next note.
uORB::Subscription _tune_control_sub{ORB_ID(tune_control)};
tune_control_s _tune{};
Tunes _tunes = Tunes();
};