Compare commits

..

No commits in common. "0a97a07da0a342c586abde6533fe22b049c050a2" and "938978f5d4449ba45db2a3751052472608b28f56" have entirely different histories.

14 changed files with 38 additions and 358 deletions

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.16)
cmake_minimum_required(VERSION 3.18)
project("FT22 Steering Wheel Display")
@ -60,9 +60,6 @@ add_executable(
src/AMI.cpp
src/widgets.cpp
src/util.cpp
src/DriverView.cpp
src/BaseDataSource.cpp
src/DemoDataSource.cpp
)
target_include_directories(
stw-display PUBLIC
@ -81,4 +78,4 @@ target_link_libraries(
${I2C_LIBRARY}
fmt
spdlog::spdlog)
add_dependencies(stw-display fmt)
add_dependencies(stw-display fmt)

View File

@ -2,7 +2,6 @@
#include "AMI.h"
#include "AppState.h"
#include "DriverView.h"
#include "MissionSelect.h"
#include "defines.h"
@ -45,7 +44,6 @@ private:
std::unique_ptr<AppState> state;
std::unique_ptr<MissionSelect> mission_select;
std::unique_ptr<AMI> ami;
std::unique_ptr<DriverView> driver_view;
bool running;

View File

@ -1,7 +1,5 @@
#pragma once
#include "DemoDataSource.h"
#include <fmt/ostream.h>
#include <optional>
@ -34,7 +32,6 @@ public:
AppView get_view() const;
Mission get_mission() const;
DemoDataSource get_data_source() const;
private:
void unmarshal_mission_select(uint8_t* data);
@ -48,7 +45,6 @@ private:
#ifndef NDEBUG
friend class KeyboardHandler;
DemoDataSource data_source;
#endif // !NDEBUG
};

View File

@ -1,34 +0,0 @@
#pragma once
class BaseDataSource {
public:
double get_speed(); // in km/h
double get_brake_balance(); // 0.0 - 1.0
double get_hv_voltage(); // in V
double get_hv_voltage_ratio(); // 0.0 - 1.0
double get_lv_voltage(); // in V
double get_battery_temperature(); // in °C
double get_throttle_ratio(); // 0.0 - 1.0
double get_brake_pressure_front_bar(); // in bar
double get_brake_pressure_rear_bar(); // in bar
bool brake_balance_change_is_recent(); // true if the last change was less
// than 1 second ago
virtual void poll() = 0;
protected:
void set_brake_balance(double value);
double speed;
double brake_balance;
double hv_voltage;
double hv_voltage_ratio;
double lv_voltage;
double battery_temperature;
double throttle_ratio;
double brake_pressure_front_bar;
double brake_pressure_rear_bar;
int time_of_last_brake_balance_change;
};

View File

@ -1,13 +0,0 @@
#pragma once
#include "BaseDataSource.h"
class DemoDataSource final : public BaseDataSource {
public:
DemoDataSource();
~DemoDataSource();
void poll() override;
void bump_brake_balance_up();
void bump_brake_balance_down();
};

View File

@ -1,30 +0,0 @@
#pragma once
#include "View.h"
#include "defines.h"
#include "util.h"
#include "widgets.h"
class DriverView final : public View {
public:
DriverView(SDL_Renderer* renderer);
~DriverView();
void draw(const AppState& state) override;
private:
TTF_Font* font_large;
TTF_Font* font_detail;
TTF_Font* font_tiny;
TTF_Font* font_giant;
TTF_Font* font_medium;
std::unique_ptr<TextWidget> speed_widget;
std::unique_ptr<TextWidget> speed_hint_widget;
std::unique_ptr<TextWidget> brake_balance_widget;
std::unique_ptr<TextWidget> brake_balance_hint_widget;
std::unique_ptr<TextWidget> general_info_widget;
std::unique_ptr<TextWidget> focus_widget;
std::unique_ptr<TextWidget> focus_hint_widget;
};

View File

@ -79,13 +79,12 @@ public:
~TextWidget();
void update_text(const std::string& new_text);
void set_wrap_width(int width);
protected:
TTF_Font* font;
std::string text;
int wrap_width = -1; // -1 means no wrapping (default behavior)
SDL_Texture* generate_text(const std::string& text);
};

View File

@ -2,8 +2,6 @@
#include "AMI.h"
#include "AppState.h"
#include "DemoDataSource.h"
#include "DriverView.h"
#include "MissionSelect.h"
#include "events.h"
@ -17,7 +15,6 @@
#include <fmt/format.h>
#include <spdlog/spdlog.h>
#include <iostream>
#include <memory>
#include <queue>
#include <stdexcept>
@ -63,11 +60,6 @@ void App::init_state() {
int App::run() {
mission_select = std::make_unique<MissionSelect>(renderer);
ami = std::make_unique<AMI>(renderer);
driver_view = std::make_unique<DriverView>(renderer);
#ifndef NDEBUG
std::cout << "Change brake balance with W (+) and S (-)" << std::endl;
#endif
running = true;
@ -118,9 +110,6 @@ void App::render() {
case AppView::AMI:
ami->draw(*state);
break;
case AppView::DRIVER:
driver_view->draw(*state);
break;
default:
throw std::runtime_error(fmt::format("Unknown view: {}", view));
}
@ -149,51 +138,39 @@ SDLManager::~SDLManager() {
SDL_Quit();
}
/*
Handle key presses
*/
void KeyboardHandler::handle_keyup(const SDL_Event& ev, AppState& state) {
auto pressed_key = ev.key.keysym.sym;
// we are in the first screen (mission select)
if (state.view == AppView::MISSION_SELECT) {
// handle change selected mission
if (pressed_key == SDLK_DOWN || pressed_key == SDLK_RIGHT) {
auto new_mission = static_cast<int>(state.mission) + 1;
if (new_mission > 7) {
new_mission = 1;
}
state.mission = static_cast<Mission>(new_mission);
} else if (pressed_key == SDLK_UP || pressed_key == SDLK_LEFT) {
auto new_mission = static_cast<int>(state.mission) - 1;
if (new_mission < 1) {
new_mission = 7;
}
state.mission = static_cast<Mission>(new_mission);
// commit mission selection
} else if (pressed_key == SDLK_RETURN) {
if (state.mission == Mission::MANUAL) {
state.view = AppView::DRIVER;
} else {
state.view = AppView::AMI;
}
switch (ev.key.keysym.sym) {
case SDLK_DOWN:
case SDLK_RIGHT:
if (state.mission == Mission::MANUAL) {
state.mission = Mission::ACCELERATION;
} else {
state.mission = static_cast<Mission>(static_cast<int>(state.mission) + 1);
}
}
// go back to mission select
if (state.view == AppView::AMI || state.view == AppView::DRIVER) {
if (pressed_key == SDLK_ESCAPE) {
state.view = AppView::MISSION_SELECT;
break;
case SDLK_UP:
case SDLK_LEFT:
if (state.mission == Mission::NONE ||
state.mission == Mission::ACCELERATION) {
state.mission = Mission::MANUAL;
} else {
state.mission = static_cast<Mission>(static_cast<int>(state.mission) - 1);
}
break;
case SDLK_RETURN:
case SDLK_KP_ENTER:
switch (state.view) {
case AppView::MISSION_SELECT:
state.view = AppView::AMI;
break;
case AppView::AMI:
case AppView::DRIVER:
case AppView::TESTING:
break;
default:
throw std::runtime_error(fmt::format("Unknown view: {}", state.view));
}
break;
// We can just ignore other keypresses, so no need for a default clause
}
// handle mock brake balance change
if (pressed_key == SDLK_w) {
state.data_source.bump_brake_balance_up();
} else if (pressed_key == SDLK_s) {
state.data_source.bump_brake_balance_down();
}
}
}

View File

@ -76,7 +76,6 @@ AppState::~AppState() {
}
void AppState::poll() {
data_source.poll();
if (!i2c_dev_file) {
return;
}
@ -113,6 +112,4 @@ void AppState::unmarshal_mission_select(uint8_t* data) {
void AppState::unmarshal_ami(uint8_t* data) {
mission = static_cast<Mission>(data[1]);
spdlog::info("Mission after poll: {}", mission);
}
DemoDataSource AppState::get_data_source() const { return data_source; }
}

View File

@ -1,35 +0,0 @@
#include "BaseDataSource.h"
#include <SDL2/SDL.h>
double BaseDataSource::get_speed() { return speed; }
double BaseDataSource::get_brake_balance() { return brake_balance; }
double BaseDataSource::get_hv_voltage() { return hv_voltage; }
double BaseDataSource::get_hv_voltage_ratio() { return hv_voltage_ratio; }
double BaseDataSource::get_lv_voltage() { return lv_voltage; }
double BaseDataSource::get_battery_temperature() { return battery_temperature; }
double BaseDataSource::get_throttle_ratio() { return throttle_ratio; }
double BaseDataSource::get_brake_pressure_front_bar() {
return brake_pressure_front_bar;
}
double BaseDataSource::get_brake_pressure_rear_bar() {
return brake_pressure_rear_bar;
}
void BaseDataSource::set_brake_balance(double value) {
brake_balance = value;
time_of_last_brake_balance_change = SDL_GetTicks();
}
bool BaseDataSource::brake_balance_change_is_recent() {
return time_of_last_brake_balance_change != 0 &&
SDL_GetTicks() - time_of_last_brake_balance_change < 500;
}

View File

@ -1,33 +0,0 @@
#include "DemoDataSource.h"
#include <cmath>
#include <iostream>
DemoDataSource::DemoDataSource() {
brake_balance = 0.5;
lv_voltage = 13.1;
battery_temperature = 20.1;
throttle_ratio = 0.01;
brake_pressure_front_bar = 1;
brake_pressure_rear_bar = 1;
}
DemoDataSource::~DemoDataSource() {}
void DemoDataSource::poll() {
int min_voltage = 150;
int max_voltage = 480;
static int count = 0;
count++;
hv_voltage = 220.1 + std::cos(count / 1000.0 + 2) * 50;
hv_voltage_ratio = (hv_voltage - min_voltage) / (max_voltage - min_voltage);
speed = 10 + std::sin(count / 100.0) * 5;
}
void DemoDataSource::bump_brake_balance_up() {
set_brake_balance(brake_balance + 0.01);
}
void DemoDataSource::bump_brake_balance_down() {
set_brake_balance(brake_balance - 0.01);
}

View File

@ -1,131 +0,0 @@
#include "DriverView.h"
#include "DemoDataSource.h"
#include "defines.h"
#include <fmt/format.h>
#include <iostream>
#define LARGE_FONT_SIZE_POINTS 170
#define GIANT_FONT_SIZE_POINTS 250
#define MEDIUM_FONT_SIZE_POINTS 50
#define SOC_BAR_HEIGHT 80
#define SPEED_TEXT_X 22
#define SPEED_TEXT_Y 50
DriverView::DriverView(SDL_Renderer* renderer)
: View{renderer}, font_large{util::load_font("resources/Avenir-Book.ttf",
LARGE_FONT_SIZE_POINTS)},
font_detail{util::load_font("resources/Avenir-Book.ttf", 20)},
font_tiny{util::load_font("resources/Avenir-Book.ttf", 15)},
font_giant{
util::load_font("resources/Avenir-Book.ttf", GIANT_FONT_SIZE_POINTS)},
font_medium{util::load_font("resources/Avenir-Book.ttf",
MEDIUM_FONT_SIZE_POINTS)} {
speed_widget = std::make_unique<TextWidget>(renderer, font_large, "");
speed_widget->set_position(SPEED_TEXT_X, SPEED_TEXT_Y);
speed_widget->set_alignment(Alignment::CENTER, Alignment::TOP);
speed_hint_widget = std::make_unique<TextWidget>(renderer, font_tiny, "SPD");
speed_hint_widget->set_position(20, 210);
speed_hint_widget->set_alignment(Alignment::CENTER, Alignment::TOP);
brake_balance_widget = std::make_unique<TextWidget>(renderer, font_large, "");
brake_balance_widget->set_position(SPEED_TEXT_X + SCREEN_WIDTH / 2,
SPEED_TEXT_Y);
brake_balance_widget->set_alignment(Alignment::CENTER, Alignment::TOP);
brake_balance_hint_widget =
std::make_unique<TextWidget>(renderer, font_tiny, "BB");
brake_balance_hint_widget->set_position(20 + SCREEN_WIDTH / 2, 210);
brake_balance_hint_widget->set_alignment(Alignment::CENTER, Alignment::TOP);
general_info_widget = std::make_unique<TextWidget>(renderer, font_detail, "");
general_info_widget->set_position(SPEED_TEXT_X, SPEED_TEXT_Y + 200);
general_info_widget->set_alignment(Alignment::LEFT, Alignment::TOP);
general_info_widget->set_wrap_width(SCREEN_WIDTH - 50);
focus_widget = std::make_unique<TextWidget>(renderer, font_giant, "");
focus_widget->set_position(120, 50);
focus_widget->set_alignment(Alignment::LEFT, Alignment::TOP);
focus_hint_widget = std::make_unique<TextWidget>(renderer, font_medium, "");
focus_hint_widget->set_position(120, 30);
// focus_hint_widget->set_alignment(Alignment::CENTER, Alignment::CENTER);
}
DriverView::~DriverView() {
TTF_CloseFont(font_medium);
TTF_CloseFont(font_large);
TTF_CloseFont(font_detail);
TTF_CloseFont(font_tiny);
TTF_CloseFont(font_giant);
}
void DriverView::draw(const AppState& state) {
auto ds = state.get_data_source();
auto hv_ratio = ds.get_hv_voltage_ratio();
SDL_Rect rect;
rect.x = 0;
rect.y = 0;
rect.w = SCREEN_WIDTH;
rect.h = SCREEN_HEIGHT;
static auto hv_low = false;
// hysteresis for the hv voltage low
if (hv_ratio < 0.15) {
hv_low = true;
} else if (hv_ratio > 0.2) {
hv_low = false;
}
int red = hv_low ? 0xa0 : 0x30;
SDL_SetRenderDrawColor(renderer, red, 0x30, 0x30, 255);
SDL_RenderFillRect(renderer, &rect);
auto brake_balance_text = fmt::format("{:.0f}", ds.get_brake_balance() * 100);
if (ds.brake_balance_change_is_recent()) {
SDL_SetRenderDrawColor(renderer, 255, 69, 100, 255);
SDL_RenderFillRect(renderer, &rect);
focus_widget->update_text(brake_balance_text);
focus_widget->draw();
focus_hint_widget->update_text("BBAL");
focus_hint_widget->draw();
return;
}
rect.w = SCREEN_WIDTH * hv_ratio;
rect.h = SOC_BAR_HEIGHT;
SDL_SetRenderDrawColor(renderer, 76, 255, 0, 255);
SDL_RenderFillRect(renderer, &rect);
speed_widget->update_text(fmt::format("{:02.0f}", ds.get_speed()));
speed_widget->draw();
speed_hint_widget->draw();
brake_balance_widget->update_text(brake_balance_text);
brake_balance_widget->draw();
brake_balance_hint_widget->draw();
// draw rectangles around the speed and brake balance widgets
SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 255);
rect.x = 0;
rect.y = SOC_BAR_HEIGHT;
rect.w = SCREEN_WIDTH / 2;
rect.h = 150;
SDL_RenderDrawRect(renderer, &rect);
rect.x = SCREEN_WIDTH / 2;
SDL_RenderDrawRect(renderer, &rect);
auto general_string = fmt::format(
"HV: {:.0f}V | LV: {:.1f}V | B_TEMP: {:.1f}C\nAPPS: {:.0f}% | BPF: "
"{:.0f} bar | BPR: {:.0f} bar",
ds.get_hv_voltage(), ds.get_lv_voltage(), ds.get_battery_temperature(),
ds.get_throttle_ratio() * 100, ds.get_brake_pressure_front_bar(),
ds.get_brake_pressure_rear_bar());
general_info_widget->update_text(general_string);
general_info_widget->draw();
}

View File

@ -1,7 +1,6 @@
#include "MissionSelect.h"
#include "AppState.h"
#include "DriverView.h"
#include "defines.h"
#include "events.h"
#include "util.h"

View File

@ -167,13 +167,8 @@ void TextWidget::update_text(const std::string& new_text) {
}
SDL_Texture* TextWidget::generate_text(const std::string& text) {
SDL_Surface* surf;
if (wrap_width == -1) {
surf = TTF_RenderText_Blended(font, text.c_str(), {0xFF, 0xFF, 0XFF, 0xFF});
} else {
surf = TTF_RenderText_Blended_Wrapped(font, text.c_str(),
{0xFF, 0xFF, 0XFF, 0xFF}, wrap_width);
}
SDL_Surface* surf =
TTF_RenderText_Solid(font, text.c_str(), {0xFF, 0xFF, 0XFF, 0xFF});
if (surf == nullptr) {
throw std::runtime_error(
fmt::format("Unable to render text surface: {}", TTF_GetError()));
@ -186,8 +181,6 @@ SDL_Texture* TextWidget::generate_text(const std::string& text) {
return texture;
}
void TextWidget::set_wrap_width(int width) { wrap_width = width; }
ListWidget::ListWidget(SDL_Renderer* renderer, int element_height,
Alignment element_alignment)
: Widget{renderer}, element_height{element_height},