Broke up Create-a-simple-app into multable wikis

NotPike 2022-01-21 00:36:46 -05:00
parent d4ad533c65
commit 951f3e1f06

@ -1,8 +1,10 @@
This simple application will explain the basic of widgets and simple navigation.
## About
This wiki will go over how to create a simple application and show you the basics in the Mayhem code base. This is to be considered a "hello world" application. :)
We are going to create few simple controls and link the app to the main menu. The idea is to get familiar with the way things work.
# First steps
## First steps
There is a lot of flexibility, but for now we are going to follow patterns already found on the source code. For example, the name of the files and classes will be inspired by existing code.
@ -98,13 +100,12 @@ set(CPPSRC
In this moment you should be able to compile and test the app in your device. For your reference here's the link to the [Compile-firmware](https://github.com/eried/portapack-mayhem/wiki/Compile-firmware) wiki. The new app should appear in the menu you picked in `ui_navigation.cpp`.
# Adding functionality
## Adding Widgets and Basic Functionality
## Widgets
Widgets are the elements that compose the UI of your custom app. Widgets are defined inside [`firmware\common\ui_widget.hpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/ui_widget.hpp) and widget functions can be found inside [`firmware\common\ui_widget.cpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/ui_widget.cpp). In order to be able to use them, you must `#include "ui_widget.hpp"` into your app .hpp file.
There are different type of widget. here you will find a list of available widget, with their respecting constructor. For all the methods available, you should go and see the [ui_widget.hpp](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/ui_widget.hpp) file.
### Attach a generic widget to you application
### Attach a Generic Widget to Your Application
In order to display a widget into your app, you can either use the function `add_child()` or `add_children()`.
Both those functions shall be called within the code of your `NewAppGameView(NavigationView &nav){}` constructor. The difference between the two function is simple: the first one allows you to add a single widget, while the second one allows you to add an undefined number of widgets.
@ -120,25 +121,9 @@ add_children({
});
```
### Available widgets
There are several different widgets, and more might be added, so you should always go and check whether new widgets have been added or not. Here you will find a list of most basic widgets.
There are several different widgets that we're going to use for our new app. A more complete list can be found on the [Widgets](https://github.com/eried/portapack-mayhem/wiki/Widgets) wiki. More might be added so you should always go and check whether new widgets have been added or not.
#### Text
The text widgets add a simple text area to your app. Here you can find it's declaration and prototype:
```
Text my_text_widget{
Rect parent_rect,
std::string text
};
```
To be noted that `Rect parent_rect` has it's own definition inside another file, but let's say that you would like to add a text widget with the text "Hello World", positioned at the top left corner (spaced 10 from both top margin and left margin), with width 100 and height 24, you cold do it in this way:
```
Text hello_world_text_widget(
{10, 10, 100, 24}, // Coordinates are: int:x (px), int:y (px), int:width (px), int:height (px)
"Hello world!"
);
```
#### Button
Buttons allows you to do something when you press them. Here you can find it's declaration and prototype:
@ -158,6 +143,7 @@ Button my_button(
);
```
#### Labels
Labels are a text element that can be used to describe other widgets. Here you can find it's declaration and prototype:
@ -190,6 +176,7 @@ NewAppView::NewAppView(NavigationView &nav) {
}
```
#### LiveDateTime
LiveDateTime gives you the dynamic date and time. Here you can find it's declaration and prototype:
@ -222,40 +209,7 @@ If you want to enable seconds you'll need use the `set_seconds_enabled(bool new_
```
my_liveDateTime.set_seconds_enabled(true);
```
#### BigFrequency
BigFrequency is used for displaying a radio frequency. Here you can find it's declaration and prototype:
```
Labels my_bigFrequency_widget{
Rect parent_rect,
rf::Frequency frequency
};
```
For example, let's say you want a label called `my_bigFrequency`. You will need to add this to `apps/ui_newapp.hpp`:
```
Labels my_bigFrequency(
{10, 10, 28*8, 52}, // Coordinates are: int:x (px), int:y (px), int:width (px), int:height (px)
0 // Beginning frequency in hz
);
```
In `apps/ui_newapp.cpp` you'll need to add the `my_bigFrequency` pointer to add_child() or add_children():
```
NewAppView::NewAppView(NavigationView &nav) {
// Widget pointers
add_children({
&my_bigFrequency,
});
}
```
To set a frequency you can use the function `set(const rf::Frequency frequency)`:
```
my_bigFrequency.set(433000000); // 433MHz
```
#### ProgressBar
@ -295,199 +249,6 @@ To change the value of progress for the progress bar use the `set_value(const ui
my_progressBar.set_value(5); // 50% Complete
```
#### Console
Console can be used as large text field where you can output mutable lines of text. Here you can find it's declaration and prototype:
```
Console my_console_widget{
Rect parent_rect
};
```
For example, let's say you want a label called `my_console`. You will need to add this to `apps/ui_newapp.hpp`:
```
Console my_console {
{ 2*8, 10, 208, 200 }, // Coordinates are: int:x (px), int:y (px), int:width (px), int:height (px)
};
```
In `apps/ui_newapp.cpp` you'll need to add the `my_console` pointer to add_child() or add_children():
```
NewAppView::NewAppView(NavigationView &nav) {
// Widget pointers
add_children({
&my_console,
});
}
```
To write to 'my_console' you'll need to use the `write(std::string message)` function:
```
my_console.write("Hello World");
```
For automatic new line, you can also use the `writeln(std::string message)` function if you don't want to add a `\n' at the end of every string:
```
my_console.writeln("Hello World but on a new line!");
```
To enable scrolling you can use the `enable_scrolling(bool enable)` function::
```
my_console.enable_scrolling(true);
```
#### Checkbox
Checkboxs are a boolean (True/False) widget that allows you chose between one of two options . Here you can find it's declaration and prototype:
```
Checkbox my_checkbox_widget{
Point parent_pos,
size_t length,
std::string text,
bool small
};
```
For example, let's say you want a checkbox called `my_checkbox`. You will need to add this to `apps/ui_newapp.hpp`:
```
Checkbox my_checkbox(
{10, 20}, // Coordinates are: int:x (px), int:y (px)
4, // Length
"my_checkbox_text", // Title
false // Checkbox Size: true == small(16X16px), false == regular(24X24px)
);
```
In `apps/ui_newapp.cpp` you'll need to add the `my_checkbox` pointer to add_child() or add_children():
```
NewAppView::NewAppView(NavigationView &nav) {
// Widget pointers
add_children({
&my_checkbox,
});
}
```
Functions within `apps/ui_newapp.cpp` are able to lookup the value of `my_checkbox` with Checkbox's `value()` function:
```
if(my_checkbox.value()) {
do_a(); // If checkbox is selected (green check mark)
} else {
do_b(); // If checkbox is NOT selected (red X)
}
```
You can also set the value for `my_checkbox` with the `set_value(const bool value)` function:
```
my_checkbox.set_value(true); // Checkbox selected (green check mark)
my_checkbox.set_value(false); // Checkbox deselected (red X)
```
If you want your checkbox to automatically perform an action when toggled you can add this [Lambda](https://www.geeksforgeeks.org/lambda-expression-in-c/) to `apps/ui_newapp.cpp`:
```
NewAppView::NewAppView(NavigationView &nav) {
// Add widget pointers
add_children({
&my_checkbox,
});
// When checkbox is toggled do...
my_checkbox.on_select = [this](Checkbox&, bool v) {
if(v){
do_a(); // If checkbox is selected (green check mark)
} else {
do_b(); // If checkbox is NOT selected (red X)
}
};
}
```
#### Image
Images can be displayed within your app. Here you can find it's declaration and prototype:
```
Image my_Image_widget{
Rect parent_rect,
Bitmap* bitmap,
Color foreground,
Color background
};
```
Images need to be a Bitmap object before they can be displayed. Bellow is an example of the code needed to create a Bitmap from [`firmware/application/bitmap.hpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/application/bitmap.hpp).
```
static constexpr uint8_t bitmap_stripes_data[] = {
0xFF, 0x03, 0xC0,
0xFF, 0x01, 0xE0,
0xFF, 0x00, 0xF0,
0x7F, 0x00, 0xF8,
0x3F, 0x00, 0xFC,
0x1F, 0x00, 0xFE,
0x0F, 0x00, 0xFF,
0x07, 0x80, 0xFF,
};
static constexpr Bitmap bitmap_stripes {
{ 24, 8 }, bitmap_stripes_data
};
```
With the Bitmap object created we can now define the image `my_image`. You will need to add this to `apps/ui_newapp.hpp`:
```
Image my_image(
{10, 10, 24, 8}, // Coordinates are: int:x (px), int:y (px), int:width (px), int:height (px)
&Bitmap, // Pointer to your bitmap
Color::white(), // Color Foreground
Color::black() // Color Background
);
```
**Note:** Colors are defined in [`firmware/common/ui.hpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/ui.hpp)
In `apps/ui_newapp.cpp` you'll need to add the `my_image` pointer to add_child() or add_children():
```
NewAppView::NewAppView(NavigationView &nav) {
// Widget pointers
add_children({
&my_image,
});
}
```
#### OptionsField
OptionsField is a widget that allows you to create a field, in which you can change its value with the wheel on your portapack. Here you can find it's declaration and prototype:
```
OptionsField my_OptionsField_widget{
Point parent_pos,
int length,
options_t options
};
```
parent_pos is an array of two integer which tells where the top left corner of the widget should be positioned. The length is an integer which tells how many options you have into your options parameter. The options_t field is an array of options in which your portapack can choose to display.
For example, let's say you want an optionsFild called `my_optionsField`, with 3 options, positioned at 10 from top and 10 from left:
```
OptionsField my_optionsField{
{10,10}, // Coordinates are: int:x (px), int:y (px)
7, // Char length for option title
{
{"option1",0},
{"option2",1}, // Options {"KEY", int VALUE}
{"option3",2}
}
};
```
Note that the number following the "option_x" string value, should be the value that you could retrieve from the optionField with the function `my_optionsField.selected_index_value();`
#### NumberField
@ -555,439 +316,6 @@ NewAppView::NewAppView(NavigationView &nav) {
}
```
#### Waveform
Waveforms are used to display a sign wave from a signal source. **Note:** The X axes represents time while the Y axes represents amplitude. Here you can find it's declaration and prototype:
```
Labels my_waveform_widget{
Rect parent_rect,
int16_t * data,
uint32_t length,
int32_t offset,
bool digital,
Color color
};
```
For example, let's say you want a Waveform called `my_waveform`. You will need to add this to `apps/ui_newapp.hpp`:
```
Waveform my_waveform(
{0, 5*16, 240, 64}, // Coordinates are: int:x (px), int:y (px), int:width (px), int:height (px)
waveform_buffer, // RX data
128, // Length, how many elements in the waveform_buffer array
0, // Offset
false, // Digital
Color::white() // Sine wave color
);
```
**Note:** Colors are defined in [`firmware/common/ui.hpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/ui.hpp).
The data being displayed by `my_waveform` needs to be a `int16_t` array. You can declare this variable in `apps/ui_newapp.hpp`:
```
class NewAppView : public View
{
public:
...
private:
int16_t waveform_buffer[128]; // Data for Waveform
...
};
```
In `apps/ui_newapp.cpp` you'll need to add the `my_waveform` pointer to add_child() or add_children():
```
NewAppView::NewAppView(NavigationView &nav) {
// Widget pointers
add_children({
&my_waveform,
});
}
```
If your input data has a variable length you can use the `set_length(const uint32_t new_length)` function to update the Waveform:
```
my_waveform.set_length(9001) // THAT'S OVER 18KB!!
```
#### VuMeter
VuMeters are used to visually represent the sound intensity of an audio source. Here you can find it's declaration and prototype:
```
Labels my_vuMeter_widget{
Rect parent_rect,
uint32_t LEDs,
bool show_max
};
```
For example, let's say you want a VuMeter called `my_vuMeter`. You will need to add this to `apps/ui_newapp.hpp`:
```
VuMeter my_vuMeter(
{ 0*8, 1*8, 2*8, 33*8}, // Coordinates are: int:x (px), int:y (px), int:width (px), int:height (px)
12, // LEDs
true // Show max
);
```
In `apps/ui_newapp.cpp` you'll need to add the `my_vuMeter` pointer to add_child() or add_children():
```
NewAppView::NewAppView(NavigationView &nav) {
// Widget pointers
add_children({
&my_vuMeter,
});
}
```
To set the value for `my_vuMeter` use the `set_value(const uint32_t new_value)` function:
```
my_vuMeter.set_value(123); // Max is 255
```
## Access radio hardware
Now were going to dive into something a little more complex and bring radios into the mix. If youre not familiar with the LPC43xx please read up on the [Firmware Architecture](https://github.com/eried/portapack-mayhem/wiki/Firmware-Architecture) before continuing.
So far weve only been dealing with application code with is ran on the M0 of the LPC43xx. Now were going to start working with the baseband side of the codebase which is ran on the LPC43xxs M4 processor. Both of these processors use 8k worth of shared memory from `0x1008_8000` to `0x1008_a000` to pass messages to and from each other. The M0 controls **ALL** operations of the portapack while the M4 mostly handles the DSP and radio functions.
Complexitys aside with the two processors, accessing the HackRF's radio hardware has been simplified with helper classes such as the [`TransmitterModel`](https://github.com/eried/portapack-mayhem/blob/next/firmware/application/transmitter_model.cpp) and [`ReceiverModel`](https://github.com/eried/portapack-mayhem/blob/next/firmware/application/receiver_model.cpp). Both of these classes interface with the M4 baseband processes and gives us a more piratical way to control the radio.
Other classes and structs such as [`baseband api`](https://github.com/eried/portapack-mayhem/blob/next/firmware/application/baseband_api.cpp) and [`SharedMemory`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/portapack_shared_memory.hpp) also bridge the gap between the M0 and M4. Even though the M4's primary responsability is to handle DSP with the radio hardware the M0 can still be used to decode data. For example, classes found in `firmware/application/protocols/` like [`encoders`](https://github.com/eried/portapack-mayhem/blob/next/firmware/application/protocols/encoders.cpp) still send data too and from the two processors but also encodes and decodes messages at the higher level protocols.
### TX
The code bellow is an example OOK TX application using [`TransmitterModel`](https://github.com/eried/portapack-mayhem/blob/next/firmware/application/transmitter_model.cpp), [`encoders`](https://github.com/eried/portapack-mayhem/blob/next/firmware/application/protocols/encoders.cpp), and [`baseband`](https://github.com/eried/portapack-mayhem/blob/next/firmware/application/baseband_api.cpp).
#### ui_newapp.hpp
....
// Include TransmitterModel
#include "transmitter_model.hpp"
namespace ui
{
class NewAppView : public View // App class declaration
{
public:
....
private:
....
void start_tx(std::string& message); // Function declarations
void stop_tx();
void on_tx_progress(const uint32_t progress, const bool done);
MessageHandlerRegistration message_handler_tx_progress { // MessageHandlerRegistration class which relays
Message::ID::TXProgress, // Message::ID::TXProgress messages to your app
[this](const Message* const p) { // code from baseband. The Ternary Operator passes
const auto message = *reinterpret_cast<const TXProgressMessage*>(p); // an uint32_t progressvalue and a bool stating if
this->on_tx_progress(message.progress, message.done); // TX progress has be complete.
}};
};
}
#### ui_newapp.cpp
....
// Include encoders and baseband
#include "encoders.hpp"
#include "baseband_api.hpp"
using namespace portapack;
namespace ui
{
void NewAppView::start_tx(std::string& message) // Message input as "101101"
{
size_t bitstream_length = make_bitstream(message); // Function from encoders.hpp. Encodes then
// sets message to TX data pointer via...
// uint8_t * bitstream = shared_memory.bb_data.data;
// on line 34 of encoders.cpp and returns length.
transmitter_model.set_tuning_frequency(433920000); // Center frequency in hz
transmitter_model.set_sampling_rate(OOK_SAMPLERATE); // (2280000) Value from encoders.hpp
transmitter_model.set_rf_amp(true); // RF amp on
transmitter_model.set_baseband_bandwidth(1750000); // Bandwidth
transmitter_model.enable(); // Radio enable
baseband::set_ook_data( // ASK/OOK TX function
bitstream_length, // Length of message
OOK_SAMPLERATE / 1766, // Symble period (560us), Sample Rate / Baud
4, // Repeat transmissions
100 // Pause symbles
);
}
void NewAppView::stop_tx() // Stop TX function
{
transmitter_model.disable(); // Disable transmitter_model
// Add UI logic to let the user know the TX has stoped
}
NewAppView::NewAppView(NavigationView &nav) // Application Main
{
baseband::run_image(portapack::spi_flash::image_tag_ook); // M4 processor is being told to run proc_ook.cpp
// found in the firmware/baseband/ folder. M4 is
// then reset after this command.
// UI widget logic and calls to
// start_tx() goes here.
}
void NewAppView::on_tx_progress(const uint32_t progress, const bool done) // Function logic for when the message handler
{ // sends a TXProgressMessage.
if(done) {
stop_tx();
} else {
// UI logic, update ProgressBar with progress var
}
}
}
### RX
Building from the example code for TX lets talk about how the baseband processes are started on the M4. The application code on the M0 uses the baseband api `baseband::run_image` to tell the M4 to run a process. The baseband images are defined in [`spi_image.hpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/spi_image.hpp) as the struct `image_tag_t`. These structs and have a 4 char array tag being used as an ID. Below is an example `image_tag_t` for AFSK RX.
```
constexpr image_tag_t image_tag_afsk_rx { 'P', 'A', 'F', 'R' };
```
Under [`firmware/baseband/CMakeLists.txt`](https://github.com/eried/portapack-mayhem/blob/next/firmware/baseband/CMakeLists.txt) the following code snippet shows how the baseband processes are linked to the images defined in [`spi_image.hpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/spi_image.hpp).
```
### AFSK RX
set(MODE_CPPSRC
proc_afskrx.cpp
)
DeclareTargets(PAFR afskrx)
```
In `firmware/baseband`, process or "proc" code for the M4 processor like [`proc_afskrx.cpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/baseband/proc_afskrx.cpp) for example can be found here. These proc classes are ran by [`BasebandThread`](https://github.com/eried/portapack-mayhem/blob/next/firmware/baseband/baseband_thread.cpp). All proc classes inherent [`BasebandProcessor`](https://github.com/eried/portapack-mayhem/blob/next/firmware/baseband/baseband_processor.hpp) and must include the parent functions.
#### baseband_processor.hpp
```
#ifndef __BASEBAND_PROCESSOR_H__
#define __BASEBAND_PROCESSOR_H__
#include "dsp_types.hpp"
#include "channel_stats_collector.hpp"
#include "message.hpp"
class BasebandProcessor {
public:
virtual ~BasebandProcessor() = default; // Constructor
virtual void execute(const buffer_c8_t& buffer) = 0; // DSP code for TX/RX, shared_memory messages can be sent to
// M0 application code from this function.
virtual void on_message(const Message* const) { }; // Shared_memory messages from M0 application code
protected:
void feed_channel_stats(const buffer_c16_t& channel);
private:
ChannelStatsCollector channel_stats { };
};
```
Now that we have a better idea how M0 can drive the M4 lets talk about the Messaging between the two processors. The [`Message`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/message.hpp) class found under `firmware/common/`. Common code is used both by application (M0) and baseband (M4). Messages are handled by EventDispatcher found in [`event_m4.cpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/baseband/event_m4.cpp) for the baseband code and [`event_m0.cpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/application/event_m0.cpp) for the application code. Within the same file [`firmware/commen/message.hpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/message.hpp) you can find definitions for spacific message classes and ID. Bellow is an example message class for AFSK RX.
#### message.hpp
```
class Message {
public:
static constexpr size_t MAX_SIZE = 512;
enum class ID : uint32_t {
/* Assign consecutive IDs. IDs are used to index array. */
....
AFSKRxConfigure = 22,
AFSKData = 47,
....
};
}
....
// Application Messages (M0) -> Baseband (M4)
class AFSKRxConfigureMessage : public Message {
public:
constexpr AFSKRxConfigureMessage(
const uint32_t baudrate,
const uint32_t word_length,
const uint32_t trigger_value,
const bool trigger_word
) : Message { ID::AFSKRxConfigure },
baudrate(baudrate),
word_length(word_length),
trigger_value(trigger_value),
trigger_word(trigger_word)
{
}
const uint32_t baudrate;
const uint32_t word_length;
const uint32_t trigger_value;
const bool trigger_word;
};
// Baseband Messages (M4) -> Application (M0)
class AFSKDataMessage : public Message {
public:
constexpr AFSKDataMessage(
const bool is_data,
const uint32_t value
) : Message { ID::AFSKData },
is_data { is_data },
value { value }
{
}
bool is_data;
uint32_t value;
};
```
[`SharedMemory`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/portapack_shared_memory.hpp) found in `firmware/common/` is used to pass data inbetween the application code (M0) to the baseband code (M4). Below is an example from [`proc_afskrx.cpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/baseband/proc_afskrx.cpp) on how data is sent back to the application [`AFSKRxView`](https://github.com/eried/portapack-mayhem/blob/next/firmware/application/apps/ui_afsk_rx.cpp).
#### proc_afskrx.cpp
```
#include "portapack_shared_memory.hpp"
void AFSKRxProcessor::execute(const buffer_c8_t& buffer) {
.... // RX Logic
shared_memory.application_queue.push(data_message); // data_message is an AFSKDataMessage object
.... // MORE RX Logic
};
```
Continuing to use the same AFSK RX proc code above, below is an example of an RX AFSK application.
#### ui_newapp.hpp
....
// Include ReceiverModel
#include "receiver_model.hpp"
namespace ui
{
class NewAppView : public View // App class declaration
{
public:
....
private:
....
void start_rx(); // Function declarations
void stop_rx();
void on_data();
MessageHandlerRegistration message_handler_packet { // MessageHandlerRegistration class which relays
Message::ID::AFSKData, // relays messages to your app code from baseband.
[this](Message* const p) { // Every time you get a AFSKData message the
const auto message = static_cast<const AFSKDataMessage*>(p); // on_data() function will be triggered.
this->on_data(message->value, message->is_data);
}};
};
}
#### ui_newapp.cpp
....
#include "modems.hpp"
#include "audio.hpp"
#include "string_format.hpp"
#include "baseband_api.hpp"
#include "portapack_persistent_memory.hpp"
using namespace portapack;
using namespace modems;
namespace ui
{
void NewAppView::start_rx() // Start RX function
{
auto def_bell202 = &modem_defs[0]; // Bell202 baud rate
persistent_memory::set_modem_baudrate(def_bell202->baudrate); // Set RX modem to 1200 baud
serial_format_t serial_format; // Declare packet format for message
serial_format.data_bits = 7; // Bit length
serial_format.parity = EVEN; // Even or odd parity bit
serial_format.stop_bits = 1; // Stop bit
serial_format.bit_order = LSB_FIRST; // LSB or MSB first
persistent_memory::set_serial_format(serial_format); // Set RX packet format
baseband::set_afsk(persistent_memory::modem_baudrate(), 8, 0, false); // Baud rate, word length, trigger value, trigger word
receiver_model.set_tuning_frequency(433920000); // Center frequency in hz
receiver_model.set_sampling_rate(3072000); // Sampling rate
receiver_model.set_baseband_bandwidth(1750000); // Bandwidth
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); // Modulation
receiver_model.enable(); // Start RX
audio::set_rate(audio::Rate::Hz_24000); // Play RX audio to headphone jack
audio::output::start();
}
void NewAppView::stop_rx() // Stop RX function
{
audio::output::stop(); // Stop Audio
receiver_model.disable(); // Stop RX
baseband::shutdown(); // Stop M4 proc process
}
NewAppView::NewAppView(NavigationView &nav) // Application Main
{
baseband::run_image(portapack::spi_flash::image_tag_afsk_rx); // M4 processor is being told to run proc_afskrx.cpp
// found in the firmware/baseband/ folder. M4 is
// then reset after this command.
// UI widget logic and calls to start_rx()
// and stop_rx() goes here.
}
void NewAppView::on_data(uint32_t value, bool is_data) // Function logic for when the message handler
{ // sends a AFSKData.
if(is_data) {
// RX data handling Logic
}
}
}
# Wrap up
Bellow is an example "Hello World" application that shows off a few widgets and logic that controls their functions.