mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-07-04 22:33:36 -07:00
POCSAG
This commit is contained in:
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 |
Reference in New Issue
Block a user