AP_Notify: merge ToneAlarm files, use new AP_HAL tonealarm interface

This commit is contained in:
Jonathan Challinger 2018-07-24 18:30:05 -07:00 committed by Andrew Tridgell
parent f5f6220283
commit b31ddedfef
9 changed files with 473 additions and 449 deletions

View File

@ -23,9 +23,7 @@
#include "PCA9685LED_I2C.h" #include "PCA9685LED_I2C.h"
#include "OreoLED_PX4.h" #include "OreoLED_PX4.h"
#include "RCOutputRGBLed.h" #include "RCOutputRGBLed.h"
#include "ToneAlarm_Linux.h" #include "ToneAlarm.h"
#include "ToneAlarm_ChibiOS.h"
#include "ToneAlarm_PX4.h"
#include "ToshibaLED_I2C.h" #include "ToshibaLED_I2C.h"
#include "VRBoard_LED.h" #include "VRBoard_LED.h"
#include "DiscreteRGBLed.h" #include "DiscreteRGBLed.h"
@ -259,7 +257,7 @@ void AP_Notify::add_backends(void)
// Add noise making devices // Add noise making devices
#if CONFIG_HAL_BOARD == HAL_BOARD_PX4 || \ #if CONFIG_HAL_BOARD == HAL_BOARD_PX4 || \
CONFIG_HAL_BOARD == HAL_BOARD_VRBRAIN CONFIG_HAL_BOARD == HAL_BOARD_VRBRAIN
ADD_BACKEND(new ToneAlarm_PX4()); ADD_BACKEND(new ToneAlarm());
// ChibiOS noise makers // ChibiOS noise makers
#elif CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS #elif CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS
@ -267,7 +265,7 @@ void AP_Notify::add_backends(void)
ADD_BACKEND(new Buzzer()); ADD_BACKEND(new Buzzer());
#endif #endif
#ifdef HAL_PWM_ALARM #ifdef HAL_PWM_ALARM
ADD_BACKEND(new ToneAlarm_ChibiOS()); ADD_BACKEND(new ToneAlarm());
#endif #endif
// Linux noise makers // Linux noise makers
@ -286,7 +284,7 @@ void AP_Notify::add_backends(void)
ADD_BACKEND(new Buzzer()); ADD_BACKEND(new Buzzer());
#else // other linux #else // other linux
ADD_BACKEND(new ToneAlarm_Linux()); ADD_BACKEND(new ToneAlarm());
#endif #endif
// F4Light noise makers // F4Light noise makers

View File

@ -0,0 +1,274 @@
#include "MMLPlayer.h"
#include <ctype.h>
#include <math.h>
#include <AP_HAL/AP_HAL.h>
extern const AP_HAL::HAL& hal;
void MMLPlayer::update() {
// Check if note is over
if (_playing && AP_HAL::micros()-_note_start_us > _note_duration_us) {
next_action();
}
}
void MMLPlayer::play(const char* string) {
stop();
_string = string;
_next = 0;
_tempo = 120;
_default_note_length = 4;
_note_mode = MODE_NORMAL;
_octave = 4;
_volume = 255;
_silence_duration = 0;
_repeat = false;
_playing = true;
next_action();
}
void MMLPlayer::stop() {
_playing = false;
hal.util->toneAlarm_set_buzzer_tone(0,0);
}
void MMLPlayer::start_silence(float duration) {
_note_start_us = AP_HAL::micros();
_note_duration_us = duration*1e6;
hal.util->toneAlarm_set_buzzer_tone(0, 0);
}
void MMLPlayer::start_note(float duration, float frequency, float volume) {
_note_start_us = AP_HAL::micros();
_note_duration_us = duration*1e6;
hal.util->toneAlarm_set_buzzer_tone(frequency, volume);
}
char MMLPlayer::next_char() {
while (_string[_next] != '\0' && isspace(_string[_next])) {
_next++;
}
return toupper(_string[_next]);
}
uint8_t MMLPlayer::next_number() {
uint8_t ret = 0;
while (isdigit(next_char())) {
ret = (ret*10) + (next_char() - '0');
_next++;
}
return ret;
}
size_t MMLPlayer::next_dots() {
size_t ret = 0;
while (next_char() == '.') {
ret++;
_next++;
}
return ret;
}
float MMLPlayer::rest_duration(uint32_t rest_length, uint8_t dots) {
float whole_note_period = 240.0f / _tempo;
if (rest_length == 0) {
rest_length = 1;
}
float rest_period = whole_note_period/rest_length;
float dot_extension = rest_period/2;
while (dots--) {
rest_period += dot_extension;
dot_extension *= 0.5f;
}
return rest_period;
}
void MMLPlayer::next_action() {
if (_silence_duration > 0) {
start_silence(_silence_duration);
_silence_duration = 0;
return;
}
uint8_t note = 0;
uint8_t note_length;
while (note == 0) {
char c = next_char();
if (c == '\0') {
if (_repeat) {
play(_string);
} else {
stop();
}
return;
}
_next++;
switch(c) {
case 'V': {
_volume = next_number();
break;
}
case 'L': {
_default_note_length = next_number();
if (_default_note_length == 0) {
stop();
return;
}
break;
}
case 'O':
_octave = next_number();
if (_octave > 6) {
_octave = 6;
}
break;
case '<':
if (_octave > 0) {
_octave--;
}
break;
case '>':
if (_octave < 6) {
_octave++;
}
break;
case 'M':
c = next_char();
if (c == '\0') {
stop();
return;
}
_next++;
switch (c) {
case 'N':
_note_mode = MODE_NORMAL;
break;
case 'L':
_note_mode = MODE_LEGATO;
break;
case 'S':
_note_mode = MODE_STACCATO;
break;
case 'F':
_repeat = false;
break;
case 'B':
_repeat = true;
break;
default:
stop();
return;
}
break;
case 'R':
case 'P': {
uint8_t num = next_number();
uint8_t dots = next_dots();
start_silence(rest_duration(num, dots));
return;
}
case 'T':
_tempo = next_number();
if (_tempo < 32) {
stop();
return;
}
break;
case 'N':
note = next_number();
note_length = _default_note_length;
if (note > 84) {
stop();
return;
}
if (note == 0) {
uint8_t num = next_number();
uint8_t dots = next_dots();
start_silence(rest_duration(num, dots));
return;
}
break;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'G': {
uint8_t note_tab[] = {9,11,0,2,4,5,7};
note = note_tab[c-'A'] + (_octave*12) + 1;
c = next_char();
switch (c) {
case '#':
case '+':
if (note < 84) {
note++;
}
_next++;
break;
case '-':
if (note > 1) {
note--;
}
_next++;
break;
default:
break;
}
note_length = next_number();
if (note_length == 0) {
note_length = _default_note_length;
}
break;
}
default:
stop();
return;
}
}
// Avoid division by zero
if (_tempo == 0 || note_length == 0) {
stop();
return;
}
float note_period = 240.0f / (float)_tempo / (float)note_length;
switch (_note_mode) {
case MODE_NORMAL:
_silence_duration = note_period/8;
break;
case MODE_STACCATO:
_silence_duration = note_period/4;
break;
case MODE_LEGATO:
_silence_duration = 0;
break;
}
note_period -= _silence_duration;
float dot_extension = note_period/2;
uint8_t dots = next_dots();
while (dots--) {
note_period += dot_extension;
dot_extension *= 0.5f;
}
float note_frequency = 880.0f * expf(logf(2.0f) * ((int)note - 46) / 12.0f);
float note_volume = _volume/255.0f;
start_note(note_period, note_frequency, note_volume);
}

View File

@ -0,0 +1,50 @@
#pragma once
#include <stdint.h>
#include <stdlib.h>
class MMLPlayer {
public:
void update();
void play(const char* string);
void stop();
private:
bool _playing;
uint32_t _note_duration_us;
uint32_t _note_start_us;
const char* _string;
uint8_t _tempo;
uint8_t _default_note_length;
uint8_t _volume;
size_t _next;
uint8_t _octave;
float _silence_duration;
bool _repeat;
enum node_mode_t {
MODE_NORMAL,
MODE_LEGATO,
MODE_STACCATO
} _note_mode;
void start_silence(float duration);
void start_note(float duration, float frequency, float volume);
char next_char();
uint8_t next_number();
size_t next_dots();
float rest_duration(uint32_t rest_length, uint8_t dots);
// Called when the MML player should start the next action
void next_action();
};

View File

@ -1,120 +1,120 @@
/* /*
ToneAlarm PX4 driver * ToneAlarm ChibiOS driver
*/ */
/* /*
This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. * (at your option) any later version.
*
This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. * GNU General Public License for more details.
*
You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <AP_HAL/AP_HAL.h> #include <AP_HAL/AP_HAL.h>
#if CONFIG_HAL_BOARD == HAL_BOARD_PX4 || CONFIG_HAL_BOARD == HAL_BOARD_VRBRAIN #ifdef HAL_PWM_ALARM
#include "ToneAlarm_PX4.h" #include "ToneAlarm.h"
#include "AP_Notify.h" #include "AP_Notify.h"
#include <sys/types.h> // #include <sys/types.h>
#include <sys/stat.h> // #include <sys/stat.h>
#include <fcntl.h> // #include <unistd.h>
#include <unistd.h>
#include <string.h> #include <string.h>
#include <drivers/drv_tone_alarm.h>
#include <stdio.h> #include <stdio.h>
#include <errno.h>
extern const AP_HAL::HAL& hal; extern const AP_HAL::HAL& hal;
const ToneAlarm_PX4::Tone ToneAlarm_PX4::_tones[] { const ToneAlarm::Tone ToneAlarm::_tones[] {
#define AP_NOTIFY_PX4_TONE_QUIET_NEG_FEEDBACK 0 #define AP_NOTIFY_TONE_QUIET_NEG_FEEDBACK 0
{ "MFT200L4<<<B#A#2", false }, { "MFT200L4<<<B#A#2", false },
#define AP_NOTIFY_PX4_TONE_LOUD_NEG_FEEDBACK 1 #define AP_NOTIFY_TONE_LOUD_NEG_FEEDBACK 1
{ "MFT100L4>B#A#2P8B#A#2", false }, { "MFT100L4>B#A#2P8B#A#2", false },
#define AP_NOTIFY_PX4_TONE_QUIET_NEU_FEEDBACK 2 #define AP_NOTIFY_TONE_QUIET_NEU_FEEDBACK 2
{ "MFT200L4<B#", false }, { "MFT200L4<B#", false },
#define AP_NOTIFY_PX4_TONE_LOUD_NEU_FEEDBACK 3 #define AP_NOTIFY_TONE_LOUD_NEU_FEEDBACK 3
{ "MFT100L4>B#", false }, { "MFT100L4>B#", false },
#define AP_NOTIFY_PX4_TONE_QUIET_POS_FEEDBACK 4 #define AP_NOTIFY_TONE_QUIET_POS_FEEDBACK 4
{ "MFT200L4<A#B#", false }, { "MFT200L4<A#B#", false },
#define AP_NOTIFY_PX4_TONE_LOUD_POS_FEEDBACK 5 #define AP_NOTIFY_TONE_LOUD_POS_FEEDBACK 5
{ "MFT100L4>A#B#", false }, { "MFT100L4>A#B#", false },
#define AP_NOTIFY_PX4_TONE_LOUD_READY_OR_FINISHED 6 #define AP_NOTIFY_TONE_LOUD_READY_OR_FINISHED 6
{ "MFT100L4>G#6A#6B#4", false }, { "MFT100L4>G#6A#6B#4", false },
#define AP_NOTIFY_PX4_TONE_QUIET_READY_OR_FINISHED 7 #define AP_NOTIFY_TONE_QUIET_READY_OR_FINISHED 7
{ "MFT200L4<G#6A#6B#4", false }, { "MFT200L4<G#6A#6B#4", false },
#define AP_NOTIFY_PX4_TONE_LOUD_ATTENTION_NEEDED 8 #define AP_NOTIFY_TONE_LOUD_ATTENTION_NEEDED 8
{ "MFT100L4>A#A#A#A#", false }, { "MFT100L4>A#A#A#A#", false },
#define AP_NOTIFY_PX4_TONE_QUIET_ARMING_WARNING 9 #define AP_NOTIFY_TONE_QUIET_ARMING_WARNING 9
{ "MNT75L1O2G", false }, { "MNT75L1O2G", false },
#define AP_NOTIFY_PX4_TONE_LOUD_WP_COMPLETE 10 #define AP_NOTIFY_TONE_LOUD_WP_COMPLETE 10
{ "MFT200L8G>C3", false }, { "MFT200L8G>C3", false },
#define AP_NOTIFY_PX4_TONE_LOUD_LAND_WARNING_CTS 11 #define AP_NOTIFY_TONE_LOUD_LAND_WARNING_CTS 11
{ "MBT200L2A-G-A-G-A-G-", true }, { "MBT200L2A-G-A-G-A-G-", true },
#define AP_NOTIFY_PX4_TONE_LOUD_VEHICLE_LOST_CTS 12 #define AP_NOTIFY_TONE_LOUD_VEHICLE_LOST_CTS 12
{ "MBT200>A#1", true }, { "MBT200>A#1", true },
#define AP_NOTIFY_PX4_TONE_LOUD_BATTERY_ALERT_CTS 13 #define AP_NOTIFY_TONE_LOUD_BATTERY_ALERT_CTS 13
{ "MBNT255>A#8A#8A#8A#8A#8A#8A#8A#8A#8A#8A#8A#8A#8A#8A#8A#8", true }, { "MBNT255>A#8A#8A#8A#8A#8A#8A#8A#8A#8A#8A#8A#8A#8A#8A#8A#8", true },
#define AP_NOTIFY_PX4_TONE_QUIET_COMPASS_CALIBRATING_CTS 14 #define AP_NOTIFY_TONE_QUIET_COMPASS_CALIBRATING_CTS 14
{ "MBNT255<C16P2", true }, { "MBNT255<C16P2", true },
#define AP_NOTIFY_PX4_TONE_WAITING_FOR_THROW 15 #define AP_NOTIFY_TONE_WAITING_FOR_THROW 15
{ "MBNT90L4O2A#O3DFN0N0N0", true}, { "MBNT90L4O2A#O3DFN0N0N0", true},
#define AP_NOTIFY_PX4_TONE_LOUD_1 16 #define AP_NOTIFY_TONE_LOUD_1 16
{ "MFT100L8>B", false}, { "MFT100L8>B", false},
#define AP_NOTIFY_PX4_TONE_LOUD_2 17 #define AP_NOTIFY_TONE_LOUD_2 17
{ "MFT100L8>BB", false}, { "MFT100L8>BB", false},
#define AP_NOTIFY_PX4_TONE_LOUD_3 18 #define AP_NOTIFY_TONE_LOUD_3 18
{ "MFT100L8>BBB", false}, { "MFT100L8>BBB", false},
#define AP_NOTIFY_PX4_TONE_LOUD_4 19 #define AP_NOTIFY_TONE_LOUD_4 19
{ "MFT100L8>BBBB", false}, { "MFT100L8>BBBB", false},
#define AP_NOTIFY_PX4_TONE_LOUD_5 20 #define AP_NOTIFY_TONE_LOUD_5 20
{ "MFT100L8>BBBBB", false}, { "MFT100L8>BBBBB", false},
#define AP_NOTIFY_PX4_TONE_LOUD_6 21 #define AP_NOTIFY_TONE_LOUD_6 21
{ "MFT100L8>BBBBBB", false}, { "MFT100L8>BBBBBB", false},
#define AP_NOTIFY_PX4_TONE_LOUD_7 22 #define AP_NOTIFY_TONE_LOUD_7 22
{ "MFT100L8>BBBBBBB", false}, { "MFT100L8>BBBBBBB", false},
#define AP_NOTIFY_PX4_TONE_TUNING_START 23 #define AP_NOTIFY_TONE_TUNING_START 23
{ "MFT100L20>C#D#", false}, { "MFT100L20>C#D#", false},
#define AP_NOTIFY_PX4_TONE_TUNING_SAVE 24 #define AP_NOTIFY_TONE_TUNING_SAVE 24
{ "MFT100L10DBDB>", false}, { "MFT100L10DBDB>", false},
#define AP_NOTIFY_PX4_TONE_TUNING_ERROR 25 #define AP_NOTIFY_TONE_TUNING_ERROR 25
{ "MFT100L10>BBBBBBBB", false}, { "MFT100L10>BBBBBBBB", false},
#define AP_NOTIFY_PX4_TONE_LEAK_DETECTED 26 #define AP_NOTIFY_TONE_LEAK_DETECTED 26
{ "MBT255L8>A+AA-", true}, { "MBT255L8>A+AA-", true},
#define AP_NOTIFY_PX4_TONE_QUIET_SHUTDOWN 27 #define AP_NOTIFY_TONE_QUIET_SHUTDOWN 27
{ "MFMST200L32O3ceP32cdP32ceP32c<c>c<cccP8L32>c>c<P32<c<c", false }, { "MFMST200L32O3ceP32cdP32ceP32c<c>c<cccP8L32>c>c<P32<c<c", false },
#define AP_NOTIFY_PX4_TONE_QUIET_NOT_READY_OR_NOT_FINISHED 28 #define AP_NOTIFY_TONE_QUIET_NOT_READY_OR_NOT_FINISHED 28
{ "MFT200L4<B#4A#6G#6", false }, { "MFT200L4<B#4A#6G#6", false },
#define AP_NOTIFY_TONE_STARTUP 29
{ "MFT240L8O4aO5dcO4aO5dcO4aO5dcL16dcdcdcdc", false },
}; };
bool ToneAlarm_PX4::init() bool ToneAlarm::init()
{ {
// open the tone alarm device if (!hal.util->toneAlarm_init()) {
_tonealarm_fd = open(TONEALARM0_DEVICE_PATH, O_WRONLY);
if (_tonealarm_fd == -1) {
hal.console->printf("ToneAlarm_PX4: Unable to open " TONEALARM0_DEVICE_PATH);
return false; return false;
} }
_sem = hal.util->new_semaphore();
// set initial boot states. This prevents us issuing a arming // set initial boot states. This prevents us issuing a arming
// warning in plane and rover on every boot // warning in plane and rover on every boot
flags.armed = AP_Notify::flags.armed; flags.armed = AP_Notify::flags.armed;
flags.failsafe_battery = AP_Notify::flags.failsafe_battery; flags.failsafe_battery = AP_Notify::flags.failsafe_battery;
flags.pre_arm_check = 1; flags.pre_arm_check = 1;
_cont_tone_playing = -1; _cont_tone_playing = -1;
hal.scheduler->register_timer_process(FUNCTOR_BIND(this, &ToneAlarm::_timer_task, void));
play_tone(AP_NOTIFY_TONE_STARTUP);
return true; return true;
} }
// play_tune - play one of the pre-defined tunes // play_tune - play one of the pre-defined tunes
void ToneAlarm_PX4::play_tone(const uint8_t tone_index) void ToneAlarm::play_tone(const uint8_t tone_index)
{ {
uint32_t tnow_ms = AP_HAL::millis(); uint32_t tnow_ms = AP_HAL::millis();
const Tone &tone_requested = _tones[tone_index]; const Tone &tone_requested = _tones[tone_index];
@ -129,11 +129,26 @@ void ToneAlarm_PX4::play_tone(const uint8_t tone_index)
play_string(tone_requested.str); play_string(tone_requested.str);
} }
void ToneAlarm_PX4::play_string(const char *str) { void ToneAlarm::_timer_task() {
write(_tonealarm_fd, str, strlen(str) + 1); if (_sem) {
_sem->take(HAL_SEMAPHORE_BLOCK_FOREVER);
_mml_player.update();
_sem->give();
}
} }
void ToneAlarm_PX4::stop_cont_tone() { void ToneAlarm::play_string(const char *str) {
if (_sem) {
_sem->take(HAL_SEMAPHORE_BLOCK_FOREVER);
_mml_player.stop();
strncpy(_tone_buf, str, AP_NOTIFY_TONEALARM_TONE_BUF_SIZE);
_tone_buf[AP_NOTIFY_TONEALARM_TONE_BUF_SIZE-1] = 0;
_mml_player.play(_tone_buf);
_sem->give();
}
}
void ToneAlarm::stop_cont_tone() {
if(_cont_tone_playing == _tone_playing) { if(_cont_tone_playing == _tone_playing) {
play_string(""); play_string("");
_tone_playing = -1; _tone_playing = -1;
@ -141,7 +156,7 @@ void ToneAlarm_PX4::stop_cont_tone() {
_cont_tone_playing = -1; _cont_tone_playing = -1;
} }
void ToneAlarm_PX4::check_cont_tone() { void ToneAlarm::check_cont_tone() {
uint32_t tnow_ms = AP_HAL::millis(); uint32_t tnow_ms = AP_HAL::millis();
// if we are supposed to be playing a continuous tone, // if we are supposed to be playing a continuous tone,
// and it was interrupted, and the interrupting tone has timed out, // and it was interrupted, and the interrupting tone has timed out,
@ -153,7 +168,7 @@ void ToneAlarm_PX4::check_cont_tone() {
} }
// update - updates led according to timed_updated. Should be called at 50Hz // update - updates led according to timed_updated. Should be called at 50Hz
void ToneAlarm_PX4::update() void ToneAlarm::update()
{ {
// exit if buzzer is not enabled // exit if buzzer is not enabled
if (pNotify->buzzer_enabled() == false) { if (pNotify->buzzer_enabled() == false) {
@ -161,10 +176,10 @@ void ToneAlarm_PX4::update()
} }
check_cont_tone(); check_cont_tone();
if (AP_Notify::flags.powering_off) { if (AP_Notify::flags.powering_off) {
if (!flags.powering_off) { if (!flags.powering_off) {
play_tone(AP_NOTIFY_PX4_TONE_QUIET_SHUTDOWN); play_tone(AP_NOTIFY_TONE_QUIET_SHUTDOWN);
} }
flags.powering_off = AP_Notify::flags.powering_off; flags.powering_off = AP_Notify::flags.powering_off;
return; return;
@ -172,10 +187,10 @@ void ToneAlarm_PX4::update()
if (AP_Notify::flags.compass_cal_running != flags.compass_cal_running) { if (AP_Notify::flags.compass_cal_running != flags.compass_cal_running) {
if(AP_Notify::flags.compass_cal_running) { if(AP_Notify::flags.compass_cal_running) {
play_tone(AP_NOTIFY_PX4_TONE_QUIET_COMPASS_CALIBRATING_CTS); play_tone(AP_NOTIFY_TONE_QUIET_COMPASS_CALIBRATING_CTS);
play_tone(AP_NOTIFY_PX4_TONE_QUIET_POS_FEEDBACK); play_tone(AP_NOTIFY_TONE_QUIET_POS_FEEDBACK);
} else { } else {
if(_cont_tone_playing == AP_NOTIFY_PX4_TONE_QUIET_COMPASS_CALIBRATING_CTS) { if(_cont_tone_playing == AP_NOTIFY_TONE_QUIET_COMPASS_CALIBRATING_CTS) {
stop_cont_tone(); stop_cont_tone();
} }
} }
@ -183,22 +198,22 @@ void ToneAlarm_PX4::update()
flags.compass_cal_running = AP_Notify::flags.compass_cal_running; flags.compass_cal_running = AP_Notify::flags.compass_cal_running;
if (AP_Notify::events.compass_cal_canceled) { if (AP_Notify::events.compass_cal_canceled) {
play_tone(AP_NOTIFY_PX4_TONE_QUIET_NEU_FEEDBACK); play_tone(AP_NOTIFY_TONE_QUIET_NEU_FEEDBACK);
return; return;
} }
if (AP_Notify::events.initiated_compass_cal) { if (AP_Notify::events.initiated_compass_cal) {
play_tone(AP_NOTIFY_PX4_TONE_QUIET_NEU_FEEDBACK); play_tone(AP_NOTIFY_TONE_QUIET_NEU_FEEDBACK);
return; return;
} }
if (AP_Notify::events.compass_cal_saved) { if (AP_Notify::events.compass_cal_saved) {
play_tone(AP_NOTIFY_PX4_TONE_QUIET_READY_OR_FINISHED); play_tone(AP_NOTIFY_TONE_QUIET_READY_OR_FINISHED);
return; return;
} }
if (AP_Notify::events.compass_cal_failed) { if (AP_Notify::events.compass_cal_failed) {
play_tone(AP_NOTIFY_PX4_TONE_QUIET_NEG_FEEDBACK); play_tone(AP_NOTIFY_TONE_QUIET_NEG_FEEDBACK);
return; return;
} }
@ -209,45 +224,45 @@ void ToneAlarm_PX4::update()
// notify the user when autotune or mission completes // notify the user when autotune or mission completes
if (AP_Notify::flags.armed && (AP_Notify::events.autotune_complete || AP_Notify::events.mission_complete)) { if (AP_Notify::flags.armed && (AP_Notify::events.autotune_complete || AP_Notify::events.mission_complete)) {
play_tone(AP_NOTIFY_PX4_TONE_LOUD_READY_OR_FINISHED); play_tone(AP_NOTIFY_TONE_LOUD_READY_OR_FINISHED);
} }
//notify the user when autotune fails //notify the user when autotune fails
if (AP_Notify::flags.armed && (AP_Notify::events.autotune_failed)) { if (AP_Notify::flags.armed && (AP_Notify::events.autotune_failed)) {
play_tone(AP_NOTIFY_PX4_TONE_LOUD_NEG_FEEDBACK); play_tone(AP_NOTIFY_TONE_LOUD_NEG_FEEDBACK);
} }
// notify the user when a waypoint completes // notify the user when a waypoint completes
if (AP_Notify::events.waypoint_complete) { if (AP_Notify::events.waypoint_complete) {
play_tone(AP_NOTIFY_PX4_TONE_LOUD_WP_COMPLETE); play_tone(AP_NOTIFY_TONE_LOUD_WP_COMPLETE);
} }
// notify the user when their mode change was successful // notify the user when their mode change was successful
if (AP_Notify::events.user_mode_change) { if (AP_Notify::events.user_mode_change) {
if (AP_Notify::flags.armed) { if (AP_Notify::flags.armed) {
play_tone(AP_NOTIFY_PX4_TONE_LOUD_NEU_FEEDBACK); play_tone(AP_NOTIFY_TONE_LOUD_NEU_FEEDBACK);
} else { } else {
play_tone(AP_NOTIFY_PX4_TONE_QUIET_NEU_FEEDBACK); play_tone(AP_NOTIFY_TONE_QUIET_NEU_FEEDBACK);
} }
} }
// notify the user when their mode change failed // notify the user when their mode change failed
if (AP_Notify::events.user_mode_change_failed) { if (AP_Notify::events.user_mode_change_failed) {
if (AP_Notify::flags.armed) { if (AP_Notify::flags.armed) {
play_tone(AP_NOTIFY_PX4_TONE_LOUD_NEG_FEEDBACK); play_tone(AP_NOTIFY_TONE_LOUD_NEG_FEEDBACK);
} else { } else {
play_tone(AP_NOTIFY_PX4_TONE_QUIET_NEG_FEEDBACK); play_tone(AP_NOTIFY_TONE_QUIET_NEG_FEEDBACK);
} }
} }
// failsafe initiated mode change // failsafe initiated mode change
if(AP_Notify::events.failsafe_mode_change) { if(AP_Notify::events.failsafe_mode_change) {
play_tone(AP_NOTIFY_PX4_TONE_LOUD_ATTENTION_NEEDED); play_tone(AP_NOTIFY_TONE_LOUD_ATTENTION_NEEDED);
} }
// notify the user when arming fails // notify the user when arming fails
if (AP_Notify::events.arming_failed) { if (AP_Notify::events.arming_failed) {
play_tone(AP_NOTIFY_PX4_TONE_QUIET_NEG_FEEDBACK); play_tone(AP_NOTIFY_TONE_QUIET_NEG_FEEDBACK);
} }
// notify the user when RC contact is lost // notify the user when RC contact is lost
@ -256,13 +271,13 @@ void ToneAlarm_PX4::update()
if (flags.failsafe_radio) { if (flags.failsafe_radio) {
// armed case handled by events.failsafe_mode_change // armed case handled by events.failsafe_mode_change
if (!AP_Notify::flags.armed) { if (!AP_Notify::flags.armed) {
play_tone(AP_NOTIFY_PX4_TONE_QUIET_NEG_FEEDBACK); play_tone(AP_NOTIFY_TONE_QUIET_NEG_FEEDBACK);
} }
} else { } else {
if (AP_Notify::flags.armed) { if (AP_Notify::flags.armed) {
play_tone(AP_NOTIFY_PX4_TONE_LOUD_POS_FEEDBACK); play_tone(AP_NOTIFY_TONE_LOUD_POS_FEEDBACK);
} else { } else {
play_tone(AP_NOTIFY_PX4_TONE_QUIET_POS_FEEDBACK); play_tone(AP_NOTIFY_TONE_QUIET_POS_FEEDBACK);
} }
} }
} }
@ -271,12 +286,12 @@ void ToneAlarm_PX4::update()
if (flags.pre_arm_check != AP_Notify::flags.pre_arm_check) { if (flags.pre_arm_check != AP_Notify::flags.pre_arm_check) {
flags.pre_arm_check = AP_Notify::flags.pre_arm_check; flags.pre_arm_check = AP_Notify::flags.pre_arm_check;
if (flags.pre_arm_check) { if (flags.pre_arm_check) {
play_tone(AP_NOTIFY_PX4_TONE_QUIET_READY_OR_FINISHED); play_tone(AP_NOTIFY_TONE_QUIET_READY_OR_FINISHED);
_have_played_ready_tone = true; _have_played_ready_tone = true;
} else { } else {
// only play sad tone if we've ever played happy tone: // only play sad tone if we've ever played happy tone:
if (_have_played_ready_tone) { if (_have_played_ready_tone) {
play_tone(AP_NOTIFY_PX4_TONE_QUIET_NOT_READY_OR_NOT_FINISHED); play_tone(AP_NOTIFY_TONE_QUIET_NOT_READY_OR_NOT_FINISHED);
} }
} }
} }
@ -286,12 +301,12 @@ void ToneAlarm_PX4::update()
flags.armed = AP_Notify::flags.armed; flags.armed = AP_Notify::flags.armed;
if (flags.armed) { if (flags.armed) {
// arming tune // arming tune
play_tone(AP_NOTIFY_PX4_TONE_QUIET_ARMING_WARNING); play_tone(AP_NOTIFY_TONE_QUIET_ARMING_WARNING);
}else{ }else{
// disarming tune // disarming tune
play_tone(AP_NOTIFY_PX4_TONE_QUIET_NEU_FEEDBACK); play_tone(AP_NOTIFY_TONE_QUIET_NEU_FEEDBACK);
if (!flags.leak_detected) { if (!flags.leak_detected) {
stop_cont_tone(); stop_cont_tone();
} }
} }
} }
@ -301,7 +316,7 @@ void ToneAlarm_PX4::update()
flags.failsafe_battery = AP_Notify::flags.failsafe_battery; flags.failsafe_battery = AP_Notify::flags.failsafe_battery;
if (flags.failsafe_battery) { if (flags.failsafe_battery) {
// battery warning tune // battery warning tune
play_tone(AP_NOTIFY_PX4_TONE_LOUD_BATTERY_ALERT_CTS); play_tone(AP_NOTIFY_TONE_LOUD_BATTERY_ALERT_CTS);
} }
} }
@ -310,7 +325,7 @@ void ToneAlarm_PX4::update()
flags.parachute_release = AP_Notify::flags.parachute_release; flags.parachute_release = AP_Notify::flags.parachute_release;
if (flags.parachute_release) { if (flags.parachute_release) {
// parachute release warning tune // parachute release warning tune
play_tone(AP_NOTIFY_PX4_TONE_LOUD_ATTENTION_NEEDED); play_tone(AP_NOTIFY_TONE_LOUD_ATTENTION_NEEDED);
} }
} }
@ -318,7 +333,7 @@ void ToneAlarm_PX4::update()
if (flags.vehicle_lost != AP_Notify::flags.vehicle_lost) { if (flags.vehicle_lost != AP_Notify::flags.vehicle_lost) {
flags.vehicle_lost = AP_Notify::flags.vehicle_lost; flags.vehicle_lost = AP_Notify::flags.vehicle_lost;
if (flags.vehicle_lost) { if (flags.vehicle_lost) {
play_tone(AP_NOTIFY_PX4_TONE_LOUD_VEHICLE_LOST_CTS); play_tone(AP_NOTIFY_TONE_LOUD_VEHICLE_LOST_CTS);
} else { } else {
stop_cont_tone(); stop_cont_tone();
} }
@ -328,7 +343,7 @@ void ToneAlarm_PX4::update()
if (flags.waiting_for_throw != AP_Notify::flags.waiting_for_throw) { if (flags.waiting_for_throw != AP_Notify::flags.waiting_for_throw) {
flags.waiting_for_throw = AP_Notify::flags.waiting_for_throw; flags.waiting_for_throw = AP_Notify::flags.waiting_for_throw;
if (flags.waiting_for_throw) { if (flags.waiting_for_throw) {
play_tone(AP_NOTIFY_PX4_TONE_WAITING_FOR_THROW); play_tone(AP_NOTIFY_TONE_WAITING_FOR_THROW);
} else { } else {
stop_cont_tone(); stop_cont_tone();
} }
@ -337,44 +352,44 @@ void ToneAlarm_PX4::update()
if (flags.leak_detected != AP_Notify::flags.leak_detected) { if (flags.leak_detected != AP_Notify::flags.leak_detected) {
flags.leak_detected = AP_Notify::flags.leak_detected; flags.leak_detected = AP_Notify::flags.leak_detected;
if (flags.leak_detected) { if (flags.leak_detected) {
play_tone(AP_NOTIFY_PX4_TONE_LEAK_DETECTED); play_tone(AP_NOTIFY_TONE_LEAK_DETECTED);
} else { } else {
stop_cont_tone(); stop_cont_tone();
} }
} }
if (AP_Notify::events.tune_started) { if (AP_Notify::events.tune_started) {
play_tone(AP_NOTIFY_PX4_TONE_TUNING_START); play_tone(AP_NOTIFY_TONE_TUNING_START);
AP_Notify::events.tune_started = 0; AP_Notify::events.tune_started = 0;
} }
if (AP_Notify::events.tune_next) { if (AP_Notify::events.tune_next) {
// signify which parameter in the set is starting // signify which parameter in the set is starting
play_tone(AP_NOTIFY_PX4_TONE_LOUD_1 + (AP_Notify::events.tune_next-1)); play_tone(AP_NOTIFY_TONE_LOUD_1 + (AP_Notify::events.tune_next-1));
AP_Notify::events.tune_next = 0; AP_Notify::events.tune_next = 0;
} }
if (AP_Notify::events.tune_save) { if (AP_Notify::events.tune_save) {
play_tone(AP_NOTIFY_PX4_TONE_TUNING_SAVE); play_tone(AP_NOTIFY_TONE_TUNING_SAVE);
AP_Notify::events.tune_save = 0; AP_Notify::events.tune_save = 0;
} }
if (AP_Notify::events.tune_error) { if (AP_Notify::events.tune_error) {
play_tone(AP_NOTIFY_PX4_TONE_TUNING_ERROR); play_tone(AP_NOTIFY_TONE_TUNING_ERROR);
AP_Notify::events.tune_error = 0; AP_Notify::events.tune_error = 0;
} }
} }
/* /*
handle a PLAY_TUNE message * handle a PLAY_TUNE message
*/ */
void ToneAlarm_PX4::handle_play_tune(mavlink_message_t *msg) void ToneAlarm::handle_play_tune(mavlink_message_t *msg)
{ {
// decode mavlink message // decode mavlink message
mavlink_play_tune_t packet; mavlink_play_tune_t packet;
mavlink_msg_play_tune_decode(msg, &packet); mavlink_msg_play_tune_decode(msg, &packet);
play_string(packet.tune); play_string(packet.tune);
} }
#endif // CONFIG_HAL_BOARD == HAL_BOARD_PX4 #endif // HAL_PWM_ALARM

View File

@ -1,28 +1,30 @@
/* /*
ToneAlarm PX4 driver * ToneAlarm PX4 driver
*/ */
/* /*
This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. * (at your option) any later version.
*
This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. * GNU General Public License for more details.
*
You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "NotifyDevice.h" #include "NotifyDevice.h"
#include "MMLPlayer.h"
// wait 2 seconds before assuming a tone is done and continuing the continuous tone // wait 2 seconds before assuming a tone is done and continuing the continuous tone
#define AP_NOTIFY_PX4_MAX_TONE_LENGTH_MS 2000 #define AP_NOTIFY_PX4_MAX_TONE_LENGTH_MS 2000
#define AP_NOTIFY_TONEALARM_TONE_BUF_SIZE 512
class ToneAlarm_PX4: public NotifyDevice class ToneAlarm: public NotifyDevice
{ {
public: public:
/// init - initialised the tone alarm /// init - initialised the tone alarm
@ -33,7 +35,7 @@ public:
// handle a PLAY_TUNE message // handle a PLAY_TUNE message
void handle_play_tune(mavlink_message_t *msg); void handle_play_tune(mavlink_message_t *msg);
private: private:
/// play_tune - play one of the pre-defined tunes /// play_tune - play one of the pre-defined tunes
void play_tone(const uint8_t tone_index); void play_tone(const uint8_t tone_index);
@ -47,7 +49,8 @@ private:
// check_cont_tone - check if we should begin playing a continuous tone // check_cont_tone - check if we should begin playing a continuous tone
void check_cont_tone(); void check_cont_tone();
int _tonealarm_fd; // file descriptor for the tone alarm // timer task - runs at 1khz
void _timer_task();
/// tonealarm_type - bitmask of states we track /// tonealarm_type - bitmask of states we track
struct tonealarm_type { struct tonealarm_type {
@ -74,4 +77,8 @@ private:
}; };
const static Tone _tones[]; const static Tone _tones[];
AP_HAL::Semaphore* _sem;
MMLPlayer _mml_player;
char _tone_buf[AP_NOTIFY_TONEALARM_TONE_BUF_SIZE];
}; };

View File

@ -1,128 +0,0 @@
/*
ToneAlarm Linux driver
*/
/*
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_HAL/AP_HAL.h>
#if CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS
#include "ToneAlarm_ChibiOS.h"
#include <AP_HAL_ChibiOS/ToneAlarm.h>
#include <AP_Math/AP_Math.h>
#include "AP_Notify.h"
extern const AP_HAL::HAL& hal;
bool ToneAlarm_ChibiOS::init()
{
// open the tone alarm device
bool _initialized = hal.util->toneAlarm_init();
if (!_initialized) {
hal.console->printf("AP_Notify: Failed to initialise ToneAlarm");
return false;
}
// set initial boot states. This prevents us issuing a arming
// warning in plane and rover on every boot
flags.armed = AP_Notify::flags.armed;
flags.failsafe_battery = AP_Notify::flags.failsafe_battery;
return true;
}
// play_tune - play one of the pre-defined tunes
bool ToneAlarm_ChibiOS::play_tune(uint8_t tune_number)
{
hal.util->toneAlarm_set_tune(tune_number);
return true;
}
// update - updates led according to timed_updated. Should be called at 50Hz
void ToneAlarm_ChibiOS::update()
{
// exit if buzzer is not enabled
if (pNotify->buzzer_enabled() == false) {
return;
}
// check for arming failure
if (AP_Notify::events.arming_failed) {
play_tune(TONE_ARMING_FAILURE_TUNE);
}
// check if arming status has changed
if (flags.armed != AP_Notify::flags.armed) {
flags.armed = AP_Notify::flags.armed;
if (flags.armed) {
// arming tune
play_tune(TONE_ARMING_WARNING_TUNE);
}else{
// disarming tune
play_tune(TONE_NOTIFY_NEUTRAL_TUNE);
}
}
// notify the user when their mode change was successful
if (AP_Notify::events.user_mode_change) {
if (AP_Notify::flags.armed) {
play_tune(TONE_NOTIFY_MODE_CHANGE_LOUD);
} else {
play_tune(TONE_NOTIFY_MODE_CHANGE_SOFT);
}
}
// check if battery status has changed
if (flags.failsafe_battery != AP_Notify::flags.failsafe_battery) {
flags.failsafe_battery = AP_Notify::flags.failsafe_battery;
if (flags.failsafe_battery) {
// low battery warning tune
play_tune(TONE_BATTERY_WARNING_FAST_TUNE);
}
}
// check parachute release
if (flags.parachute_release != AP_Notify::flags.parachute_release) {
flags.parachute_release = AP_Notify::flags.parachute_release;
if (flags.parachute_release) {
// parachute release warning tune
play_tune(TONE_PARACHUTE_RELEASE_TUNE);
}
}
}
/*
handle a PLAY_TUNE message
*/
void ToneAlarm_ChibiOS::handle_play_tune(mavlink_message_t *msg)
{
// decode mavlink message
mavlink_play_tune_t packet;
mavlink_msg_play_tune_decode(msg, &packet);
char str[100];
strncpy(str, packet.tune, MIN(sizeof(packet.tune), sizeof(str)-1));
str[sizeof(str)-1] = 0;
uint8_t len = strlen(str);
uint8_t len2 = strnlen(packet.tune2, sizeof(packet.tune2));
len2 = MIN((sizeof(str)-1)-len, len2);
strncpy(str+len, packet.tune2, len2);
str[sizeof(str)-1] = 0;
hal.util->toneAlarm_set_tune_string(str);
}
#endif // CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS

View File

@ -1,44 +0,0 @@
/*
ToneAlarm Linux driver
*/
/*
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/>.
*/
#pragma once
#include "NotifyDevice.h"
class ToneAlarm_ChibiOS: public NotifyDevice
{
public:
/// init - initialised the tone alarm
bool init(void);
/// update - updates led according to timed_updated. Should be called at 50Hz
void update();
// handle a PLAY_TUNE message
void handle_play_tune(mavlink_message_t *msg);
private:
/// play_tune - play one of the pre-defined tunes
bool play_tune(uint8_t tune_number);
/// tonealarm_type - bitmask of states we track
struct tonealarm_type {
bool armed : 1; // false = disarmed, true = armed
bool failsafe_battery : 1; // true if battery failsafe
bool parachute_release : 1; // true if parachute is being released
} flags;
};

View File

@ -1,105 +0,0 @@
/*
ToneAlarm Linux driver
*/
/*
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_HAL/AP_HAL.h>
#if CONFIG_HAL_BOARD == HAL_BOARD_LINUX
#include "ToneAlarm_Linux.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <AP_HAL_Linux/Util.h>
#include "AP_Notify.h"
extern const AP_HAL::HAL& hal;
bool ToneAlarm_Linux::init()
{
// open the tone alarm device
bool initialized = hal.util->toneAlarm_init();
if (!initialized) {
hal.console->printf("AP_Notify: Failed to initialise ToneAlarm");
return false;
}
// set initial boot states. This prevents us issuing a arming
// warning in plane and rover on every boot
flags.armed = AP_Notify::flags.armed;
flags.failsafe_battery = AP_Notify::flags.failsafe_battery;
return true;
}
// play_tune - play one of the pre-defined tunes
bool ToneAlarm_Linux::play_tune(uint8_t tune_number)
{
hal.util->toneAlarm_set_tune(tune_number);
return true;
}
// update - updates led according to timed_updated. Should be called at 50Hz
void ToneAlarm_Linux::update()
{
// exit if buzzer is not enabled
if (pNotify->buzzer_enabled() == false) {
return;
}
// check for arming failure
if (AP_Notify::events.arming_failed) {
play_tune(TONE_ARMING_FAILURE_TUNE);
}
// check if arming status has changed
if (flags.armed != AP_Notify::flags.armed) {
flags.armed = AP_Notify::flags.armed;
if (flags.armed) {
// arming tune
play_tune(TONE_ARMING_WARNING_TUNE);
}else{
// disarming tune
play_tune(TONE_NOTIFY_NEUTRAL_TUNE);
}
}
// check if battery status has changed
if (flags.failsafe_battery != AP_Notify::flags.failsafe_battery) {
flags.failsafe_battery = AP_Notify::flags.failsafe_battery;
if (flags.failsafe_battery) {
// low battery warning tune
play_tune(TONE_BATTERY_WARNING_FAST_TUNE);
}
}
// check parachute release
if (flags.parachute_release != AP_Notify::flags.parachute_release) {
flags.parachute_release = AP_Notify::flags.parachute_release;
if (flags.parachute_release) {
// parachute release warning tune
play_tune(TONE_PARACHUTE_RELEASE_TUNE);
}
}
}
#endif // CONFIG_HAL_BOARD == HAL_BOARD_PX4

View File

@ -1,43 +0,0 @@
/*
ToneAlarm Linux driver
*/
/*
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/>.
*/
#pragma once
#include "NotifyDevice.h"
class ToneAlarm_Linux: public NotifyDevice
{
public:
/// init - initialised the tone alarm
bool init(void);
/// update - updates led according to timed_updated. Should be called at 50Hz
void update();
private:
/// play_tune - play one of the pre-defined tunes
bool play_tune(uint8_t tune_number);
bool _initialized = false;
/// tonealarm_type - bitmask of states we track
struct tonealarm_type {
bool armed : 1; // false = disarmed, true = armed
bool failsafe_battery : 1; // true if battery failsafe
bool parachute_release : 1; // true if parachute is being released
} flags;
};