This commit is contained in:
RogueMaster
2022-12-12 13:39:19 -05:00
parent 78621b59be
commit 640106273e
9 changed files with 626 additions and 0 deletions
+2
View File
@@ -21,6 +21,7 @@ Thank you to all the supporters!
- Updated: [Automatic shutdown on idle #1647 (By SHxKenzuto)](https://github.com/flipperdevices/flipperzero-firmware/pull/1647)
- Fixed Cannot Parse File error thanks to @ClaraCrazy
- Fixed snake scoring showing +7
- Added: [POCSAG (By Shmuma)](https://github.com/Shmuma/flipper-zero-pocsag)
## Install from Release
FLASH STOCK FIRST BEFORE UPDATING TO CUSTOM FIRMWARE!
@@ -219,6 +220,7 @@ $ ./fbt resources icons dolphin_ext
- [Paint (By n-o-T-I-n-s-a-n-e)](https://github.com/n-o-T-I-n-s-a-n-e)
- [Password Generator (By anakod)](https://github.com/anakod/flipper_passgen)
- [PicoPass Reader (By Bettse)](https://github.com/flipperdevices/flipperzero-firmware/pull/1366)
- [POCSAG (By Shmuma)](https://github.com/Shmuma/flipper-zero-pocsag)
- [POCSAG Pager (By XMasterx & Shmuma)](https://github.com/xMasterX/flipper-pager)
- [Pomodoro Timer (By sbrin)](https://github.com/sbrin/flipperzero_pomodoro)
- [RFID Fuzzer (By Ganapati)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/245) [Changes by Unleashed/xMasterX](https://github.com/DarkFlippers/unleashed-firmware)
@@ -0,0 +1,14 @@
App(
appid="POCSAG",
name="POCSAG",
apptype=FlipperAppType.EXTERNAL,
entry_point="pocsag",
cdefines=["APP_POCSAG"],
requires=[
"gui",
],
stack_size=1 * 1024,
order=90,
fap_icon="pocsag.png",
fap_category="Tools",
)
@@ -0,0 +1,111 @@
#include <furi.h>
#include <furi_hal.h>
#include <cc1101.h>
#include <furi_hal_subghz_configs.h>
static const uint8_t furi_hal_subghz_preset_2fsk_dev9_5khz_async_regs[][2] = {
/* GPIO GD0 */
{CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input
/* Frequency Synthesizer Control */
{CC1101_FSCTRL1, 0x06}, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz
/* Packet engine */
{CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening
{CC1101_PKTCTRL1, 0x04},
// // Modem Configuration
{CC1101_MDMCFG0, 0x00},
{CC1101_MDMCFG1, 0x02},
{CC1101_MDMCFG2, 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized)
{CC1101_MDMCFG3, 0x83}, // Data rate is 4.79794 kBaud
{CC1101_MDMCFG4, 0x67}, //Rx BW filter is 270.833333 kHz
{CC1101_DEVIATN, 0x24}, //Deviation 9.5 kHz
/* Main Radio Control State Machine */
{CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us)
/* Frequency Offset Compensation Configuration */
{CC1101_FOCCFG,
0x16}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off
/* Automatic Gain Control */
{CC1101_AGCCTRL0,
0x91}, //10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary
{CC1101_AGCCTRL1,
0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET
{CC1101_AGCCTRL2, 0x07}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB
/* Wake on radio and timeouts control */
{CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours
/* Frontend configuration */
{CC1101_FREND0, 0x10}, // Adjusts current TX LO buffer
{CC1101_FREND1, 0x56},
/* End */
{0, 0},
};
//
//
//static const uint8_t furi_hal_subghz_preset_ook_650khz_async_regs[][2] = {
// // https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz-group/sub-1-ghz/f/sub-1-ghz-forum/382066/cc1101---don-t-know-the-correct-registers-configuration
//
// /* GPIO GD0 */
// {CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input
//
// /* FIFO and internals */
// {CC1101_FIFOTHR, 0x07}, // The only important bit is ADC_RETENTION
//
// /* Packet engine */
// {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening
//
// /* Frequency Synthesizer Control */
// {CC1101_FSCTRL1, 0x06}, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz
//
// // Modem Configuration
// {CC1101_MDMCFG0, 0x00}, // Channel spacing is 25kHz
// {CC1101_MDMCFG1, 0x00}, // Channel spacing is 25kHz
// {CC1101_MDMCFG2, 0x30}, // Format ASK/OOK, No preamble/sync
// {CC1101_MDMCFG3, 0x32}, // Data rate is 3.79372 kBaud
// {CC1101_MDMCFG4, 0x17}, // Rx BW filter is 650.000kHz
//
// /* Main Radio Control State Machine */
// {CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us)
//
// /* Frequency Offset Compensation Configuration */
// {CC1101_FOCCFG,
// 0x18}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off
//
// /* Automatic Gain Control */
// // {CC1101_AGCTRL0,0x40}, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary
// // {CC1101_AGCTRL1,0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET
// // {CC1101_AGCCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB
// //MAGN_TARGET for RX filter BW =< 100 kHz is 0x3. For higher RX filter BW's MAGN_TARGET is 0x7.
// {CC1101_AGCCTRL0,
// 0x91}, // 10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary
// {CC1101_AGCCTRL1,
// 0x0}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET
// {CC1101_AGCCTRL2, 0x07}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB
//
// /* Wake on radio and timeouts control */
// {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours
//
// /* Frontend configuration */
// {CC1101_FREND0, 0x11}, // Adjusts current TX LO buffer + high is PATABLE[1]
// {CC1101_FREND1, 0xB6}, //
//
// /* End */
// {0, 0},
//};
void pocsag_hal_reset() {
furi_hal_subghz_reset();
furi_hal_subghz_idle();
furi_hal_subghz_load_registers((uint8_t*)furi_hal_subghz_preset_2fsk_dev9_5khz_async_regs);
furi_hal_subghz_load_patable(furi_hal_subghz_preset_2fsk_async_patable);
// furi_hal_subghz_load_registers((uint8_t*)furi_hal_subghz_preset_ook_650khz_async_regs);
// furi_hal_subghz_load_patable(furi_hal_subghz_preset_ook_async_patable);
}
@@ -0,0 +1,3 @@
#pragma once
void pocsag_hal_reset();
@@ -0,0 +1,258 @@
#include "pocsag_tx_rx_worker.h"
#include "pocsag_hal.h"
#include <furi.h>
#define TAG "POCSAGTxRxWorker"
#define POCSAG_TXRX_WORKER_BUF_SIZE 2048
//you can not set more than 62 because it will not fit into the FIFO CC1101
#define POCSAG_TXRX_WORKER_MAX_TXRX_SIZE 60
#define POCSAG_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF 40
struct PocsagTxRxWorker {
FuriThread* thread;
FuriStreamBuffer* stream_tx;
FuriStreamBuffer* stream_rx;
volatile bool worker_running;
volatile bool worker_stoping;
PocsagTxRxWorkerStatus status;
uint32_t frequency;
PocsagTxRxWorkerCallbackHaveRead callback_have_read;
void* context_have_read;
};
bool pocsag_tx_rx_worker_write(PocsagTxRxWorker* instance, uint8_t* data, size_t size) {
furi_assert(instance);
bool ret = false;
size_t stream_tx_free_byte = furi_stream_buffer_spaces_available(instance->stream_tx);
if(size && (stream_tx_free_byte >= size)) {
if(furi_stream_buffer_send(
instance->stream_tx, data, size, POCSAG_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF) ==
size) {
ret = true;
}
}
return ret;
}
size_t pocsag_tx_rx_worker_available(PocsagTxRxWorker* instance) {
furi_assert(instance);
return furi_stream_buffer_bytes_available(instance->stream_rx);
}
size_t pocsag_tx_rx_worker_read(PocsagTxRxWorker* instance, uint8_t* data, size_t size) {
furi_assert(instance);
return furi_stream_buffer_receive(instance->stream_rx, data, size, 0);
}
void pocsag_tx_rx_worker_set_callback_have_read(
PocsagTxRxWorker* instance,
PocsagTxRxWorkerCallbackHaveRead callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
furi_assert(context);
instance->callback_have_read = callback;
instance->context_have_read = context;
}
bool pocsag_tx_rx_worker_rx(PocsagTxRxWorker* instance, uint8_t* data, uint8_t* size) {
uint8_t timeout = 100;
bool ret = false;
if(instance->status != PocsagTxRxWorkerStatusRx) {
furi_hal_subghz_rx();
instance->status = PocsagTxRxWorkerStatusRx;
furi_delay_tick(1);
}
//waiting for reception to complete
while(furi_hal_gpio_read(&gpio_cc1101_g0)) {
furi_delay_tick(1);
if(!--timeout) {
FURI_LOG_W(TAG, "RX cc1101_g0 timeout");
furi_hal_subghz_flush_rx();
furi_hal_subghz_rx();
break;
}
}
if(furi_hal_subghz_rx_pipe_not_empty()) {
FURI_LOG_I(
TAG,
"RSSI: %03.1fdbm LQI: %d",
(double)furi_hal_subghz_get_rssi(),
furi_hal_subghz_get_lqi());
if(furi_hal_subghz_is_rx_data_crc_valid()) {
furi_hal_subghz_read_packet(data, size);
ret = true;
}
furi_hal_subghz_flush_rx();
furi_hal_subghz_rx();
}
return ret;
}
void pocsag_tx_rx_worker_tx(PocsagTxRxWorker* instance, uint8_t* data, size_t size) {
uint8_t timeout = 200;
if(instance->status != PocsagTxRxWorkerStatusIDLE) {
furi_hal_subghz_idle();
}
furi_hal_subghz_write_packet(data, size);
furi_hal_subghz_tx(); //start send
instance->status = PocsagTxRxWorkerStatusTx;
while(!furi_hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be set -> sync transmitted
furi_delay_tick(1);
if(!--timeout) {
FURI_LOG_W(TAG, "TX !cc1101_g0 timeout");
break;
}
}
while(furi_hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be cleared -> end of packet
furi_delay_tick(1);
if(!--timeout) {
FURI_LOG_W(TAG, "TX cc1101_g0 timeout");
break;
}
}
furi_hal_subghz_idle();
instance->status = PocsagTxRxWorkerStatusIDLE;
}
/** Worker thread
*
* @param context
* @return exit code
*/
static int32_t pocsag_tx_rx_worker_thread(void* context) {
PocsagTxRxWorker* instance = context;
FURI_LOG_I(TAG, "Worker start");
// furi_hal_subghz_reset();
// furi_hal_subghz_idle();
// furi_hal_subghz_load_preset(FuriHalSubGhzPresetGFSK9_99KbAsync);
pocsag_hal_reset();
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
furi_hal_subghz_set_frequency_and_path(instance->frequency);
furi_hal_subghz_flush_rx();
uint8_t data[POCSAG_TXRX_WORKER_MAX_TXRX_SIZE + 1] = {0};
size_t size_tx = 0;
uint8_t size_rx[1] = {0};
uint8_t timeout_tx = 0;
bool callback_rx = false;
while(instance->worker_running) {
//transmit
size_tx = furi_stream_buffer_bytes_available(instance->stream_tx);
if(size_tx > 0 && !timeout_tx) {
timeout_tx = 10; //20ms
if(size_tx > POCSAG_TXRX_WORKER_MAX_TXRX_SIZE) {
furi_stream_buffer_receive(
instance->stream_tx,
&data,
POCSAG_TXRX_WORKER_MAX_TXRX_SIZE,
POCSAG_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF);
pocsag_tx_rx_worker_tx(instance, data, POCSAG_TXRX_WORKER_MAX_TXRX_SIZE);
} else {
//todo checking that he managed to write all the data to the TX buffer
furi_stream_buffer_receive(
instance->stream_tx, &data, size_tx, POCSAG_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF);
pocsag_tx_rx_worker_tx(instance, data, size_tx);
}
} else {
//recive
if(pocsag_tx_rx_worker_rx(instance, data, size_rx)) {
if(furi_stream_buffer_spaces_available(instance->stream_rx) >= size_rx[0]) {
if(instance->callback_have_read &&
furi_stream_buffer_bytes_available(instance->stream_rx) == 0) {
callback_rx = true;
}
//todo checking that he managed to write all the data to the RX buffer
furi_stream_buffer_send(
instance->stream_rx,
&data,
size_rx[0],
POCSAG_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF);
if(callback_rx) {
instance->callback_have_read(instance->context_have_read);
callback_rx = false;
}
} else {
//todo RX buffer overflow
}
}
}
if(timeout_tx) timeout_tx--;
furi_delay_tick(1);
}
furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);
furi_hal_subghz_sleep();
FURI_LOG_I(TAG, "Worker stop");
return 0;
}
PocsagTxRxWorker* pocsag_tx_rx_worker_alloc() {
PocsagTxRxWorker* instance = malloc(sizeof(PocsagTxRxWorker));
instance->thread =
furi_thread_alloc_ex("PocsagTxRxWorker", 2048, pocsag_tx_rx_worker_thread, instance);
instance->stream_tx =
furi_stream_buffer_alloc(sizeof(uint8_t) * POCSAG_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t));
instance->stream_rx =
furi_stream_buffer_alloc(sizeof(uint8_t) * POCSAG_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t));
instance->status = PocsagTxRxWorkerStatusIDLE;
instance->worker_stoping = true;
return instance;
}
void pocsag_tx_rx_worker_free(PocsagTxRxWorker* instance) {
furi_assert(instance);
furi_assert(!instance->worker_running);
furi_stream_buffer_free(instance->stream_tx);
furi_stream_buffer_free(instance->stream_rx);
furi_thread_free(instance->thread);
free(instance);
}
bool pocsag_tx_rx_worker_start(PocsagTxRxWorker* instance, uint32_t frequency) {
furi_assert(instance);
furi_assert(!instance->worker_running);
bool res = false;
furi_stream_buffer_reset(instance->stream_tx);
furi_stream_buffer_reset(instance->stream_rx);
instance->worker_running = true;
if(furi_hal_region_is_frequency_allowed(frequency)) {
instance->frequency = frequency;
res = true;
}
furi_thread_start(instance->thread);
return res;
}
void pocsag_tx_rx_worker_stop(PocsagTxRxWorker* instance) {
furi_assert(instance);
furi_assert(instance->worker_running);
instance->worker_running = false;
furi_thread_join(instance->thread);
}
bool pocsag_tx_rx_worker_is_running(PocsagTxRxWorker* instance) {
furi_assert(instance);
return instance->worker_running;
}
@@ -0,0 +1,89 @@
#pragma once
#include <furi_hal.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*PocsagTxRxWorkerCallbackHaveRead)(void* context);
typedef struct PocsagTxRxWorker PocsagTxRxWorker;
typedef enum {
PocsagTxRxWorkerStatusIDLE,
PocsagTxRxWorkerStatusTx,
PocsagTxRxWorkerStatusRx,
} PocsagTxRxWorkerStatus;
/**
* SubGhzTxRxWorker, add data to transfer
* @param instance Pointer to a SubGhzTxRxWorker instance
* @param data *data
* @param size data size
* @return bool true if ok
*/
bool pocsag_tx_rx_worker_write(PocsagTxRxWorker* instance, uint8_t* data, size_t size);
/**
* SubGhzTxRxWorker, get available data
* @param instance Pointer to a SubGhzTxRxWorker instance
* @return size_t data size
*/
size_t pocsag_tx_rx_worker_available(PocsagTxRxWorker* instance);
/**
* SubGhzTxRxWorker, read data
* @param instance Pointer to a SubGhzTxRxWorker instance
* @param data *data
* @param size max data size, which can be read
* @return size_t data size, how much is actually read
*/
size_t pocsag_tx_rx_worker_read(PocsagTxRxWorker* instance, uint8_t* data, size_t size);
/**
* Сallback SubGhzTxRxWorker when there is data to read in an empty buffer
* @param instance Pointer to a SubGhzTxRxWorker instance
* @param callback SubGhzTxRxWorkerCallbackHaveRead callback
* @param context
*/
void pocsag_tx_rx_worker_set_callback_have_read(
PocsagTxRxWorker* instance,
PocsagTxRxWorkerCallbackHaveRead callback,
void* context);
/**
* Allocate SubGhzTxRxWorker
* @return SubGhzTxRxWorker* Pointer to a SubGhzTxRxWorker instance
*/
PocsagTxRxWorker* pocsag_tx_rx_worker_alloc();
/**
* Free PocsagTxRxWorker
* @param instance Pointer to a SubGhzTxRxWorker instance
*/
void pocsag_tx_rx_worker_free(PocsagTxRxWorker* instance);
/**
* Start SubGhzTxRxWorker
* @param instance Pointer to a SubGhzTxRxWorker instance
* @return bool - true if ok
*/
bool pocsag_tx_rx_worker_start(PocsagTxRxWorker* instance, uint32_t frequency);
/**
* Stop SubGhzTxRxWorker
* @param instance Pointer to a SubGhzTxRxWorker instance
*/
void pocsag_tx_rx_worker_stop(PocsagTxRxWorker* instance);
/**
* Check if worker is running
* @param instance Pointer to a SubGhzTxRxWorker instance
* @return bool - true if running
*/
bool pocsag_tx_rx_worker_is_running(PocsagTxRxWorker* instance);
#ifdef __cplusplus
}
#endif
+124
View File
@@ -0,0 +1,124 @@
#include "pocsag.h"
#include "core/pocsag_hal.h"
#define TAG "POCSAG"
enum {
PocsagSubmenuIndexReceive,
PocsagSubmenuIndexStopReceive,
PocsagSubmenuIndexTestSend,
};
#define FREQUENCY 433920000
//439987500
uint32_t pocsag_exit(void* context) {
UNUSED(context);
return VIEW_NONE;
}
void subghz_tx_rx_read_callback(void* context) {
furi_assert(context);
PocsagApp* app = context;
uint8_t data[16];
size_t cnt;
FURI_LOG_I(TAG, "Callback");
cnt = pocsag_tx_rx_worker_read(app->subghz_tx_rx, data, sizeof(data));
FURI_LOG_I(TAG, "%d", cnt);
}
void pocsag_submenu_callback(void* context, uint32_t index) {
uint8_t buf[] = {
1,2,3,4,5,6,7,8,9,0,
1,2,3,4,5,6,7,8,9,0,
1,2,3,4,5,6,7,8,9,0,
1,2,3,4,5,6,7,8,9,0,
1,2,3,4,5,6,7,8,9,0,
1,2,3,4,5,6,7,8,9,0,
1,2,3,4,5,6,7,8,9,0,
1,2,3,4,5,6,7,8,9,0,
1,2,3,4,5,6,7,8,9,0,
1,2,3,4,5,6,7,8,9,0,
};
furi_assert(context);
PocsagApp* app = context;
switch (index) {
case PocsagSubmenuIndexReceive:
if (pocsag_tx_rx_worker_is_running(app->subghz_tx_rx))
FURI_LOG_I(TAG, "Receiver is already running...");
else {
FURI_LOG_I(TAG, "Start receiver...");
furi_hal_power_suppress_charge_enter();
pocsag_tx_rx_worker_start(app->subghz_tx_rx, FREQUENCY);
}
break;
case PocsagSubmenuIndexStopReceive:
FURI_LOG_I(TAG, "Stop receiver...");
pocsag_tx_rx_worker_stop(app->subghz_tx_rx);
furi_hal_power_suppress_charge_exit();
break;
case PocsagSubmenuIndexTestSend:
if (pocsag_tx_rx_worker_is_running(app->subghz_tx_rx)) {
pocsag_tx_rx_worker_write(app->subghz_tx_rx, buf, sizeof(buf));
}
break;
}
}
PocsagApp* pocsag_app_alloc() {
PocsagApp* app = malloc(sizeof(PocsagApp));
app->gui = furi_record_open(RECORD_GUI);
app->view_dispatcher = view_dispatcher_alloc();
view_dispatcher_enable_queue(app->view_dispatcher);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
app->submenu = submenu_alloc();
submenu_add_item(app->submenu, "Receive", PocsagSubmenuIndexReceive, pocsag_submenu_callback, app);
submenu_add_item(app->submenu, "Stop receive", PocsagSubmenuIndexStopReceive, pocsag_submenu_callback, app);
submenu_add_item(app->submenu, "Test send", PocsagSubmenuIndexTestSend, pocsag_submenu_callback, app);
view_set_previous_callback(submenu_get_view(app->submenu), pocsag_exit);
view_dispatcher_add_view(
app->view_dispatcher, PocsagViewSubmenu, submenu_get_view(app->submenu));
app->view_id = PocsagViewSubmenu;
view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id);
app->subghz_tx_rx = pocsag_tx_rx_worker_alloc();
pocsag_tx_rx_worker_set_callback_have_read(app->subghz_tx_rx, subghz_tx_rx_read_callback, app);
// pocsag_hal_reset();
return app;
}
void pocsag_app_free(PocsagApp* app) {
furi_assert(app);
view_dispatcher_remove_view(app->view_dispatcher, PocsagViewSubmenu);
submenu_free(app->submenu);
furi_record_close(RECORD_GUI);
app->gui = NULL;
if(pocsag_tx_rx_worker_is_running(app->subghz_tx_rx)) {
pocsag_tx_rx_worker_stop(app->subghz_tx_rx);
furi_hal_power_suppress_charge_exit();
}
pocsag_tx_rx_worker_free(app->subghz_tx_rx);
free(app);
}
int32_t pocsag(void* p) {
UNUSED(p);
PocsagApp* app = pocsag_app_alloc();
view_dispatcher_run(app->view_dispatcher);
pocsag_app_free(app);
return 0;
}
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include <furi.h>
#include <gui/gui.h>
#include <gui/view.h>
#include <gui/view_dispatcher.h>
#include <notification/notification.h>
#include <gui/modules/submenu.h>
#include "core/pocsag_tx_rx_worker.h"
typedef struct {
Gui* gui;
ViewDispatcher* view_dispatcher;
Submenu* submenu;
uint32_t view_id;
PocsagTxRxWorker* subghz_tx_rx;
} PocsagApp;
enum {
PocsagViewSubmenu,
};
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB