Add nrf24 channel scanner

This commit is contained in:
Sil333033
2023-08-19 12:28:30 +02:00
parent 1eb445d217
commit 8015ea29a2
7 changed files with 518 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
App(
appid="nrf24channelscanner",
name="[NRF24] Channel scanner",
apptype=FlipperAppType.EXTERNAL,
entry_point="nrf24channelscanner_main",
stack_size=2 * 1024,
requires=["gui"],
fap_category="GPIO",
fap_version=(1, 1),
fap_icon_assets="images",
fap_icon="fapicon.png",
fap_description="Scans 2.4Ghz frequency for usage data.",
fap_private_libs=[
Lib(
name="nrf24",
sources=[
"nrf24.c",
],
),
],
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 967 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 978 B

View File

@@ -0,0 +1,117 @@
#include "nrf24.h"
#include <furi.h>
#include <furi_hal.h>
#include <furi_hal_resources.h>
#include <assert.h>
#include <string.h>
void nrf24_init() {
// this is needed if multiple SPI devices are connected to the same bus but with different CS pins
if(XTREME_SETTINGS()->spi_nrf24_handle == SpiDefault) {
furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeOutputPushPull);
furi_hal_gpio_write(&gpio_ext_pc3, true);
} else if(XTREME_SETTINGS()->spi_nrf24_handle == SpiExtra) {
furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeOutputPushPull);
furi_hal_gpio_write(&gpio_ext_pa4, true);
}
furi_hal_spi_bus_handle_init(nrf24_HANDLE);
furi_hal_spi_acquire(nrf24_HANDLE);
furi_hal_gpio_init(nrf24_CE_PIN, GpioModeOutputPushPull, GpioPullUp, GpioSpeedVeryHigh);
furi_hal_gpio_write(nrf24_CE_PIN, false);
}
void nrf24_deinit() {
furi_hal_spi_release(nrf24_HANDLE);
furi_hal_spi_bus_handle_deinit(nrf24_HANDLE);
furi_hal_gpio_write(nrf24_CE_PIN, false);
furi_hal_gpio_init(nrf24_CE_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
// resetting the CS pins to floating
if(XTREME_SETTINGS()->spi_nrf24_handle == SpiDefault) {
furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeAnalog);
} else if(XTREME_SETTINGS()->spi_nrf24_handle == SpiExtra) {
furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeAnalog);
}
}
void nrf24_spi_trx(
FuriHalSpiBusHandle* handle,
uint8_t* tx,
uint8_t* rx,
uint8_t size,
uint32_t timeout) {
UNUSED(timeout);
furi_hal_gpio_write(handle->cs, false);
furi_hal_spi_bus_trx(handle, tx, rx, size, nrf24_TIMEOUT);
furi_hal_gpio_write(handle->cs, true);
}
uint8_t nrf24_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data) {
uint8_t tx[2] = {W_REGISTER | (REGISTER_MASK & reg), data};
uint8_t rx[2] = {0};
nrf24_spi_trx(handle, tx, rx, 2, nrf24_TIMEOUT);
return rx[0];
}
uint8_t nrf24_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size) {
uint8_t tx[size + 1];
uint8_t rx[size + 1];
memset(rx, 0, size + 1);
tx[0] = R_REGISTER | (REGISTER_MASK & reg);
memset(&tx[1], 0, size);
nrf24_spi_trx(handle, tx, rx, size + 1, nrf24_TIMEOUT);
memcpy(data, &rx[1], size);
return rx[0];
}
uint8_t nrf24_flush_rx(FuriHalSpiBusHandle* handle) {
uint8_t tx[] = {FLUSH_RX};
uint8_t rx[] = {0};
nrf24_spi_trx(handle, tx, rx, 1, nrf24_TIMEOUT);
return rx[0];
}
uint8_t nrf24_get_rdp(FuriHalSpiBusHandle* handle) {
uint8_t rdp;
nrf24_read_reg(handle, REG_RDP, &rdp, 1);
return rdp;
}
uint8_t nrf24_status(FuriHalSpiBusHandle* handle) {
uint8_t status;
uint8_t tx[] = {R_REGISTER | (REGISTER_MASK & REG_STATUS)};
nrf24_spi_trx(handle, tx, &status, 1, nrf24_TIMEOUT);
return status;
}
uint8_t nrf24_set_idle(FuriHalSpiBusHandle* handle) {
uint8_t status = 0;
uint8_t cfg = 0;
nrf24_read_reg(handle, REG_CONFIG, &cfg, 1);
cfg &= 0xfc; // clear bottom two bits to power down the radio
status = nrf24_write_reg(handle, REG_CONFIG, cfg);
furi_hal_gpio_write(nrf24_CE_PIN, false);
return status;
}
uint8_t nrf24_set_rx_mode(FuriHalSpiBusHandle* handle, bool nodelay) {
uint8_t status = 0;
uint8_t cfg = 0;
nrf24_read_reg(handle, REG_CONFIG, &cfg, 1);
cfg |= 0x03; // PWR_UP, and PRIM_RX
status = nrf24_write_reg(handle, REG_CONFIG, cfg);
furi_hal_gpio_write(nrf24_CE_PIN, true);
if(!nodelay) furi_delay_ms(2000);
return status;
}
bool nrf24_check_connected(FuriHalSpiBusHandle* handle) {
uint8_t status = nrf24_status(handle);
if(status != 0x00) {
return true;
} else {
return false;
}
}

View File

@@ -0,0 +1,129 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <furi_hal_spi.h>
#include <xtreme.h>
#ifdef __cplusplus
extern "C" {
#endif
#define R_REGISTER 0x00
#define W_REGISTER 0x20
#define REGISTER_MASK 0x1F
#define ACTIVATE 0x50
#define R_RX_PL_WID 0x60
#define R_RX_PAYLOAD 0x61
#define W_TX_PAYLOAD 0xA0
#define W_TX_PAYLOAD_NOACK 0xB0
#define W_ACK_PAYLOAD 0xA8
#define FLUSH_TX 0xE1
#define FLUSH_RX 0xE2
#define REUSE_TX_PL 0xE3
#define RF24_NOP 0xFF
#define REG_CONFIG 0x00
#define REG_EN_AA 0x01
#define REG_EN_RXADDR 0x02
#define REG_SETUP_AW 0x03
#define REG_SETUP_RETR 0x04
#define REG_RDP 0x09
#define REG_DYNPD 0x1C
#define REG_FEATURE 0x1D
#define REG_RF_SETUP 0x06
#define REG_STATUS 0x07
#define REG_RX_ADDR_P0 0x0A
#define REG_RF_CH 0x05
#define REG_TX_ADDR 0x10
#define RX_PW_P0 0x11
#define TX_DS 0x20
#define MAX_RT 0x10
#define nrf24_TIMEOUT 500
#define nrf24_CE_PIN &gpio_ext_pb2
#define nrf24_HANDLE \
(XTREME_SETTINGS()->spi_nrf24_handle == SpiDefault ? &furi_hal_spi_bus_handle_external : \
&furi_hal_spi_bus_handle_external_extra)
/* Low level API */
/** Write device register
*
* @param handle - pointer to FuriHalSpiHandle
* @param reg - register
* @param data - data to write
*
* @return device status
*/
uint8_t nrf24_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data);
/** Read device register
*
* @param handle - pointer to FuriHalSpiHandle
* @param reg - register
* @param[out] data - pointer to data
*
* @return device status
*/
uint8_t nrf24_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size);
/** Power down the radio
*
* @param handle - pointer to FuriHalSpiHandle
*
* @return device status
*/
uint8_t nrf24_set_idle(FuriHalSpiBusHandle* handle);
/** Sets the radio to RX mode
*
* @param handle - pointer to FuriHalSpiHandle
*
* @return device status
*/
uint8_t nrf24_set_rx_mode(FuriHalSpiBusHandle* handle, bool nodelay);
/*=============================================================================================================*/
/* High level API */
/** Must call this before using any other nrf24 API
*
*/
void nrf24_init();
/** Must call this when we end using nrf24 device
*
*/
void nrf24_deinit();
/** Send flush rx command
*
* @param handle - pointer to FuriHalSpiHandle
*
* @return device status
*/
uint8_t nrf24_flush_rx(FuriHalSpiBusHandle* handle);
/** Gets RDP from register 0x09
*
* @param handle - pointer to FuriHalSpiHandle
*
* @return RDP from register 0x09
*/
uint8_t nrf24_get_rdp(FuriHalSpiBusHandle* handle);
/** Gets the current status flags from the STATUS register
*
* @param handle - pointer to FuriHalSpiHandle
*
* @return status flags
*/
uint8_t nrf24_status(FuriHalSpiBusHandle* handle);
bool nrf24_check_connected(FuriHalSpiBusHandle* handle);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,251 @@
#include <stdio.h>
#include <furi.h>
#include <gui/gui.h>
#include <input/input.h>
#include <gui/elements.h>
#include <notification/notification_messages.h>
#include <nrf24.h>
#include "nrf24channelscanner_icons.h"
const uint8_t num_channels = 128;
static uint8_t nrf24values[128] = {0}; //to store channel data
bool ifNotFoundNrf = false; //to show error message
bool szuz = true; //to show welcome screen
static bool isScanning = false; //to track the progress
static bool stopNrfScan = false; //to exit thread
static bool threadStoppedsoFree = false; //indicate if I can free the thread from ram.
static uint8_t currCh = 0; //for the progress bar or the channel selector
static int delayPerChan = 5; //can set via up / down.
bool showFreq = true;
FuriThread* thread;
typedef enum {
EventTypeKey,
EventTypeTick,
} EventType;
typedef struct {
EventType type;
InputEvent input;
} Event;
static void draw_callback(Canvas* canvas, void* ctx) {
UNUSED(ctx);
canvas_clear(canvas);
canvas_set_bitmap_mode(canvas, 1);
canvas_draw_icon(canvas, 100, 0, &I_Pin_back_arrow_10x8);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 112, 8, "Exit");
canvas_draw_icon(canvas, 1, 0, &I_Ok_btn_9x9);
canvas_set_font(canvas, FontSecondary);
if(isScanning) {
canvas_draw_str(canvas, 12, 8, "Stop");
} else {
canvas_draw_str(canvas, 12, 8, "Scan");
}
canvas_draw_line(canvas, 0, 11, 127, 11);
if(ifNotFoundNrf) {
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 23, 35, "NRF24 not found!");
return;
}
canvas_draw_line(canvas, currCh, 12, currCh, 13); //draw the current channel
//draw hello mesage
if(szuz) {
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 1, 22, "Up / Down to change channel time.");
canvas_draw_str(canvas, 1, 36, "Left / Right to select channel,");
canvas_draw_str(canvas, 1, 48, "to get it's frequency");
}
//draw freq ir the progress
canvas_set_font(canvas, FontSecondary);
if(isScanning) {
canvas_draw_str(canvas, 37, 8, "scanning");
} else {
if(showFreq) {
int freq = 2400 + currCh;
char strfreq[10] = {32};
itoa(freq, strfreq, 10);
strfreq[4] = ' ';
strfreq[5] = 'M';
strfreq[6] = 'H';
strfreq[7] = 'Z';
strfreq[8] = 0;
canvas_draw_str(canvas, 40, 8, strfreq);
} else {
//show delay
int dly = delayPerChan;
char strdel[10] = {32};
itoa(dly, strdel, 10);
if(dly < 10) strdel[1] = ' ';
if(dly < 100) strdel[2] = ' ';
if(dly < 1000) strdel[3] = ' ';
strdel[4] = ' ';
strdel[5] = 'm';
strdel[6] = 's';
strdel[7] = 0;
canvas_draw_str(canvas, 40, 8, strdel);
}
}
//draw the chart
for(int i = 0; i < num_channels; ++i) {
int h = 64 - nrf24values[i];
if(h < 11) h = 12;
canvas_draw_line(canvas, i, h, i, 64);
}
}
static void input_callback(InputEvent* input_event, void* ctx) {
furi_assert(ctx);
FuriMessageQueue* event_queue = ctx;
Event event = {.type = EventTypeKey, .input = *input_event};
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
static int32_t scanner(void* context) {
UNUSED(context);
isScanning = true;
stopNrfScan = false;
threadStoppedsoFree = false;
uint8_t tmp = 0;
currCh = 0;
nrf24_set_rx_mode(nrf24_HANDLE, false);
nrf24_write_reg(nrf24_HANDLE, REG_EN_AA, 0x0);
nrf24_write_reg(nrf24_HANDLE, REG_RF_SETUP, 0x0f);
for(uint8_t j = 0; j < 15;) { //scan until stopped!
if(stopNrfScan) break;
for(uint8_t i = 0; i < num_channels; i++) {
if(stopNrfScan) break;
currCh = i;
nrf24_write_reg(nrf24_HANDLE, REG_RF_CH, i);
nrf24_set_rx_mode(nrf24_HANDLE, true);
for(uint8_t ii = 0; ii < 3; ++ii) {
nrf24_flush_rx(nrf24_HANDLE);
furi_delay_ms(delayPerChan);
tmp = nrf24_get_rdp(nrf24_HANDLE);
if(tmp > 0) nrf24values[i]++;
if(nrf24values[i] > 50) j = 254; //stop, bc maxed
}
}
}
nrf24_set_idle(nrf24_HANDLE);
isScanning = false;
threadStoppedsoFree = true;
currCh = 0;
return 0;
}
void ChangeFreq(int delta) {
currCh += delta;
if(currCh > num_channels) currCh = 0;
showFreq = true;
}
void ChangeDelay(int delta) {
delayPerChan += delta;
if(delayPerChan > 100) delayPerChan = 100;
if(delayPerChan < 1) delayPerChan = 1;
if(delayPerChan == 11) delayPerChan = 10; //to get it rounded :)
if(delayPerChan == 6) delayPerChan = 5; //to get it rounded :)
showFreq = false;
}
// Main entry of the application
int32_t nrf24channelscanner_main(void* p) {
UNUSED(p);
Event event;
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(Event));
nrf24_init();
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, draw_callback, NULL);
view_port_input_callback_set(view_port, input_callback, event_queue);
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
while(true) {
furi_check(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk);
if(event.type == EventTypeKey) {
szuz = false; //hit any button, so hide welcome screen
if(event.input.type == InputTypeLong && event.input.key == InputKeyBack) {
if(isScanning) {
stopNrfScan = true; //if running, stop it.
notification_message(notification, &sequence_blink_yellow_100);
furi_thread_join(thread);
furi_thread_free(thread);
}
break;
}
if(event.input.type == InputTypeShort && event.input.key == InputKeyOk) {
if(isScanning) {
notification_message(notification, &sequence_blink_yellow_100);
stopNrfScan = true;
furi_thread_join(thread);
furi_thread_free(thread);
threadStoppedsoFree = false; //to prevent double free
continue;
}
memset(nrf24values, 0, sizeof(nrf24values));
if(nrf24_check_connected(nrf24_HANDLE)) {
threadStoppedsoFree = false;
ifNotFoundNrf = false;
notification_message(notification, &sequence_blink_green_100);
thread = furi_thread_alloc();
furi_thread_set_name(thread, "nrfscannerth");
furi_thread_set_stack_size(thread, 1024);
furi_thread_set_callback(thread, scanner);
furi_thread_start(thread);
} else {
ifNotFoundNrf = true;
notification_message(notification, &sequence_error);
}
}
//change the delay
if(event.input.type == InputTypeShort && event.input.key == InputKeyUp) {
ChangeDelay(5);
}
if(event.input.type == InputTypeShort && event.input.key == InputKeyDown) {
ChangeDelay(-5);
}
if(!isScanning) {
if(event.input.type == InputTypeLong && event.input.key == InputKeyLeft)
ChangeFreq(-10);
if(event.input.type == InputTypeShort && event.input.key == InputKeyLeft)
ChangeFreq(-1);
if(event.input.type == InputTypeLong && event.input.key == InputKeyRight)
ChangeFreq(10);
if(event.input.type == InputTypeShort && event.input.key == InputKeyRight)
ChangeFreq(1);
}
}
if(threadStoppedsoFree) {
threadStoppedsoFree = false;
furi_thread_join(thread);
furi_thread_free(thread);
}
}
nrf24_deinit();
furi_message_queue_free(event_queue);
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
furi_record_close(RECORD_GUI);
return 0;
}