Skip to content

Commit a1de780

Browse files
authored
Add files via upload
1 parent a14c6f1 commit a1de780

4 files changed

Lines changed: 351 additions & 0 deletions

File tree

application.fam

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
App(
2+
appid="pmrscan",
3+
name="PMRScan",
4+
apptype=FlipperAppType.EXTERNAL,
5+
entry_point="pmrscan_app",
6+
requires=["gui", "subghz", "furi"],
7+
cdefines=["APP_PMRSCAN"],
8+
stack_size=2 * 1024,
9+
fap_category="Sub-GHz",
10+
fap_version="0.1",
11+
fap_icon_assets="assets",
12+
fap_author="MeiosisKMZ",
13+
fap_description="PMR446 scanner for Flipper",
14+
fap_icon="icons/pmrscan.png",
15+
fap_weburl="https://github.com/MeiosisKMZ/",
16+
)

icons/pmrscan.png

523 Bytes
Loading

pmrscan_app.c

Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
#include "pmrscan_app.h"
2+
#include <furi.h>
3+
#include <furi_hal.h>
4+
#include <furi_hal_gpio.h>
5+
#include <gui/elements.h>
6+
#include <furi_hal_speaker.h>
7+
#include <subghz/devices/devices.h>
8+
9+
#define TAG "PMRScanApp"
10+
11+
#define PMRSCAN_DEFAULT_RSSI (-100.0f)
12+
#define PMRSCAN_DEFAULT_SENSITIVITY (-85.0f)
13+
14+
#define PMRSCAN_VOLUME_QUIET 0.0f
15+
#define PMRSCAN_VOLUME_LOUD 1.0f
16+
17+
#define SUBGHZ_DEVICE_NAME "cc1101_int"
18+
19+
const uint32_t freqs[] = {
20+
0,
21+
446006250,
22+
446018750,
23+
446031250,
24+
446043750,
25+
446056250,
26+
446068750,
27+
446081250,
28+
446093750,
29+
446106250,
30+
446118750,
31+
446131250,
32+
446143750,
33+
446156250,
34+
446168750,
35+
446181250,
36+
446193750,
37+
};
38+
const int num_channels = sizeof(freqs) / sizeof(freqs[0]);
39+
const int min_channel = 1;
40+
const int max_channel = num_channels - 1;
41+
42+
static void speaker_mute(PMRScanApp* app) {
43+
if(app->speaker_acquired && furi_hal_speaker_is_mine()) {
44+
subghz_devices_set_async_mirror_pin(app->radio_device, NULL);
45+
furi_hal_speaker_set_volume(PMRSCAN_VOLUME_QUIET);
46+
}
47+
}
48+
49+
static void speaker_loud(PMRScanApp* app) {
50+
if(app->speaker_acquired && furi_hal_speaker_is_mine()) {
51+
subghz_devices_set_async_mirror_pin(app->radio_device, &gpio_speaker);
52+
furi_hal_speaker_set_volume(PMRSCAN_VOLUME_LOUD);
53+
}
54+
}
55+
56+
static void pmrscan_draw_callback(Canvas* canvas, void* context) {
57+
furi_assert(canvas);
58+
furi_assert(context);
59+
PMRScanApp* app = (PMRScanApp*)context;
60+
canvas_clear(canvas);
61+
canvas_set_font(canvas, FontPrimary);
62+
canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, "PMRScan");
63+
64+
canvas_set_font(canvas, FontSecondary);
65+
#define BUFLEN 32
66+
char buf[BUFLEN + 1] = "";
67+
snprintf(buf, BUFLEN, "< Chan %ld (%.4f) >", app->freq_channel, (double)app->frequency / 1000000);
68+
canvas_draw_str_aligned(canvas, 64, 18, AlignCenter, AlignTop, buf);
69+
70+
snprintf(buf, BUFLEN, "RSSI: %.2f", (double)app->rssi);
71+
canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignTop, buf);
72+
73+
snprintf(buf, BUFLEN, "^ Sens: %.2f V", (double)app->sensitivity);
74+
canvas_draw_str_aligned(canvas, 64, 42, AlignCenter, AlignTop, buf);
75+
76+
canvas_draw_str_aligned(canvas, 64, 54, AlignCenter, AlignTop, app->scanning ? "Scanning ()" : "Locked ()");
77+
}
78+
79+
static void pmrscan_input_callback(InputEvent* input_event, void* context) {
80+
furi_assert(context);
81+
FuriMessageQueue* event_queue = context;
82+
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
83+
FURI_LOG_D(TAG, "Exit pmrscan_input_callback");
84+
}
85+
86+
static void pmrscan_rx_callback(const void* data, size_t size, void* context) {
87+
UNUSED(data);
88+
UNUSED(context);
89+
UNUSED(size);
90+
}
91+
92+
static void pmrscan_update_rssi(PMRScanApp* app) {
93+
furi_assert(app);
94+
if(app->radio_device) {
95+
app->rssi = subghz_devices_get_rssi(app->radio_device);
96+
} else {
97+
FURI_LOG_E(TAG, "Radio device is NULL");
98+
app->rssi = PMRSCAN_DEFAULT_RSSI;
99+
}
100+
}
101+
102+
static bool pmrscan_init_subghz(PMRScanApp* app) {
103+
furi_assert(app);
104+
subghz_devices_init();
105+
106+
const SubGhzDevice* device = subghz_devices_get_by_name(SUBGHZ_DEVICE_NAME);
107+
if(!device) {
108+
FURI_LOG_E(TAG, "Failed to get SubGhzDevice");
109+
return false;
110+
}
111+
FURI_LOG_I(TAG, "SubGhzDevice obtained: %s", subghz_devices_get_name(device));
112+
113+
app->radio_device = device;
114+
115+
subghz_devices_begin(device);
116+
subghz_devices_reset(device);
117+
if(!subghz_devices_is_frequency_valid(device, app->frequency)) {
118+
FURI_LOG_E(TAG, "Invalid frequency: %lu", app->frequency);
119+
return false;
120+
}
121+
subghz_devices_load_preset(device, FuriHalSubGhzPreset2FSKDev238Async, NULL);
122+
subghz_devices_set_frequency(device, app->frequency);
123+
subghz_devices_start_async_rx(device, pmrscan_rx_callback, app);
124+
if(furi_hal_speaker_acquire(30)) {
125+
app->speaker_acquired = true;
126+
speaker_mute(app);
127+
} else {
128+
app->speaker_acquired = false;
129+
FURI_LOG_E(TAG, "Failed to acquire speaker");
130+
}
131+
return true;
132+
}
133+
134+
static uint32_t pmrscan_next_channel(PMRScanApp* app) {
135+
uint32_t channel = app->freq_channel;
136+
137+
if(app->scan_dir == ScanDirDown)
138+
channel -= 1;
139+
else
140+
channel += 1;
141+
142+
if(channel < min_channel)
143+
channel = max_channel;
144+
else if(channel > max_channel)
145+
channel = min_channel;
146+
147+
return channel;
148+
}
149+
150+
static void pmrscan_process_scanning(PMRScanApp* app) {
151+
furi_assert(app);
152+
pmrscan_update_rssi(app);
153+
bool signal_detected = (app->rssi > app->sensitivity);
154+
155+
if(signal_detected) {
156+
if(app->scanning) {
157+
app->scanning = false;
158+
}
159+
} else {
160+
if(!app->scanning) {
161+
app->scanning = true;
162+
}
163+
}
164+
165+
if(app->scanning) {
166+
speaker_mute(app);
167+
} else {
168+
speaker_loud(app);
169+
}
170+
171+
uint32_t new_channel = pmrscan_next_channel(app);
172+
uint32_t new_frequency = freqs[new_channel];
173+
174+
if(!subghz_devices_is_frequency_valid(app->radio_device, new_frequency)) {
175+
new_channel = min_channel;
176+
new_frequency = freqs[new_channel];
177+
}
178+
179+
subghz_devices_flush_rx(app->radio_device);
180+
subghz_devices_stop_async_rx(app->radio_device);
181+
182+
subghz_devices_idle(app->radio_device);
183+
app->freq_channel = new_channel;
184+
app->frequency = new_frequency;
185+
subghz_devices_set_frequency(app->radio_device, app->frequency);
186+
187+
subghz_devices_start_async_rx(app->radio_device, pmrscan_rx_callback, app);
188+
}
189+
190+
PMRScanApp* pmrscan_app_alloc() {
191+
PMRScanApp* app = malloc(sizeof(PMRScanApp));
192+
if(!app) {
193+
FURI_LOG_E(TAG, "Failed to allocate PMRScanApp");
194+
return NULL;
195+
}
196+
197+
app->view_port = view_port_alloc();
198+
199+
app->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
200+
201+
app->running = true;
202+
app->freq_channel = min_channel;
203+
app->frequency = freqs[app->freq_channel];
204+
app->rssi = PMRSCAN_DEFAULT_RSSI;
205+
app->sensitivity = PMRSCAN_DEFAULT_SENSITIVITY;
206+
app->scanning = true;
207+
app->scan_dir = ScanDirUp;
208+
app->speaker_acquired = false;
209+
app->radio_device = NULL;
210+
211+
view_port_draw_callback_set(app->view_port, pmrscan_draw_callback, app);
212+
213+
view_port_input_callback_set(app->view_port, pmrscan_input_callback, app->event_queue);
214+
215+
app->gui = furi_record_open(RECORD_GUI);
216+
217+
gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen);
218+
219+
return app;
220+
}
221+
222+
void pmrscan_app_free(PMRScanApp* app) {
223+
furi_assert(app);
224+
if(app->speaker_acquired && furi_hal_speaker_is_mine()) {
225+
subghz_devices_set_async_mirror_pin(app->radio_device, NULL);
226+
furi_hal_speaker_set_volume(PMRSCAN_VOLUME_LOUD);
227+
furi_hal_speaker_release();
228+
app->speaker_acquired = false;
229+
}
230+
231+
if(app->radio_device) {
232+
subghz_devices_flush_rx(app->radio_device);
233+
subghz_devices_stop_async_rx(app->radio_device);
234+
subghz_devices_idle(app->radio_device);
235+
subghz_devices_sleep(app->radio_device);
236+
subghz_devices_end(app->radio_device);
237+
}
238+
239+
subghz_devices_deinit();
240+
gui_remove_view_port(app->gui, app->view_port);
241+
view_port_free(app->view_port);
242+
243+
furi_message_queue_free(app->event_queue);
244+
245+
furi_record_close(RECORD_GUI);
246+
247+
free(app);
248+
}
249+
250+
int32_t pmrscan_app(void* p) {
251+
UNUSED(p);
252+
FURI_LOG_I(TAG, "Enter pmrscan_app");
253+
254+
PMRScanApp* app = pmrscan_app_alloc();
255+
if(!app) {
256+
FURI_LOG_E(TAG, "Failed to allocate app");
257+
return 1;
258+
}
259+
260+
if(!pmrscan_init_subghz(app)) {
261+
FURI_LOG_E(TAG, "Failed to initialize SubGHz");
262+
pmrscan_app_free(app);
263+
return 255;
264+
}
265+
266+
InputEvent event;
267+
while(app->running) {
268+
if(app->scanning) {
269+
speaker_mute(app);
270+
pmrscan_process_scanning(app);
271+
} else {
272+
speaker_loud(app);
273+
pmrscan_update_rssi(app);
274+
}
275+
276+
if(furi_message_queue_get(app->event_queue, &event, 10) == FuriStatusOk) {
277+
if(event.type == InputTypeShort) {
278+
if(event.key == InputKeyOk) {
279+
app->scanning = !app->scanning;
280+
FURI_LOG_I(TAG, "Toggled scanning: %d", app->scanning);
281+
} else if(event.key == InputKeyUp) {
282+
app->sensitivity += 1.0f;
283+
FURI_LOG_I(TAG, "Increased sensitivity: %f", (double)app->sensitivity);
284+
} else if(event.key == InputKeyDown) {
285+
app->sensitivity -= 1.0f;
286+
FURI_LOG_I(TAG, "Decreased sensitivity: %f", (double)app->sensitivity);
287+
} else if(event.key == InputKeyLeft) {
288+
app->scan_dir = ScanDirDown;
289+
FURI_LOG_I(TAG, "Scan direction set to down");
290+
} else if(event.key == InputKeyRight) {
291+
app->scan_dir = ScanDirUp;
292+
FURI_LOG_I(TAG, "Scan direction set to up");
293+
} else if(event.key == InputKeyBack) {
294+
app->running = false;
295+
FURI_LOG_I(TAG, "Exiting app");
296+
}
297+
}
298+
}
299+
300+
view_port_update(app->view_port);
301+
furi_delay_ms(500);
302+
}
303+
304+
pmrscan_app_free(app);
305+
return 0;
306+
}

pmrscan_app.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#pragma once
2+
3+
#include <gui/gui.h>
4+
#include <gui/view_port.h>
5+
#include <subghz/devices/devices.h>
6+
7+
typedef enum {
8+
ScanDirDown,
9+
ScanDirUp,
10+
} ScanDir;
11+
12+
typedef struct {
13+
Gui* gui;
14+
ViewPort* view_port;
15+
FuriMessageQueue* event_queue;
16+
bool running;
17+
uint32_t freq_channel;
18+
uint32_t frequency;
19+
float rssi;
20+
float sensitivity;
21+
bool scanning;
22+
ScanDir scan_dir;
23+
const SubGhzDevice* radio_device;
24+
bool speaker_acquired;
25+
} PMRScanApp;
26+
27+
PMRScanApp* pmrscan_app_alloc(void);
28+
void pmrscan_app_free(PMRScanApp* app);
29+
int32_t pmrscan_app(void* p);

0 commit comments

Comments
 (0)