diff --git a/firmware/application/Makefile b/firmware/application/Makefile index 5149b13c..bd488ea7 100755 --- a/firmware/application/Makefile +++ b/firmware/application/Makefile @@ -180,6 +180,7 @@ CPPSRC = main.cpp \ log_file.cpp \ manchester.cpp \ string_format.cpp \ + temperature_logger.cpp \ ../common/utility.cpp \ ../common/chibios_cpp.cpp \ ../common/debug.cpp \ diff --git a/firmware/application/main.cpp b/firmware/application/main.cpp index a30bba25..21a5874e 100755 --- a/firmware/application/main.cpp +++ b/firmware/application/main.cpp @@ -129,6 +129,8 @@ private: void handle_rtc_tick() { sd_card::poll_inserted(); + + portapack::temperature_logger.second_tick(); } static ui::Widget* touch_widget(ui::Widget* const w, ui::TouchEvent event) { diff --git a/firmware/application/portapack.cpp b/firmware/application/portapack.cpp index 1491fe6a..e9460c50 100644 --- a/firmware/application/portapack.cpp +++ b/firmware/application/portapack.cpp @@ -65,6 +65,8 @@ ReceiverModel receiver_model { clock_manager }; +TemperatureLogger temperature_logger; + class Power { public: void init() { diff --git a/firmware/application/portapack.hpp b/firmware/application/portapack.hpp index ae424f36..cbe0b665 100644 --- a/firmware/application/portapack.hpp +++ b/firmware/application/portapack.hpp @@ -29,6 +29,7 @@ #include "lcd_ili9341.hpp" #include "radio.hpp" +#include "temperature_logger.hpp" namespace portapack { @@ -45,6 +46,8 @@ extern si5351::Si5351 clock_generator; extern ReceiverModel receiver_model; +extern TemperatureLogger temperature_logger; + void init(); void shutdown(); diff --git a/firmware/application/temperature_logger.cpp b/firmware/application/temperature_logger.cpp new file mode 100644 index 00000000..b2317ab9 --- /dev/null +++ b/firmware/application/temperature_logger.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * 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 2, 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "temperature_logger.hpp" + +#include "radio.hpp" + +#include + +void TemperatureLogger::second_tick() { + sample_phase++; + if( sample_phase >= sample_interval ) { + push_sample(read_sample()); + } +} + +size_t TemperatureLogger::size() const { + return std::min(samples.size(), samples_count); +} + +std::vector TemperatureLogger::history() const { + std::vector result; + + const auto n = size(); + result.resize(n); + + // Copy the last N samples from the buffer, since new samples are added at the end. + std::copy(samples.cend() - n, samples.cend(), result.data()); + + return result; +} + +TemperatureLogger::sample_t TemperatureLogger::read_sample() { + // MAX2837 does not return a valid temperature if in "shutdown" mode. + return radio::second_if.temp_sense() & 0x1f; +} + +void TemperatureLogger::push_sample(const TemperatureLogger::sample_t sample) { + // Started out building a pseudo-FIFO, then got lazy. + + // Shift samples: samples[1:] -> samples[0:-1] + // New sample goes into samples[-1] + std::copy(samples.cbegin() + 1, samples.cend(), samples.begin()); + samples.back() = sample; + samples_count++; + sample_phase = 0; +} diff --git a/firmware/application/temperature_logger.hpp b/firmware/application/temperature_logger.hpp new file mode 100644 index 00000000..5095c338 --- /dev/null +++ b/firmware/application/temperature_logger.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * 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 2, 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __TEMPERATURE_LOGGER_H__ +#define __TEMPERATURE_LOGGER_H__ + +#include +#include +#include +#include + +class TemperatureLogger { +public: + using sample_t = uint8_t; + + void second_tick(); + + size_t size() const; + + std::vector history() const; + +private: + std::array samples; + + static constexpr size_t sample_interval = 5; + size_t sample_phase = 0; + size_t samples_count = 0; + + sample_t read_sample(); + void push_sample(const sample_t sample); +}; + +#endif/*__TEMPERATURE_LOGGER_H__*/ diff --git a/firmware/application/ui_debug.cpp b/firmware/application/ui_debug.cpp index 7e955b03..1622287f 100644 --- a/firmware/application/ui_debug.cpp +++ b/firmware/application/ui_debug.cpp @@ -57,6 +57,50 @@ void DebugMemoryView::focus() { button_done.focus(); } +/* TemperatureWidget *****************************************************/ + +void TemperatureWidget::paint(Painter& painter) { + const auto history = portapack::temperature_logger.history(); + + const auto rect = screen_rect(); + + for(size_t i=0; i(rect.right() - (history.size() - i) * 1), + static_cast(rect.bottom() - bar_height), + 1, bar_height + }; + painter.fill_rectangle(bar_rect, Color::green()); + } + + if( !history.empty() ) { + const int32_t temp = -45 + history.back() * 5; + const size_t temp_len = 3; + painter.draw_string( + { static_cast(rect.right() - (temp_len * 8)), rect.top() }, + style(), + to_string_dec_int(temp, temp_len) + ); + } +} + +/* TemperatureView *******************************************************/ + +TemperatureView::TemperatureView(NavigationView& nav) { + add_children({ { + &temperature_widget, + &button_done, + } }); + + button_done.on_select = [&nav](Button&){ nav.pop(); }; +} + +void TemperatureView::focus() { + button_done.focus(); +} + /* RegistersWidget *******************************************************/ RegistersWidget::RegistersWidget( @@ -80,6 +124,8 @@ void RegistersWidget::paint(Painter& painter) { } void RegistersWidget::draw_legend(const Coord left, Painter& painter) { + const auto pos = screen_pos(); + for(size_t i=0; i((i / config.registers_per_row) * row_height) @@ -87,7 +133,7 @@ void RegistersWidget::draw_legend(const Coord left, Painter& painter) { const auto text = to_string_hex(i, config.legend_length); painter.draw_string( - screen_pos() + offset, + pos + offset, style().invert(), text ); @@ -98,6 +144,8 @@ void RegistersWidget::draw_values( const Coord left, Painter& painter ) { + const auto pos = screen_pos(); + for(size_t i=0; i(left + config.legend_width() + 8 + (i % config.registers_per_row) * (config.value_width() + 8)), @@ -108,7 +156,7 @@ void RegistersWidget::draw_values( const auto text = to_string_hex(value, config.value_length); painter.draw_string( - screen_pos() + offset, + pos + offset, style(), text ); @@ -152,7 +200,7 @@ void RegistersView::focus() { /* DebugMenuView *********************************************************/ DebugMenuView::DebugMenuView(NavigationView& nav) { - add_items<7>({ { + add_items<8>({ { { "Memory", [&nav](){ nav.push(); } }, { "Radio State", [&nav](){ nav.push(); } }, { "SD Card", [&nav](){ nav.push(); } }, @@ -172,6 +220,7 @@ DebugMenuView::DebugMenuView(NavigationView& nav) { "WM8731", RegistersWidgetConfig { wolfson::wm8731::reg_count, 1, 3, 4 }, [](const size_t register_number) { return portapack::audio_codec.read(register_number); } ); } }, + { "Temperature", [&nav](){ nav.push(); } }, } }); on_left = [&nav](){ nav.pop(); }; } diff --git a/firmware/application/ui_debug.hpp b/firmware/application/ui_debug.hpp index 6ed9b4e9..cfd7f7ad 100644 --- a/firmware/application/ui_debug.hpp +++ b/firmware/application/ui_debug.hpp @@ -82,6 +82,36 @@ private: }; }; +class TemperatureWidget : public Widget { +public: + explicit constexpr TemperatureWidget( + Rect parent_rect + ) : Widget { parent_rect } + { + } + + void paint(Painter& painter) override; + +private: +}; + +class TemperatureView : public View { +public: + explicit TemperatureView(NavigationView& nav); + + void focus() override; + +private: + TemperatureWidget temperature_widget { + { 0, 16, 240, 192 }, + }; + + Button button_done { + { 72, 256, 96, 24 }, + "Done" + }; +}; + struct RegistersWidgetConfig { size_t registers_count; size_t legend_length;