diff --git a/.clang-format b/.clang-format index 5e6b0f7..aa48fc1 100644 --- a/.clang-format +++ b/.clang-format @@ -68,6 +68,10 @@ ForEachMacros: - BOOST_FOREACH IncludeBlocks: Regroup IncludeCategories: + # This will hopefully only catch stdlib headers + - Regex: "^<[^/]*>" + Priority: 5 + SortPriority: 0 - Regex: '^[<"]SDL2/' Priority: 2 SortPriority: 0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c9ebe1..b1f00e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,12 @@ set(CMAKE_CXX_STANDARD 17) find_package(SDL2 REQUIRED) find_package(SDL2TTF REQUIRED) find_package(SDL2IMAGE REQUIRED) +find_library(I2C_LIBRARY i2c REQUIRED) +if ("${I2C_LIBRARY}" STREQUAL "I2C_LIBRARY-NOTFOUND") + message(FATAL_ERROR "libi2c not found!") +else() + message("libi2c found at ${I2C_LIBRARY}") +endif() include(FetchContent) @@ -48,6 +54,7 @@ add_executable( stw-display src/main.cpp src/App.cpp + src/AppState.cpp src/View.cpp src/MissionSelect.cpp src/widgets.cpp @@ -67,6 +74,7 @@ target_link_libraries( ${SDL2_LIBRARIES} ${SDL2TTF_LIBRARY} ${SDL2IMAGE_LIBRARY} + ${I2C_LIBRARY} fmt spdlog::spdlog) add_dependencies(stw-display fmt) \ No newline at end of file diff --git a/include/App.h b/include/App.h index 8424f78..f15a2d7 100644 --- a/include/App.h +++ b/include/App.h @@ -1,5 +1,6 @@ #pragma once +#include "AppState.h" #include "MissionSelect.h" #include "defines.h" @@ -7,8 +8,6 @@ #include -enum class AppView { MISSION_SELECT, AMI, DRIVER, TESTING }; - class SDLManager { public: SDLManager(); @@ -24,6 +23,7 @@ public: private: void init_sdl(); + void init_state(); void handle_events(); void render(); @@ -33,10 +33,10 @@ private: // others and its destructor is called after the others. SDLManager sdl_manager; + std::unique_ptr state; std::unique_ptr mission_select; bool running; - AppView view; SDL_Window* window; SDL_Renderer* renderer; diff --git a/include/AppState.h b/include/AppState.h new file mode 100644 index 0000000..0598dd4 --- /dev/null +++ b/include/AppState.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include +#include +#include + +constexpr int I2C_SLAVE_ADDR = 0x20; +constexpr int I2C_POLL_COMMAND = 0x00; + +enum class AppView { MISSION_SELECT, AMI, DRIVER, TESTING }; +std::ostream& operator<<(std::ostream& os, AppView view); + +class AppState { +public: + AppState(const std::string& i2c_dev_path); + ~AppState(); + + void poll(); + + AppView get_view(); + +private: + // This is optional so that we can still run the code on a PC without I2C + std::optional i2c_dev_file; + + AppView view; +}; \ No newline at end of file diff --git a/src/App.cpp b/src/App.cpp index 2b1bb14..be714c5 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -1,5 +1,6 @@ #include "App.h" +#include "AppState.h" #include "MissionSelect.h" #include "events.h" @@ -13,10 +14,14 @@ #include #include +#include #include #include -App::App() : view{AppView::MISSION_SELECT} { init_sdl(); } +App::App() { + init_sdl(); + init_state(); +} App::~App() { // Destroy window @@ -43,6 +48,14 @@ void App::init_sdl() { } } +void App::init_state() { + try { + state = std::make_unique("/dev/i2c-3"); + } catch (const std::runtime_error& e) { + spdlog::error("Couldn't create AppState: {}", e.what()); + } +} + int App::run() { mission_select = std::make_unique(renderer); @@ -58,6 +71,10 @@ int App::run() { frames = 0; } + if (state != nullptr) { + state->poll(); + } + handle_events(); render(); @@ -93,22 +110,24 @@ void App::handle_events() { } } + AppView view = state->get_view(); switch (view) { case AppView::MISSION_SELECT: mission_select->handle_events(events); break; default: - throw std::runtime_error(fmt::format("Unknown view: {}", (int)view)); + throw std::runtime_error(fmt::format("Unknown view: {}", view)); } } void App::render() { + AppView view = state->get_view(); switch (view) { case AppView::MISSION_SELECT: mission_select->draw(); break; default: - throw std::runtime_error(fmt::format("Unknown view: {}", (int)view)); + throw std::runtime_error(fmt::format("Unknown view: {}", view)); } SDL_RenderPresent(renderer); } diff --git a/src/AppState.cpp b/src/AppState.cpp new file mode 100644 index 0000000..f11a10f --- /dev/null +++ b/src/AppState.cpp @@ -0,0 +1,69 @@ +#include "AppState.h" + +#include +#include + +#include +#include +extern "C" { +#include +} +#include +#include + +#include +#include +#include + +std::ostream& operator<<(std::ostream& os, AppView view) { + switch (view) { + case AppView::MISSION_SELECT: + return os << "MISSION_SELECT"; + case AppView::AMI: + return os << "AMI"; + case AppView::DRIVER: + return os << "DRIVER"; + case AppView::TESTING: + return os << "TESTING"; + default: + return os << "UNKNOWN_VIEW[" << static_cast(view) << "]"; + } +} + +AppState::AppState(const std::string& i2c_dev_path) + : view{AppView::MISSION_SELECT} { + i2c_dev_file = open(i2c_dev_path.c_str(), O_RDWR); + if (i2c_dev_file < 0) { + spdlog::error("Couldn't open I2C device: {}", errno); + i2c_dev_file = std::nullopt; + return; + } + + if (ioctl(*i2c_dev_file, I2C_SLAVE, I2C_SLAVE_ADDR) < 0) { + spdlog::error("Couldn't set I2C slave address: {}", errno); + close(*i2c_dev_file); + i2c_dev_file = std::nullopt; + } +} + +AppState::~AppState() { + if (i2c_dev_file) { + close(*i2c_dev_file); + } +} + +void AppState::poll() { + if (!i2c_dev_file) { + return; + } + + uint8_t data[32]; + int count = i2c_smbus_read_block_data(*i2c_dev_file, I2C_POLL_COMMAND, data); + if (count < 0) { + throw std::runtime_error(fmt::format("Couldn't poll I2C slave: {}", errno)); + } + view = static_cast(data[0]); + spdlog::info("View after poll: {}", view); +} + +AppView AppState::get_view() { return view; } \ No newline at end of file