Add tinyfsm, start converting core functions to an FSM-based event loop

This commit is contained in:
jacqueline
2023-05-19 21:21:27 +10:00
parent b320a6a863
commit a6ab150405
68 changed files with 2961 additions and 356 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
idf_component_register(
SRCS "main.cpp" "app_console.cpp"
SRCS "main.cpp"
INCLUDE_DIRS "."
REQUIRES "audio" "drivers" "dev_console" "drivers" "database" "ui")
REQUIRES "audio" "ui" "system_fsm" "events")
target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
-263
View File
@@ -1,263 +0,0 @@
#include "app_console.hpp"
#include <dirent.h>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string>
#include "audio_playback.hpp"
#include "database.hpp"
#include "esp_console.h"
#include "esp_log.h"
namespace console {
static AppConsole* sInstance = nullptr;
std::string toSdPath(const std::string& filepath) {
return std::string(drivers::kStoragePath) + "/" + filepath;
}
int CmdListDir(int argc, char** argv) {
static const std::string usage = "usage: ls [directory]";
if (argc > 2) {
std::cout << usage << std::endl;
return 1;
}
std::string path;
if (argc == 2) {
path = toSdPath(argv[1]);
} else {
path = toSdPath("");
}
DIR* dir;
struct dirent* ent;
dir = opendir(path.c_str());
while ((ent = readdir(dir))) {
std::cout << ent->d_name << std::endl;
}
closedir(dir);
return 0;
}
void RegisterListDir() {
esp_console_cmd_t cmd{.command = "ls",
.help = "Lists SD contents",
.hint = NULL,
.func = &CmdListDir,
.argtable = NULL};
esp_console_cmd_register(&cmd);
}
int CmdPlayFile(int argc, char** argv) {
static const std::string usage = "usage: play [file]";
if (argc != 2) {
std::cout << usage << std::endl;
return 1;
}
std::string path = "/";
sInstance->playback_->Play(path + argv[1]);
return 0;
}
void RegisterPlayFile() {
esp_console_cmd_t cmd{.command = "play",
.help = "Begins playback of the file at the given path",
.hint = "filepath",
.func = &CmdPlayFile,
.argtable = NULL};
esp_console_cmd_register(&cmd);
}
int CmdToggle(int argc, char** argv) {
static const std::string usage = "usage: toggle";
if (argc != 1) {
std::cout << usage << std::endl;
return 1;
}
// sInstance->playback_->Toggle();
return 0;
}
void RegisterToggle() {
esp_console_cmd_t cmd{.command = "toggle",
.help = "Toggles between play and pause",
.hint = NULL,
.func = &CmdToggle,
.argtable = NULL};
esp_console_cmd_register(&cmd);
}
int CmdVolume(int argc, char** argv) {
static const std::string usage = "usage: volume [0-255]";
if (argc != 2) {
std::cout << usage << std::endl;
return 1;
}
long int raw_vol = strtol(argv[1], NULL, 10);
if (raw_vol < 0 || raw_vol > 255) {
std::cout << usage << std::endl;
return 1;
}
// sInstance->playback_->SetVolume((uint8_t)raw_vol);
return 0;
}
void RegisterVolume() {
esp_console_cmd_t cmd{
.command = "vol",
.help = "Changes the volume (between 0 and 254. 255 is mute.)",
.hint = NULL,
.func = &CmdVolume,
.argtable = NULL};
esp_console_cmd_register(&cmd);
}
int CmdAudioStatus(int argc, char** argv) {
static const std::string usage = "usage: audio";
if (argc != 1) {
std::cout << usage << std::endl;
return 1;
}
sInstance->playback_->LogStatus();
return 0;
}
void RegisterAudioStatus() {
esp_console_cmd_t cmd{.command = "audio",
.help = "logs the current status of the audio pipeline",
.hint = NULL,
.func = &CmdAudioStatus,
.argtable = NULL};
esp_console_cmd_register(&cmd);
}
int CmdDbInit(int argc, char** argv) {
static const std::string usage = "usage: db_init";
if (argc != 1) {
std::cout << usage << std::endl;
return 1;
}
sInstance->database_->Update();
return 0;
}
void RegisterDbInit() {
esp_console_cmd_t cmd{
.command = "db_init",
.help = "scans for playable files and adds them to the database",
.hint = NULL,
.func = &CmdDbInit,
.argtable = NULL};
esp_console_cmd_register(&cmd);
}
int CmdDbSongs(int argc, char** argv) {
static const std::string usage = "usage: db_songs";
if (argc != 1) {
std::cout << usage << std::endl;
return 1;
}
std::unique_ptr<database::Result<database::Song>> res(
sInstance->database_->GetSongs(5).get());
while (true) {
for (database::Song s : res->values()) {
std::cout << s.tags().title.value_or("[BLANK]") << std::endl;
}
if (res->next_page()) {
auto continuation = res->next_page().value();
res.reset(sInstance->database_->GetPage(&continuation).get());
} else {
break;
}
}
return 0;
}
void RegisterDbSongs() {
esp_console_cmd_t cmd{.command = "db_songs",
.help = "lists titles of ALL songs in the database",
.hint = NULL,
.func = &CmdDbSongs,
.argtable = NULL};
esp_console_cmd_register(&cmd);
}
int CmdDbDump(int argc, char** argv) {
static const std::string usage = "usage: db_dump";
if (argc != 1) {
std::cout << usage << std::endl;
return 1;
}
std::cout << "=== BEGIN DUMP ===" << std::endl;
std::unique_ptr<database::Result<std::string>> res(
sInstance->database_->GetDump(5).get());
while (true) {
for (std::string s : res->values()) {
std::cout << s << std::endl;
}
if (res->next_page()) {
auto continuation = res->next_page().value();
res.reset(
sInstance->database_->GetPage<std::string>(&continuation).get());
} else {
break;
}
}
std::cout << "=== END DUMP ===" << std::endl;
return 0;
}
void RegisterDbDump() {
esp_console_cmd_t cmd{.command = "db_dump",
.help = "prints every key/value pair in the db",
.hint = NULL,
.func = &CmdDbDump,
.argtable = NULL};
esp_console_cmd_register(&cmd);
}
AppConsole::AppConsole(audio::AudioPlayback* playback,
database::Database* database)
: playback_(playback), database_(database) {
sInstance = this;
}
AppConsole::~AppConsole() {
sInstance = nullptr;
}
auto AppConsole::RegisterExtraComponents() -> void {
RegisterListDir();
RegisterPlayFile();
RegisterToggle();
RegisterVolume();
RegisterAudioStatus();
RegisterDbInit();
RegisterDbSongs();
RegisterDbDump();
}
} // namespace console
-25
View File
@@ -1,25 +0,0 @@
#pragma once
#include <memory>
#include "audio_playback.hpp"
#include "console.hpp"
#include "database.hpp"
#include "storage.hpp"
namespace console {
class AppConsole : public Console {
public:
explicit AppConsole(audio::AudioPlayback* playback,
database::Database* database);
virtual ~AppConsole();
audio::AudioPlayback* playback_;
database::Database* database_;
protected:
virtual auto RegisterExtraComponents() -> void;
};
} // namespace console
+9 -105
View File
@@ -1,114 +1,18 @@
#include <dirent.h>
#include <stdint.h>
#include <stdio.h>
#include <cstddef>
#include <cstdint>
#include <memory>
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "driver/sdspi_host.h"
#include "driver/spi_common.h"
#include "driver/spi_master.h"
#include "driver_cache.hpp"
#include "esp_freertos_hooks.h"
#include "esp_heap_caps.h"
#include "esp_intr_alloc.h"
#include "esp_log.h"
#include "freertos/portmacro.h"
#include "freertos/projdefs.h"
#include "hal/gpio_types.h"
#include "hal/spi_types.h"
#include "app_console.hpp"
#include "audio_playback.hpp"
#include "battery.hpp"
#include "dac.hpp"
#include "database.hpp"
#include "display.hpp"
#include "display_init.hpp"
#include "gpio_expander.hpp"
#include "i2c.hpp"
#include "lvgl_task.hpp"
#include "samd.hpp"
#include "spi.hpp"
#include "storage.hpp"
#include "touchwheel.hpp"
#include "tinyfsm.hpp"
static const char* TAG = "MAIN";
#include "audio_fsm.hpp"
#include "event_queue.hpp"
#include "system_fsm.hpp"
#include "ui_fsm.hpp"
extern "C" void app_main(void) {
ESP_LOGI(TAG, "Initialising peripherals");
tinyfsm::FsmList<system_fsm::SystemState, ui::UiState,
audio::AudioState>::start();
ESP_ERROR_CHECK(drivers::init_i2c());
ESP_ERROR_CHECK(drivers::init_spi());
std::unique_ptr<drivers::DriverCache> drivers =
std::make_unique<drivers::DriverCache>();
ESP_LOGI(TAG, "Init GPIOs");
drivers::GpioExpander* expander = drivers->AcquireGpios();
ESP_LOGI(TAG, "Init SAMD comms");
drivers::Samd samd;
ESP_LOGI(TAG, "It might have worked? Let's read something!");
auto res = samd.ReadChargeStatus();
if (res) {
ESP_LOGI(TAG, "Charge status is %d", static_cast<uint8_t>(*res));
} else {
ESP_LOGI(TAG, "no charge status?");
}
ESP_LOGI(TAG, "Enable power rails for development");
expander->with(
[&](auto& gpio) { gpio.set_pin(drivers::GpioExpander::AMP_EN, 1); });
ESP_LOGI(TAG, "Init battery measurement");
drivers::Battery* battery = new drivers::Battery();
ESP_LOGI(TAG, "it's reading %d mV!", (int)battery->Millivolts());
ESP_LOGI(TAG, "Init SD card");
auto storage = drivers->AcquireStorage();
if (!storage) {
ESP_LOGE(TAG, "Failed! Do you have an SD card?");
}
ESP_LOGI(TAG, "Init touch wheel");
std::shared_ptr<drivers::TouchWheel> touchwheel =
drivers->AcquireTouchWheel();
std::atomic<bool> lvgl_quit;
TaskHandle_t lvgl_task_handle;
ui::StartLvgl(drivers.get(), &lvgl_quit, &lvgl_task_handle);
std::unique_ptr<audio::AudioPlayback> playback;
if (storage) {
ESP_LOGI(TAG, "Init audio pipeline");
playback = std::make_unique<audio::AudioPlayback>(drivers.get());
}
ESP_LOGI(TAG, "Init database");
std::unique_ptr<database::Database> db;
auto db_res = database::Database::Open();
if (db_res.has_value()) {
db.reset(db_res.value());
}
ESP_LOGI(TAG, "Waiting for background tasks before launching console...");
vTaskDelay(pdMS_TO_TICKS(1000));
ESP_LOGI(TAG, "Launch console");
console::AppConsole console(playback.get(), db.get());
console.Launch();
uint8_t prev_position = 0;
auto& queue = events::EventQueue::GetInstance();
while (1) {
touchwheel->Update();
auto wheel_data = touchwheel->GetTouchWheelData();
if (wheel_data.wheel_position != prev_position) {
prev_position = wheel_data.wheel_position;
ESP_LOGI(TAG, "Touch wheel pos: %u", prev_position);
}
vTaskDelay(pdMS_TO_TICKS(100));
queue.Service(portMAX_DELAY);
}
}