Skip to content

Commit

Permalink
Create the Shopping Cart Lock app (#2326)
Browse files Browse the repository at this point in the history
  • Loading branch information
RocketGod-git authored Nov 8, 2024
1 parent 079329f commit 09dff44
Show file tree
Hide file tree
Showing 9 changed files with 415 additions and 4 deletions.
6 changes: 5 additions & 1 deletion firmware/application/external/external.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,10 @@ set(EXTCPPSRC
#acars
external/acars_rx/main.cpp
external/acars_rx/acars_app.cpp


#shoppingcart_lock
external/shoppingcart_lock/main.cpp
external/shoppingcart_lock/shoppingcart_lock.cpp
)

set(EXTAPPLIST
Expand Down Expand Up @@ -135,4 +138,5 @@ set(EXTAPPLIST
sstvtx
random_password
#acars_rx
shoppingcart_lock
)
7 changes: 6 additions & 1 deletion firmware/application/external/external.ld
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ MEMORY
ram_external_app_sstvtx(rwx) : org = 0xADC70000, len = 32k
ram_external_app_random_password(rwx) : org = 0xADC80000, len = 32k
ram_external_app_acars_rx(rwx) : org = 0xADC90000, len = 32k
ram_external_app_shoppingcart_lock(rwx) : org = 0xADCA0000, len = 32k
}

SECTIONS
Expand Down Expand Up @@ -166,7 +167,6 @@ SECTIONS
*(*ui*external_app*tpmsrx*);
} > ram_external_app_tpmsrx


.external_app_protoview : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_protoview.application_information));
Expand Down Expand Up @@ -204,6 +204,11 @@ SECTIONS
*(*ui*external_app*acars_rx*);
} > ram_external_app_acars_rx

.external_app_shoppingcart_lock : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_shoppingcart_lock.application_information));
*(*ui*external_app*shoppingcart_lock*);
} > ram_external_app_shoppingcart_lock



Expand Down
63 changes: 63 additions & 0 deletions firmware/application/external/shoppingcart_lock/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@

// RocketGod's Shopping Cart Lock app
// https://betaskynet.com
#include "ui.hpp"
#include "shoppingcart_lock.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"

namespace ui::external_app::shoppingcart_lock {
void initialize_app(NavigationView& nav) {
baseband::run_image(portapack::spi_flash::image_tag_audio_tx);
nav.push<ShoppingCartLock>();
}
} // namespace ui::external_app::shoppingcart_lock

extern "C" {

__attribute__((section(".external_app.app_shoppingcart_lock.application_information"), used)) application_information_t _application_information_shoppingcart_lock = {
(uint8_t*)0x00000000,
ui::external_app::shoppingcart_lock::initialize_app,
CURRENT_HEADER_VERSION,
VERSION_MD5,
"Cart Lock",
{
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x7E,
0x7E,
0x00,
0x00,
0x00,
0x00,
0x00,
0x7E,
0x81,
0x81,
0x7E,
0x00,
0x00,
0x00,
0x7E,
0x81,
0x81,
0x81,
0x81,
0x7E,
0x00,
},
ui::Color::red().v,
app_location_t::UTILITIES,
{'P', 'A', 'T', 'X'},
0x00000000,
};
}
235 changes: 235 additions & 0 deletions firmware/application/external/shoppingcart_lock/shoppingcart_lock.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
// RocketGod's Shopping Cart Lock app
// https://betaskynet.com
#include "shoppingcart_lock.hpp"

using namespace portapack;

namespace ui::external_app::shoppingcart_lock {

void ShoppingCartLock::log_event(const std::string& message) {
static const size_t MAX_LOG_LINES = 50;
static std::vector<std::string> message_history;

message_history.push_back(message);
if (message_history.size() > MAX_LOG_LINES) {
message_history.erase(message_history.begin());
menu_view.clear();
for (const auto& msg : message_history) {
menu_view.add_item({msg,
ui::Theme::getInstance()->fg_green->foreground,
nullptr,
[](KeyEvent) {}});
}
} else {
menu_view.add_item({message,
ui::Theme::getInstance()->fg_green->foreground,
nullptr,
[](KeyEvent) {}});
}

menu_view.set_highlighted(menu_view.item_count() - 1);
}

bool ShoppingCartLock::is_active() const {
return (bool)replay_thread;
}

void ShoppingCartLock::focus() {
menu_view.focus();
}

void ShoppingCartLock::stop() {
log_event(">>> STOP_SEQUENCE_START");
if (is_active()) {
log_event("... Resetting Replay Thread");
replay_thread.reset();
}
log_event("... Stopping Audio Output");
audio::output::stop();

log_event("... Resetting State Variables");
transmitter_model.disable();
ready_signal = false;
thread_sync_complete = false;
looping = false;
current_file = "";
log_event("<<< STOP_SEQUENCE_COMPLETE");
}

std::string ShoppingCartLock::list_wav_files() {
log_event(">>> WAV_SCAN_START");
auto reader = std::make_unique<WAVFileReader>();
bool found_lock = false;
bool found_unlock = false;

for (const auto& entry : std::filesystem::directory_iterator(wav_dir, u"*")) {
if (std::filesystem::is_regular_file(entry.status())) {
auto filename = entry.path().filename().string();
std::transform(filename.begin(), filename.end(), filename.begin(), ::tolower);

if (filename == shoppingcart_lock_file || filename == shoppingcart_unlock_file) {
std::string file_path = (wav_dir / filename).string();
if (reader->open(file_path)) {
if (filename == shoppingcart_lock_file) {
found_lock = true;
log_event("... Found: " + shoppingcart_lock_file);
log_event("Sample Rate: " + std::to_string(reader->sample_rate()));
log_event("Channels: " + std::to_string(reader->channels()));
log_event("Bits/Sample: " + std::to_string(reader->bits_per_sample()));
}
if (filename == shoppingcart_unlock_file) {
found_unlock = true;
log_event("... Found: " + shoppingcart_unlock_file);
log_event("Sample Rate: " + std::to_string(reader->sample_rate()));
log_event("Channels: " + std::to_string(reader->channels()));
log_event("Bits/Sample: " + std::to_string(reader->bits_per_sample()));
}
}
}

if (found_lock && found_unlock) {
break;
}
}
}

if (!found_lock || !found_unlock) {
log_event("!!! Missing Required Files:");
if (!found_lock) log_event("!!! Missing: " + shoppingcart_lock_file);
if (!found_unlock) log_event("!!! Missing: " + shoppingcart_unlock_file);
menu_view.hidden(true);
text_empty.hidden(false);
} else {
log_event("... All Required Files Found");
menu_view.hidden(false);
text_empty.hidden(true);
}

log_event("<<< WAV_SCAN_COMPLETE");
return found_lock && found_unlock ? "Required WAV files found" : "Missing required WAV files";
}

void ShoppingCartLock::wait_for_thread() {
uint32_t timeout = 100;
while (!ready_signal && timeout > 0) {
chThdYield();
timeout--;
}
}

void ShoppingCartLock::restart_playback() {
auto reader = std::make_unique<WAVFileReader>();
std::string file_path = (wav_dir / current_file).string();

if (!reader->open(file_path)) return;

replay_thread = std::make_unique<ReplayThread>(
std::move(reader),
BUFFER_SIZE,
NUM_BUFFERS,
&ready_signal,
[](uint32_t return_code) {
ReplayThreadDoneMessage message{return_code};
EventDispatcher::send_message(message);
});

log_event(">> SENDING <<");
audio::output::start();
transmitter_model.enable();
}

void ShoppingCartLock::play_audio(const std::string& filename, bool loop) {
auto reader = std::make_unique<WAVFileReader>();
stop();

std::string file_path = (wav_dir / filename).string();
if (!reader->open(file_path)) {
nav_.display_modal("Error", "Cannot open " + filename);
return;
}

const uint32_t wav_sample_rate = reader->sample_rate();
const uint16_t wav_bits_per_sample = reader->bits_per_sample();

current_file = filename;
looping = loop;

replay_thread = std::make_unique<ReplayThread>(
std::move(reader),
BUFFER_SIZE,
NUM_BUFFERS,
&ready_signal,
[](uint32_t return_code) {
ReplayThreadDoneMessage message{return_code};
EventDispatcher::send_message(message);
});

wait_for_thread();

log_event("... Configuring Baseband");

const uint32_t bb_sample_rate = 1536000;
const uint32_t decimation = bb_sample_rate / wav_sample_rate;

baseband::set_audiotx_config(
bb_sample_rate / decimation,
0.0f,
5.0f,
wav_bits_per_sample,
wav_bits_per_sample,
0,
true,
false,
false,
false);

baseband::set_sample_rate(wav_sample_rate);

log_event("... Starting Audio Output");
audio::output::start();
log_event("... Setting Max Volume");
audio::headphone::set_volume(audio::headphone::volume_range().max);

transmitter_model.enable();

log_event(">>> Playback Started <<<");
}

ShoppingCartLock::ShoppingCartLock(NavigationView& nav)
: nav_{nav} {
add_children({&menu_view,
&text_empty,
&button_lock,
&button_unlock,
&button_stop});

button_lock.on_select = [this](Button&) {
if (is_active()) stop();
log_event(">>> LOCK_SEQUENCE_START");
play_audio(shoppingcart_lock_file, true);
};

button_unlock.on_select = [this](Button&) {
if (is_active()) stop();
log_event(">>> UNLOCK_SEQUENCE_START");
play_audio(shoppingcart_unlock_file, true);
};

button_stop.on_select = [this](Button&) {
log_event(">>> STOPPING AUDIO");
stop();
};

list_wav_files();

log_event("[+] INITIALIZATION COMPLETE");
log_event("[+] PORTAPACK ARMED");
log_event("[*] STATUS: READY");
}

ShoppingCartLock::~ShoppingCartLock() {
stop();
baseband::shutdown();
}

} // namespace ui::external_app::shoppingcart_lock
Loading

0 comments on commit 09dff44

Please sign in to comment.