Просмотр исходного кода

Add nrf24channelscanner from https://github.com/xMasterX/all-the-plugins

git-subtree-dir: nrf24channelscanner
git-subtree-mainline: b571571153a956d41035d87a59aff3a59febbbde
git-subtree-split: 24381006256e1b16737fd22f7b3183aa0649f8f0
Willy-JL 2 лет назад
Родитель
Сommit
1514a65bf4

+ 1 - 0
nrf24channelscanner/.gitsubtree

@@ -0,0 +1 @@
+https://github.com/xMasterX/all-the-plugins dev non_catalog_apps/nrf24channelscanner

+ 21 - 0
nrf24channelscanner/application.fam

@@ -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",
+            ],
+        ),
+    ],
+)

BIN
nrf24channelscanner/fapicon.png


BIN
nrf24channelscanner/images/Ok_btn_9x9.png


BIN
nrf24channelscanner/images/Pin_back_arrow_10x8.png


+ 101 - 0
nrf24channelscanner/lib/nrf24/nrf24.c

@@ -0,0 +1,101 @@
+#include "nrf24.h"
+#include <furi.h>
+#include <furi_hal.h>
+#include <furi_hal_resources.h>
+#include <assert.h>
+#include <string.h>
+
+void nrf24_init() {
+    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);
+}
+
+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;
+    }
+}

+ 126 - 0
nrf24channelscanner/lib/nrf24/nrf24.h

@@ -0,0 +1,126 @@
+#pragma once
+#include <stdbool.h>
+#include <stdint.h>
+#include <furi_hal_spi.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 &furi_hal_spi_bus_handle_external
+
+/* 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

+ 265 - 0
nrf24channelscanner/nrf24channelscanner.c

@@ -0,0 +1,265 @@
+#include <stdio.h>
+#include <furi.h>
+#include <furi_hal.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));
+
+    uint8_t attempts = 0;
+    bool otg_was_enabled = furi_hal_power_is_otg_enabled();
+    while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
+        furi_hal_power_enable_otg();
+        furi_delay_ms(10);
+    }
+
+
+    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();
+
+    if(furi_hal_power_is_otg_enabled() && !otg_was_enabled) {
+        furi_hal_power_disable_otg();
+    }
+    
+    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;
+}