mirror of
https://github.com/OpenSolo/OpenSolo.git
synced 2025-04-29 22:24:32 +02:00
233 lines
5.6 KiB
C++
233 lines
5.6 KiB
C++
#include "ui_arming.h"
|
|
#include "ui.h"
|
|
#include "lockout.h"
|
|
#include "flightmanager.h"
|
|
#include "buttonmanager.h"
|
|
#include "battery.h"
|
|
#include "haptic.h"
|
|
#include "resources-gen.h"
|
|
|
|
const Gfx::FontAsset & UiArming::InstructionFont = HelveticaNeueLTProRoman26;
|
|
|
|
UiArming::UiArming() :
|
|
progressClearPending(false),
|
|
armStateDirty(true),
|
|
waitingForGps(false),
|
|
progressW(0),
|
|
blinkTicker(0),
|
|
progressBar(Ui::HintBoxY + Ui::HintBoxBorderWeight)
|
|
{
|
|
}
|
|
|
|
void UiArming::init()
|
|
{
|
|
Gfx::clear(0x0);
|
|
|
|
Ui::instance.topBar.init();
|
|
|
|
if (FlightManager::instance.mustWaitForGpsToArm()) {
|
|
writeWaitingForGps();
|
|
} else {
|
|
waitingForGps = false;
|
|
writeInstructions();
|
|
}
|
|
|
|
UiTelemetry & telem = Ui::instance.telem;
|
|
|
|
clearArmingProgress();
|
|
telem.bottomBox.init(UiBottomBox::DisplayArming);
|
|
telem.bottomBox.update();
|
|
}
|
|
|
|
void UiArming::update()
|
|
{
|
|
const FlightManager & fm = FlightManager::instance;
|
|
const Telemetry & t = fm.telemVals();
|
|
|
|
Ui::instance.topBar.update();
|
|
|
|
if (!t.flightBatteryInitialized()) {
|
|
/*
|
|
* if we haven't gotten a battery level from the vehicle yet,
|
|
* we don't want to start arming.
|
|
* would be good to communicate this to the user somehow, but
|
|
* for now we'll just rely on them pressing FLY again if they
|
|
* try to arm before we've gotten a battery reading.
|
|
*/
|
|
return;
|
|
}
|
|
|
|
if (t.battLevel < FlightManager::BatteryLevelCritical) {
|
|
Ui::instance.pendEvent(Event::FlightBatteryTooLowForTakeoff);
|
|
return;
|
|
}
|
|
|
|
if (Battery::instance.level() <= Battery::CRITICALLY_LOW_PERCENT &&
|
|
!Battery::instance.chargerIsPresent())
|
|
{
|
|
Ui::instance.pendEvent(Event::ControllerBatteryTooLowForTakeoff);
|
|
return;
|
|
}
|
|
|
|
if (fm.mustWaitForGpsToArm()) {
|
|
if (!waitingForGps) {
|
|
writeWaitingForGps();
|
|
}
|
|
} else {
|
|
// otherwise, ensure we draw the arming state
|
|
if (waitingForGps) {
|
|
armStateDirty = true;
|
|
}
|
|
waitingForGps = false;
|
|
}
|
|
|
|
Button & b = ButtonManager::button(Io::ButtonFly);
|
|
|
|
// handle arming state changes
|
|
if (armStateDirty && !waitingForGps) {
|
|
armStateDirty = false;
|
|
|
|
if (b.wasHeld()) {
|
|
clearArmingProgress();
|
|
}
|
|
writeInstructions();
|
|
}
|
|
|
|
// progress bar
|
|
if (b.isPressed()) {
|
|
drawArmingProgress();
|
|
} else {
|
|
if (progressClearPending) {
|
|
progressClearPending = false;
|
|
clearArmingProgress();
|
|
}
|
|
}
|
|
|
|
// handle FLY button led
|
|
if (!fm.armed()) {
|
|
b.greenLedOff();
|
|
} else {
|
|
const SysTime::Ticks now = SysTime::now();
|
|
if (now - blinkTicker > SysTime::msTicks(ARMED_LED_BLINK_MILLIS)) {
|
|
b.greenLedToggle();
|
|
blinkTicker = now;
|
|
}
|
|
}
|
|
|
|
if (progressW == 0 && !b.isPressed()) {
|
|
Ui::instance.telem.bottomBox.update();
|
|
}
|
|
}
|
|
|
|
void UiArming::writeWaitingForGps()
|
|
{
|
|
uint16_t y = Ui::DefaultFlyBtnY + Ui::FlyBtnTextOffset;
|
|
const Gfx::FontAsset & f = InstructionFont;
|
|
const Gfx::FontAsset & f2 = HelveticaNeueLTProRoman;
|
|
|
|
// clear from the top of the fly button to the top of the hint box
|
|
Gfx::fillRect(Gfx::Rect(0, Ui::DefaultFlyBtnY, Gfx::WIDTH, Ui::HintBoxY - y), 0x0);
|
|
|
|
Gfx::writeCanvasCenterJustified(y, "Searching for GPS", f);
|
|
y += f.height() + 6;
|
|
|
|
Gfx::writeCanvasCenterJustified(y, "may take a few minutes", f2);
|
|
y += f2.height() + 3;
|
|
|
|
Gfx::writeCanvasCenterJustified(y, "Solo requires a clear view of the sky", f2);
|
|
|
|
waitingForGps = true;
|
|
}
|
|
|
|
void UiArming::writeInstructions()
|
|
{
|
|
if (!FlightManager::instance.armed()) {
|
|
Ui::writeFlyBtnInstructions("Hold ", " to start motors");
|
|
} else {
|
|
Ui::writeFlyBtnInstructions("Hold ", " to take off");
|
|
}
|
|
}
|
|
|
|
void UiArming::onFlyButtonEvt(Button *b, Button::Event evt)
|
|
{
|
|
/*
|
|
* Latch button events from ISR context so we can draw them
|
|
* in display update context.
|
|
*
|
|
* Previously, we left the progress bar up if the release
|
|
* came from a hold event, signifying that the hold was
|
|
* successful and the operation is in progress, but that
|
|
* was found to be confusing, so just clearing on any release now.
|
|
*/
|
|
|
|
UNUSED(b);
|
|
|
|
switch (evt) {
|
|
case Button::Press:
|
|
if (waitingForGps) {
|
|
Haptic::startPattern(Haptic::UhUh);
|
|
}
|
|
break;
|
|
|
|
case Button::Release:
|
|
progressClearPending = true;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void UiArming::onArmFailed()
|
|
{
|
|
progressClearPending = true;
|
|
}
|
|
|
|
void UiArming::onTakeoffFailed()
|
|
{
|
|
progressClearPending = true;
|
|
}
|
|
|
|
void UiArming::drawArmingProgress()
|
|
{
|
|
if (!Lockout::isUnlocked()) {
|
|
return;
|
|
}
|
|
|
|
if (waitingForGps) {
|
|
return;
|
|
}
|
|
|
|
Button & b = ButtonManager::button(Io::ButtonFly);
|
|
if (b.isHeld()) {
|
|
// only want to draw progress up until the hold event
|
|
return;
|
|
}
|
|
|
|
unsigned millis = MIN(b.pressDuration() / SysTime::msTicks(1), Button::HoldMillis);
|
|
unsigned prog = scale(millis, 0U, Button::HoldMillis, 0U, Gfx::WIDTH);
|
|
|
|
// XXX: could keep track of where we were and just draw the delta...
|
|
if (progressW == 0 || progressW > prog) {
|
|
progressBar.clear();
|
|
}
|
|
|
|
if (progressW != prog) {
|
|
progressW = prog;
|
|
progressBar.update(progressW);
|
|
}
|
|
}
|
|
|
|
void UiArming::clearArmingProgress()
|
|
{
|
|
/*
|
|
* Replace the bottom area with bottomBox if we're not showing progress.
|
|
*/
|
|
|
|
if (progressW) {
|
|
Ui::instance.telem.bottomBox.init(UiBottomBox::DisplayArming);
|
|
}
|
|
|
|
progressW = 0;
|
|
}
|