Merge remote-tracking branch 'upstream/dev' into nfcf

This commit is contained in:
nullableVoidPtr
2023-05-07 21:25:03 +08:00
168 changed files with 3731 additions and 2119 deletions

View File

@@ -4,7 +4,6 @@ on:
push: push:
branches: branches:
- dev - dev
- "release*"
tags: tags:
- '*' - '*'
pull_request: pull_request:
@@ -19,7 +18,7 @@ jobs:
runs-on: [self-hosted,FlipperZeroShell] runs-on: [self-hosted,FlipperZeroShell]
steps: steps:
- name: 'Wipe workspace' - name: 'Wipe workspace'
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
- name: 'Checkout code' - name: 'Checkout code'
uses: actions/checkout@v3 uses: actions/checkout@v3
@@ -167,7 +166,7 @@ jobs:
target: [f7, f18] target: [f7, f18]
steps: steps:
- name: 'Wipe workspace' - name: 'Wipe workspace'
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
- name: 'Checkout code' - name: 'Checkout code'
uses: actions/checkout@v3 uses: actions/checkout@v3
@@ -194,12 +193,14 @@ jobs:
TARGET="$(echo '${{ matrix.target }}' | sed 's/f//')"; \ TARGET="$(echo '${{ matrix.target }}' | sed 's/f//')"; \
./fbt TARGET_HW=$TARGET DEBUG=0 COMPACT=1 fap_dist updater_package ./fbt TARGET_HW=$TARGET DEBUG=0 COMPACT=1 fap_dist updater_package
echo "sdk-file=$(ls dist/${{ matrix.target }}-*/flipper-z-${{ matrix.target }}-sdk-*.zip)" >> $GITHUB_OUTPUT echo "sdk-file=$(ls dist/${{ matrix.target }}-*/flipper-z-${{ matrix.target }}-sdk-*.zip)" >> $GITHUB_OUTPUT
echo "hw-target-code=$TARGET" >> $GITHUB_OUTPUT
- name: Deploy uFBT with SDK - name: Deploy uFBT with SDK
uses: flipperdevices/flipperzero-ufbt-action@v0.1.0 uses: flipperdevices/flipperzero-ufbt-action@v0.1.0
with: with:
task: setup task: setup
sdk-file: ${{ steps.build-fw.outputs.sdk-file }} sdk-file: ${{ steps.build-fw.outputs.sdk-file }}
sdk-hw-target: ${{ steps.build-fw.outputs.hw-target-code }}
- name: Build test app with SDK - name: Build test app with SDK
run: | run: |
@@ -207,7 +208,7 @@ jobs:
cd testapp cd testapp
ufbt create APPID=testapp ufbt create APPID=testapp
ufbt ufbt
- name: Build example & external apps with uFBT - name: Build example & external apps with uFBT
run: | run: |
for appdir in 'applications/external' 'applications/examples'; do for appdir in 'applications/external' 'applications/examples'; do

View File

@@ -4,7 +4,6 @@ on:
push: push:
branches: branches:
- dev - dev
- "release*"
tags: tags:
- '*' - '*'
pull_request: pull_request:
@@ -19,7 +18,7 @@ jobs:
runs-on: [self-hosted,FlipperZeroShell] runs-on: [self-hosted,FlipperZeroShell]
steps: steps:
- name: 'Wipe workspace' - name: 'Wipe workspace'
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
- name: 'Checkout code' - name: 'Checkout code'
uses: actions/checkout@v3 uses: actions/checkout@v3
@@ -64,7 +63,7 @@ jobs:
else else
echo "Python Lint: all good ✨" >> $GITHUB_STEP_SUMMARY; echo "Python Lint: all good ✨" >> $GITHUB_STEP_SUMMARY;
fi fi
- name: 'Check C++ code formatting' - name: 'Check C++ code formatting'
id: syntax_check_cpp id: syntax_check_cpp
if: always() if: always()

View File

@@ -13,7 +13,7 @@ jobs:
runs-on: [self-hosted,FlipperZeroShell] runs-on: [self-hosted,FlipperZeroShell]
steps: steps:
- name: 'Wipe workspace' - name: 'Wipe workspace'
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
- name: 'Checkout code' - name: 'Checkout code'
uses: actions/checkout@v3 uses: actions/checkout@v3

View File

@@ -4,7 +4,6 @@ on:
push: push:
branches: branches:
- dev - dev
- "release*"
tags: tags:
- '*' - '*'
pull_request: pull_request:
@@ -20,7 +19,7 @@ jobs:
runs-on: [self-hosted, FlipperZeroShell] runs-on: [self-hosted, FlipperZeroShell]
steps: steps:
- name: 'Wipe workspace' - name: 'Wipe workspace'
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
- name: 'Checkout code' - name: 'Checkout code'
uses: actions/checkout@v3 uses: actions/checkout@v3

View File

@@ -13,7 +13,7 @@ jobs:
runs-on: [self-hosted, FlipperZeroUnitTest] runs-on: [self-hosted, FlipperZeroUnitTest]
steps: steps:
- name: 'Wipe workspace' - name: 'Wipe workspace'
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
- name: Checkout code - name: Checkout code
uses: actions/checkout@v3 uses: actions/checkout@v3

View File

@@ -13,13 +13,13 @@ jobs:
runs-on: [self-hosted, FlipperZeroUpdaterTest] runs-on: [self-hosted, FlipperZeroUpdaterTest]
steps: steps:
- name: 'Wipe workspace' - name: 'Wipe workspace'
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
- name: Checkout code - name: Checkout code
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
fetch-depth: 1 fetch-depth: 1
submodules: false submodules: false
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
- name: 'Get flipper from device manager (mock)' - name: 'Get flipper from device manager (mock)'
@@ -50,7 +50,7 @@ jobs:
echo "tag=$(git tag -l --sort=-version:refname | grep -v "rc\|RC" | head -1)" >> $GITHUB_OUTPUT echo "tag=$(git tag -l --sort=-version:refname | grep -v "rc\|RC" | head -1)" >> $GITHUB_OUTPUT
- name: 'Wipe workspace' - name: 'Wipe workspace'
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
- name: 'Checkout latest release' - name: 'Checkout latest release'
uses: actions/checkout@v3 uses: actions/checkout@v3

1
.gitignore vendored
View File

@@ -2,6 +2,7 @@
*.swp *.swp
*.swo *.swo
*.gdb_history *.gdb_history
*.old
# LSP # LSP

View File

@@ -11,6 +11,7 @@ enum HidDebugSubmenuIndex {
HidSubmenuIndexMedia, HidSubmenuIndexMedia,
HidSubmenuIndexTikTok, HidSubmenuIndexTikTok,
HidSubmenuIndexMouse, HidSubmenuIndexMouse,
HidSubmenuIndexMouseClicker,
HidSubmenuIndexMouseJiggler, HidSubmenuIndexMouseJiggler,
}; };
@@ -32,6 +33,9 @@ static void hid_submenu_callback(void* context, uint32_t index) {
} else if(index == HidSubmenuIndexTikTok) { } else if(index == HidSubmenuIndexTikTok) {
app->view_id = BtHidViewTikTok; app->view_id = BtHidViewTikTok;
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok); view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok);
} else if(index == HidSubmenuIndexMouseClicker) {
app->view_id = HidViewMouseClicker;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseClicker);
} else if(index == HidSubmenuIndexMouseJiggler) { } else if(index == HidSubmenuIndexMouseJiggler) {
app->view_id = HidViewMouseJiggler; app->view_id = HidViewMouseJiggler;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseJiggler); view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseJiggler);
@@ -53,6 +57,7 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con
hid_keyboard_set_connected_status(hid->hid_keyboard, connected); hid_keyboard_set_connected_status(hid->hid_keyboard, connected);
hid_media_set_connected_status(hid->hid_media, connected); hid_media_set_connected_status(hid->hid_media, connected);
hid_mouse_set_connected_status(hid->hid_mouse, connected); hid_mouse_set_connected_status(hid->hid_mouse, connected);
hid_mouse_clicker_set_connected_status(hid->hid_mouse_clicker, connected);
hid_mouse_jiggler_set_connected_status(hid->hid_mouse_jiggler, connected); hid_mouse_jiggler_set_connected_status(hid->hid_mouse_jiggler, connected);
hid_tiktok_set_connected_status(hid->hid_tiktok, connected); hid_tiktok_set_connected_status(hid->hid_tiktok, connected);
} }
@@ -114,6 +119,12 @@ Hid* hid_alloc(HidTransport transport) {
hid_submenu_callback, hid_submenu_callback,
app); app);
} }
submenu_add_item(
app->device_type_submenu,
"Mouse Clicker",
HidSubmenuIndexMouseClicker,
hid_submenu_callback,
app);
submenu_add_item( submenu_add_item(
app->device_type_submenu, app->device_type_submenu,
"Mouse Jiggler", "Mouse Jiggler",
@@ -172,6 +183,15 @@ Hid* hid_app_alloc_view(void* context) {
view_dispatcher_add_view( view_dispatcher_add_view(
app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse)); app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse));
// Mouse clicker view
app->hid_mouse_clicker = hid_mouse_clicker_alloc(app);
view_set_previous_callback(
hid_mouse_clicker_get_view(app->hid_mouse_clicker), hid_exit_confirm_view);
view_dispatcher_add_view(
app->view_dispatcher,
HidViewMouseClicker,
hid_mouse_clicker_get_view(app->hid_mouse_clicker));
// Mouse jiggler view // Mouse jiggler view
app->hid_mouse_jiggler = hid_mouse_jiggler_alloc(app); app->hid_mouse_jiggler = hid_mouse_jiggler_alloc(app);
view_set_previous_callback( view_set_previous_callback(
@@ -205,6 +225,8 @@ void hid_free(Hid* app) {
hid_media_free(app->hid_media); hid_media_free(app->hid_media);
view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse); view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse);
hid_mouse_free(app->hid_mouse); hid_mouse_free(app->hid_mouse);
view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseClicker);
hid_mouse_clicker_free(app->hid_mouse_clicker);
view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseJiggler); view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseJiggler);
hid_mouse_jiggler_free(app->hid_mouse_jiggler); hid_mouse_jiggler_free(app->hid_mouse_jiggler);
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok); view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok);

View File

@@ -20,6 +20,7 @@
#include "views/hid_keyboard.h" #include "views/hid_keyboard.h"
#include "views/hid_media.h" #include "views/hid_media.h"
#include "views/hid_mouse.h" #include "views/hid_mouse.h"
#include "views/hid_mouse_clicker.h"
#include "views/hid_mouse_jiggler.h" #include "views/hid_mouse_jiggler.h"
#include "views/hid_tiktok.h" #include "views/hid_tiktok.h"
@@ -43,6 +44,7 @@ struct Hid {
HidKeyboard* hid_keyboard; HidKeyboard* hid_keyboard;
HidMedia* hid_media; HidMedia* hid_media;
HidMouse* hid_mouse; HidMouse* hid_mouse;
HidMouseClicker* hid_mouse_clicker;
HidMouseJiggler* hid_mouse_jiggler; HidMouseJiggler* hid_mouse_jiggler;
HidTikTok* hid_tiktok; HidTikTok* hid_tiktok;

View File

@@ -4,6 +4,7 @@ typedef enum {
HidViewKeyboard, HidViewKeyboard,
HidViewMedia, HidViewMedia,
HidViewMouse, HidViewMouse,
HidViewMouseClicker,
HidViewMouseJiggler, HidViewMouseJiggler,
BtHidViewTikTok, BtHidViewTikTok,
HidViewExitConfirm, HidViewExitConfirm,

View File

@@ -0,0 +1,214 @@
#include "hid_mouse_clicker.h"
#include <gui/elements.h>
#include "../hid.h"
#include "hid_icons.h"
#define TAG "HidMouseClicker"
#define DEFAULT_CLICK_RATE 1
#define MAXIMUM_CLICK_RATE 60
struct HidMouseClicker {
View* view;
Hid* hid;
FuriTimer* timer;
};
typedef struct {
bool connected;
bool running;
int rate;
HidTransport transport;
} HidMouseClickerModel;
static void hid_mouse_clicker_start_or_restart_timer(void* context) {
furi_assert(context);
HidMouseClicker* hid_mouse_clicker = context;
if(furi_timer_is_running(hid_mouse_clicker->timer)) {
furi_timer_stop(hid_mouse_clicker->timer);
}
with_view_model(
hid_mouse_clicker->view,
HidMouseClickerModel * model,
{
furi_timer_start(
hid_mouse_clicker->timer, furi_kernel_get_tick_frequency() / model->rate);
},
true);
}
static void hid_mouse_clicker_draw_callback(Canvas* canvas, void* context) {
furi_assert(context);
HidMouseClickerModel* model = context;
// Header
if(model->transport == HidTransportBle) {
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
}
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse Clicker");
// Ok
canvas_draw_icon(canvas, 63, 25, &I_Space_65x18);
if(model->running) {
canvas_set_font(canvas, FontPrimary);
FuriString* rate_label = furi_string_alloc();
furi_string_printf(rate_label, "%d clicks/s\n\nUp / Down", model->rate);
elements_multiline_text(canvas, AlignLeft, 35, furi_string_get_cstr(rate_label));
canvas_set_font(canvas, FontSecondary);
furi_string_free(rate_label);
elements_slightly_rounded_box(canvas, 66, 27, 60, 13);
canvas_set_color(canvas, ColorWhite);
} else {
canvas_set_font(canvas, FontPrimary);
elements_multiline_text(canvas, AlignLeft, 35, "Press Start\nto start\nclicking");
canvas_set_font(canvas, FontSecondary);
}
canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9);
if(model->running) {
elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Stop");
} else {
elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Start");
}
canvas_set_color(canvas, ColorBlack);
// Back
canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8);
elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Quit");
}
static void hid_mouse_clicker_timer_callback(void* context) {
furi_assert(context);
HidMouseClicker* hid_mouse_clicker = context;
with_view_model(
hid_mouse_clicker->view,
HidMouseClickerModel * model,
{
if(model->running) {
hid_hal_mouse_press(hid_mouse_clicker->hid, HID_MOUSE_BTN_LEFT);
hid_hal_mouse_release(hid_mouse_clicker->hid, HID_MOUSE_BTN_LEFT);
}
},
false);
}
static void hid_mouse_clicker_enter_callback(void* context) {
hid_mouse_clicker_start_or_restart_timer(context);
}
static void hid_mouse_clicker_exit_callback(void* context) {
furi_assert(context);
HidMouseClicker* hid_mouse_clicker = context;
furi_timer_stop(hid_mouse_clicker->timer);
}
static bool hid_mouse_clicker_input_callback(InputEvent* event, void* context) {
furi_assert(context);
HidMouseClicker* hid_mouse_clicker = context;
bool consumed = false;
bool rate_changed = false;
if(event->type != InputTypeShort && event->type != InputTypeRepeat) {
return false;
}
with_view_model(
hid_mouse_clicker->view,
HidMouseClickerModel * model,
{
switch(event->key) {
case InputKeyOk:
model->running = !model->running;
consumed = true;
break;
case InputKeyUp:
if(model->rate < MAXIMUM_CLICK_RATE) {
model->rate++;
}
rate_changed = true;
consumed = true;
break;
case InputKeyDown:
if(model->rate > 1) {
model->rate--;
}
rate_changed = true;
consumed = true;
break;
default:
consumed = true;
break;
}
},
true);
if(rate_changed) {
hid_mouse_clicker_start_or_restart_timer(context);
}
return consumed;
}
HidMouseClicker* hid_mouse_clicker_alloc(Hid* hid) {
HidMouseClicker* hid_mouse_clicker = malloc(sizeof(HidMouseClicker));
hid_mouse_clicker->view = view_alloc();
view_set_context(hid_mouse_clicker->view, hid_mouse_clicker);
view_allocate_model(
hid_mouse_clicker->view, ViewModelTypeLocking, sizeof(HidMouseClickerModel));
view_set_draw_callback(hid_mouse_clicker->view, hid_mouse_clicker_draw_callback);
view_set_input_callback(hid_mouse_clicker->view, hid_mouse_clicker_input_callback);
view_set_enter_callback(hid_mouse_clicker->view, hid_mouse_clicker_enter_callback);
view_set_exit_callback(hid_mouse_clicker->view, hid_mouse_clicker_exit_callback);
hid_mouse_clicker->hid = hid;
hid_mouse_clicker->timer = furi_timer_alloc(
hid_mouse_clicker_timer_callback, FuriTimerTypePeriodic, hid_mouse_clicker);
with_view_model(
hid_mouse_clicker->view,
HidMouseClickerModel * model,
{
model->transport = hid->transport;
model->rate = DEFAULT_CLICK_RATE;
},
true);
return hid_mouse_clicker;
}
void hid_mouse_clicker_free(HidMouseClicker* hid_mouse_clicker) {
furi_assert(hid_mouse_clicker);
furi_timer_stop(hid_mouse_clicker->timer);
furi_timer_free(hid_mouse_clicker->timer);
view_free(hid_mouse_clicker->view);
free(hid_mouse_clicker);
}
View* hid_mouse_clicker_get_view(HidMouseClicker* hid_mouse_clicker) {
furi_assert(hid_mouse_clicker);
return hid_mouse_clicker->view;
}
void hid_mouse_clicker_set_connected_status(HidMouseClicker* hid_mouse_clicker, bool connected) {
furi_assert(hid_mouse_clicker);
with_view_model(
hid_mouse_clicker->view,
HidMouseClickerModel * model,
{ model->connected = connected; },
true);
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include <gui/view.h>
typedef struct Hid Hid;
typedef struct HidMouseClicker HidMouseClicker;
HidMouseClicker* hid_mouse_clicker_alloc(Hid* bt_hid);
void hid_mouse_clicker_free(HidMouseClicker* hid_mouse_clicker);
View* hid_mouse_clicker_get_view(HidMouseClicker* hid_mouse_clicker);
void hid_mouse_clicker_set_connected_status(HidMouseClicker* hid_mouse_clicker, bool connected);

View File

@@ -95,7 +95,7 @@ static bool hid_mouse_jiggler_input_callback(InputEvent* event, void* context) {
bool consumed = false; bool consumed = false;
if(event->key == InputKeyOk) { if(event->type == InputTypeShort && event->key == InputKeyOk) {
with_view_model( with_view_model(
hid_mouse_jiggler->view, hid_mouse_jiggler->view,
HidMouseJigglerModel * model, HidMouseJigglerModel * model,

View File

@@ -34,7 +34,7 @@ void picopass_scene_read_card_success_on_enter(void* context) {
uint8_t csn[PICOPASS_BLOCK_LEN] = {0}; uint8_t csn[PICOPASS_BLOCK_LEN] = {0};
memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN); memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN);
for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
furi_string_cat_printf(csn_str, "%02X ", csn[i]); furi_string_cat_printf(csn_str, "%02X", csn[i]);
} }
bool no_key = picopass_is_memset(pacs->key, 0x00, PICOPASS_BLOCK_LEN); bool no_key = picopass_is_memset(pacs->key, 0x00, PICOPASS_BLOCK_LEN);

View File

@@ -16,6 +16,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = {
&ws_protocol_auriol_th, &ws_protocol_auriol_th,
&ws_protocol_oregon_v1, &ws_protocol_oregon_v1,
&ws_protocol_tx_8300, &ws_protocol_tx_8300,
&ws_protocol_wendox_w6726,
}; };
const SubGhzProtocolRegistry weather_station_protocol_registry = { const SubGhzProtocolRegistry weather_station_protocol_registry = {

View File

@@ -16,5 +16,6 @@
#include "auriol_hg0601a.h" #include "auriol_hg0601a.h"
#include "oregon_v1.h" #include "oregon_v1.h"
#include "tx_8300.h" #include "tx_8300.h"
#include "wendox_w6726.h"
extern const SubGhzProtocolRegistry weather_station_protocol_registry; extern const SubGhzProtocolRegistry weather_station_protocol_registry;

View File

@@ -0,0 +1,299 @@
#include "wendox_w6726.h"
#define TAG "WSProtocolWendoxW6726"
/*
* Wendox W6726
*
* Temperature -50C to +70C
* _ _ _ __ _
* _| |___| |___| |___ ... | |_| |__...._______________
* preamble data guard time
*
* 3 reps every 3 minutes
* in the first message 11 bytes of the preamble in the rest by 7
*
* bit 0: 1955-hi, 5865-lo
* bit 1: 5865-hi, 1955-lo
* guard time: 12*1955+(lo last bit)
* data: 29 bit
*
* IIIII | ZTTTTTTTTT | uuuuuuuBuu | CCCC
*
* I: identification;
* Z: temperature sign;
* T: temperature sign dependent +12C;
* B: battery low; flag to indicate low battery voltage;
* C: CRC4 (polynomial = 0x9, start_data = 0xD);
* u: unknown;
*/
static const SubGhzBlockConst ws_protocol_wendox_w6726_const = {
.te_short = 1955,
.te_long = 5865,
.te_delta = 300,
.min_count_bit_for_found = 29,
};
struct WSProtocolDecoderWendoxW6726 {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
WSBlockGeneric generic;
uint16_t header_count;
};
struct WSProtocolEncoderWendoxW6726 {
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
WSBlockGeneric generic;
};
typedef enum {
WendoxW6726DecoderStepReset = 0,
WendoxW6726DecoderStepCheckPreambule,
WendoxW6726DecoderStepSaveDuration,
WendoxW6726DecoderStepCheckDuration,
} WendoxW6726DecoderStep;
const SubGhzProtocolDecoder ws_protocol_wendox_w6726_decoder = {
.alloc = ws_protocol_decoder_wendox_w6726_alloc,
.free = ws_protocol_decoder_wendox_w6726_free,
.feed = ws_protocol_decoder_wendox_w6726_feed,
.reset = ws_protocol_decoder_wendox_w6726_reset,
.get_hash_data = ws_protocol_decoder_wendox_w6726_get_hash_data,
.serialize = ws_protocol_decoder_wendox_w6726_serialize,
.deserialize = ws_protocol_decoder_wendox_w6726_deserialize,
.get_string = ws_protocol_decoder_wendox_w6726_get_string,
};
const SubGhzProtocolEncoder ws_protocol_wendox_w6726_encoder = {
.alloc = NULL,
.free = NULL,
.deserialize = NULL,
.stop = NULL,
.yield = NULL,
};
const SubGhzProtocol ws_protocol_wendox_w6726 = {
.name = WS_PROTOCOL_WENDOX_W6726_NAME,
.type = SubGhzProtocolWeatherStation,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
.decoder = &ws_protocol_wendox_w6726_decoder,
.encoder = &ws_protocol_wendox_w6726_encoder,
};
void* ws_protocol_decoder_wendox_w6726_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
WSProtocolDecoderWendoxW6726* instance = malloc(sizeof(WSProtocolDecoderWendoxW6726));
instance->base.protocol = &ws_protocol_wendox_w6726;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void ws_protocol_decoder_wendox_w6726_free(void* context) {
furi_assert(context);
WSProtocolDecoderWendoxW6726* instance = context;
free(instance);
}
void ws_protocol_decoder_wendox_w6726_reset(void* context) {
furi_assert(context);
WSProtocolDecoderWendoxW6726* instance = context;
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
}
static bool ws_protocol_wendox_w6726_check(WSProtocolDecoderWendoxW6726* instance) {
if(!instance->decoder.decode_data) return false;
uint8_t msg[] = {
instance->decoder.decode_data >> 28,
instance->decoder.decode_data >> 20,
instance->decoder.decode_data >> 12,
instance->decoder.decode_data >> 4};
uint8_t crc = subghz_protocol_blocks_crc4(msg, 4, 0x9, 0xD);
return (crc == (instance->decoder.decode_data & 0x0F));
}
/**
* Analysis of received data
* @param instance Pointer to a WSBlockGeneric* instance
*/
static void ws_protocol_wendox_w6726_remote_controller(WSBlockGeneric* instance) {
instance->id = (instance->data >> 24) & 0xFF;
instance->battery_low = (instance->data >> 6) & 1;
instance->channel = WS_NO_CHANNEL;
if(((instance->data >> 23) & 1)) {
instance->temp = (float)(((instance->data >> 14) & 0x1FF) + 12) / 10.0f;
} else {
instance->temp = (float)((~(instance->data >> 14) & 0x1FF) + 1 - 12) / -10.0f;
}
if(instance->temp < -50.0f) {
instance->temp = -50.0f;
} else if(instance->temp > 70.0f) {
instance->temp = 70.0f;
}
instance->btn = WS_NO_BTN;
instance->humidity = WS_NO_HUMIDITY;
}
void ws_protocol_decoder_wendox_w6726_feed(void* context, bool level, uint32_t duration) {
furi_assert(context);
WSProtocolDecoderWendoxW6726* instance = context;
switch(instance->decoder.parser_step) {
case WendoxW6726DecoderStepReset:
if((level) && (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) <
ws_protocol_wendox_w6726_const.te_delta)) {
instance->decoder.parser_step = WendoxW6726DecoderStepCheckPreambule;
instance->decoder.te_last = duration;
instance->header_count = 0;
}
break;
case WendoxW6726DecoderStepCheckPreambule:
if(level) {
instance->decoder.te_last = duration;
} else {
if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) <
ws_protocol_wendox_w6726_const.te_delta * 1) &&
(DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_long) <
ws_protocol_wendox_w6726_const.te_delta * 2)) {
instance->header_count++;
} else if((instance->header_count > 4) && (instance->header_count < 12)) {
if((DURATION_DIFF(
instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) <
ws_protocol_wendox_w6726_const.te_delta * 2) &&
(DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) <
ws_protocol_wendox_w6726_const.te_delta)) {
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration;
} else {
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
}
} else {
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
}
}
break;
case WendoxW6726DecoderStepSaveDuration:
if(level) {
instance->decoder.te_last = duration;
instance->decoder.parser_step = WendoxW6726DecoderStepCheckDuration;
} else {
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
}
break;
case WendoxW6726DecoderStepCheckDuration:
if(!level) {
if(duration >
ws_protocol_wendox_w6726_const.te_short + ws_protocol_wendox_w6726_const.te_long) {
if(DURATION_DIFF(
instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) <
ws_protocol_wendox_w6726_const.te_delta) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration;
} else if(
DURATION_DIFF(
instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) <
ws_protocol_wendox_w6726_const.te_delta * 2) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration;
} else {
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
}
if((instance->decoder.decode_count_bit ==
ws_protocol_wendox_w6726_const.min_count_bit_for_found) &&
ws_protocol_wendox_w6726_check(instance)) {
instance->generic.data = instance->decoder.decode_data;
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
ws_protocol_wendox_w6726_remote_controller(&instance->generic);
if(instance->base.callback)
instance->base.callback(&instance->base, instance->base.context);
}
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
} else if(
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) <
ws_protocol_wendox_w6726_const.te_delta) &&
(DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_long) <
ws_protocol_wendox_w6726_const.te_delta * 3)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration;
} else if(
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) <
ws_protocol_wendox_w6726_const.te_delta * 2) &&
(DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) <
ws_protocol_wendox_w6726_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration;
} else {
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
}
} else {
instance->decoder.parser_step = WendoxW6726DecoderStepReset;
}
break;
}
}
uint8_t ws_protocol_decoder_wendox_w6726_get_hash_data(void* context) {
furi_assert(context);
WSProtocolDecoderWendoxW6726* instance = context;
return subghz_protocol_blocks_get_hash_data(
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
}
SubGhzProtocolStatus ws_protocol_decoder_wendox_w6726_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_assert(context);
WSProtocolDecoderWendoxW6726* instance = context;
return ws_block_generic_serialize(&instance->generic, flipper_format, preset);
}
SubGhzProtocolStatus
ws_protocol_decoder_wendox_w6726_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
WSProtocolDecoderWendoxW6726* instance = context;
return ws_block_generic_deserialize_check_count_bit(
&instance->generic,
flipper_format,
ws_protocol_wendox_w6726_const.min_count_bit_for_found);
}
void ws_protocol_decoder_wendox_w6726_get_string(void* context, FuriString* output) {
furi_assert(context);
WSProtocolDecoderWendoxW6726* instance = context;
furi_string_printf(
output,
"%s %dbit\r\n"
"Key:0x%lX%08lX\r\n"
"Sn:0x%lX Ch:%d Bat:%d\r\n"
"Temp:%3.1f C Hum:%d%%",
instance->generic.protocol_name,
instance->generic.data_count_bit,
(uint32_t)(instance->generic.data >> 32),
(uint32_t)(instance->generic.data),
instance->generic.id,
instance->generic.channel,
instance->generic.battery_low,
(double)instance->generic.temp,
instance->generic.humidity);
}

View File

@@ -0,0 +1,80 @@
#pragma once
#include <lib/subghz/protocols/base.h>
#include <lib/subghz/blocks/const.h>
#include <lib/subghz/blocks/decoder.h>
#include <lib/subghz/blocks/encoder.h>
#include "ws_generic.h"
#include <lib/subghz/blocks/math.h>
#define WS_PROTOCOL_WENDOX_W6726_NAME "Wendox W6726"
typedef struct WSProtocolDecoderWendoxW6726 WSProtocolDecoderWendoxW6726;
typedef struct WSProtocolEncoderWendoxW6726 WSProtocolEncoderWendoxW6726;
extern const SubGhzProtocolDecoder ws_protocol_wendox_w6726_decoder;
extern const SubGhzProtocolEncoder ws_protocol_wendox_w6726_encoder;
extern const SubGhzProtocol ws_protocol_wendox_w6726;
/**
* Allocate WSProtocolDecoderWendoxW6726.
* @param environment Pointer to a SubGhzEnvironment instance
* @return WSProtocolDecoderWendoxW6726* pointer to a WSProtocolDecoderWendoxW6726 instance
*/
void* ws_protocol_decoder_wendox_w6726_alloc(SubGhzEnvironment* environment);
/**
* Free WSProtocolDecoderWendoxW6726.
* @param context Pointer to a WSProtocolDecoderWendoxW6726 instance
*/
void ws_protocol_decoder_wendox_w6726_free(void* context);
/**
* Reset decoder WSProtocolDecoderWendoxW6726.
* @param context Pointer to a WSProtocolDecoderWendoxW6726 instance
*/
void ws_protocol_decoder_wendox_w6726_reset(void* context);
/**
* Parse a raw sequence of levels and durations received from the air.
* @param context Pointer to a WSProtocolDecoderWendoxW6726 instance
* @param level Signal level true-high false-low
* @param duration Duration of this level in, us
*/
void ws_protocol_decoder_wendox_w6726_feed(void* context, bool level, uint32_t duration);
/**
* Getting the hash sum of the last randomly received parcel.
* @param context Pointer to a WSProtocolDecoderWendoxW6726 instance
* @return hash Hash sum
*/
uint8_t ws_protocol_decoder_wendox_w6726_get_hash_data(void* context);
/**
* Serialize data WSProtocolDecoderWendoxW6726.
* @param context Pointer to a WSProtocolDecoderWendoxW6726 instance
* @param flipper_format Pointer to a FlipperFormat instance
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
* @return status
*/
SubGhzProtocolStatus ws_protocol_decoder_wendox_w6726_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
/**
* Deserialize data WSProtocolDecoderWendoxW6726.
* @param context Pointer to a WSProtocolDecoderWendoxW6726 instance
* @param flipper_format Pointer to a FlipperFormat instance
* @return status
*/
SubGhzProtocolStatus
ws_protocol_decoder_wendox_w6726_deserialize(void* context, FlipperFormat* flipper_format);
/**
* Getting a textual representation of the received data.
* @param context Pointer to a WSProtocolDecoderWendoxW6726 instance
* @param output Resulting text
*/
void ws_protocol_decoder_wendox_w6726_get_string(void* context, FuriString* output);

View File

@@ -12,7 +12,7 @@
#define MENU_ITEMS 4u #define MENU_ITEMS 4u
#define UNLOCK_CNT 3 #define UNLOCK_CNT 3
#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f #define SUBGHZ_RAW_THRESHOLD_MIN -90.0f
typedef struct { typedef struct {
FuriString* item_str; FuriString* item_str;
uint8_t type; uint8_t type;
@@ -69,10 +69,10 @@ void ws_view_receiver_set_rssi(WSReceiver* instance, float rssi) {
instance->view, instance->view,
WSReceiverModel * model, WSReceiverModel * model,
{ {
if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) {
model->u_rssi = 0; model->u_rssi = 0;
} else { } else {
model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN); model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_THRESHOLD_MIN);
} }
}, },
true); true);

View File

@@ -152,22 +152,22 @@ static int32_t ducky_fnc_waitforbutton(BadUsbScript* bad_usb, const char* line,
} }
static const DuckyCmd ducky_commands[] = { static const DuckyCmd ducky_commands[] = {
{"REM ", NULL, -1}, {"REM", NULL, -1},
{"ID ", NULL, -1}, {"ID", NULL, -1},
{"DELAY ", ducky_fnc_delay, -1}, {"DELAY", ducky_fnc_delay, -1},
{"STRING ", ducky_fnc_string, 0}, {"STRING", ducky_fnc_string, 0},
{"STRINGLN ", ducky_fnc_string, 1}, {"STRINGLN", ducky_fnc_string, 1},
{"DEFAULT_DELAY ", ducky_fnc_defdelay, -1}, {"DEFAULT_DELAY", ducky_fnc_defdelay, -1},
{"DEFAULTDELAY ", ducky_fnc_defdelay, -1}, {"DEFAULTDELAY", ducky_fnc_defdelay, -1},
{"STRINGDELAY ", ducky_fnc_strdelay, -1}, {"STRINGDELAY", ducky_fnc_strdelay, -1},
{"STRING_DELAY ", ducky_fnc_strdelay, -1}, {"STRING_DELAY", ducky_fnc_strdelay, -1},
{"REPEAT ", ducky_fnc_repeat, -1}, {"REPEAT", ducky_fnc_repeat, -1},
{"SYSRQ ", ducky_fnc_sysrq, -1}, {"SYSRQ", ducky_fnc_sysrq, -1},
{"ALTCHAR ", ducky_fnc_altchar, -1}, {"ALTCHAR", ducky_fnc_altchar, -1},
{"ALTSTRING ", ducky_fnc_altstring, -1}, {"ALTSTRING", ducky_fnc_altstring, -1},
{"ALTCODE ", ducky_fnc_altstring, -1}, {"ALTCODE", ducky_fnc_altstring, -1},
{"HOLD ", ducky_fnc_hold, -1}, {"HOLD", ducky_fnc_hold, -1},
{"RELEASE ", ducky_fnc_release, -1}, {"RELEASE", ducky_fnc_release, -1},
{"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1}, {"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1},
}; };
@@ -175,8 +175,15 @@ static const DuckyCmd ducky_commands[] = {
#define WORKER_TAG TAG "Worker" #define WORKER_TAG TAG "Worker"
int32_t ducky_execute_cmd(BadUsbScript* bad_usb, const char* line) { int32_t ducky_execute_cmd(BadUsbScript* bad_usb, const char* line) {
size_t cmd_word_len = strcspn(line, " ");
for(size_t i = 0; i < COUNT_OF(ducky_commands); i++) { for(size_t i = 0; i < COUNT_OF(ducky_commands); i++) {
if(strncmp(line, ducky_commands[i].name, strlen(ducky_commands[i].name)) == 0) { size_t cmd_compare_len = strlen(ducky_commands[i].name);
if(cmd_compare_len != cmd_word_len) {
continue;
}
if(strncmp(line, ducky_commands[i].name, cmd_compare_len) == 0) {
if(ducky_commands[i].callback == NULL) { if(ducky_commands[i].callback == NULL) {
return 0; return 0;
} else { } else {

View File

@@ -6,14 +6,13 @@ typedef enum {
SubGhzCustomEventManagerSetRAW, SubGhzCustomEventManagerSetRAW,
//SubmenuIndex //SubmenuIndex
SubmenuIndexPricenton, SubmenuIndexPricenton_433,
SubmenuIndexPricenton_315,
SubmenuIndexNiceFlo12bit, SubmenuIndexNiceFlo12bit,
SubmenuIndexNiceFlo24bit, SubmenuIndexNiceFlo24bit,
SubmenuIndexCAME12bit, SubmenuIndexCAME12bit,
SubmenuIndexCAME24bit, SubmenuIndexCAME24bit,
SubmenuIndexCAMETwee, SubmenuIndexCAMETwee,
SubmenuIndexNeroSketch,
SubmenuIndexNeroRadio,
SubmenuIndexGateTX, SubmenuIndexGateTX,
SubmenuIndexDoorHan_315_00, SubmenuIndexDoorHan_315_00,
SubmenuIndexDoorHan_433_92, SubmenuIndexDoorHan_433_92,

View File

@@ -261,7 +261,7 @@ SubGhzFrequencyAnalyzerWorker* subghz_frequency_analyzer_worker_alloc(void* cont
instance->thread = furi_thread_alloc_ex( instance->thread = furi_thread_alloc_ex(
"SubGhzFAWorker", 2048, subghz_frequency_analyzer_worker_thread, instance); "SubGhzFAWorker", 2048, subghz_frequency_analyzer_worker_thread, instance);
SubGhz* subghz = context; SubGhz* subghz = context;
instance->setting = subghz->setting; instance->setting = subghz_txrx_get_setting(subghz->txrx);
return instance; return instance;
} }

View File

@@ -0,0 +1,60 @@
#include "subghz_threshold_rssi.h"
#include <float_tools.h>
#include "../subghz_i.h"
#define TAG "SubGhzThresholdRssi"
#define THRESHOLD_RSSI_LOW_COUNT 10
struct SubGhzThresholdRssi {
float threshold_rssi;
uint8_t threshold_rssi_low_count;
};
SubGhzThresholdRssi* subghz_threshold_rssi_alloc(void) {
SubGhzThresholdRssi* instance = malloc(sizeof(SubGhzThresholdRssi));
instance->threshold_rssi = SUBGHZ_RAW_THRESHOLD_MIN;
instance->threshold_rssi_low_count = THRESHOLD_RSSI_LOW_COUNT;
return instance;
}
void subghz_threshold_rssi_free(SubGhzThresholdRssi* instance) {
furi_assert(instance);
free(instance);
}
void subghz_threshold_rssi_set(SubGhzThresholdRssi* instance, float rssi) {
furi_assert(instance);
instance->threshold_rssi = rssi;
}
float subghz_threshold_rssi_get(SubGhzThresholdRssi* instance) {
furi_assert(instance);
return instance->threshold_rssi;
}
SubGhzThresholdRssiData subghz_threshold_get_rssi_data(SubGhzThresholdRssi* instance) {
furi_assert(instance);
float rssi = furi_hal_subghz_get_rssi();
SubGhzThresholdRssiData ret = {.rssi = rssi, .is_above = false};
if(float_is_equal(instance->threshold_rssi, SUBGHZ_RAW_THRESHOLD_MIN)) {
ret.is_above = true;
} else {
if(rssi < instance->threshold_rssi) {
instance->threshold_rssi_low_count++;
if(instance->threshold_rssi_low_count > THRESHOLD_RSSI_LOW_COUNT) {
instance->threshold_rssi_low_count = THRESHOLD_RSSI_LOW_COUNT;
}
ret.is_above = false;
} else {
instance->threshold_rssi_low_count = 0;
}
if(instance->threshold_rssi_low_count == THRESHOLD_RSSI_LOW_COUNT) {
ret.is_above = false;
} else {
ret.is_above = true;
}
}
return ret;
}

View File

@@ -0,0 +1,43 @@
#pragma once
#include <furi.h>
typedef struct {
float rssi; /**< Current RSSI */
bool is_above; /**< Exceeded threshold level */
} SubGhzThresholdRssiData;
typedef struct SubGhzThresholdRssi SubGhzThresholdRssi;
/** Allocate SubGhzThresholdRssi
*
* @return SubGhzThresholdRssi*
*/
SubGhzThresholdRssi* subghz_threshold_rssi_alloc(void);
/** Free SubGhzThresholdRssi
*
* @param instance Pointer to a SubGhzThresholdRssi
*/
void subghz_threshold_rssi_free(SubGhzThresholdRssi* instance);
/** Set threshold
*
* @param instance Pointer to a SubGhzThresholdRssi
* @param rssi RSSI threshold
*/
void subghz_threshold_rssi_set(SubGhzThresholdRssi* instance, float rssi);
/** Get threshold
*
* @param instance Pointer to a SubGhzThresholdRssi
* @return float RSSI threshold
*/
float subghz_threshold_rssi_get(SubGhzThresholdRssi* instance);
/** Check threshold
*
* @param instance Pointer to a SubGhzThresholdRssi
* @return SubGhzThresholdRssiData
*/
SubGhzThresholdRssiData subghz_threshold_get_rssi_data(SubGhzThresholdRssi* instance);

View File

@@ -0,0 +1,521 @@
#include "subghz_txrx_i.h"
#include <lib/subghz/protocols/protocol_items.h>
#define TAG "SubGhz"
SubGhzTxRx* subghz_txrx_alloc() {
SubGhzTxRx* instance = malloc(sizeof(SubGhzTxRx));
instance->setting = subghz_setting_alloc();
subghz_setting_load(instance->setting, EXT_PATH("subghz/assets/setting_user"));
instance->preset = malloc(sizeof(SubGhzRadioPreset));
instance->preset->name = furi_string_alloc();
subghz_txrx_set_preset(
instance, "AM650", subghz_setting_get_default_frequency(instance->setting), NULL, 0);
instance->txrx_state = SubGhzTxRxStateSleep;
subghz_txrx_hopper_set_state(instance, SubGhzHopperStateOFF);
subghz_txrx_speaker_set_state(instance, SubGhzSpeakerStateDisable);
instance->worker = subghz_worker_alloc();
instance->fff_data = flipper_format_string_alloc();
instance->environment = subghz_environment_alloc();
instance->is_database_loaded = subghz_environment_load_keystore(
instance->environment, EXT_PATH("subghz/assets/keeloq_mfcodes"));
subghz_environment_load_keystore(
instance->environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user"));
subghz_environment_set_came_atomo_rainbow_table_file_name(
instance->environment, EXT_PATH("subghz/assets/came_atomo"));
subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
instance->environment, EXT_PATH("subghz/assets/alutech_at_4n"));
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
instance->environment, EXT_PATH("subghz/assets/nice_flor_s"));
subghz_environment_set_protocol_registry(
instance->environment, (void*)&subghz_protocol_registry);
instance->receiver = subghz_receiver_alloc_init(instance->environment);
subghz_worker_set_overrun_callback(
instance->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset);
subghz_worker_set_pair_callback(
instance->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode);
subghz_worker_set_context(instance->worker, instance->receiver);
return instance;
}
void subghz_txrx_free(SubGhzTxRx* instance) {
furi_assert(instance);
subghz_worker_free(instance->worker);
subghz_receiver_free(instance->receiver);
subghz_environment_free(instance->environment);
flipper_format_free(instance->fff_data);
furi_string_free(instance->preset->name);
subghz_setting_free(instance->setting);
free(instance->preset);
free(instance);
}
bool subghz_txrx_is_database_loaded(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->is_database_loaded;
}
void subghz_txrx_set_preset(
SubGhzTxRx* instance,
const char* preset_name,
uint32_t frequency,
uint8_t* preset_data,
size_t preset_data_size) {
furi_assert(instance);
furi_string_set(instance->preset->name, preset_name);
SubGhzRadioPreset* preset = instance->preset;
preset->frequency = frequency;
preset->data = preset_data;
preset->data_size = preset_data_size;
}
const char* subghz_txrx_get_preset_name(SubGhzTxRx* instance, const char* preset) {
UNUSED(instance);
const char* preset_name = "";
if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) {
preset_name = "AM270";
} else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) {
preset_name = "AM650";
} else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev238Async")) {
preset_name = "FM238";
} else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev476Async")) {
preset_name = "FM476";
} else if(!strcmp(preset, "FuriHalSubGhzPresetCustom")) {
preset_name = "CUSTOM";
} else {
FURI_LOG_E(TAG, "Unknown preset");
}
return preset_name;
}
SubGhzRadioPreset subghz_txrx_get_preset(SubGhzTxRx* instance) {
furi_assert(instance);
return *instance->preset;
}
void subghz_txrx_get_frequency_and_modulation(
SubGhzTxRx* instance,
FuriString* frequency,
FuriString* modulation) {
furi_assert(instance);
SubGhzRadioPreset* preset = instance->preset;
if(frequency != NULL) {
furi_string_printf(
frequency,
"%03ld.%02ld",
preset->frequency / 1000000 % 1000,
preset->frequency / 10000 % 100);
}
if(modulation != NULL) {
furi_string_printf(modulation, "%.2s", furi_string_get_cstr(preset->name));
}
}
static void subghz_txrx_begin(SubGhzTxRx* instance, uint8_t* preset_data) {
furi_assert(instance);
furi_hal_subghz_reset();
furi_hal_subghz_idle();
furi_hal_subghz_load_custom_preset(preset_data);
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
instance->txrx_state = SubGhzTxRxStateIDLE;
}
static uint32_t subghz_txrx_rx(SubGhzTxRx* instance, uint32_t frequency) {
furi_assert(instance);
if(!furi_hal_subghz_is_frequency_valid(frequency)) {
furi_crash("SubGhz: Incorrect RX frequency.");
}
furi_assert(
instance->txrx_state != SubGhzTxRxStateRx && instance->txrx_state != SubGhzTxRxStateSleep);
furi_hal_subghz_idle();
uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency);
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
furi_hal_subghz_flush_rx();
subghz_txrx_speaker_on(instance);
furi_hal_subghz_rx();
furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, instance->worker);
subghz_worker_start(instance->worker);
instance->txrx_state = SubGhzTxRxStateRx;
return value;
}
static void subghz_txrx_idle(SubGhzTxRx* instance) {
furi_assert(instance);
furi_assert(instance->txrx_state != SubGhzTxRxStateSleep);
furi_hal_subghz_idle();
subghz_txrx_speaker_off(instance);
instance->txrx_state = SubGhzTxRxStateIDLE;
}
static void subghz_txrx_rx_end(SubGhzTxRx* instance) {
furi_assert(instance);
furi_assert(instance->txrx_state == SubGhzTxRxStateRx);
if(subghz_worker_is_running(instance->worker)) {
subghz_worker_stop(instance->worker);
furi_hal_subghz_stop_async_rx();
}
furi_hal_subghz_idle();
subghz_txrx_speaker_off(instance);
instance->txrx_state = SubGhzTxRxStateIDLE;
}
void subghz_txrx_sleep(SubGhzTxRx* instance) {
furi_assert(instance);
furi_hal_subghz_sleep();
instance->txrx_state = SubGhzTxRxStateSleep;
}
static bool subghz_txrx_tx(SubGhzTxRx* instance, uint32_t frequency) {
furi_assert(instance);
if(!furi_hal_subghz_is_frequency_valid(frequency)) {
furi_crash("SubGhz: Incorrect TX frequency.");
}
furi_assert(instance->txrx_state != SubGhzTxRxStateSleep);
furi_hal_subghz_idle();
furi_hal_subghz_set_frequency_and_path(frequency);
furi_hal_gpio_write(&gpio_cc1101_g0, false);
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
bool ret = furi_hal_subghz_tx();
if(ret) {
subghz_txrx_speaker_on(instance);
instance->txrx_state = SubGhzTxRxStateTx;
}
return ret;
}
SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* flipper_format) {
furi_assert(instance);
furi_assert(flipper_format);
subghz_txrx_stop(instance);
SubGhzTxRxStartTxState ret = SubGhzTxRxStartTxStateErrorParserOthers;
FuriString* temp_str = furi_string_alloc();
uint32_t repeat = 200;
do {
if(!flipper_format_rewind(flipper_format)) {
FURI_LOG_E(TAG, "Rewind error");
break;
}
if(!flipper_format_read_string(flipper_format, "Protocol", temp_str)) {
FURI_LOG_E(TAG, "Missing Protocol");
break;
}
if(!flipper_format_insert_or_update_uint32(flipper_format, "Repeat", &repeat, 1)) {
FURI_LOG_E(TAG, "Unable Repeat");
break;
}
ret = SubGhzTxRxStartTxStateOk;
SubGhzRadioPreset* preset = instance->preset;
instance->transmitter =
subghz_transmitter_alloc_init(instance->environment, furi_string_get_cstr(temp_str));
if(instance->transmitter) {
if(subghz_transmitter_deserialize(instance->transmitter, flipper_format) ==
SubGhzProtocolStatusOk) {
if(strcmp(furi_string_get_cstr(preset->name), "") != 0) {
subghz_txrx_begin(
instance,
subghz_setting_get_preset_data_by_name(
instance->setting, furi_string_get_cstr(preset->name)));
if(preset->frequency) {
if(!subghz_txrx_tx(instance, preset->frequency)) {
FURI_LOG_E(TAG, "Only Rx");
ret = SubGhzTxRxStartTxStateErrorOnlyRx;
}
} else {
ret = SubGhzTxRxStartTxStateErrorParserOthers;
}
} else {
FURI_LOG_E(
TAG, "Unknown name preset \" %s \"", furi_string_get_cstr(preset->name));
ret = SubGhzTxRxStartTxStateErrorParserOthers;
}
if(ret == SubGhzTxRxStartTxStateOk) {
//Start TX
furi_hal_subghz_start_async_tx(
subghz_transmitter_yield, instance->transmitter);
}
} else {
ret = SubGhzTxRxStartTxStateErrorParserOthers;
}
} else {
ret = SubGhzTxRxStartTxStateErrorParserOthers;
}
if(ret != SubGhzTxRxStartTxStateOk) {
subghz_transmitter_free(instance->transmitter);
if(instance->txrx_state != SubGhzTxRxStateIDLE) {
subghz_txrx_idle(instance);
}
}
} while(false);
furi_string_free(temp_str);
return ret;
}
void subghz_txrx_rx_start(SubGhzTxRx* instance) {
furi_assert(instance);
subghz_txrx_stop(instance);
subghz_txrx_begin(
instance,
subghz_setting_get_preset_data_by_name(
subghz_txrx_get_setting(instance), furi_string_get_cstr(instance->preset->name)));
subghz_txrx_rx(instance, instance->preset->frequency);
}
void subghz_txrx_set_need_save_callback(
SubGhzTxRx* instance,
SubGhzTxRxNeedSaveCallback callback,
void* context) {
furi_assert(instance);
instance->need_save_callback = callback;
instance->need_save_context = context;
}
static void subghz_txrx_tx_stop(SubGhzTxRx* instance) {
furi_assert(instance);
furi_assert(instance->txrx_state == SubGhzTxRxStateTx);
//Stop TX
furi_hal_subghz_stop_async_tx();
subghz_transmitter_stop(instance->transmitter);
subghz_transmitter_free(instance->transmitter);
//if protocol dynamic then we save the last upload
if(instance->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) {
if(instance->need_save_callback) {
instance->need_save_callback(instance->need_save_context);
}
}
subghz_txrx_idle(instance);
subghz_txrx_speaker_off(instance);
//Todo: Show message
// notification_message(notifications, &sequence_reset_red);
}
FlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->fff_data;
}
SubGhzSetting* subghz_txrx_get_setting(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->setting;
}
void subghz_txrx_stop(SubGhzTxRx* instance) {
furi_assert(instance);
switch(instance->txrx_state) {
case SubGhzTxRxStateTx:
subghz_txrx_tx_stop(instance);
subghz_txrx_speaker_unmute(instance);
break;
case SubGhzTxRxStateRx:
subghz_txrx_rx_end(instance);
subghz_txrx_speaker_mute(instance);
break;
default:
break;
}
}
void subghz_txrx_hopper_update(SubGhzTxRx* instance) {
furi_assert(instance);
switch(instance->hopper_state) {
case SubGhzHopperStateOFF:
case SubGhzHopperStatePause:
return;
case SubGhzHopperStateRSSITimeOut:
if(instance->hopper_timeout != 0) {
instance->hopper_timeout--;
return;
}
break;
default:
break;
}
float rssi = -127.0f;
if(instance->hopper_state != SubGhzHopperStateRSSITimeOut) {
// See RSSI Calculation timings in CC1101 17.3 RSSI
rssi = furi_hal_subghz_get_rssi();
// Stay if RSSI is high enough
if(rssi > -90.0f) {
instance->hopper_timeout = 10;
instance->hopper_state = SubGhzHopperStateRSSITimeOut;
return;
}
} else {
instance->hopper_state = SubGhzHopperStateRunnig;
}
// Select next frequency
if(instance->hopper_idx_frequency <
subghz_setting_get_hopper_frequency_count(instance->setting) - 1) {
instance->hopper_idx_frequency++;
} else {
instance->hopper_idx_frequency = 0;
}
if(instance->txrx_state == SubGhzTxRxStateRx) {
subghz_txrx_rx_end(instance);
};
if(instance->txrx_state == SubGhzTxRxStateIDLE) {
subghz_receiver_reset(instance->receiver);
instance->preset->frequency =
subghz_setting_get_hopper_frequency(instance->setting, instance->hopper_idx_frequency);
subghz_txrx_rx(instance, instance->preset->frequency);
}
}
SubGhzHopperState subghz_txrx_hopper_get_state(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->hopper_state;
}
void subghz_txrx_hopper_set_state(SubGhzTxRx* instance, SubGhzHopperState state) {
furi_assert(instance);
instance->hopper_state = state;
}
void subghz_txrx_hopper_unpause(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->hopper_state == SubGhzHopperStatePause) {
instance->hopper_state = SubGhzHopperStateRunnig;
}
}
void subghz_txrx_hopper_pause(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->hopper_state == SubGhzHopperStateRunnig) {
instance->hopper_state = SubGhzHopperStatePause;
}
}
void subghz_txrx_speaker_on(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->speaker_state == SubGhzSpeakerStateEnable) {
if(furi_hal_speaker_acquire(30)) {
furi_hal_subghz_set_async_mirror_pin(&gpio_speaker);
} else {
instance->speaker_state = SubGhzSpeakerStateDisable;
}
}
}
void subghz_txrx_speaker_off(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->speaker_state != SubGhzSpeakerStateDisable) {
if(furi_hal_speaker_is_mine()) {
furi_hal_subghz_set_async_mirror_pin(NULL);
furi_hal_speaker_release();
if(instance->speaker_state == SubGhzSpeakerStateShutdown)
instance->speaker_state = SubGhzSpeakerStateDisable;
}
}
}
void subghz_txrx_speaker_mute(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->speaker_state == SubGhzSpeakerStateEnable) {
if(furi_hal_speaker_is_mine()) {
furi_hal_subghz_set_async_mirror_pin(NULL);
}
}
}
void subghz_txrx_speaker_unmute(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->speaker_state == SubGhzSpeakerStateEnable) {
if(furi_hal_speaker_is_mine()) {
furi_hal_subghz_set_async_mirror_pin(&gpio_speaker);
}
}
}
void subghz_txrx_speaker_set_state(SubGhzTxRx* instance, SubGhzSpeakerState state) {
furi_assert(instance);
instance->speaker_state = state;
}
SubGhzSpeakerState subghz_txrx_speaker_get_state(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->speaker_state;
}
bool subghz_txrx_load_decoder_by_name_protocol(SubGhzTxRx* instance, const char* name_protocol) {
furi_assert(instance);
furi_assert(name_protocol);
bool res = false;
instance->decoder_result =
subghz_receiver_search_decoder_base_by_name(instance->receiver, name_protocol);
if(instance->decoder_result) {
res = true;
}
return res;
}
SubGhzProtocolDecoderBase* subghz_txrx_get_decoder(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->decoder_result;
}
bool subghz_txrx_protocol_is_serializable(SubGhzTxRx* instance) {
furi_assert(instance);
return (
(instance->decoder_result->protocol->flag & SubGhzProtocolFlag_Save) ==
SubGhzProtocolFlag_Save);
}
bool subghz_txrx_protocol_is_transmittable(SubGhzTxRx* instance, bool check_type) {
furi_assert(instance);
const SubGhzProtocol* protocol = instance->decoder_result->protocol;
if(check_type) {
return (
((protocol->flag & SubGhzProtocolFlag_Send) == SubGhzProtocolFlag_Send) &&
protocol->encoder->deserialize && protocol->type == SubGhzProtocolTypeStatic);
}
return (
((protocol->flag & SubGhzProtocolFlag_Send) == SubGhzProtocolFlag_Send) &&
protocol->encoder->deserialize);
}
void subghz_txrx_receiver_set_filter(SubGhzTxRx* instance, SubGhzProtocolFlag filter) {
furi_assert(instance);
subghz_receiver_set_filter(instance->receiver, filter);
}
void subghz_txrx_set_rx_calback(
SubGhzTxRx* instance,
SubGhzReceiverCallback callback,
void* context) {
subghz_receiver_set_rx_callback(instance->receiver, callback, context);
}
void subghz_txrx_set_raw_file_encoder_worker_callback_end(
SubGhzTxRx* instance,
SubGhzProtocolEncoderRAWCallbackEnd callback,
void* context) {
subghz_protocol_raw_file_encoder_worker_set_callback_end(
(SubGhzProtocolEncoderRAW*)subghz_transmitter_get_protocol_instance(instance->transmitter),
callback,
context);
}

View File

@@ -0,0 +1,290 @@
#pragma once
#include "subghz_types.h"
#include <lib/subghz/subghz_worker.h>
#include <lib/subghz/subghz_setting.h>
#include <lib/subghz/receiver.h>
#include <lib/subghz/transmitter.h>
#include <lib/subghz/protocols/raw.h>
typedef struct SubGhzTxRx SubGhzTxRx;
typedef void (*SubGhzTxRxNeedSaveCallback)(void* context);
typedef enum {
SubGhzTxRxStartTxStateOk,
SubGhzTxRxStartTxStateErrorOnlyRx,
SubGhzTxRxStartTxStateErrorParserOthers,
} SubGhzTxRxStartTxState;
/**
* Allocate SubGhzTxRx
*
* @return SubGhzTxRx* pointer to SubGhzTxRx
*/
SubGhzTxRx* subghz_txrx_alloc();
/**
* Free SubGhzTxRx
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_free(SubGhzTxRx* instance);
/**
* Check if the database is loaded
*
* @param instance Pointer to a SubGhzTxRx
* @return bool True if the database is loaded
*/
bool subghz_txrx_is_database_loaded(SubGhzTxRx* instance);
/**
* Set preset
*
* @param instance Pointer to a SubGhzTxRx
* @param preset_name Name of preset
* @param frequency Frequency in Hz
* @param preset_data Data of preset
* @param preset_data_size Size of preset data
*/
void subghz_txrx_set_preset(
SubGhzTxRx* instance,
const char* preset_name,
uint32_t frequency,
uint8_t* preset_data,
size_t preset_data_size);
/**
* Get name of preset
*
* @param instance Pointer to a SubGhzTxRx
* @param preset String of preset
* @return const char* Name of preset
*/
const char* subghz_txrx_get_preset_name(SubGhzTxRx* instance, const char* preset);
/**
* Get of preset
*
* @param instance Pointer to a SubGhzTxRx
* @return SubGhzRadioPreset Preset
*/
SubGhzRadioPreset subghz_txrx_get_preset(SubGhzTxRx* instance);
/**
* Get string frequency and modulation
*
* @param instance Pointer to a SubGhzTxRx
* @param frequency Pointer to a string frequency
* @param modulation Pointer to a string modulation
*/
void subghz_txrx_get_frequency_and_modulation(
SubGhzTxRx* instance,
FuriString* frequency,
FuriString* modulation);
/**
* Start TX CC1101
*
* @param instance Pointer to a SubGhzTxRx
* @param flipper_format Pointer to a FlipperFormat
* @return SubGhzTxRxStartTxState
*/
SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* flipper_format);
/**
* Start RX CC1101
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_rx_start(SubGhzTxRx* instance);
/**
* Stop TX/RX CC1101
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_stop(SubGhzTxRx* instance);
/**
* Set sleep mode CC1101
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_sleep(SubGhzTxRx* instance);
/**
* Update frequency CC1101 in automatic mode (hopper)
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_hopper_update(SubGhzTxRx* instance);
/**
* Get state hopper
*
* @param instance Pointer to a SubGhzTxRx
* @return SubGhzHopperState
*/
SubGhzHopperState subghz_txrx_hopper_get_state(SubGhzTxRx* instance);
/**
* Set state hopper
*
* @param instance Pointer to a SubGhzTxRx
* @param state State hopper
*/
void subghz_txrx_hopper_set_state(SubGhzTxRx* instance, SubGhzHopperState state);
/**
* Unpause hopper
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_hopper_unpause(SubGhzTxRx* instance);
/**
* Set pause hopper
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_hopper_pause(SubGhzTxRx* instance);
/**
* Speaker on
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_speaker_on(SubGhzTxRx* instance);
/**
* Speaker off
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_speaker_off(SubGhzTxRx* instance);
/**
* Speaker mute
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_speaker_mute(SubGhzTxRx* instance);
/**
* Speaker unmute
*
* @param instance Pointer to a SubGhzTxRx
*/
void subghz_txrx_speaker_unmute(SubGhzTxRx* instance);
/**
* Set state speaker
*
* @param instance Pointer to a SubGhzTxRx
* @param state State speaker
*/
void subghz_txrx_speaker_set_state(SubGhzTxRx* instance, SubGhzSpeakerState state);
/**
* Get state speaker
*
* @param instance Pointer to a SubGhzTxRx
* @return SubGhzSpeakerState
*/
SubGhzSpeakerState subghz_txrx_speaker_get_state(SubGhzTxRx* instance);
/**
* load decoder by name protocol
*
* @param instance Pointer to a SubGhzTxRx
* @param name_protocol Name protocol
* @return bool True if the decoder is loaded
*/
bool subghz_txrx_load_decoder_by_name_protocol(SubGhzTxRx* instance, const char* name_protocol);
/**
* Get decoder
*
* @param instance Pointer to a SubGhzTxRx
* @return SubGhzProtocolDecoderBase* Pointer to a SubGhzProtocolDecoderBase
*/
SubGhzProtocolDecoderBase* subghz_txrx_get_decoder(SubGhzTxRx* instance);
/**
* Set callback for save data
*
* @param instance Pointer to a SubGhzTxRx
* @param callback Callback for save data
* @param context Context for callback
*/
void subghz_txrx_set_need_save_callback(
SubGhzTxRx* instance,
SubGhzTxRxNeedSaveCallback callback,
void* context);
/**
* Get pointer to a load data key
*
* @param instance Pointer to a SubGhzTxRx
* @return FlipperFormat*
*/
FlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance);
/**
* Get pointer to a SugGhzSetting
*
* @param instance Pointer to a SubGhzTxRx
* @return SubGhzSetting*
*/
SubGhzSetting* subghz_txrx_get_setting(SubGhzTxRx* instance);
/**
* Is it possible to save this protocol
*
* @param instance Pointer to a SubGhzTxRx
* @return bool True if it is possible to save this protocol
*/
bool subghz_txrx_protocol_is_serializable(SubGhzTxRx* instance);
/**
* Is it possible to send this protocol
*
* @param instance Pointer to a SubGhzTxRx
* @return bool True if it is possible to send this protocol
*/
bool subghz_txrx_protocol_is_transmittable(SubGhzTxRx* instance, bool check_type);
/**
* Set filter, what types of decoder to use
*
* @param instance Pointer to a SubGhzTxRx
* @param filter Filter
*/
void subghz_txrx_receiver_set_filter(SubGhzTxRx* instance, SubGhzProtocolFlag filter);
/**
* Set callback for receive data
*
* @param instance Pointer to a SubGhzTxRx
* @param callback Callback for receive data
* @param context Context for callback
*/
void subghz_txrx_set_rx_calback(
SubGhzTxRx* instance,
SubGhzReceiverCallback callback,
void* context);
/**
* Set callback for Raw decoder, end of data transfer
*
* @param instance Pointer to a SubGhzTxRx
* @param callback Callback for Raw decoder, end of data transfer
* @param context Context for callback
*/
void subghz_txrx_set_raw_file_encoder_worker_callback_end(
SubGhzTxRx* instance,
SubGhzProtocolEncoderRAWCallbackEnd callback,
void* context);

View File

@@ -0,0 +1,164 @@
#include "subghz_txrx_i.h"
#include "subghz_txrx_create_potocol_key.h"
#include <lib/subghz/transmitter.h>
#include <lib/subghz/protocols/protocol_items.h>
#include <lib/subghz/protocols/protocol_items.h>
#include <lib/subghz/protocols/keeloq.h>
#include <lib/subghz/protocols/secplus_v1.h>
#include <lib/subghz/protocols/secplus_v2.h>
#include <flipper_format/flipper_format_i.h>
#include <lib/toolbox/stream/stream.h>
#include <lib/subghz/protocols/raw.h>
#define TAG "SubGhzCreateProtocolKey"
bool subghz_txrx_gen_data_protocol(
void* context,
const char* preset_name,
uint32_t frequency,
const char* protocol_name,
uint64_t key,
uint32_t bit) {
furi_assert(context);
SubGhzTxRx* instance = context;
bool res = false;
subghz_txrx_set_preset(instance, preset_name, frequency, NULL, 0);
instance->decoder_result =
subghz_receiver_search_decoder_base_by_name(instance->receiver, protocol_name);
if(instance->decoder_result == NULL) {
//TODO: Error
// furi_string_set(error_str, "Protocol not\nfound!");
// scene_manager_next_scene(scene_manager, SubGhzSceneShowErrorSub);
FURI_LOG_E(TAG, "Protocol not found!");
return false;
}
do {
Stream* fff_data_stream = flipper_format_get_raw_stream(instance->fff_data);
stream_clean(fff_data_stream);
if(subghz_protocol_decoder_base_serialize(
instance->decoder_result, instance->fff_data, instance->preset) !=
SubGhzProtocolStatusOk) {
FURI_LOG_E(TAG, "Unable to serialize");
break;
}
if(!flipper_format_update_uint32(instance->fff_data, "Bit", &bit, 1)) {
FURI_LOG_E(TAG, "Unable to update Bit");
break;
}
uint8_t key_data[sizeof(uint64_t)] = {0};
for(size_t i = 0; i < sizeof(uint64_t); i++) {
key_data[sizeof(uint64_t) - i - 1] = (key >> (i * 8)) & 0xFF;
}
if(!flipper_format_update_hex(instance->fff_data, "Key", key_data, sizeof(uint64_t))) {
FURI_LOG_E(TAG, "Unable to update Key");
break;
}
res = true;
} while(false);
return res;
}
bool subghz_txrx_gen_data_protocol_and_te(
SubGhzTxRx* instance,
const char* preset_name,
uint32_t frequency,
const char* protocol_name,
uint64_t key,
uint32_t bit,
uint32_t te) {
furi_assert(instance);
bool ret = false;
if(subghz_txrx_gen_data_protocol(instance, preset_name, frequency, protocol_name, key, bit)) {
if(!flipper_format_update_uint32(instance->fff_data, "TE", (uint32_t*)&te, 1)) {
FURI_LOG_E(TAG, "Unable to update Te");
} else {
ret = true;
}
}
return ret;
}
bool subghz_txrx_gen_keelog_protocol(
SubGhzTxRx* instance,
const char* name_preset,
uint32_t frequency,
const char* name_sysmem,
uint32_t serial,
uint8_t btn,
uint16_t cnt) {
furi_assert(instance);
bool ret = false;
serial &= 0x0FFFFFFF;
instance->transmitter =
subghz_transmitter_alloc_init(instance->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME);
subghz_txrx_set_preset(instance, name_preset, frequency, NULL, 0);
if(instance->transmitter) {
subghz_protocol_keeloq_create_data(
subghz_transmitter_get_protocol_instance(instance->transmitter),
instance->fff_data,
serial,
btn,
cnt,
name_sysmem,
instance->preset);
ret = true;
}
subghz_transmitter_free(instance->transmitter);
return ret;
}
bool subghz_txrx_gen_secplus_v2_protocol(
SubGhzTxRx* instance,
const char* name_preset,
uint32_t frequency,
uint32_t serial,
uint8_t btn,
uint32_t cnt) {
furi_assert(instance);
bool ret = false;
instance->transmitter =
subghz_transmitter_alloc_init(instance->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME);
subghz_txrx_set_preset(instance, name_preset, frequency, NULL, 0);
if(instance->transmitter) {
subghz_protocol_secplus_v2_create_data(
subghz_transmitter_get_protocol_instance(instance->transmitter),
instance->fff_data,
serial,
btn,
cnt,
instance->preset);
ret = true;
}
return ret;
}
bool subghz_txrx_gen_secplus_v1_protocol(
SubGhzTxRx* instance,
const char* name_preset,
uint32_t frequency) {
furi_assert(instance);
bool ret = false;
uint32_t serial = (uint32_t)rand();
while(!subghz_protocol_secplus_v1_check_fixed(serial)) {
serial = (uint32_t)rand();
}
if(subghz_txrx_gen_data_protocol(
instance,
name_preset,
frequency,
SUBGHZ_PROTOCOL_SECPLUS_V1_NAME,
(uint64_t)serial << 32 | 0xE6000000,
42)) {
ret = true;
}
return ret;
}

View File

@@ -0,0 +1,96 @@
#pragma once
#include "subghz_types.h"
#include "subghz_txrx.h"
/**
* Generate data for protocol
*
* @param instance Pointer to a SubGhzTxRx
* @param preset_name Name of preset
* @param frequency Frequency in Hz
* @param protocol_name Name of protocol
* @param key Key
* @param bit Bit
* @return bool True if success
*/
bool subghz_txrx_gen_data_protocol(
void* context,
const char* preset_name,
uint32_t frequency,
const char* protocol_name,
uint64_t key,
uint32_t bit);
/**
* Generate data for protocol and te
*
* @param instance Pointer to a SubGhzTxRx
* @param preset_name Name of preset
* @param frequency Frequency in Hz
* @param protocol_name Name of protocol
* @param key Key
* @param bit Bit
* @param te Te
* @return bool True if success
*/
bool subghz_txrx_gen_data_protocol_and_te(
SubGhzTxRx* instance,
const char* preset_name,
uint32_t frequency,
const char* protocol_name,
uint64_t key,
uint32_t bit,
uint32_t te);
/**
* Generate data Keeloq protocol
*
* @param instance Pointer to a SubGhzTxRx
* @param name_preset Name of preset
* @param frequency Frequency in Hz
* @param name_sysmem Name of Keeloq sysmem
* @param serial Serial number
* @param btn Button
* @param cnt Counter
* @return bool True if success
*/
bool subghz_txrx_gen_keelog_protocol(
SubGhzTxRx* instance,
const char* name_preset,
uint32_t frequency,
const char* name_sysmem,
uint32_t serial,
uint8_t btn,
uint16_t cnt);
/**
* Generate data SecPlus v2 protocol
*
* @param instance Pointer to a SubGhzTxRx
* @param name_preset Name of preset
* @param frequency Frequency in Hz
* @param serial Serial number
* @param btn Button
* @param cnt Counter
* @return bool True if success
*/
bool subghz_txrx_gen_secplus_v2_protocol(
SubGhzTxRx* instance,
const char* name_preset,
uint32_t frequency,
uint32_t serial,
uint8_t btn,
uint32_t cnt);
/**
* Generate data SecPlus v1 protocol
*
* @param instance Pointer to a SubGhzTxRx
* @param name_preset Name of preset
* @param frequency Frequency in Hz
* @return bool True if success
*/
bool subghz_txrx_gen_secplus_v1_protocol(
SubGhzTxRx* instance,
const char* name_preset,
uint32_t frequency);

View File

@@ -0,0 +1,27 @@
#pragma once
#include "subghz_txrx.h"
struct SubGhzTxRx {
SubGhzWorker* worker;
SubGhzEnvironment* environment;
SubGhzReceiver* receiver;
SubGhzTransmitter* transmitter;
SubGhzProtocolDecoderBase* decoder_result;
FlipperFormat* fff_data;
SubGhzRadioPreset* preset;
SubGhzSetting* setting;
uint8_t hopper_timeout;
uint8_t hopper_idx_frequency;
bool is_database_loaded;
SubGhzHopperState hopper_state;
SubGhzTxRxState txrx_state;
SubGhzSpeakerState speaker_state;
SubGhzTxRxNeedSaveCallback need_save_callback;
void* need_save_context;
};

View File

@@ -77,3 +77,10 @@ typedef enum {
SubGhzViewIdTestCarrier, SubGhzViewIdTestCarrier,
SubGhzViewIdTestPacket, SubGhzViewIdTestPacket,
} SubGhzViewId; } SubGhzViewId;
/** SubGhz load type file */
typedef enum {
SubGhzLoadTypeFileNoLoad,
SubGhzLoadTypeFileKey,
SubGhzLoadTypeFileRaw,
} SubGhzLoadTypeFile;

View File

@@ -19,7 +19,7 @@ void subghz_scene_delete_on_enter(void* context) {
modulation_str = furi_string_alloc(); modulation_str = furi_string_alloc();
text = furi_string_alloc(); text = furi_string_alloc();
subghz_get_frequency_modulation(subghz, frequency_str, modulation_str); subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str);
widget_add_string_element( widget_add_string_element(
subghz->widget, subghz->widget,
78, 78,
@@ -37,7 +37,7 @@ void subghz_scene_delete_on_enter(void* context) {
AlignTop, AlignTop,
FontSecondary, FontSecondary,
furi_string_get_cstr(modulation_str)); furi_string_get_cstr(modulation_str));
subghz_protocol_decoder_base_get_string(subghz->txrx->decoder_result, text); subghz_protocol_decoder_base_get_string(subghz_txrx_get_decoder(subghz->txrx), text);
widget_add_string_multiline_element( widget_add_string_multiline_element(
subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(text)); subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(text));

View File

@@ -33,7 +33,7 @@ void subghz_scene_delete_raw_on_enter(void* context) {
widget_add_string_element( widget_add_string_element(
subghz->widget, 38, 25, AlignLeft, AlignTop, FontSecondary, "RAW signal"); subghz->widget, 38, 25, AlignLeft, AlignTop, FontSecondary, "RAW signal");
subghz_get_frequency_modulation(subghz, frequency_str, modulation_str); subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str);
widget_add_string_element( widget_add_string_element(
subghz->widget, subghz->widget,
35, 35,

View File

@@ -37,27 +37,23 @@ void subghz_scene_need_saving_on_enter(void* context) {
bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) { bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) {
SubGhz* subghz = context; SubGhz* subghz = context;
if(event.type == SceneManagerEventTypeBack) { if(event.type == SceneManagerEventTypeBack) {
subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack);
scene_manager_previous_scene(subghz->scene_manager); scene_manager_previous_scene(subghz->scene_manager);
return true; return true;
} else if(event.type == SceneManagerEventTypeCustom) { } else if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzCustomEventSceneStay) { if(event.event == SubGhzCustomEventSceneStay) {
subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack);
scene_manager_previous_scene(subghz->scene_manager); scene_manager_previous_scene(subghz->scene_manager);
return true; return true;
} else if(event.event == SubGhzCustomEventSceneExit) { } else if(event.event == SubGhzCustomEventSceneExit) {
if(subghz->txrx->rx_key_state == SubGhzRxKeyStateExit) { SubGhzRxKeyState state = subghz_rx_key_state_get(subghz);
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);
subghz_preset_init(
subghz, if(state == SubGhzRxKeyStateExit) {
"AM650", subghz_set_default_preset(subghz);
subghz_setting_get_default_frequency(subghz->setting),
NULL,
0);
scene_manager_search_and_switch_to_previous_scene( scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneStart); subghz->scene_manager, SubGhzSceneStart);
} else { } else {
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
scene_manager_previous_scene(subghz->scene_manager); scene_manager_previous_scene(subghz->scene_manager);
} }

View File

@@ -3,11 +3,9 @@
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
#include <lib/subghz/protocols/raw.h> #include <lib/subghz/protocols/raw.h>
#include <lib/toolbox/path.h> #include <lib/toolbox/path.h>
#include <float_tools.h>
#define RAW_FILE_NAME "Raw_signal_" #define RAW_FILE_NAME "Raw_signal_"
#define TAG "SubGhzSceneReadRAW" #define TAG "SubGhzSceneReadRAW"
#define RAW_THRESHOLD_RSSI_LOW_COUNT 10
bool subghz_scene_read_raw_update_filename(SubGhz* subghz) { bool subghz_scene_read_raw_update_filename(SubGhz* subghz) {
bool ret = false; bool ret = false;
@@ -15,12 +13,13 @@ bool subghz_scene_read_raw_update_filename(SubGhz* subghz) {
FuriString* temp_str; FuriString* temp_str;
temp_str = furi_string_alloc(); temp_str = furi_string_alloc();
do { do {
if(!flipper_format_rewind(subghz->txrx->fff_data)) { FlipperFormat* fff_data = subghz_txrx_get_fff_data(subghz->txrx);
if(!flipper_format_rewind(fff_data)) {
FURI_LOG_E(TAG, "Rewind error"); FURI_LOG_E(TAG, "Rewind error");
break; break;
} }
if(!flipper_format_read_string(subghz->txrx->fff_data, "File_name", temp_str)) { if(!flipper_format_read_string(fff_data, "File_name", temp_str)) {
FURI_LOG_E(TAG, "Missing File_name"); FURI_LOG_E(TAG, "Missing File_name");
break; break;
} }
@@ -38,13 +37,10 @@ static void subghz_scene_read_raw_update_statusbar(void* context) {
furi_assert(context); furi_assert(context);
SubGhz* subghz = context; SubGhz* subghz = context;
FuriString* frequency_str; FuriString* frequency_str = furi_string_alloc();
FuriString* modulation_str; FuriString* modulation_str = furi_string_alloc();
frequency_str = furi_string_alloc(); subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str);
modulation_str = furi_string_alloc();
subghz_get_frequency_modulation(subghz, frequency_str, modulation_str);
subghz_read_raw_add_data_statusbar( subghz_read_raw_add_data_statusbar(
subghz->subghz_read_raw, subghz->subghz_read_raw,
furi_string_get_cstr(frequency_str), furi_string_get_cstr(frequency_str),
@@ -69,13 +65,13 @@ void subghz_scene_read_raw_callback_end_tx(void* context) {
void subghz_scene_read_raw_on_enter(void* context) { void subghz_scene_read_raw_on_enter(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
FuriString* file_name; FuriString* file_name = furi_string_alloc();
file_name = furi_string_alloc();
switch(subghz->txrx->rx_key_state) { float threshold_rssi = subghz_threshold_rssi_get(subghz->threshold_rssi);
switch(subghz_rx_key_state_get(subghz)) {
case SubGhzRxKeyStateBack: case SubGhzRxKeyStateBack:
subghz_read_raw_set_status( subghz_read_raw_set_status(
subghz->subghz_read_raw, SubGhzReadRAWStatusIDLE, "", subghz->txrx->raw_threshold_rssi); subghz->subghz_read_raw, SubGhzReadRAWStatusIDLE, "", threshold_rssi);
break; break;
case SubGhzRxKeyStateRAWLoad: case SubGhzRxKeyStateRAWLoad:
path_extract_filename(subghz->file_path, file_name, true); path_extract_filename(subghz->file_path, file_name, true);
@@ -83,8 +79,7 @@ void subghz_scene_read_raw_on_enter(void* context) {
subghz->subghz_read_raw, subghz->subghz_read_raw,
SubGhzReadRAWStatusLoadKeyTX, SubGhzReadRAWStatusLoadKeyTX,
furi_string_get_cstr(file_name), furi_string_get_cstr(file_name),
subghz->txrx->raw_threshold_rssi); threshold_rssi);
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
break; break;
case SubGhzRxKeyStateRAWSave: case SubGhzRxKeyStateRAWSave:
path_extract_filename(subghz->file_path, file_name, true); path_extract_filename(subghz->file_path, file_name, true);
@@ -92,66 +87,51 @@ void subghz_scene_read_raw_on_enter(void* context) {
subghz->subghz_read_raw, subghz->subghz_read_raw,
SubGhzReadRAWStatusSaveKey, SubGhzReadRAWStatusSaveKey,
furi_string_get_cstr(file_name), furi_string_get_cstr(file_name),
subghz->txrx->raw_threshold_rssi); threshold_rssi);
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
break; break;
default: default:
subghz_read_raw_set_status( subghz_read_raw_set_status(
subghz->subghz_read_raw, subghz->subghz_read_raw, SubGhzReadRAWStatusStart, "", threshold_rssi);
SubGhzReadRAWStatusStart,
"",
subghz->txrx->raw_threshold_rssi);
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
break; break;
} }
if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateBack) {
subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);
}
furi_string_free(file_name); furi_string_free(file_name);
subghz_scene_read_raw_update_statusbar(subghz); subghz_scene_read_raw_update_statusbar(subghz);
//set callback view raw //set callback view raw
subghz_read_raw_set_callback(subghz->subghz_read_raw, subghz_scene_read_raw_callback, subghz); subghz_read_raw_set_callback(subghz->subghz_read_raw, subghz_scene_read_raw_callback, subghz);
subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name( furi_check(subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, SUBGHZ_PROTOCOL_RAW_NAME));
subghz->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME);
furi_assert(subghz->txrx->decoder_result);
//set filter RAW feed //set filter RAW feed
subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_RAW); subghz_txrx_receiver_set_filter(subghz->txrx, SubGhzProtocolFlag_RAW);
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReadRAW); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReadRAW);
} }
bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
SubGhz* subghz = context; SubGhz* subghz = context;
bool consumed = false; bool consumed = false;
SubGhzProtocolDecoderRAW* decoder_raw =
(SubGhzProtocolDecoderRAW*)subghz_txrx_get_decoder(subghz->txrx);
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) { switch(event.event) {
case SubGhzCustomEventViewReadRAWBack: case SubGhzCustomEventViewReadRAWBack:
//Stop TX
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { subghz_txrx_stop(subghz->txrx);
subghz_tx_stop(subghz);
subghz_sleep(subghz);
}
//Stop RX
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
subghz_rx_end(subghz);
subghz_sleep(subghz);
};
//Stop save file //Stop save file
subghz_protocol_raw_save_to_file_stop( subghz_protocol_raw_save_to_file_stop(decoder_raw);
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result);
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
//needed save? //needed save?
if((subghz->txrx->rx_key_state == SubGhzRxKeyStateAddKey) || if((subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) ||
(subghz->txrx->rx_key_state == SubGhzRxKeyStateBack)) { (subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateBack)) {
subghz->txrx->rx_key_state = SubGhzRxKeyStateExit; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateExit);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving);
} else { } else {
//Restore default setting //Restore default setting
subghz_preset_init( subghz_set_default_preset(subghz);
subghz,
"AM650",
subghz_setting_get_default_frequency(subghz->setting),
NULL,
0);
if(!scene_manager_search_and_switch_to_previous_scene( if(!scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneSaved)) { subghz->scene_manager, SubGhzSceneSaved)) {
if(!scene_manager_search_and_switch_to_previous_scene( if(!scene_manager_search_and_switch_to_previous_scene(
@@ -165,16 +145,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
break; break;
case SubGhzCustomEventViewReadRAWTXRXStop: case SubGhzCustomEventViewReadRAWTXRXStop:
//Stop TX subghz_txrx_stop(subghz->txrx);
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
subghz_tx_stop(subghz);
subghz_sleep(subghz);
}
//Stop RX
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
subghz_rx_end(subghz);
subghz_sleep(subghz);
};
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
consumed = true; consumed = true;
break; break;
@@ -187,13 +158,13 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
break; break;
case SubGhzCustomEventViewReadRAWErase: case SubGhzCustomEventViewReadRAWErase:
if(subghz->txrx->rx_key_state == SubGhzRxKeyStateAddKey) { if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) {
if(subghz_scene_read_raw_update_filename(subghz)) { if(subghz_scene_read_raw_update_filename(subghz)) {
furi_string_set(subghz->file_path_tmp, subghz->file_path); furi_string_set(subghz->file_path_tmp, subghz->file_path);
subghz_delete_file(subghz); subghz_delete_file(subghz);
} }
} }
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);
notification_message(subghz->notifications, &sequence_reset_rgb); notification_message(subghz->notifications, &sequence_reset_rgb);
consumed = true; consumed = true;
break; break;
@@ -203,7 +174,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
if(subghz_scene_read_raw_update_filename(subghz)) { if(subghz_scene_read_raw_update_filename(subghz)) {
scene_manager_set_scene_state( scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet); subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet);
subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWLoad);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneMoreRAW); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneMoreRAW);
consumed = true; consumed = true;
} else { } else {
@@ -223,33 +194,22 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
if(subghz_file_available(subghz) && subghz_scene_read_raw_update_filename(subghz)) { if(subghz_file_available(subghz) && subghz_scene_read_raw_update_filename(subghz)) {
//start send //start send
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { if(!subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx))) {
subghz_rx_end(subghz); subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack);
} subghz_read_raw_set_status(
if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || subghz->subghz_read_raw,
(subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { SubGhzReadRAWStatusIDLE,
if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) { "",
subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; subghz_threshold_rssi_get(subghz->threshold_rssi));
subghz_read_raw_set_status( } else {
subghz->subghz_read_raw, if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneSaved) ||
SubGhzReadRAWStatusIDLE, !scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneStart)) {
"", DOLPHIN_DEED(DolphinDeedSubGhzSend);
subghz->txrx->raw_threshold_rssi);
} else {
if(scene_manager_has_previous_scene(
subghz->scene_manager, SubGhzSceneSaved) ||
!scene_manager_has_previous_scene(
subghz->scene_manager, SubGhzSceneStart)) {
DOLPHIN_DEED(DolphinDeedSubGhzSend);
}
// set callback end tx
subghz_protocol_raw_file_encoder_worker_set_callback_end(
(SubGhzProtocolEncoderRAW*)subghz_transmitter_get_protocol_instance(
subghz->txrx->transmitter),
subghz_scene_read_raw_callback_end_tx,
subghz);
subghz->state_notifications = SubGhzNotificationStateTx;
} }
// set callback end tx
subghz_txrx_set_raw_file_encoder_worker_callback_end(
subghz->txrx, subghz_scene_read_raw_callback_end_tx, subghz);
subghz->state_notifications = SubGhzNotificationStateTx;
} }
} else { } else {
if(!scene_manager_search_and_switch_to_previous_scene( if(!scene_manager_search_and_switch_to_previous_scene(
@@ -263,33 +223,22 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
case SubGhzCustomEventViewReadRAWSendStop: case SubGhzCustomEventViewReadRAWSendStop:
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { subghz_txrx_stop(subghz->txrx);
subghz_speaker_unmute(subghz);
subghz_tx_stop(subghz);
subghz_sleep(subghz);
}
subghz_read_raw_stop_send(subghz->subghz_read_raw); subghz_read_raw_stop_send(subghz->subghz_read_raw);
consumed = true; consumed = true;
break; break;
case SubGhzCustomEventViewReadRAWIDLE: case SubGhzCustomEventViewReadRAWIDLE:
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_txrx_stop(subghz->txrx);
subghz_rx_end(subghz); size_t spl_count = subghz_protocol_raw_get_sample_write(decoder_raw);
subghz_sleep(subghz);
};
size_t spl_count = subghz_protocol_raw_get_sample_write( subghz_protocol_raw_save_to_file_stop(decoder_raw);
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result);
subghz_protocol_raw_save_to_file_stop( FuriString* temp_str = furi_string_alloc();
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result);
FuriString* temp_str;
temp_str = furi_string_alloc();
furi_string_printf( furi_string_printf(
temp_str, "%s/%s%s", SUBGHZ_RAW_FOLDER, RAW_FILE_NAME, SUBGHZ_APP_EXTENSION); temp_str, "%s/%s%s", SUBGHZ_RAW_FOLDER, RAW_FILE_NAME, SUBGHZ_APP_EXTENSION);
subghz_protocol_raw_gen_fff_data( subghz_protocol_raw_gen_fff_data(
subghz->txrx->fff_data, furi_string_get_cstr(temp_str)); subghz_txrx_get_fff_data(subghz->txrx), furi_string_get_cstr(temp_str));
furi_string_free(temp_str); furi_string_free(temp_str);
if(spl_count > 0) { if(spl_count > 0) {
@@ -299,32 +248,21 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
} }
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey);
consumed = true; consumed = true;
break; break;
case SubGhzCustomEventViewReadRAWREC: case SubGhzCustomEventViewReadRAWREC:
if(subghz->txrx->rx_key_state != SubGhzRxKeyStateIDLE) { if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateIDLE) {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving);
} else { } else {
subghz->txrx->raw_threshold_rssi_low_count = RAW_THRESHOLD_RSSI_LOW_COUNT; SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);
if(subghz_protocol_raw_save_to_file_init( if(subghz_protocol_raw_save_to_file_init(decoder_raw, RAW_FILE_NAME, &preset)) {
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result,
RAW_FILE_NAME,
subghz->txrx->preset)) {
DOLPHIN_DEED(DolphinDeedSubGhzRawRec); DOLPHIN_DEED(DolphinDeedSubGhzRawRec);
if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || subghz_txrx_rx_start(subghz->txrx);
(subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) {
subghz_begin(
subghz,
subghz_setting_get_preset_data_by_name(
subghz->setting,
furi_string_get_cstr(subghz->txrx->preset->name)));
subghz_rx(subghz, subghz->txrx->preset->frequency);
}
subghz->state_notifications = SubGhzNotificationStateRx; subghz->state_notifications = SubGhzNotificationStateRx;
subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey);
} else { } else {
furi_string_set(subghz->error_str, "Function requires\nan SD card."); furi_string_set(subghz->error_str, "Function requires\nan SD card.");
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
@@ -337,7 +275,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
if(subghz_file_available(subghz) && subghz_scene_read_raw_update_filename(subghz)) { if(subghz_file_available(subghz) && subghz_scene_read_raw_update_filename(subghz)) {
scene_manager_set_scene_state( scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSetRAW); subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSetRAW);
subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
} else { } else {
if(!scene_manager_search_and_switch_to_previous_scene( if(!scene_manager_search_and_switch_to_previous_scene(
@@ -356,41 +294,15 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
switch(subghz->state_notifications) { switch(subghz->state_notifications) {
case SubGhzNotificationStateRx: case SubGhzNotificationStateRx:
notification_message(subghz->notifications, &sequence_blink_cyan_10); notification_message(subghz->notifications, &sequence_blink_cyan_10);
subghz_read_raw_update_sample_write( subghz_read_raw_update_sample_write(
subghz->subghz_read_raw, subghz->subghz_read_raw, subghz_protocol_raw_get_sample_write(decoder_raw));
subghz_protocol_raw_get_sample_write(
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result));
float rssi = furi_hal_subghz_get_rssi();
if(float_is_equal(subghz->txrx->raw_threshold_rssi, SUBGHZ_RAW_TRESHOLD_MIN)) {
subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, true);
subghz_protocol_raw_save_to_file_pause(
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, false);
} else {
if(rssi < subghz->txrx->raw_threshold_rssi) {
subghz->txrx->raw_threshold_rssi_low_count++;
if(subghz->txrx->raw_threshold_rssi_low_count > RAW_THRESHOLD_RSSI_LOW_COUNT) {
subghz->txrx->raw_threshold_rssi_low_count = RAW_THRESHOLD_RSSI_LOW_COUNT;
}
subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, false);
} else {
subghz->txrx->raw_threshold_rssi_low_count = 0;
}
if(subghz->txrx->raw_threshold_rssi_low_count == RAW_THRESHOLD_RSSI_LOW_COUNT) {
subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, false);
subghz_protocol_raw_save_to_file_pause(
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, true);
subghz_speaker_mute(subghz);
} else {
subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, true);
subghz_protocol_raw_save_to_file_pause(
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, false);
subghz_speaker_unmute(subghz);
}
}
SubGhzThresholdRssiData ret_rssi =
subghz_threshold_get_rssi_data(subghz->threshold_rssi);
subghz_read_raw_add_data_rssi(
subghz->subghz_read_raw, ret_rssi.rssi, ret_rssi.is_above);
subghz_protocol_raw_save_to_file_pause(decoder_raw, !ret_rssi.is_above);
break; break;
case SubGhzNotificationStateTx: case SubGhzNotificationStateTx:
notification_message(subghz->notifications, &sequence_blink_magenta_10); notification_message(subghz->notifications, &sequence_blink_magenta_10);
@@ -407,13 +319,10 @@ void subghz_scene_read_raw_on_exit(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
//Stop CC1101 //Stop CC1101
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_txrx_stop(subghz->txrx);
subghz_rx_end(subghz);
subghz_sleep(subghz);
};
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
notification_message(subghz->notifications, &sequence_reset_rgb); notification_message(subghz->notifications, &sequence_reset_rgb);
//filter restoration //filter restoration
subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter); subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter);
} }

View File

@@ -35,16 +35,12 @@ static const NotificationSequence subghs_sequence_rx_locked = {
static void subghz_scene_receiver_update_statusbar(void* context) { static void subghz_scene_receiver_update_statusbar(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
FuriString* history_stat_str; FuriString* history_stat_str = furi_string_alloc();
history_stat_str = furi_string_alloc(); if(!subghz_history_get_text_space_left(subghz->history, history_stat_str)) {
if(!subghz_history_get_text_space_left(subghz->txrx->history, history_stat_str)) { FuriString* frequency_str = furi_string_alloc();
FuriString* frequency_str; FuriString* modulation_str = furi_string_alloc();
FuriString* modulation_str;
frequency_str = furi_string_alloc(); subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str);
modulation_str = furi_string_alloc();
subghz_get_frequency_modulation(subghz, frequency_str, modulation_str);
subghz_view_receiver_add_data_statusbar( subghz_view_receiver_add_data_statusbar(
subghz->subghz_receiver, subghz->subghz_receiver,
@@ -74,80 +70,68 @@ static void subghz_scene_add_to_history_callback(
void* context) { void* context) {
furi_assert(context); furi_assert(context);
SubGhz* subghz = context; SubGhz* subghz = context;
FuriString* str_buff; SubGhzHistory* history = subghz->history;
str_buff = furi_string_alloc(); FuriString* str_buff = furi_string_alloc();
if(subghz_history_add_to_history(subghz->txrx->history, decoder_base, subghz->txrx->preset)) { SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);
if(subghz_history_add_to_history(history, decoder_base, &preset)) {
furi_string_reset(str_buff); furi_string_reset(str_buff);
subghz->state_notifications = SubGhzNotificationStateRxDone; subghz->state_notifications = SubGhzNotificationStateRxDone;
uint16_t item_history = subghz_history_get_item(history);
subghz_history_get_text_item_menu( subghz_history_get_text_item_menu(history, str_buff, item_history - 1);
subghz->txrx->history, str_buff, subghz_history_get_item(subghz->txrx->history) - 1);
subghz_view_receiver_add_item_to_menu( subghz_view_receiver_add_item_to_menu(
subghz->subghz_receiver, subghz->subghz_receiver,
furi_string_get_cstr(str_buff), furi_string_get_cstr(str_buff),
subghz_history_get_type_protocol( subghz_history_get_type_protocol(history, item_history - 1));
subghz->txrx->history, subghz_history_get_item(subghz->txrx->history) - 1));
subghz_scene_receiver_update_statusbar(subghz); subghz_scene_receiver_update_statusbar(subghz);
} }
subghz_receiver_reset(receiver); subghz_receiver_reset(receiver);
furi_string_free(str_buff); furi_string_free(str_buff);
subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey);
} }
void subghz_scene_receiver_on_enter(void* context) { void subghz_scene_receiver_on_enter(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
SubGhzHistory* history = subghz->history;
FuriString* str_buff; FuriString* str_buff;
str_buff = furi_string_alloc(); str_buff = furi_string_alloc();
if(subghz->txrx->rx_key_state == SubGhzRxKeyStateIDLE) { if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateIDLE) {
subghz_preset_init( subghz_set_default_preset(subghz);
subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); subghz_history_reset(history);
subghz_history_reset(subghz->txrx->history); subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart);
subghz->txrx->rx_key_state = SubGhzRxKeyStateStart;
} }
subghz_view_receiver_set_lock(subghz->subghz_receiver, subghz->lock); subghz_view_receiver_set_lock(subghz->subghz_receiver, subghz_is_locked(subghz));
//Load history to receiver //Load history to receiver
subghz_view_receiver_exit(subghz->subghz_receiver); subghz_view_receiver_exit(subghz->subghz_receiver);
for(uint8_t i = 0; i < subghz_history_get_item(subghz->txrx->history); i++) { for(uint8_t i = 0; i < subghz_history_get_item(history); i++) {
furi_string_reset(str_buff); furi_string_reset(str_buff);
subghz_history_get_text_item_menu(subghz->txrx->history, str_buff, i); subghz_history_get_text_item_menu(history, str_buff, i);
subghz_view_receiver_add_item_to_menu( subghz_view_receiver_add_item_to_menu(
subghz->subghz_receiver, subghz->subghz_receiver,
furi_string_get_cstr(str_buff), furi_string_get_cstr(str_buff),
subghz_history_get_type_protocol(subghz->txrx->history, i)); subghz_history_get_type_protocol(history, i));
subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey);
} }
furi_string_free(str_buff); furi_string_free(str_buff);
subghz_scene_receiver_update_statusbar(subghz); subghz_scene_receiver_update_statusbar(subghz);
subghz_view_receiver_set_callback( subghz_view_receiver_set_callback(
subghz->subghz_receiver, subghz_scene_receiver_callback, subghz); subghz->subghz_receiver, subghz_scene_receiver_callback, subghz);
subghz_receiver_set_rx_callback( subghz_txrx_set_rx_calback(subghz->txrx, subghz_scene_add_to_history_callback, subghz);
subghz->txrx->receiver, subghz_scene_add_to_history_callback, subghz);
subghz->state_notifications = SubGhzNotificationStateRx; subghz->state_notifications = SubGhzNotificationStateRx;
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_txrx_rx_start(subghz->txrx);
subghz_rx_end(subghz); subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->idx_menu_chosen);
};
if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) ||
(subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) {
subghz_begin(
subghz,
subghz_setting_get_preset_data_by_name(
subghz->setting, furi_string_get_cstr(subghz->txrx->preset->name)));
subghz_rx(subghz, subghz->txrx->preset->frequency);
}
subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->txrx->idx_menu_chosen);
//to use a universal decoder, we are looking for a link to it //to use a universal decoder, we are looking for a link to it
subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name( furi_check(
subghz->txrx->receiver, SUBGHZ_PROTOCOL_BIN_RAW_NAME); subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, SUBGHZ_PROTOCOL_BIN_RAW_NAME));
furi_assert(subghz->txrx->decoder_result);
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver);
} }
@@ -160,41 +144,31 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
case SubGhzCustomEventViewReceiverBack: case SubGhzCustomEventViewReceiverBack:
// Stop CC1101 Rx // Stop CC1101 Rx
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_txrx_stop(subghz->txrx);
subghz_rx_end(subghz); subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF);
subghz_sleep(subghz); subghz->idx_menu_chosen = 0;
}; subghz_txrx_set_rx_calback(subghz->txrx, NULL, subghz);
subghz->txrx->hopper_state = SubGhzHopperStateOFF;
subghz->txrx->idx_menu_chosen = 0;
subghz_receiver_set_rx_callback(subghz->txrx->receiver, NULL, subghz);
if(subghz->txrx->rx_key_state == SubGhzRxKeyStateAddKey) { if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) {
subghz->txrx->rx_key_state = SubGhzRxKeyStateExit; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateExit);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving);
} else { } else {
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);
subghz_preset_init( subghz_set_default_preset(subghz);
subghz,
"AM650",
subghz_setting_get_default_frequency(subghz->setting),
NULL,
0);
scene_manager_search_and_switch_to_previous_scene( scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneStart); subghz->scene_manager, SubGhzSceneStart);
} }
consumed = true; consumed = true;
break; break;
case SubGhzCustomEventViewReceiverOK: case SubGhzCustomEventViewReceiverOK:
subghz->txrx->idx_menu_chosen = subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver);
subghz_view_receiver_get_idx_menu(subghz->subghz_receiver);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo);
DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo); DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo);
consumed = true; consumed = true;
break; break;
case SubGhzCustomEventViewReceiverConfig: case SubGhzCustomEventViewReceiverConfig:
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
subghz->txrx->idx_menu_chosen = subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver);
subghz_view_receiver_get_idx_menu(subghz->subghz_receiver);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig);
consumed = true; consumed = true;
break; break;
@@ -203,30 +177,30 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
consumed = true; consumed = true;
break; break;
case SubGhzCustomEventViewReceiverUnlock: case SubGhzCustomEventViewReceiverUnlock:
subghz->lock = SubGhzLockOff; subghz_unlock(subghz);
consumed = true; consumed = true;
break; break;
default: default:
break; break;
} }
} else if(event.type == SceneManagerEventTypeTick) { } else if(event.type == SceneManagerEventTypeTick) {
if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) {
subghz_hopper_update(subghz); subghz_txrx_hopper_update(subghz->txrx);
subghz_scene_receiver_update_statusbar(subghz); subghz_scene_receiver_update_statusbar(subghz);
} }
//get RSSI SubGhzThresholdRssiData ret_rssi = subghz_threshold_get_rssi_data(subghz->threshold_rssi);
float rssi = furi_hal_subghz_get_rssi();
subghz_receiver_rssi(subghz->subghz_receiver, rssi); subghz_receiver_rssi(subghz->subghz_receiver, ret_rssi.rssi);
subghz_protocol_decoder_bin_raw_data_input_rssi( subghz_protocol_decoder_bin_raw_data_input_rssi(
(SubGhzProtocolDecoderBinRAW*)subghz->txrx->decoder_result, rssi); (SubGhzProtocolDecoderBinRAW*)subghz_txrx_get_decoder(subghz->txrx), ret_rssi.rssi);
switch(subghz->state_notifications) { switch(subghz->state_notifications) {
case SubGhzNotificationStateRx: case SubGhzNotificationStateRx:
notification_message(subghz->notifications, &sequence_blink_cyan_10); notification_message(subghz->notifications, &sequence_blink_cyan_10);
break; break;
case SubGhzNotificationStateRxDone: case SubGhzNotificationStateRxDone:
if(subghz->lock != SubGhzLockOn) { if(!subghz_is_locked(subghz)) {
notification_message(subghz->notifications, &subghs_sequence_rx); notification_message(subghz->notifications, &subghs_sequence_rx);
} else { } else {
notification_message(subghz->notifications, &subghs_sequence_rx_locked); notification_message(subghz->notifications, &subghs_sequence_rx_locked);

View File

@@ -72,13 +72,15 @@ const uint32_t bin_raw_value[BIN_RAW_COUNT] = {
uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) { uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) {
furi_assert(context); furi_assert(context);
SubGhz* subghz = context; SubGhz* subghz = context;
SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
uint8_t index = 0; uint8_t index = 0;
for(uint8_t i = 0; i < subghz_setting_get_frequency_count(subghz->setting); i++) { for(uint8_t i = 0; i < subghz_setting_get_frequency_count(setting); i++) {
if(value == subghz_setting_get_frequency(subghz->setting, i)) { if(value == subghz_setting_get_frequency(setting, i)) {
index = i; index = i;
break; break;
} else { } else {
index = subghz_setting_get_frequency_default_index(subghz->setting); index = subghz_setting_get_frequency_default_index(setting);
} }
} }
return index; return index;
@@ -87,13 +89,15 @@ uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void*
uint8_t subghz_scene_receiver_config_next_preset(const char* preset_name, void* context) { uint8_t subghz_scene_receiver_config_next_preset(const char* preset_name, void* context) {
furi_assert(context); furi_assert(context);
SubGhz* subghz = context; SubGhz* subghz = context;
SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
uint8_t index = 0; uint8_t index = 0;
for(uint8_t i = 0; i < subghz_setting_get_preset_count(subghz->setting); i++) { for(uint8_t i = 0; i < subghz_setting_get_preset_count(setting); i++) {
if(!strcmp(subghz_setting_get_preset_name(subghz->setting, i), preset_name)) { if(!strcmp(subghz_setting_get_preset_name(setting, i), preset_name)) {
index = i; index = i;
break; break;
} else { } else {
// index = subghz_setting_get_frequency_default_index(subghz->setting); // index = subghz_setting_get_frequency_default_index(subghz_txrx_get_setting(subghz->txrx));
} }
} }
return index; return index;
@@ -122,70 +126,84 @@ uint8_t subghz_scene_receiver_config_hopper_value_index(
static void subghz_scene_receiver_config_set_frequency(VariableItem* item) { static void subghz_scene_receiver_config_set_frequency(VariableItem* item) {
SubGhz* subghz = variable_item_get_context(item); SubGhz* subghz = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
if(subghz->txrx->hopper_state == SubGhzHopperStateOFF) { if(subghz_txrx_hopper_get_state(subghz->txrx) == SubGhzHopperStateOFF) {
char text_buf[10] = {0}; char text_buf[10] = {0};
uint32_t frequency = subghz_setting_get_frequency(setting, index);
SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);
snprintf( snprintf(
text_buf, text_buf,
sizeof(text_buf), sizeof(text_buf),
"%lu.%02lu", "%lu.%02lu",
subghz_setting_get_frequency(subghz->setting, index) / 1000000, frequency / 1000000,
(subghz_setting_get_frequency(subghz->setting, index) % 1000000) / 10000); (frequency % 1000000) / 10000);
variable_item_set_current_value_text(item, text_buf); variable_item_set_current_value_text(item, text_buf);
subghz->txrx->preset->frequency = subghz_setting_get_frequency(subghz->setting, index); subghz_txrx_set_preset(
subghz->txrx,
furi_string_get_cstr(preset.name),
frequency,
preset.data,
preset.data_size);
} else { } else {
variable_item_set_current_value_index( variable_item_set_current_value_index(
item, subghz_setting_get_frequency_default_index(subghz->setting)); item, subghz_setting_get_frequency_default_index(setting));
} }
} }
static void subghz_scene_receiver_config_set_preset(VariableItem* item) { static void subghz_scene_receiver_config_set_preset(VariableItem* item) {
SubGhz* subghz = variable_item_get_context(item); SubGhz* subghz = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text( SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
item, subghz_setting_get_preset_name(subghz->setting, index));
subghz_preset_init( variable_item_set_current_value_text(item, subghz_setting_get_preset_name(setting, index));
subghz,
subghz_setting_get_preset_name(subghz->setting, index), SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);
subghz->txrx->preset->frequency, subghz_txrx_set_preset(
subghz_setting_get_preset_data(subghz->setting, index), subghz->txrx,
subghz_setting_get_preset_data_size(subghz->setting, index)); subghz_setting_get_preset_name(setting, index),
preset.frequency,
subghz_setting_get_preset_data(setting, index),
subghz_setting_get_preset_data_size(setting, index));
} }
static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) { static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) {
SubGhz* subghz = variable_item_get_context(item); SubGhz* subghz = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
VariableItem* frequency_item = (VariableItem*)scene_manager_get_scene_state(
subghz->scene_manager, SubGhzSceneReceiverConfig);
variable_item_set_current_value_text(item, hopping_text[index]); variable_item_set_current_value_text(item, hopping_text[index]);
if(hopping_value[index] == SubGhzHopperStateOFF) { if(hopping_value[index] == SubGhzHopperStateOFF) {
char text_buf[10] = {0}; char text_buf[10] = {0};
uint32_t frequency = subghz_setting_get_default_frequency(setting);
SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);
snprintf( snprintf(
text_buf, text_buf,
sizeof(text_buf), sizeof(text_buf),
"%lu.%02lu", "%lu.%02lu",
subghz_setting_get_default_frequency(subghz->setting) / 1000000, frequency / 1000000,
(subghz_setting_get_default_frequency(subghz->setting) % 1000000) / 10000); (frequency % 1000000) / 10000);
variable_item_set_current_value_text( variable_item_set_current_value_text(frequency_item, text_buf);
(VariableItem*)scene_manager_get_scene_state(
subghz->scene_manager, SubGhzSceneReceiverConfig), subghz_txrx_set_preset(
text_buf); subghz->txrx,
subghz->txrx->preset->frequency = subghz_setting_get_default_frequency(subghz->setting); furi_string_get_cstr(preset.name),
frequency,
preset.data,
preset.data_size);
variable_item_set_current_value_index( variable_item_set_current_value_index(
(VariableItem*)scene_manager_get_scene_state( frequency_item, subghz_setting_get_frequency_default_index(setting));
subghz->scene_manager, SubGhzSceneReceiverConfig),
subghz_setting_get_frequency_default_index(subghz->setting));
} else { } else {
variable_item_set_current_value_text( variable_item_set_current_value_text(frequency_item, " -----");
(VariableItem*)scene_manager_get_scene_state(
subghz->scene_manager, SubGhzSceneReceiverConfig),
" -----");
variable_item_set_current_value_index( variable_item_set_current_value_index(
(VariableItem*)scene_manager_get_scene_state( frequency_item, subghz_setting_get_frequency_default_index(setting));
subghz->scene_manager, SubGhzSceneReceiverConfig),
subghz_setting_get_frequency_default_index(subghz->setting));
} }
subghz->txrx->hopper_state = hopping_value[index]; subghz_txrx_hopper_set_state(subghz->txrx, hopping_value[index]);
} }
static void subghz_scene_receiver_config_set_speaker(VariableItem* item) { static void subghz_scene_receiver_config_set_speaker(VariableItem* item) {
@@ -193,7 +211,7 @@ static void subghz_scene_receiver_config_set_speaker(VariableItem* item) {
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, speaker_text[index]); variable_item_set_current_value_text(item, speaker_text[index]);
subghz->txrx->speaker_state = speaker_value[index]; subghz_txrx_speaker_set_state(subghz->txrx, speaker_value[index]);
} }
static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) { static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) {
@@ -201,8 +219,8 @@ static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) {
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, bin_raw_text[index]); variable_item_set_current_value_text(item, bin_raw_text[index]);
subghz->txrx->filter = bin_raw_value[index]; subghz->filter = bin_raw_value[index];
subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter); subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter);
} }
static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) { static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) {
@@ -210,7 +228,7 @@ static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* it
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, raw_theshold_rssi_text[index]); variable_item_set_current_value_text(item, raw_theshold_rssi_text[index]);
subghz->txrx->raw_threshold_rssi = raw_theshold_rssi_value[index]; subghz_threshold_rssi_set(subghz->threshold_rssi, raw_theshold_rssi_value[index]);
} }
static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) {
@@ -226,25 +244,27 @@ void subghz_scene_receiver_config_on_enter(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
VariableItem* item; VariableItem* item;
uint8_t value_index; uint8_t value_index;
SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);
item = variable_item_list_add( item = variable_item_list_add(
subghz->variable_item_list, subghz->variable_item_list,
"Frequency:", "Frequency:",
subghz_setting_get_frequency_count(subghz->setting), subghz_setting_get_frequency_count(setting),
subghz_scene_receiver_config_set_frequency, subghz_scene_receiver_config_set_frequency,
subghz); subghz);
value_index = value_index = subghz_scene_receiver_config_next_frequency(preset.frequency, subghz);
subghz_scene_receiver_config_next_frequency(subghz->txrx->preset->frequency, subghz);
scene_manager_set_scene_state( scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneReceiverConfig, (uint32_t)item); subghz->scene_manager, SubGhzSceneReceiverConfig, (uint32_t)item);
variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_index(item, value_index);
char text_buf[10] = {0}; char text_buf[10] = {0};
uint32_t frequency = subghz_setting_get_frequency(setting, value_index);
snprintf( snprintf(
text_buf, text_buf,
sizeof(text_buf), sizeof(text_buf),
"%lu.%02lu", "%lu.%02lu",
subghz_setting_get_frequency(subghz->setting, value_index) / 1000000, frequency / 1000000,
(subghz_setting_get_frequency(subghz->setting, value_index) % 1000000) / 10000); (frequency % 1000000) / 10000);
variable_item_set_current_value_text(item, text_buf); variable_item_set_current_value_text(item, text_buf);
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
@@ -256,7 +276,7 @@ void subghz_scene_receiver_config_on_enter(void* context) {
subghz_scene_receiver_config_set_hopping_running, subghz_scene_receiver_config_set_hopping_running,
subghz); subghz);
value_index = subghz_scene_receiver_config_hopper_value_index( value_index = subghz_scene_receiver_config_hopper_value_index(
subghz->txrx->hopper_state, hopping_value, HOPPING_COUNT, subghz); subghz_txrx_hopper_get_state(subghz->txrx), hopping_value, HOPPING_COUNT, subghz);
variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, hopping_text[value_index]); variable_item_set_current_value_text(item, hopping_text[value_index]);
} }
@@ -264,14 +284,14 @@ void subghz_scene_receiver_config_on_enter(void* context) {
item = variable_item_list_add( item = variable_item_list_add(
subghz->variable_item_list, subghz->variable_item_list,
"Modulation:", "Modulation:",
subghz_setting_get_preset_count(subghz->setting), subghz_setting_get_preset_count(setting),
subghz_scene_receiver_config_set_preset, subghz_scene_receiver_config_set_preset,
subghz); subghz);
value_index = subghz_scene_receiver_config_next_preset( value_index =
furi_string_get_cstr(subghz->txrx->preset->name), subghz); subghz_scene_receiver_config_next_preset(furi_string_get_cstr(preset.name), subghz);
variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text( variable_item_set_current_value_text(
item, subghz_setting_get_preset_name(subghz->setting, value_index)); item, subghz_setting_get_preset_name(setting, value_index));
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
SubGhzCustomEventManagerSet) { SubGhzCustomEventManagerSet) {
@@ -281,7 +301,7 @@ void subghz_scene_receiver_config_on_enter(void* context) {
BIN_RAW_COUNT, BIN_RAW_COUNT,
subghz_scene_receiver_config_set_bin_raw, subghz_scene_receiver_config_set_bin_raw,
subghz); subghz);
value_index = value_index_uint32(subghz->txrx->filter, bin_raw_value, BIN_RAW_COUNT); value_index = value_index_uint32(subghz->filter, bin_raw_value, BIN_RAW_COUNT);
variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, bin_raw_text[value_index]); variable_item_set_current_value_text(item, bin_raw_text[value_index]);
} }
@@ -292,7 +312,8 @@ void subghz_scene_receiver_config_on_enter(void* context) {
SPEAKER_COUNT, SPEAKER_COUNT,
subghz_scene_receiver_config_set_speaker, subghz_scene_receiver_config_set_speaker,
subghz); subghz);
value_index = value_index_uint32(subghz->txrx->speaker_state, speaker_value, SPEAKER_COUNT); value_index = value_index_uint32(
subghz_txrx_speaker_get_state(subghz->txrx), speaker_value, SPEAKER_COUNT);
variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, speaker_text[value_index]); variable_item_set_current_value_text(item, speaker_text[value_index]);
@@ -313,7 +334,9 @@ void subghz_scene_receiver_config_on_enter(void* context) {
subghz_scene_receiver_config_set_raw_threshold_rssi, subghz_scene_receiver_config_set_raw_threshold_rssi,
subghz); subghz);
value_index = value_index_float( value_index = value_index_float(
subghz->txrx->raw_threshold_rssi, raw_theshold_rssi_value, RAW_THRESHOLD_RSSI_COUNT); subghz_threshold_rssi_get(subghz->threshold_rssi),
raw_theshold_rssi_value,
RAW_THRESHOLD_RSSI_COUNT);
variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, raw_theshold_rssi_text[value_index]); variable_item_set_current_value_text(item, raw_theshold_rssi_text[value_index]);
} }
@@ -326,7 +349,7 @@ bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent even
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzCustomEventSceneSettingLock) { if(event.event == SubGhzCustomEventSceneSettingLock) {
subghz->lock = SubGhzLockOn; subghz_lock(subghz);
scene_manager_previous_scene(subghz->scene_manager); scene_manager_previous_scene(subghz->scene_manager);
consumed = true; consumed = true;
} }

View File

@@ -19,20 +19,19 @@ void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, v
static bool subghz_scene_receiver_info_update_parser(void* context) { static bool subghz_scene_receiver_info_update_parser(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name(
subghz->txrx->receiver,
subghz_history_get_protocol_name(subghz->txrx->history, subghz->txrx->idx_menu_chosen));
if(subghz->txrx->decoder_result) { if(subghz_txrx_load_decoder_by_name_protocol(
subghz->txrx,
subghz_history_get_protocol_name(subghz->history, subghz->idx_menu_chosen))) {
//todo we are trying to deserialize without checking for errors, since it is assumed that we just received this chignal //todo we are trying to deserialize without checking for errors, since it is assumed that we just received this chignal
subghz_protocol_decoder_base_deserialize( subghz_protocol_decoder_base_deserialize(
subghz->txrx->decoder_result, subghz_txrx_get_decoder(subghz->txrx),
subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen)); subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen));
SubGhzRadioPreset* preset = SubGhzRadioPreset* preset =
subghz_history_get_radio_preset(subghz->txrx->history, subghz->txrx->idx_menu_chosen); subghz_history_get_radio_preset(subghz->history, subghz->idx_menu_chosen);
subghz_preset_init( subghz_txrx_set_preset(
subghz, subghz->txrx,
furi_string_get_cstr(preset->name), furi_string_get_cstr(preset->name),
preset->frequency, preset->frequency,
preset->data, preset->data,
@@ -47,15 +46,11 @@ void subghz_scene_receiver_info_on_enter(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
if(subghz_scene_receiver_info_update_parser(subghz)) { if(subghz_scene_receiver_info_update_parser(subghz)) {
FuriString* frequency_str; FuriString* frequency_str = furi_string_alloc();
FuriString* modulation_str; FuriString* modulation_str = furi_string_alloc();
FuriString* text; FuriString* text = furi_string_alloc();
frequency_str = furi_string_alloc(); subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str);
modulation_str = furi_string_alloc();
text = furi_string_alloc();
subghz_get_frequency_modulation(subghz, frequency_str, modulation_str);
widget_add_string_element( widget_add_string_element(
subghz->widget, subghz->widget,
78, 78,
@@ -73,7 +68,7 @@ void subghz_scene_receiver_info_on_enter(void* context) {
AlignTop, AlignTop,
FontSecondary, FontSecondary,
furi_string_get_cstr(modulation_str)); furi_string_get_cstr(modulation_str));
subghz_protocol_decoder_base_get_string(subghz->txrx->decoder_result, text); subghz_protocol_decoder_base_get_string(subghz_txrx_get_decoder(subghz->txrx), text);
widget_add_string_multiline_element( widget_add_string_multiline_element(
subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(text)); subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(text));
@@ -81,8 +76,7 @@ void subghz_scene_receiver_info_on_enter(void* context) {
furi_string_free(modulation_str); furi_string_free(modulation_str);
furi_string_free(text); furi_string_free(text);
if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Save) == if(subghz_txrx_protocol_is_serializable(subghz->txrx)) {
SubGhzProtocolFlag_Save) {
widget_add_button_element( widget_add_button_element(
subghz->widget, subghz->widget,
GuiButtonTypeRight, GuiButtonTypeRight,
@@ -90,10 +84,7 @@ void subghz_scene_receiver_info_on_enter(void* context) {
subghz_scene_receiver_info_callback, subghz_scene_receiver_info_callback,
subghz); subghz);
} }
if(((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Send) == if(subghz_txrx_protocol_is_transmittable(subghz->txrx, true)) {
SubGhzProtocolFlag_Send) &&
subghz->txrx->decoder_result->protocol->encoder->deserialize &&
subghz->txrx->decoder_result->protocol->type == SubGhzProtocolTypeStatic) {
widget_add_button_element( widget_add_button_element(
subghz->widget, subghz->widget,
GuiButtonTypeCenter, GuiButtonTypeCenter,
@@ -114,82 +105,49 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
SubGhz* subghz = context; SubGhz* subghz = context;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzCustomEventSceneReceiverInfoTxStart) { if(event.event == SubGhzCustomEventSceneReceiverInfoTxStart) {
//CC1101 Stop RX -> Start TX
if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) {
subghz->txrx->hopper_state = SubGhzHopperStatePause;
}
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
subghz_rx_end(subghz);
}
if(!subghz_scene_receiver_info_update_parser(subghz)) { if(!subghz_scene_receiver_info_update_parser(subghz)) {
return false; return false;
} }
if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE || //CC1101 Stop RX -> Start TX
subghz->txrx->txrx_state == SubGhzTxRxStateSleep) { subghz_txrx_hopper_pause(subghz->txrx);
if(!subghz_tx_start( if(!subghz_tx_start(
subghz, subghz,
subghz_history_get_raw_data( subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen))) {
subghz->txrx->history, subghz->txrx->idx_menu_chosen))) { subghz_txrx_rx_start(subghz->txrx);
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { subghz_txrx_hopper_unpause(subghz->txrx);
subghz_tx_stop(subghz); subghz->state_notifications = SubGhzNotificationStateRx;
} } else {
if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) { subghz->state_notifications = SubGhzNotificationStateTx;
subghz_begin(
subghz,
subghz_setting_get_preset_data_by_name(
subghz->setting,
furi_string_get_cstr(subghz->txrx->preset->name)));
subghz_rx(subghz, subghz->txrx->preset->frequency);
}
if(subghz->txrx->hopper_state == SubGhzHopperStatePause) {
subghz->txrx->hopper_state = SubGhzHopperStateRunnig;
}
subghz->state_notifications = SubGhzNotificationStateRx;
} else {
subghz->state_notifications = SubGhzNotificationStateTx;
}
} }
return true; return true;
} else if(event.event == SubGhzCustomEventSceneReceiverInfoTxStop) { } else if(event.event == SubGhzCustomEventSceneReceiverInfoTxStop) {
//CC1101 Stop Tx -> Start RX //CC1101 Stop Tx -> Start RX
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
subghz_tx_stop(subghz); subghz_txrx_rx_start(subghz->txrx);
}
if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) { subghz_txrx_hopper_unpause(subghz->txrx);
subghz_begin(
subghz,
subghz_setting_get_preset_data_by_name(
subghz->setting, furi_string_get_cstr(subghz->txrx->preset->name)));
subghz_rx(subghz, subghz->txrx->preset->frequency);
}
if(subghz->txrx->hopper_state == SubGhzHopperStatePause) {
subghz->txrx->hopper_state = SubGhzHopperStateRunnig;
}
subghz->state_notifications = SubGhzNotificationStateRx; subghz->state_notifications = SubGhzNotificationStateRx;
return true; return true;
} else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) { } else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) {
//CC1101 Stop RX -> Save //CC1101 Stop RX -> Save
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
subghz->txrx->hopper_state = SubGhzHopperStateOFF; subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF);
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
subghz_rx_end(subghz); subghz_txrx_stop(subghz->txrx);
subghz_sleep(subghz);
}
if(!subghz_scene_receiver_info_update_parser(subghz)) { if(!subghz_scene_receiver_info_update_parser(subghz)) {
return false; return false;
} }
if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Save) == if(subghz_txrx_protocol_is_serializable(subghz->txrx)) {
SubGhzProtocolFlag_Save) {
subghz_file_name_clear(subghz); subghz_file_name_clear(subghz);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
} }
return true; return true;
} }
} else if(event.type == SceneManagerEventTypeTick) { } else if(event.type == SceneManagerEventTypeTick) {
if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) {
subghz_hopper_update(subghz); subghz_txrx_hopper_update(subghz->txrx);
} }
switch(subghz->state_notifications) { switch(subghz->state_notifications) {
case SubGhzNotificationStateTx: case SubGhzNotificationStateTx:

View File

@@ -5,8 +5,7 @@
void subghz_scene_region_info_on_enter(void* context) { void subghz_scene_region_info_on_enter(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
const FuriHalRegion* const region = furi_hal_region_get(); const FuriHalRegion* const region = furi_hal_region_get();
FuriString* buffer; FuriString* buffer = furi_string_alloc();
buffer = furi_string_alloc();
if(region) { if(region) {
furi_string_cat_printf(buffer, "Region: %s, bands:\n", region->country_code); furi_string_cat_printf(buffer, "Region: %s, bands:\n", region->country_code);
for(uint16_t i = 0; i < region->bands_count; ++i) { for(uint16_t i = 0; i < region->bands_count; ++i) {

View File

@@ -3,6 +3,7 @@
typedef enum { typedef enum {
SubGhzRpcStateIdle, SubGhzRpcStateIdle,
SubGhzRpcStateLoaded, SubGhzRpcStateLoaded,
SubGhzRpcStateTx,
} SubGhzRpcState; } SubGhzRpcState;
void subghz_scene_rpc_on_enter(void* context) { void subghz_scene_rpc_on_enter(void* context) {
@@ -38,9 +39,9 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {
view_dispatcher_stop(subghz->view_dispatcher); view_dispatcher_stop(subghz->view_dispatcher);
} else if(event.event == SubGhzCustomEventSceneRpcButtonPress) { } else if(event.event == SubGhzCustomEventSceneRpcButtonPress) {
bool result = false; bool result = false;
if((subghz->txrx->txrx_state == SubGhzTxRxStateSleep) && if((state == SubGhzRpcStateLoaded)) {
(state == SubGhzRpcStateLoaded)) { result = subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx));
result = subghz_tx_start(subghz, subghz->txrx->fff_data); state = SubGhzRpcStateTx;
if(result) subghz_blink_start(subghz); if(result) subghz_blink_start(subghz);
} }
if(!result) { if(!result) {
@@ -52,10 +53,10 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {
rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonPress, result); rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonPress, result);
} else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) { } else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) {
bool result = false; bool result = false;
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { if(state == SubGhzRpcStateTx) {
subghz_txrx_stop(subghz->txrx);
subghz_blink_stop(subghz); subghz_blink_stop(subghz);
subghz_tx_stop(subghz); state = SubGhzRpcStateIdle;
subghz_sleep(subghz);
result = true; result = true;
} }
rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonRelease, result); rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonRelease, result);
@@ -93,10 +94,9 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {
void subghz_scene_rpc_on_exit(void* context) { void subghz_scene_rpc_on_exit(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
SubGhzRpcState state = scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneRpc);
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { if(state != SubGhzRpcStateIdle) {
subghz_tx_stop(subghz); subghz_txrx_stop(subghz->txrx);
subghz_sleep(subghz);
subghz_blink_stop(subghz); subghz_blink_stop(subghz);
} }

View File

@@ -35,10 +35,8 @@ void subghz_scene_save_name_on_enter(void* context) {
TextInput* text_input = subghz->text_input; TextInput* text_input = subghz->text_input;
bool dev_name_empty = false; bool dev_name_empty = false;
FuriString* file_name; FuriString* file_name = furi_string_alloc();
FuriString* dir_name; FuriString* dir_name = furi_string_alloc();
file_name = furi_string_alloc();
dir_name = furi_string_alloc();
if(!subghz_path_is_file(subghz->file_path)) { if(!subghz_path_is_file(subghz->file_path)) {
char file_name_buf[SUBGHZ_MAX_LEN_NAME] = {0}; char file_name_buf[SUBGHZ_MAX_LEN_NAME] = {0};
@@ -69,7 +67,7 @@ void subghz_scene_save_name_on_enter(void* context) {
subghz_scene_save_name_text_input_callback, subghz_scene_save_name_text_input_callback,
subghz, subghz,
subghz->file_name_tmp, subghz->file_name_tmp,
MAX_TEXT_INPUT_LEN, // buffer size MAX_TEXT_INPUT_LEN,
dev_name_empty); dev_name_empty);
ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
@@ -106,7 +104,7 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
SubGhzCustomEventManagerNoSet) { SubGhzCustomEventManagerNoSet) {
subghz_save_protocol_to_file( subghz_save_protocol_to_file(
subghz, subghz,
subghz->txrx->fff_data, subghz_txrx_get_fff_data(subghz->txrx),
furi_string_get_cstr(subghz->file_path)); furi_string_get_cstr(subghz->file_path));
scene_manager_set_scene_state( scene_manager_set_scene_state(
subghz->scene_manager, subghz->scene_manager,
@@ -115,8 +113,7 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
} else { } else {
subghz_save_protocol_to_file( subghz_save_protocol_to_file(
subghz, subghz,
subghz_history_get_raw_data( subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen),
subghz->txrx->history, subghz->txrx->idx_menu_chosen),
furi_string_get_cstr(subghz->file_path)); furi_string_get_cstr(subghz->file_path));
} }
} }
@@ -124,7 +121,8 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
SubGhzCustomEventManagerNoSet) { SubGhzCustomEventManagerNoSet) {
subghz_protocol_raw_gen_fff_data( subghz_protocol_raw_gen_fff_data(
subghz->txrx->fff_data, furi_string_get_cstr(subghz->file_path)); subghz_txrx_get_fff_data(subghz->txrx),
furi_string_get_cstr(subghz->file_path));
scene_manager_set_scene_state( scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet);
} else { } else {

View File

@@ -26,10 +26,10 @@ bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event)
if(event.event == SubGhzCustomEventSceneSaveSuccess) { if(event.event == SubGhzCustomEventSceneSaveSuccess) {
if(!scene_manager_search_and_switch_to_previous_scene( if(!scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneReceiver)) { subghz->scene_manager, SubGhzSceneReceiver)) {
subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWSave; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWSave);
if(!scene_manager_search_and_switch_to_previous_scene( if(!scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneReadRAW)) { subghz->scene_manager, SubGhzSceneReadRAW)) {
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);
if(!scene_manager_search_and_switch_to_previous_scene( if(!scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneSaved)) { subghz->scene_manager, SubGhzSceneSaved)) {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved);

View File

@@ -4,8 +4,8 @@ void subghz_scene_saved_on_enter(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
if(subghz_load_protocol_from_file(subghz)) { if(subghz_load_protocol_from_file(subghz)) {
if((!strcmp(subghz->txrx->decoder_result->protocol->name, "RAW"))) { if(subghz_get_load_type_file(subghz) == SubGhzLoadTypeFileRaw) {
subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWLoad);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW);
} else { } else {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSavedMenu); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSavedMenu);

View File

@@ -1,63 +1,10 @@
#include "../subghz_i.h" #include "../subghz_i.h"
#include <lib/subghz/protocols/keeloq.h> #include "../helpers/subghz_txrx_create_potocol_key.h"
#include <lib/subghz/protocols/secplus_v1.h>
#include <lib/subghz/protocols/secplus_v2.h>
#include <lib/subghz/blocks/math.h> #include <lib/subghz/blocks/math.h>
#include <flipper_format/flipper_format_i.h>
#include <lib/toolbox/stream/stream.h>
#include <lib/subghz/protocols/protocol_items.h> #include <lib/subghz/protocols/protocol_items.h>
#define TAG "SubGhzSetType" #define TAG "SubGhzSetType"
bool subghz_scene_set_type_submenu_gen_data_protocol(
void* context,
const char* protocol_name,
uint64_t key,
uint32_t bit,
uint32_t frequency,
const char* preset_name) {
furi_assert(context);
SubGhz* subghz = context;
bool res = false;
subghz_preset_init(subghz, preset_name, frequency, NULL, 0);
subghz->txrx->decoder_result =
subghz_receiver_search_decoder_base_by_name(subghz->txrx->receiver, protocol_name);
if(subghz->txrx->decoder_result == NULL) {
furi_string_set(subghz->error_str, "Protocol not\nfound!");
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);
return false;
}
do {
Stream* fff_data_stream = flipper_format_get_raw_stream(subghz->txrx->fff_data);
stream_clean(fff_data_stream);
if(subghz_protocol_decoder_base_serialize(
subghz->txrx->decoder_result, subghz->txrx->fff_data, subghz->txrx->preset) !=
SubGhzProtocolStatusOk) {
FURI_LOG_E(TAG, "Unable to serialize");
break;
}
if(!flipper_format_update_uint32(subghz->txrx->fff_data, "Bit", &bit, 1)) {
FURI_LOG_E(TAG, "Unable to update Bit");
break;
}
uint8_t key_data[sizeof(uint64_t)] = {0};
for(size_t i = 0; i < sizeof(uint64_t); i++) {
key_data[sizeof(uint64_t) - i - 1] = (key >> (i * 8)) & 0xFF;
}
if(!flipper_format_update_hex(subghz->txrx->fff_data, "Key", key_data, sizeof(uint64_t))) {
FURI_LOG_E(TAG, "Unable to update Key");
break;
}
res = true;
} while(false);
return res;
}
void subghz_scene_set_type_submenu_callback(void* context, uint32_t index) { void subghz_scene_set_type_submenu_callback(void* context, uint32_t index) {
SubGhz* subghz = context; SubGhz* subghz = context;
view_dispatcher_send_custom_event(subghz->view_dispatcher, index); view_dispatcher_send_custom_event(subghz->view_dispatcher, index);
@@ -69,7 +16,13 @@ void subghz_scene_set_type_on_enter(void* context) {
submenu_add_item( submenu_add_item(
subghz->submenu, subghz->submenu,
"Princeton_433", "Princeton_433",
SubmenuIndexPricenton, SubmenuIndexPricenton_433,
subghz_scene_set_type_submenu_callback,
subghz);
submenu_add_item(
subghz->submenu,
"Princeton_315",
SubmenuIndexPricenton_315,
subghz_scene_set_type_submenu_callback, subghz_scene_set_type_submenu_callback,
subghz); subghz);
submenu_add_item( submenu_add_item(
@@ -108,10 +61,6 @@ void subghz_scene_set_type_on_enter(void* context) {
SubmenuIndexCAMETwee, SubmenuIndexCAMETwee,
subghz_scene_set_type_submenu_callback, subghz_scene_set_type_submenu_callback,
subghz); subghz);
// submenu_add_item(
// subghz->submenu, "Nero Sketch", SubmenuIndexNeroSketch, subghz_scene_set_type_submenu_callback, subghz);
// submenu_add_item(
// subghz->submenu, "Nero Radio", SubmenuIndexNeroRadio, subghz_scene_set_type_submenu_callback, subghz);
submenu_add_item( submenu_add_item(
subghz->submenu, subghz->submenu,
"Gate TX_433", "Gate TX_433",
@@ -172,94 +121,59 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
bool generated_protocol = false; bool generated_protocol = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
//ToDo Fix uint32_t key = (uint32_t)rand();
uint32_t key = subghz_random_serial();
switch(event.event) { switch(event.event) {
case SubmenuIndexPricenton: case SubmenuIndexPricenton_433:
key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8 key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
if(subghz_scene_set_type_submenu_gen_data_protocol( generated_protocol = subghz_txrx_gen_data_protocol_and_te(
subghz, SUBGHZ_PROTOCOL_PRINCETON_NAME, key, 24, 433920000, "AM650")) { subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_PRINCETON_NAME, key, 24, 400);
uint32_t te = 400; break;
flipper_format_update_uint32(subghz->txrx->fff_data, "TE", (uint32_t*)&te, 1); case SubmenuIndexPricenton_315:
generated_protocol = true; key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
} generated_protocol = subghz_txrx_gen_data_protocol_and_te(
subghz->txrx, "AM650", 315000000, SUBGHZ_PROTOCOL_PRINCETON_NAME, key, 24, 400);
break; break;
case SubmenuIndexNiceFlo12bit: case SubmenuIndexNiceFlo12bit:
key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4 key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
if(subghz_scene_set_type_submenu_gen_data_protocol( generated_protocol = subghz_txrx_gen_data_protocol(
subghz, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 12, 433920000, "AM650")) { subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 12);
generated_protocol = true;
}
break; break;
case SubmenuIndexNiceFlo24bit: case SubmenuIndexNiceFlo24bit:
key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8 key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
if(subghz_scene_set_type_submenu_gen_data_protocol( generated_protocol = subghz_txrx_gen_data_protocol(
subghz, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 24, 433920000, "AM650")) { subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 24);
generated_protocol = true;
}
break; break;
case SubmenuIndexCAME12bit: case SubmenuIndexCAME12bit:
key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4 key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
if(subghz_scene_set_type_submenu_gen_data_protocol( generated_protocol = subghz_txrx_gen_data_protocol(
subghz, SUBGHZ_PROTOCOL_CAME_NAME, key, 12, 433920000, "AM650")) { subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_CAME_NAME, key, 12);
generated_protocol = true;
}
break; break;
case SubmenuIndexCAME24bit: case SubmenuIndexCAME24bit:
key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8 key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
if(subghz_scene_set_type_submenu_gen_data_protocol( generated_protocol = subghz_txrx_gen_data_protocol(
subghz, SUBGHZ_PROTOCOL_CAME_NAME, key, 24, 433920000, "AM650")) { subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_CAME_NAME, key, 24);
generated_protocol = true;
}
break; break;
case SubmenuIndexLinear_300_00: case SubmenuIndexLinear_300_00:
key = (key & 0x3FF); key = (key & 0x3FF);
if(subghz_scene_set_type_submenu_gen_data_protocol( generated_protocol = subghz_txrx_gen_data_protocol(
subghz, SUBGHZ_PROTOCOL_LINEAR_NAME, key, 10, 300000000, "AM650")) { subghz->txrx, "AM650", 300000000, SUBGHZ_PROTOCOL_LINEAR_NAME, key, 10);
generated_protocol = true;
}
break; break;
case SubmenuIndexCAMETwee: case SubmenuIndexCAMETwee:
key = (key & 0x0FFFFFF0); key = (key & 0x0FFFFFF0);
key = 0x003FFF7200000000 | (key ^ 0xE0E0E0EE); key = 0x003FFF7200000000 | (key ^ 0xE0E0E0EE);
if(subghz_scene_set_type_submenu_gen_data_protocol(
subghz, SUBGHZ_PROTOCOL_CAME_TWEE_NAME, key, 54, 433920000, "AM650")) { generated_protocol = subghz_txrx_gen_data_protocol(
generated_protocol = true; subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_CAME_TWEE_NAME, key, 54);
}
break; break;
// case SubmenuIndexNeroSketch:
// /* code */
// break;
// case SubmenuIndexNeroRadio:
// /* code */
// break;
case SubmenuIndexGateTX: case SubmenuIndexGateTX:
key = (key & 0x00F0FF00) | 0xF << 16 | 0x40; //btn 0xF, 0xC, 0xA, 0x6 (?) key = (key & 0x00F0FF00) | 0xF << 16 | 0x40; //btn 0xF, 0xC, 0xA, 0x6 (?)
uint64_t rev_key = subghz_protocol_blocks_reverse_key(key, 24); uint64_t rev_key = subghz_protocol_blocks_reverse_key(key, 24);
if(subghz_scene_set_type_submenu_gen_data_protocol( generated_protocol = subghz_txrx_gen_data_protocol(
subghz, SUBGHZ_PROTOCOL_GATE_TX_NAME, rev_key, 24, 433920000, "AM650")) { subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_GATE_TX_NAME, rev_key, 24);
generated_protocol = true;
}
break; break;
case SubmenuIndexDoorHan_433_92: case SubmenuIndexDoorHan_433_92:
subghz->txrx->transmitter = subghz_transmitter_alloc_init( generated_protocol = subghz_txrx_gen_keelog_protocol(
subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); subghz->txrx, "AM650", 433920000, "DoorHan", key, 0x2, 0x0003);
subghz_preset_init(
subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0);
if(subghz->txrx->transmitter) {
subghz_protocol_keeloq_create_data(
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
subghz->txrx->fff_data,
key & 0x0FFFFFFF,
0x2,
0x0003,
"DoorHan",
subghz->txrx->preset);
generated_protocol = true;
} else {
generated_protocol = false;
}
subghz_transmitter_free(subghz->txrx->transmitter);
if(!generated_protocol) { if(!generated_protocol) {
furi_string_set( furi_string_set(
subghz->error_str, "Function requires\nan SD card with\nfresh databases."); subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
@@ -267,23 +181,8 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
} }
break; break;
case SubmenuIndexDoorHan_315_00: case SubmenuIndexDoorHan_315_00:
subghz->txrx->transmitter = subghz_transmitter_alloc_init( generated_protocol = subghz_txrx_gen_keelog_protocol(
subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); subghz->txrx, "AM650", 315000000, "DoorHan", key, 0x2, 0x0003);
subghz_preset_init(subghz, "AM650", 315000000, NULL, 0);
if(subghz->txrx->transmitter) {
subghz_protocol_keeloq_create_data(
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
subghz->txrx->fff_data,
key & 0x0FFFFFFF,
0x2,
0x0003,
"DoorHan",
subghz->txrx->preset);
generated_protocol = true;
} else {
generated_protocol = false;
}
subghz_transmitter_free(subghz->txrx->transmitter);
if(!generated_protocol) { if(!generated_protocol) {
furi_string_set( furi_string_set(
subghz->error_str, "Function requires\nan SD card with\nfresh databases."); subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
@@ -291,86 +190,24 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
} }
break; break;
case SubmenuIndexLiftMaster_315_00: case SubmenuIndexLiftMaster_315_00:
while(!subghz_protocol_secplus_v1_check_fixed(key)) { generated_protocol =
key = subghz_random_serial(); subghz_txrx_gen_secplus_v1_protocol(subghz->txrx, "AM650", 315000000);
}
if(subghz_scene_set_type_submenu_gen_data_protocol(
subghz,
SUBGHZ_PROTOCOL_SECPLUS_V1_NAME,
(uint64_t)key << 32 | 0xE6000000,
42,
315000000,
"AM650")) {
generated_protocol = true;
}
break; break;
case SubmenuIndexLiftMaster_390_00: case SubmenuIndexLiftMaster_390_00:
while(!subghz_protocol_secplus_v1_check_fixed(key)) { generated_protocol =
key = subghz_random_serial(); subghz_txrx_gen_secplus_v1_protocol(subghz->txrx, "AM650", 390000000);
}
if(subghz_scene_set_type_submenu_gen_data_protocol(
subghz,
SUBGHZ_PROTOCOL_SECPLUS_V1_NAME,
(uint64_t)key << 32 | 0xE6000000,
42,
390000000,
"AM650")) {
generated_protocol = true;
}
break; break;
case SubmenuIndexSecPlus_v2_310_00: case SubmenuIndexSecPlus_v2_310_00:
subghz->txrx->transmitter = subghz_transmitter_alloc_init( generated_protocol = subghz_txrx_gen_secplus_v2_protocol(
subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); subghz->txrx, "AM650", 310000000, key, 0x68, 0xE500000);
subghz_preset_init(subghz, "AM650", 310000000, NULL, 0);
if(subghz->txrx->transmitter) {
subghz_protocol_secplus_v2_create_data(
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
subghz->txrx->fff_data,
key,
0x68,
0xE500000,
subghz->txrx->preset);
generated_protocol = true;
} else {
generated_protocol = false;
}
subghz_transmitter_free(subghz->txrx->transmitter);
break; break;
case SubmenuIndexSecPlus_v2_315_00: case SubmenuIndexSecPlus_v2_315_00:
subghz->txrx->transmitter = subghz_transmitter_alloc_init( generated_protocol = subghz_txrx_gen_secplus_v2_protocol(
subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); subghz->txrx, "AM650", 315000000, key, 0x68, 0xE500000);
subghz_preset_init(subghz, "AM650", 315000000, NULL, 0);
if(subghz->txrx->transmitter) {
subghz_protocol_secplus_v2_create_data(
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
subghz->txrx->fff_data,
key,
0x68,
0xE500000,
subghz->txrx->preset);
generated_protocol = true;
} else {
generated_protocol = false;
}
subghz_transmitter_free(subghz->txrx->transmitter);
break; break;
case SubmenuIndexSecPlus_v2_390_00: case SubmenuIndexSecPlus_v2_390_00:
subghz->txrx->transmitter = subghz_transmitter_alloc_init( generated_protocol = subghz_txrx_gen_secplus_v2_protocol(
subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); subghz->txrx, "AM650", 390000000, key, 0x68, 0xE500000);
subghz_preset_init(subghz, "AM650", 390000000, NULL, 0);
if(subghz->txrx->transmitter) {
subghz_protocol_secplus_v2_create_data(
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
subghz->txrx->fff_data,
key,
0x68,
0xE500000,
subghz->txrx->preset);
generated_protocol = true;
} else {
generated_protocol = false;
}
subghz_transmitter_free(subghz->txrx->transmitter);
break; break;
default: default:
return false; return false;

View File

@@ -50,9 +50,10 @@ void subghz_scene_show_error_on_enter(void* context) {
bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) { bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) {
SubGhz* subghz = context; SubGhz* subghz = context;
SubGhzCustomEvent scene_state =
scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError);
if(event.type == SceneManagerEventTypeBack) { if(event.type == SceneManagerEventTypeBack) {
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError) == if(scene_state == SubGhzCustomEventManagerSet) {
SubGhzCustomEventManagerSet) {
return false; return false;
} else { } else {
scene_manager_search_and_switch_to_previous_scene( scene_manager_search_and_switch_to_previous_scene(
@@ -61,14 +62,12 @@ bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) {
return true; return true;
} else if(event.type == SceneManagerEventTypeCustom) { } else if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzCustomEventSceneShowErrorOk) { if(event.event == SubGhzCustomEventSceneShowErrorOk) {
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError) == if(scene_state == SubGhzCustomEventManagerSet) {
SubGhzCustomEventManagerSet) {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart);
} }
return true; return true;
} else if(event.event == SubGhzCustomEventSceneShowErrorBack) { } else if(event.event == SubGhzCustomEventSceneShowErrorBack) {
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError) == if(scene_state == SubGhzCustomEventManagerSet) {
SubGhzCustomEventManagerSet) {
//exit app //exit app
if(!scene_manager_previous_scene(subghz->scene_manager)) { if(!scene_manager_previous_scene(subghz->scene_manager)) {
scene_manager_stop(subghz->scene_manager); scene_manager_stop(subghz->scene_manager);

View File

@@ -70,7 +70,7 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) {
if(event.event == SubmenuIndexReadRAW) { if(event.event == SubmenuIndexReadRAW) {
scene_manager_set_scene_state( scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexReadRAW); subghz->scene_manager, SubGhzSceneStart, SubmenuIndexReadRAW);
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW);
return true; return true;
} else if(event.event == SubmenuIndexRead) { } else if(event.event == SubmenuIndexRead) {

View File

@@ -11,32 +11,24 @@ void subghz_scene_transmitter_callback(SubGhzCustomEvent event, void* context) {
bool subghz_scene_transmitter_update_data_show(void* context) { bool subghz_scene_transmitter_update_data_show(void* context) {
SubGhz* subghz = context; SubGhz* subghz = context;
bool ret = false; bool ret = false;
if(subghz->txrx->decoder_result) { SubGhzProtocolDecoderBase* decoder = subghz_txrx_get_decoder(subghz->txrx);
FuriString* key_str;
FuriString* frequency_str;
FuriString* modulation_str;
key_str = furi_string_alloc(); if(decoder) {
frequency_str = furi_string_alloc(); FuriString* key_str = furi_string_alloc();
modulation_str = furi_string_alloc(); FuriString* frequency_str = furi_string_alloc();
uint8_t show_button = 0; FuriString* modulation_str = furi_string_alloc();
if(subghz_protocol_decoder_base_deserialize( if(subghz_protocol_decoder_base_deserialize(
subghz->txrx->decoder_result, subghz->txrx->fff_data) == SubGhzProtocolStatusOk) { decoder, subghz_txrx_get_fff_data(subghz->txrx)) == SubGhzProtocolStatusOk) {
subghz_protocol_decoder_base_get_string(subghz->txrx->decoder_result, key_str); subghz_protocol_decoder_base_get_string(decoder, key_str);
if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Send) == subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str);
SubGhzProtocolFlag_Send) {
show_button = 1;
}
subghz_get_frequency_modulation(subghz, frequency_str, modulation_str);
subghz_view_transmitter_add_data_to_show( subghz_view_transmitter_add_data_to_show(
subghz->subghz_transmitter, subghz->subghz_transmitter,
furi_string_get_cstr(key_str), furi_string_get_cstr(key_str),
furi_string_get_cstr(frequency_str), furi_string_get_cstr(frequency_str),
furi_string_get_cstr(modulation_str), furi_string_get_cstr(modulation_str),
show_button); subghz_txrx_protocol_is_transmittable(subghz->txrx, false));
ret = true; ret = true;
} }
furi_string_free(frequency_str); furi_string_free(frequency_str);
@@ -65,24 +57,16 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzCustomEventViewTransmitterSendStart) { if(event.event == SubGhzCustomEventViewTransmitterSendStart) {
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
subghz_rx_end(subghz); if(subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx))) {
} subghz->state_notifications = SubGhzNotificationStateTx;
if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || subghz_scene_transmitter_update_data_show(subghz);
(subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { DOLPHIN_DEED(DolphinDeedSubGhzSend);
if(subghz_tx_start(subghz, subghz->txrx->fff_data)) {
subghz->state_notifications = SubGhzNotificationStateTx;
subghz_scene_transmitter_update_data_show(subghz);
DOLPHIN_DEED(DolphinDeedSubGhzSend);
}
} }
return true; return true;
} else if(event.event == SubGhzCustomEventViewTransmitterSendStop) { } else if(event.event == SubGhzCustomEventViewTransmitterSendStop) {
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { subghz_txrx_stop(subghz->txrx);
subghz_tx_stop(subghz);
subghz_sleep(subghz);
}
return true; return true;
} else if(event.event == SubGhzCustomEventViewTransmitterBack) { } else if(event.event == SubGhzCustomEventViewTransmitterBack) {
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;

View File

@@ -1,9 +1,6 @@
/* Abandon hope, all ye who enter here. */ /* Abandon hope, all ye who enter here. */
#include "subghz/types.h"
#include "subghz_i.h" #include "subghz_i.h"
#include <lib/toolbox/path.h>
#include <lib/subghz/protocols/protocol_items.h>
bool subghz_custom_event_callback(void* context, uint32_t event) { bool subghz_custom_event_callback(void* context, uint32_t event) {
furi_assert(context); furi_assert(context);
@@ -49,16 +46,6 @@ static void subghz_rpc_command_callback(RpcAppSystemEvent event, void* context)
} }
} }
void subghz_blink_start(SubGhz* instance) {
furi_assert(instance);
notification_message(instance->notifications, &sequence_blink_start_magenta);
}
void subghz_blink_stop(SubGhz* instance) {
furi_assert(instance);
notification_message(instance->notifications, &sequence_blink_stop);
}
SubGhz* subghz_alloc() { SubGhz* subghz_alloc() {
SubGhz* subghz = malloc(sizeof(SubGhz)); SubGhz* subghz = malloc(sizeof(SubGhz));
@@ -163,45 +150,18 @@ SubGhz* subghz_alloc() {
SubGhzViewIdStatic, SubGhzViewIdStatic,
subghz_test_static_get_view(subghz->subghz_test_static)); subghz_test_static_get_view(subghz->subghz_test_static));
//init setting //init threshold rssi
subghz->setting = subghz_setting_alloc(); subghz->threshold_rssi = subghz_threshold_rssi_alloc();
subghz_setting_load(subghz->setting, EXT_PATH("subghz/assets/setting_user"));
//init Worker & Protocol & History & KeyBoard subghz_unlock(subghz);
subghz->lock = SubGhzLockOff; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);
subghz->txrx = malloc(sizeof(SubGhzTxRx)); subghz->history = subghz_history_alloc();
subghz->txrx->preset = malloc(sizeof(SubGhzRadioPreset)); subghz->filter = SubGhzProtocolFlag_Decodable;
subghz->txrx->preset->name = furi_string_alloc();
subghz_preset_init(
subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0);
subghz->txrx->txrx_state = SubGhzTxRxStateSleep; //init TxRx & History & KeyBoard
subghz->txrx->hopper_state = SubGhzHopperStateOFF; subghz->txrx = subghz_txrx_alloc();
subghz->txrx->speaker_state = SubGhzSpeakerStateDisable; subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter);
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; subghz_txrx_set_need_save_callback(subghz->txrx, subghz_save_to_file, subghz);
subghz->txrx->raw_threshold_rssi = SUBGHZ_RAW_TRESHOLD_MIN;
subghz->txrx->history = subghz_history_alloc();
subghz->txrx->worker = subghz_worker_alloc();
subghz->txrx->fff_data = flipper_format_string_alloc();
subghz->txrx->environment = subghz_environment_alloc();
subghz_environment_set_came_atomo_rainbow_table_file_name(
subghz->txrx->environment, EXT_PATH("subghz/assets/came_atomo"));
subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
subghz->txrx->environment, EXT_PATH("subghz/assets/alutech_at_4n"));
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
subghz->txrx->environment, EXT_PATH("subghz/assets/nice_flor_s"));
subghz_environment_set_protocol_registry(
subghz->txrx->environment, (void*)&subghz_protocol_registry);
subghz->txrx->receiver = subghz_receiver_alloc_init(subghz->txrx->environment);
subghz->txrx->filter = SubGhzProtocolFlag_Decodable;
subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter);
subghz_worker_set_overrun_callback(
subghz->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset);
subghz_worker_set_pair_callback(
subghz->txrx->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode);
subghz_worker_set_context(subghz->txrx->worker, subghz->txrx->receiver);
//Init Error_str //Init Error_str
subghz->error_str = furi_string_alloc(); subghz->error_str = furi_string_alloc();
@@ -219,7 +179,9 @@ void subghz_free(SubGhz* subghz) {
subghz->rpc_ctx = NULL; subghz->rpc_ctx = NULL;
} }
subghz_speaker_off(subghz); subghz_txrx_speaker_off(subghz->txrx);
subghz_txrx_stop(subghz->txrx);
subghz_txrx_sleep(subghz->txrx);
// Packet Test // Packet Test
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestPacket); view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestPacket);
@@ -282,18 +244,14 @@ void subghz_free(SubGhz* subghz) {
furi_record_close(RECORD_GUI); furi_record_close(RECORD_GUI);
subghz->gui = NULL; subghz->gui = NULL;
//setting // threshold rssi
subghz_setting_free(subghz->setting); subghz_threshold_rssi_free(subghz->threshold_rssi);
//Worker & Protocol & History //Worker & Protocol & History
subghz_receiver_free(subghz->txrx->receiver); subghz_history_free(subghz->history);
subghz_environment_free(subghz->txrx->environment);
subghz_worker_free(subghz->txrx->worker); //TxRx
flipper_format_free(subghz->txrx->fff_data); subghz_txrx_free(subghz->txrx);
subghz_history_free(subghz->txrx->history);
furi_string_free(subghz->txrx->preset->name);
free(subghz->txrx->preset);
free(subghz->txrx);
//Error string //Error string
furi_string_free(subghz->error_str); furi_string_free(subghz->error_str);
@@ -319,11 +277,6 @@ int32_t subghz_app(void* p) {
return 1; return 1;
} }
//Load database
bool load_database = subghz_environment_load_keystore(
subghz->txrx->environment, EXT_PATH("subghz/assets/keeloq_mfcodes"));
subghz_environment_load_keystore(
subghz->txrx->environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user"));
// Check argument and run corresponding scene // Check argument and run corresponding scene
if(p && strlen(p)) { if(p && strlen(p)) {
uint32_t rpc_ctx = 0; uint32_t rpc_ctx = 0;
@@ -340,9 +293,9 @@ int32_t subghz_app(void* p) {
if(subghz_key_load(subghz, p, true)) { if(subghz_key_load(subghz, p, true)) {
furi_string_set(subghz->file_path, (const char*)p); furi_string_set(subghz->file_path, (const char*)p);
if((!strcmp(subghz->txrx->decoder_result->protocol->name, "RAW"))) { if(subghz_get_load_type_file(subghz) == SubGhzLoadTypeFileRaw) {
//Load Raw TX //Load Raw TX
subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWLoad);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW);
} else { } else {
//Load transmitter TX //Load transmitter TX
@@ -358,7 +311,7 @@ int32_t subghz_app(void* p) {
view_dispatcher_attach_to_gui( view_dispatcher_attach_to_gui(
subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeFullscreen); subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeFullscreen);
furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER); furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER);
if(load_database) { if(subghz_txrx_is_database_loaded(subghz->txrx)) {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart);
} else { } else {
scene_manager_set_scene_state( scene_manager_set_scene_state(

View File

@@ -18,214 +18,42 @@
#define TAG "SubGhz" #define TAG "SubGhz"
void subghz_preset_init( void subghz_set_default_preset(SubGhz* subghz) {
void* context,
const char* preset_name,
uint32_t frequency,
uint8_t* preset_data,
size_t preset_data_size) {
furi_assert(context);
SubGhz* subghz = context;
furi_string_set(subghz->txrx->preset->name, preset_name);
subghz->txrx->preset->frequency = frequency;
subghz->txrx->preset->data = preset_data;
subghz->txrx->preset->data_size = preset_data_size;
}
bool subghz_set_preset(SubGhz* subghz, const char* preset) {
if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) {
furi_string_set(subghz->txrx->preset->name, "AM270");
} else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) {
furi_string_set(subghz->txrx->preset->name, "AM650");
} else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev238Async")) {
furi_string_set(subghz->txrx->preset->name, "FM238");
} else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev476Async")) {
furi_string_set(subghz->txrx->preset->name, "FM476");
} else if(!strcmp(preset, "FuriHalSubGhzPresetCustom")) {
furi_string_set(subghz->txrx->preset->name, "CUSTOM");
} else {
FURI_LOG_E(TAG, "Unknown preset");
return false;
}
return true;
}
void subghz_get_frequency_modulation(SubGhz* subghz, FuriString* frequency, FuriString* modulation) {
furi_assert(subghz); furi_assert(subghz);
if(frequency != NULL) { subghz_txrx_set_preset(
furi_string_printf( subghz->txrx,
frequency, "AM650",
"%03ld.%02ld", subghz_setting_get_default_frequency(subghz_txrx_get_setting(subghz->txrx)),
subghz->txrx->preset->frequency / 1000000 % 1000, NULL,
subghz->txrx->preset->frequency / 10000 % 100); 0);
}
if(modulation != NULL) {
furi_string_printf(modulation, "%.2s", furi_string_get_cstr(subghz->txrx->preset->name));
}
} }
void subghz_begin(SubGhz* subghz, uint8_t* preset_data) { void subghz_blink_start(SubGhz* subghz) {
furi_assert(subghz); furi_assert(subghz);
furi_hal_subghz_reset(); notification_message(subghz->notifications, &sequence_blink_stop);
furi_hal_subghz_idle(); notification_message(subghz->notifications, &sequence_blink_start_magenta);
furi_hal_subghz_load_custom_preset(preset_data);
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
subghz->txrx->txrx_state = SubGhzTxRxStateIDLE;
} }
uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency) { void subghz_blink_stop(SubGhz* subghz) {
furi_assert(subghz); furi_assert(subghz);
if(!furi_hal_subghz_is_frequency_valid(frequency)) { notification_message(subghz->notifications, &sequence_blink_stop);
furi_crash("SubGhz: Incorrect RX frequency.");
}
furi_assert(
subghz->txrx->txrx_state != SubGhzTxRxStateRx &&
subghz->txrx->txrx_state != SubGhzTxRxStateSleep);
furi_hal_subghz_idle();
uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency);
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
furi_hal_subghz_flush_rx();
subghz_speaker_on(subghz);
furi_hal_subghz_rx();
furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, subghz->txrx->worker);
subghz_worker_start(subghz->txrx->worker);
subghz->txrx->txrx_state = SubGhzTxRxStateRx;
return value;
}
static bool subghz_tx(SubGhz* subghz, uint32_t frequency) {
furi_assert(subghz);
if(!furi_hal_subghz_is_frequency_valid(frequency)) {
furi_crash("SubGhz: Incorrect TX frequency.");
}
furi_assert(subghz->txrx->txrx_state != SubGhzTxRxStateSleep);
furi_hal_subghz_idle();
furi_hal_subghz_set_frequency_and_path(frequency);
furi_hal_gpio_write(&gpio_cc1101_g0, false);
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
bool ret = furi_hal_subghz_tx();
if(ret) {
subghz_speaker_on(subghz);
subghz->txrx->txrx_state = SubGhzTxRxStateTx;
}
return ret;
}
void subghz_idle(SubGhz* subghz) {
furi_assert(subghz);
furi_assert(subghz->txrx->txrx_state != SubGhzTxRxStateSleep);
furi_hal_subghz_idle();
subghz_speaker_off(subghz);
subghz->txrx->txrx_state = SubGhzTxRxStateIDLE;
}
void subghz_rx_end(SubGhz* subghz) {
furi_assert(subghz);
furi_assert(subghz->txrx->txrx_state == SubGhzTxRxStateRx);
if(subghz_worker_is_running(subghz->txrx->worker)) {
subghz_worker_stop(subghz->txrx->worker);
furi_hal_subghz_stop_async_rx();
}
furi_hal_subghz_idle();
subghz_speaker_off(subghz);
subghz->txrx->txrx_state = SubGhzTxRxStateIDLE;
}
void subghz_sleep(SubGhz* subghz) {
furi_assert(subghz);
furi_hal_subghz_sleep();
subghz->txrx->txrx_state = SubGhzTxRxStateSleep;
} }
bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) { bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) {
furi_assert(subghz); switch(subghz_txrx_tx_start(subghz->txrx, flipper_format)) {
case SubGhzTxRxStartTxStateErrorParserOthers:
dialog_message_show_storage_error(
subghz->dialogs, "Error in protocol\nparameters\ndescription");
break;
case SubGhzTxRxStartTxStateErrorOnlyRx:
subghz_dialog_message_show_only_rx(subghz);
break;
bool ret = false; default:
FuriString* temp_str; return true;
temp_str = furi_string_alloc(); break;
uint32_t repeat = 200;
do {
if(!flipper_format_rewind(flipper_format)) {
FURI_LOG_E(TAG, "Rewind error");
break;
}
if(!flipper_format_read_string(flipper_format, "Protocol", temp_str)) {
FURI_LOG_E(TAG, "Missing Protocol");
break;
}
if(!flipper_format_insert_or_update_uint32(flipper_format, "Repeat", &repeat, 1)) {
FURI_LOG_E(TAG, "Unable Repeat");
break;
}
subghz->txrx->transmitter = subghz_transmitter_alloc_init(
subghz->txrx->environment, furi_string_get_cstr(temp_str));
if(subghz->txrx->transmitter) {
if(subghz_transmitter_deserialize(subghz->txrx->transmitter, flipper_format) ==
SubGhzProtocolStatusOk) {
if(strcmp(furi_string_get_cstr(subghz->txrx->preset->name), "") != 0) {
subghz_begin(
subghz,
subghz_setting_get_preset_data_by_name(
subghz->setting, furi_string_get_cstr(subghz->txrx->preset->name)));
} else {
FURI_LOG_E(
TAG,
"Unknown name preset \" %s \"",
furi_string_get_cstr(subghz->txrx->preset->name));
subghz_begin(
subghz, subghz_setting_get_preset_data_by_name(subghz->setting, "AM650"));
}
if(subghz->txrx->preset->frequency) {
ret = subghz_tx(subghz, subghz->txrx->preset->frequency);
} else {
ret = subghz_tx(subghz, 433920000);
}
if(ret) {
//Start TX
furi_hal_subghz_start_async_tx(
subghz_transmitter_yield, subghz->txrx->transmitter);
} else {
subghz_dialog_message_show_only_rx(subghz);
}
} else {
dialog_message_show_storage_error(
subghz->dialogs, "Error in protocol\nparameters\ndescription");
}
}
if(!ret) {
subghz_transmitter_free(subghz->txrx->transmitter);
if(subghz->txrx->txrx_state != SubGhzTxRxStateSleep) {
subghz_idle(subghz);
}
}
} while(false);
furi_string_free(temp_str);
return ret;
}
void subghz_tx_stop(SubGhz* subghz) {
furi_assert(subghz);
furi_assert(subghz->txrx->txrx_state == SubGhzTxRxStateTx);
//Stop TX
furi_hal_subghz_stop_async_tx();
subghz_transmitter_stop(subghz->txrx->transmitter);
subghz_transmitter_free(subghz->txrx->transmitter);
//if protocol dynamic then we save the last upload
if((subghz->txrx->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) &&
(subghz_path_is_file(subghz->file_path))) {
subghz_save_protocol_to_file(
subghz, subghz->txrx->fff_data, furi_string_get_cstr(subghz->file_path));
} }
subghz_idle(subghz); return false;
subghz_speaker_off(subghz);
notification_message(subghz->notifications, &sequence_reset_red);
} }
void subghz_dialog_message_show_only_rx(SubGhz* subghz) { void subghz_dialog_message_show_only_rx(SubGhz* subghz) {
@@ -254,11 +82,11 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) {
Storage* storage = furi_record_open(RECORD_STORAGE); Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
Stream* fff_data_stream = flipper_format_get_raw_stream(subghz->txrx->fff_data); Stream* fff_data_stream =
flipper_format_get_raw_stream(subghz_txrx_get_fff_data(subghz->txrx));
SubGhzLoadKeyState load_key_state = SubGhzLoadKeyStateParseErr; SubGhzLoadKeyState load_key_state = SubGhzLoadKeyStateParseErr;
FuriString* temp_str; FuriString* temp_str = furi_string_alloc();
temp_str = furi_string_alloc();
uint32_t temp_data32; uint32_t temp_data32;
do { do {
@@ -281,6 +109,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) {
break; break;
} }
//Load frequency
if(!flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) { if(!flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) {
FURI_LOG_E(TAG, "Missing Frequency"); FURI_LOG_E(TAG, "Missing Frequency");
break; break;
@@ -291,58 +120,61 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) {
break; break;
} }
subghz->txrx->preset->frequency = temp_data32; //Load preset
if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) { if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) {
FURI_LOG_E(TAG, "Missing Preset"); FURI_LOG_E(TAG, "Missing Preset");
break; break;
} }
if(!subghz_set_preset(subghz, furi_string_get_cstr(temp_str))) { furi_string_set_str(
temp_str, subghz_txrx_get_preset_name(subghz->txrx, furi_string_get_cstr(temp_str)));
if(!strcmp(furi_string_get_cstr(temp_str), "")) {
break; break;
} }
SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { if(!strcmp(furi_string_get_cstr(temp_str), "CUSTOM")) {
//Todo add Custom_preset_module //Todo add Custom_preset_module
//delete preset if it already exists //delete preset if it already exists
subghz_setting_delete_custom_preset( subghz_setting_delete_custom_preset(setting, furi_string_get_cstr(temp_str));
subghz->setting, furi_string_get_cstr(subghz->txrx->preset->name));
//load custom preset from file //load custom preset from file
if(!subghz_setting_load_custom_preset( if(!subghz_setting_load_custom_preset(
subghz->setting, setting, furi_string_get_cstr(temp_str), fff_data_file)) {
furi_string_get_cstr(subghz->txrx->preset->name),
fff_data_file)) {
FURI_LOG_E(TAG, "Missing Custom preset"); FURI_LOG_E(TAG, "Missing Custom preset");
break; break;
} }
} }
size_t preset_index = subghz_setting_get_inx_preset_by_name( size_t preset_index =
subghz->setting, furi_string_get_cstr(subghz->txrx->preset->name)); subghz_setting_get_inx_preset_by_name(setting, furi_string_get_cstr(temp_str));
subghz_preset_init( subghz_txrx_set_preset(
subghz, subghz->txrx,
furi_string_get_cstr(subghz->txrx->preset->name), furi_string_get_cstr(temp_str),
subghz->txrx->preset->frequency, temp_data32,
subghz_setting_get_preset_data(subghz->setting, preset_index), subghz_setting_get_preset_data(setting, preset_index),
subghz_setting_get_preset_data_size(subghz->setting, preset_index)); subghz_setting_get_preset_data_size(setting, preset_index));
//Load protocol
if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) { if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) {
FURI_LOG_E(TAG, "Missing Protocol"); FURI_LOG_E(TAG, "Missing Protocol");
break; break;
} }
FlipperFormat* fff_data = subghz_txrx_get_fff_data(subghz->txrx);
if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) { if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) {
//if RAW //if RAW
subghz_protocol_raw_gen_fff_data(subghz->txrx->fff_data, file_path); subghz->load_type_file = SubGhzLoadTypeFileRaw;
subghz_protocol_raw_gen_fff_data(fff_data, file_path);
} else { } else {
subghz->load_type_file = SubGhzLoadTypeFileKey;
stream_copy_full( stream_copy_full(
flipper_format_get_raw_stream(fff_data_file), flipper_format_get_raw_stream(fff_data_file),
flipper_format_get_raw_stream(subghz->txrx->fff_data)); flipper_format_get_raw_stream(fff_data));
} }
subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name( if(subghz_txrx_load_decoder_by_name_protocol(
subghz->txrx->receiver, furi_string_get_cstr(temp_str)); subghz->txrx, furi_string_get_cstr(temp_str))) {
if(subghz->txrx->decoder_result) {
SubGhzProtocolStatus status = subghz_protocol_decoder_base_deserialize( SubGhzProtocolStatus status = subghz_protocol_decoder_base_deserialize(
subghz->txrx->decoder_result, subghz->txrx->fff_data); subghz_txrx_get_decoder(subghz->txrx), fff_data);
if(status != SubGhzProtocolStatusOk) { if(status != SubGhzProtocolStatusOk) {
load_key_state = SubGhzLoadKeyStateProtocolDescriptionErr; load_key_state = SubGhzLoadKeyStateProtocolDescriptionErr;
break; break;
@@ -381,17 +213,18 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) {
} }
} }
SubGhzLoadTypeFile subghz_get_load_type_file(SubGhz* subghz) {
furi_assert(subghz);
return subghz->load_type_file;
}
bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len) { bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len) {
furi_assert(subghz); furi_assert(subghz);
Storage* storage = furi_record_open(RECORD_STORAGE); Storage* storage = furi_record_open(RECORD_STORAGE);
FuriString* temp_str; FuriString* temp_str = furi_string_alloc();
FuriString* file_name; FuriString* file_name = furi_string_alloc();
FuriString* file_path; FuriString* file_path = furi_string_alloc();
temp_str = furi_string_alloc();
file_name = furi_string_alloc();
file_path = furi_string_alloc();
bool res = false; bool res = false;
@@ -438,8 +271,7 @@ bool subghz_save_protocol_to_file(
Stream* flipper_format_stream = flipper_format_get_raw_stream(flipper_format); Stream* flipper_format_stream = flipper_format_get_raw_stream(flipper_format);
bool saved = false; bool saved = false;
FuriString* file_dir; FuriString* file_dir = furi_string_alloc();
file_dir = furi_string_alloc();
path_extract_dirname(dev_file_name, file_dir); path_extract_dirname(dev_file_name, file_dir);
do { do {
@@ -467,11 +299,21 @@ bool subghz_save_protocol_to_file(
return saved; return saved;
} }
void subghz_save_to_file(void* context) {
furi_assert(context);
SubGhz* subghz = context;
if(subghz_path_is_file(subghz->file_path)) {
subghz_save_protocol_to_file(
subghz,
subghz_txrx_get_fff_data(subghz->txrx),
furi_string_get_cstr(subghz->file_path));
}
}
bool subghz_load_protocol_from_file(SubGhz* subghz) { bool subghz_load_protocol_from_file(SubGhz* subghz) {
furi_assert(subghz); furi_assert(subghz);
FuriString* file_path; FuriString* file_path = furi_string_alloc();
file_path = furi_string_alloc();
DialogsFileBrowserOptions browser_options; DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(&browser_options, SUBGHZ_APP_EXTENSION, &I_sub1_10px); dialog_file_browser_set_basic_options(&browser_options, SUBGHZ_APP_EXTENSION, &I_sub1_10px);
@@ -551,92 +393,27 @@ bool subghz_path_is_file(FuriString* path) {
return furi_string_end_with(path, SUBGHZ_APP_EXTENSION); return furi_string_end_with(path, SUBGHZ_APP_EXTENSION);
} }
uint32_t subghz_random_serial(void) { void subghz_lock(SubGhz* subghz) {
return (uint32_t)rand();
}
void subghz_hopper_update(SubGhz* subghz) {
furi_assert(subghz); furi_assert(subghz);
subghz->lock = SubGhzLockOn;
switch(subghz->txrx->hopper_state) {
case SubGhzHopperStateOFF:
case SubGhzHopperStatePause:
return;
case SubGhzHopperStateRSSITimeOut:
if(subghz->txrx->hopper_timeout != 0) {
subghz->txrx->hopper_timeout--;
return;
}
break;
default:
break;
}
float rssi = -127.0f;
if(subghz->txrx->hopper_state != SubGhzHopperStateRSSITimeOut) {
// See RSSI Calculation timings in CC1101 17.3 RSSI
rssi = furi_hal_subghz_get_rssi();
// Stay if RSSI is high enough
if(rssi > -90.0f) {
subghz->txrx->hopper_timeout = 10;
subghz->txrx->hopper_state = SubGhzHopperStateRSSITimeOut;
return;
}
} else {
subghz->txrx->hopper_state = SubGhzHopperStateRunnig;
}
// Select next frequency
if(subghz->txrx->hopper_idx_frequency <
subghz_setting_get_hopper_frequency_count(subghz->setting) - 1) {
subghz->txrx->hopper_idx_frequency++;
} else {
subghz->txrx->hopper_idx_frequency = 0;
}
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
subghz_rx_end(subghz);
};
if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) {
subghz_receiver_reset(subghz->txrx->receiver);
subghz->txrx->preset->frequency = subghz_setting_get_hopper_frequency(
subghz->setting, subghz->txrx->hopper_idx_frequency);
subghz_rx(subghz, subghz->txrx->preset->frequency);
}
} }
void subghz_speaker_on(SubGhz* subghz) { void subghz_unlock(SubGhz* subghz) {
if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { furi_assert(subghz);
if(furi_hal_speaker_acquire(30)) { subghz->lock = SubGhzLockOff;
furi_hal_subghz_set_async_mirror_pin(&gpio_speaker);
} else {
subghz->txrx->speaker_state = SubGhzSpeakerStateDisable;
}
}
} }
void subghz_speaker_off(SubGhz* subghz) { bool subghz_is_locked(SubGhz* subghz) {
if(subghz->txrx->speaker_state != SubGhzSpeakerStateDisable) { furi_assert(subghz);
if(furi_hal_speaker_is_mine()) { return (subghz->lock == SubGhzLockOn);
furi_hal_subghz_set_async_mirror_pin(NULL);
furi_hal_speaker_release();
if(subghz->txrx->speaker_state == SubGhzSpeakerStateShutdown)
subghz->txrx->speaker_state = SubGhzSpeakerStateDisable;
}
}
} }
void subghz_speaker_mute(SubGhz* subghz) { void subghz_rx_key_state_set(SubGhz* subghz, SubGhzRxKeyState state) {
if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { furi_assert(subghz);
if(furi_hal_speaker_is_mine()) { subghz->rx_key_state = state;
furi_hal_subghz_set_async_mirror_pin(NULL);
}
}
} }
void subghz_speaker_unmute(SubGhz* subghz) { SubGhzRxKeyState subghz_rx_key_state_get(SubGhz* subghz) {
if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { furi_assert(subghz);
if(furi_hal_speaker_is_mine()) { return subghz->rx_key_state;
furi_hal_subghz_set_async_mirror_pin(&gpio_speaker);
}
}
} }

View File

@@ -25,10 +25,6 @@
#include <gui/modules/widget.h> #include <gui/modules/widget.h>
#include <subghz/scenes/subghz_scene.h> #include <subghz/scenes/subghz_scene.h>
#include <lib/subghz/subghz_worker.h>
#include <lib/subghz/subghz_setting.h>
#include <lib/subghz/receiver.h>
#include <lib/subghz/transmitter.h>
#include "subghz_history.h" #include "subghz_history.h"
@@ -37,34 +33,12 @@
#include "rpc/rpc_app.h" #include "rpc/rpc_app.h"
#include "helpers/subghz_threshold_rssi.h"
#include "helpers/subghz_txrx.h"
#define SUBGHZ_MAX_LEN_NAME 64 #define SUBGHZ_MAX_LEN_NAME 64
struct SubGhzTxRx {
SubGhzWorker* worker;
SubGhzEnvironment* environment;
SubGhzReceiver* receiver;
SubGhzTransmitter* transmitter;
SubGhzProtocolFlag filter;
SubGhzProtocolDecoderBase* decoder_result;
FlipperFormat* fff_data;
SubGhzRadioPreset* preset;
SubGhzHistory* history;
uint16_t idx_menu_chosen;
SubGhzTxRxState txrx_state;
SubGhzHopperState hopper_state;
SubGhzSpeakerState speaker_state;
uint8_t hopper_timeout;
uint8_t hopper_idx_frequency;
SubGhzRxKeyState rx_key_state;
float raw_threshold_rssi;
uint8_t raw_threshold_rssi_low_count;
};
typedef struct SubGhzTxRx SubGhzTxRx;
struct SubGhz { struct SubGhz {
Gui* gui; Gui* gui;
NotificationApp* notifications; NotificationApp* notifications;
@@ -93,47 +67,43 @@ struct SubGhz {
SubGhzTestStatic* subghz_test_static; SubGhzTestStatic* subghz_test_static;
SubGhzTestCarrier* subghz_test_carrier; SubGhzTestCarrier* subghz_test_carrier;
SubGhzTestPacket* subghz_test_packet; SubGhzTestPacket* subghz_test_packet;
FuriString* error_str;
SubGhzSetting* setting;
SubGhzLock lock;
SubGhzProtocolFlag filter;
FuriString* error_str;
SubGhzLock lock;
SubGhzThresholdRssi* threshold_rssi;
SubGhzRxKeyState rx_key_state;
SubGhzHistory* history;
uint16_t idx_menu_chosen;
SubGhzLoadTypeFile load_type_file;
void* rpc_ctx; void* rpc_ctx;
}; };
void subghz_preset_init( void subghz_set_default_preset(SubGhz* subghz);
void* context, void subghz_blink_start(SubGhz* subghz);
const char* preset_name, void subghz_blink_stop(SubGhz* subghz);
uint32_t frequency,
uint8_t* preset_data,
size_t preset_data_size);
bool subghz_set_preset(SubGhz* subghz, const char* preset);
void subghz_get_frequency_modulation(SubGhz* subghz, FuriString* frequency, FuriString* modulation);
void subghz_begin(SubGhz* subghz, uint8_t* preset_data);
uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency);
void subghz_rx_end(SubGhz* subghz);
void subghz_sleep(SubGhz* subghz);
void subghz_blink_start(SubGhz* instance);
void subghz_blink_stop(SubGhz* instance);
bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format); bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format);
void subghz_tx_stop(SubGhz* subghz);
void subghz_dialog_message_show_only_rx(SubGhz* subghz); void subghz_dialog_message_show_only_rx(SubGhz* subghz);
bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog); bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog);
bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len); bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len);
bool subghz_save_protocol_to_file( bool subghz_save_protocol_to_file(
SubGhz* subghz, SubGhz* subghz,
FlipperFormat* flipper_format, FlipperFormat* flipper_format,
const char* dev_file_name); const char* dev_file_name);
void subghz_save_to_file(void* context);
bool subghz_load_protocol_from_file(SubGhz* subghz); bool subghz_load_protocol_from_file(SubGhz* subghz);
bool subghz_rename_file(SubGhz* subghz); bool subghz_rename_file(SubGhz* subghz);
bool subghz_file_available(SubGhz* subghz); bool subghz_file_available(SubGhz* subghz);
bool subghz_delete_file(SubGhz* subghz); bool subghz_delete_file(SubGhz* subghz);
void subghz_file_name_clear(SubGhz* subghz); void subghz_file_name_clear(SubGhz* subghz);
bool subghz_path_is_file(FuriString* path); bool subghz_path_is_file(FuriString* path);
uint32_t subghz_random_serial(void); SubGhzLoadTypeFile subghz_get_load_type_file(SubGhz* subghz);
void subghz_hopper_update(SubGhz* subghz);
void subghz_speaker_on(SubGhz* subghz); void subghz_lock(SubGhz* subghz);
void subghz_speaker_off(SubGhz* subghz); void subghz_unlock(SubGhz* subghz);
void subghz_speaker_mute(SubGhz* subghz); bool subghz_is_locked(SubGhz* subghz);
void subghz_speaker_unmute(SubGhz* subghz);
void subghz_rx_key_state_set(SubGhz* subghz, SubGhzRxKeyState state);
SubGhzRxKeyState subghz_rx_key_state_get(SubGhz* subghz);

View File

@@ -12,7 +12,7 @@
#define MENU_ITEMS 4u #define MENU_ITEMS 4u
#define UNLOCK_CNT 3 #define UNLOCK_CNT 3
#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f #define SUBGHZ_RAW_THRESHOLD_MIN -90.0f
typedef struct { typedef struct {
FuriString* item_str; FuriString* item_str;
@@ -44,7 +44,7 @@ typedef enum {
} SubGhzViewReceiverBarShow; } SubGhzViewReceiverBarShow;
struct SubGhzViewReceiver { struct SubGhzViewReceiver {
SubGhzLock lock; bool lock;
uint8_t lock_count; uint8_t lock_count;
FuriTimer* timer; FuriTimer* timer;
View* view; View* view;
@@ -70,20 +70,21 @@ void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi) {
instance->view, instance->view,
SubGhzViewReceiverModel * model, SubGhzViewReceiverModel * model,
{ {
if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) {
model->u_rssi = 0; model->u_rssi = 0;
} else { } else {
model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN); model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_THRESHOLD_MIN);
} }
}, },
true); true);
} }
void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock lock) { void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, bool lock) {
furi_assert(subghz_receiver); furi_assert(subghz_receiver);
subghz_receiver->lock_count = 0; subghz_receiver->lock_count = 0;
if(lock == SubGhzLockOn) {
subghz_receiver->lock = lock; if(lock == true) {
subghz_receiver->lock = true;
with_view_model( with_view_model(
subghz_receiver->view, subghz_receiver->view,
SubGhzViewReceiverModel * model, SubGhzViewReceiverModel * model,
@@ -280,7 +281,7 @@ static void subghz_view_receiver_timer_callback(void* context) {
subghz_receiver->callback( subghz_receiver->callback(
SubGhzCustomEventViewReceiverOffDisplay, subghz_receiver->context); SubGhzCustomEventViewReceiverOffDisplay, subghz_receiver->context);
} else { } else {
subghz_receiver->lock = SubGhzLockOff; subghz_receiver->lock = false;
subghz_receiver->callback(SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context); subghz_receiver->callback(SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context);
} }
subghz_receiver->lock_count = 0; subghz_receiver->lock_count = 0;
@@ -290,7 +291,7 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) {
furi_assert(context); furi_assert(context);
SubGhzViewReceiver* subghz_receiver = context; SubGhzViewReceiver* subghz_receiver = context;
if(subghz_receiver->lock == SubGhzLockOn) { if(subghz_receiver->lock == true) {
with_view_model( with_view_model(
subghz_receiver->view, subghz_receiver->view,
SubGhzViewReceiverModel * model, SubGhzViewReceiverModel * model,
@@ -310,7 +311,7 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) {
SubGhzViewReceiverModel * model, SubGhzViewReceiverModel * model,
{ model->bar_show = SubGhzViewReceiverBarShowUnlock; }, { model->bar_show = SubGhzViewReceiverBarShowUnlock; },
true); true);
//subghz_receiver->lock = SubGhzLockOff; //subghz_receiver->lock = false;
furi_timer_start(subghz_receiver->timer, pdMS_TO_TICKS(650)); furi_timer_start(subghz_receiver->timer, pdMS_TO_TICKS(650));
} }
@@ -394,7 +395,7 @@ SubGhzViewReceiver* subghz_view_receiver_alloc() {
// View allocation and configuration // View allocation and configuration
subghz_receiver->view = view_alloc(); subghz_receiver->view = view_alloc();
subghz_receiver->lock = SubGhzLockOff; subghz_receiver->lock = false;
subghz_receiver->lock_count = 0; subghz_receiver->lock_count = 0;
view_allocate_model( view_allocate_model(
subghz_receiver->view, ViewModelTypeLocking, sizeof(SubGhzViewReceiverModel)); subghz_receiver->view, ViewModelTypeLocking, sizeof(SubGhzViewReceiverModel));

View File

@@ -10,7 +10,7 @@ typedef void (*SubGhzViewReceiverCallback)(SubGhzCustomEvent event, void* contex
void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi); void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi);
void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock keyboard); void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, bool keyboard);
void subghz_view_receiver_set_callback( void subghz_view_receiver_set_callback(
SubGhzViewReceiver* subghz_receiver, SubGhzViewReceiver* subghz_receiver,

View File

@@ -60,10 +60,10 @@ void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi, bool tra
furi_assert(instance); furi_assert(instance);
uint8_t u_rssi = 0; uint8_t u_rssi = 0;
if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) {
u_rssi = 0; u_rssi = 0;
} else { } else {
u_rssi = (uint8_t)((rssi - SUBGHZ_RAW_TRESHOLD_MIN) / 2.7); u_rssi = (uint8_t)((rssi - SUBGHZ_RAW_THRESHOLD_MIN) / 2.7);
} }
with_view_model( with_view_model(
@@ -261,9 +261,9 @@ void subghz_read_raw_draw_threshold_rssi(Canvas* canvas, SubGhzReadRAWModel* mod
uint8_t x = 118; uint8_t x = 118;
uint8_t y = 48; uint8_t y = 48;
if(model->raw_threshold_rssi > SUBGHZ_RAW_TRESHOLD_MIN) { if(model->raw_threshold_rssi > SUBGHZ_RAW_THRESHOLD_MIN) {
uint8_t x = 118; uint8_t x = 118;
y -= (uint8_t)((model->raw_threshold_rssi - SUBGHZ_RAW_TRESHOLD_MIN) / 2.7); y -= (uint8_t)((model->raw_threshold_rssi - SUBGHZ_RAW_THRESHOLD_MIN) / 2.7);
uint8_t width = 3; uint8_t width = 3;
for(uint8_t i = 0; i < x; i += width * 2) { for(uint8_t i = 0; i < x; i += width * 2) {

View File

@@ -3,7 +3,7 @@
#include <gui/view.h> #include <gui/view.h>
#include "../helpers/subghz_custom_event.h" #include "../helpers/subghz_custom_event.h"
#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f #define SUBGHZ_RAW_THRESHOLD_MIN -90.0f
typedef struct SubGhzReadRAW SubGhzReadRAW; typedef struct SubGhzReadRAW SubGhzReadRAW;

View File

@@ -14,7 +14,7 @@ typedef struct {
FuriString* frequency_str; FuriString* frequency_str;
FuriString* preset_str; FuriString* preset_str;
FuriString* key_str; FuriString* key_str;
uint8_t show_button; bool show_button;
} SubGhzViewTransmitterModel; } SubGhzViewTransmitterModel;
void subghz_view_transmitter_set_callback( void subghz_view_transmitter_set_callback(
@@ -32,7 +32,7 @@ void subghz_view_transmitter_add_data_to_show(
const char* key_str, const char* key_str,
const char* frequency_str, const char* frequency_str,
const char* preset_str, const char* preset_str,
uint8_t show_button) { bool show_button) {
furi_assert(subghz_transmitter); furi_assert(subghz_transmitter);
with_view_model( with_view_model(
subghz_transmitter->view, subghz_transmitter->view,
@@ -104,7 +104,7 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) {
furi_string_reset(model->frequency_str); furi_string_reset(model->frequency_str);
furi_string_reset(model->preset_str); furi_string_reset(model->preset_str);
furi_string_reset(model->key_str); furi_string_reset(model->key_str);
model->show_button = 0; model->show_button = false;
}, },
false); false);
return false; return false;

View File

@@ -23,4 +23,4 @@ void subghz_view_transmitter_add_data_to_show(
const char* key_str, const char* key_str,
const char* frequency_str, const char* frequency_str,
const char* preset_str, const char* preset_str,
uint8_t show_button); bool show_button);

View File

@@ -220,11 +220,9 @@ void cli_command_sysctl_debug(Cli* cli, FuriString* args, void* context) {
UNUSED(context); UNUSED(context);
if(!furi_string_cmp(args, "0")) { if(!furi_string_cmp(args, "0")) {
furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug); furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug);
loader_update_menu();
printf("Debug disabled."); printf("Debug disabled.");
} else if(!furi_string_cmp(args, "1")) { } else if(!furi_string_cmp(args, "1")) {
furi_hal_rtc_set_flag(FuriHalRtcFlagDebug); furi_hal_rtc_set_flag(FuriHalRtcFlagDebug);
loader_update_menu();
printf("Debug enabled."); printf("Debug enabled.");
} else { } else {
cli_print_usage("sysctl debug", "<1|0>", furi_string_get_cstr(args)); cli_print_usage("sysctl debug", "<1|0>", furi_string_get_cstr(args));

View File

@@ -106,10 +106,12 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) { switch(event.event) {
case DesktopMainEventOpenMenu: case DesktopMainEventOpenMenu: {
loader_show_menu(); Loader* loader = furi_record_open(RECORD_LOADER);
loader_show_menu(loader);
furi_record_close(RECORD_LOADER);
consumed = true; consumed = true;
break; } break;
case DesktopMainEventOpenLockMenu: case DesktopMainEventOpenLockMenu:
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLockMenu); scene_manager_next_scene(desktop->scene_manager, DesktopSceneLockMenu);

View File

@@ -154,6 +154,8 @@ Menu* menu_alloc() {
void menu_free(Menu* menu) { void menu_free(Menu* menu) {
furi_assert(menu); furi_assert(menu);
menu_reset(menu); menu_reset(menu);
with_view_model(
menu->view, MenuModel * model, { MenuItemArray_clear(model->items); }, false);
view_free(menu->view); view_free(menu->view);
free(menu); free(menu);
} }

View File

@@ -19,19 +19,16 @@ void view_tie_icon_animation(View* view, IconAnimation* icon_animation) {
void view_set_draw_callback(View* view, ViewDrawCallback callback) { void view_set_draw_callback(View* view, ViewDrawCallback callback) {
furi_assert(view); furi_assert(view);
furi_assert(view->draw_callback == NULL);
view->draw_callback = callback; view->draw_callback = callback;
} }
void view_set_input_callback(View* view, ViewInputCallback callback) { void view_set_input_callback(View* view, ViewInputCallback callback) {
furi_assert(view); furi_assert(view);
furi_assert(view->input_callback == NULL);
view->input_callback = callback; view->input_callback = callback;
} }
void view_set_custom_callback(View* view, ViewCustomCallback callback) { void view_set_custom_callback(View* view, ViewCustomCallback callback) {
furi_assert(view); furi_assert(view);
furi_assert(callback);
view->custom_callback = callback; view->custom_callback = callback;
} }
@@ -62,7 +59,6 @@ void view_set_update_callback_context(View* view, void* context) {
void view_set_context(View* view, void* context) { void view_set_context(View* view, void* context) {
furi_assert(view); furi_assert(view);
furi_assert(context);
view->context = context; view->context = context;
} }

View File

@@ -5,6 +5,7 @@ App(
entry_point="loader_srv", entry_point="loader_srv",
cdefines=["SRV_LOADER"], cdefines=["SRV_LOADER"],
requires=["gui"], requires=["gui"],
provides=["loader_start"],
stack_size=2 * 1024, stack_size=2 * 1024,
order=90, order=90,
sdk_headers=[ sdk_headers=[
@@ -12,3 +13,11 @@ App(
"firmware_api/firmware_api.h", "firmware_api/firmware_api.h",
], ],
) )
App(
appid="loader_start",
apptype=FlipperAppType.STARTUP,
entry_point="loader_on_system_start",
requires=["loader"],
order=90,
)

View File

@@ -6,6 +6,8 @@
/* Generated table */ /* Generated table */
#include <firmware_api_table.h> #include <firmware_api_table.h>
#include <furi_hal_info.h>
static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!"); static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!");
constexpr HashtableApiInterface elf_api_interface{ constexpr HashtableApiInterface elf_api_interface{
@@ -19,3 +21,8 @@ constexpr HashtableApiInterface elf_api_interface{
}; };
const ElfApiInterface* const firmware_api_interface = &elf_api_interface; const ElfApiInterface* const firmware_api_interface = &elf_api_interface;
extern "C" void furi_hal_info_get_api_version(uint16_t* major, uint16_t* minor) {
*major = elf_api_interface.api_version_major;
*minor = elf_api_interface.api_version_minor;
}

View File

@@ -1,76 +1,114 @@
#include "applications.h" #include "loader.h"
#include <furi.h>
#include "loader/loader.h"
#include "loader_i.h" #include "loader_i.h"
#include "loader_menu.h"
#include <applications.h>
#include <furi_hal.h>
#define TAG "LoaderSrv" #define TAG "Loader"
#define LOADER_MAGIC_THREAD_VALUE 0xDEADBEEF
// api
#define LOADER_THREAD_FLAG_SHOW_MENU (1 << 0) LoaderStatus loader_start(Loader* loader, const char* name, const char* args) {
#define LOADER_THREAD_FLAG_ALL (LOADER_THREAD_FLAG_SHOW_MENU) LoaderMessage message;
LoaderMessageLoaderStatusResult result;
static Loader* loader_instance = NULL; message.type = LoaderMessageTypeStartByName;
message.start.name = name;
static bool message.start.args = args;
loader_start_application(const FlipperApplication* application, const char* arguments) { message.api_lock = api_lock_alloc_locked();
loader_instance->application = application; message.status_value = &result;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
furi_assert(loader_instance->application_arguments == NULL); api_lock_wait_unlock_and_free(message.api_lock);
if(arguments && strlen(arguments) > 0) { return result.value;
loader_instance->application_arguments = strdup(arguments);
}
FURI_LOG_I(TAG, "Starting: %s", loader_instance->application->name);
FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode();
if(mode > FuriHalRtcHeapTrackModeNone) {
furi_thread_enable_heap_trace(loader_instance->application_thread);
} else {
furi_thread_disable_heap_trace(loader_instance->application_thread);
}
furi_thread_set_name(loader_instance->application_thread, loader_instance->application->name);
furi_thread_set_appid(
loader_instance->application_thread, loader_instance->application->appid);
furi_thread_set_stack_size(
loader_instance->application_thread, loader_instance->application->stack_size);
furi_thread_set_context(
loader_instance->application_thread, loader_instance->application_arguments);
furi_thread_set_callback(
loader_instance->application_thread, loader_instance->application->app);
furi_thread_start(loader_instance->application_thread);
return true;
} }
static void loader_menu_callback(void* _ctx, uint32_t index) { bool loader_lock(Loader* loader) {
UNUSED(index); LoaderMessage message;
const FlipperApplication* application = _ctx; LoaderMessageBoolResult result;
message.type = LoaderMessageTypeLock;
message.api_lock = api_lock_alloc_locked();
message.bool_value = &result;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
api_lock_wait_unlock_and_free(message.api_lock);
return result.value;
}
furi_assert(application->app); void loader_unlock(Loader* loader) {
furi_assert(application->name); LoaderMessage message;
message.type = LoaderMessageTypeUnlock;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
}
if(!loader_lock(loader_instance)) { bool loader_is_locked(Loader* loader) {
FURI_LOG_E(TAG, "Loader is locked"); LoaderMessage message;
return; LoaderMessageBoolResult result;
message.type = LoaderMessageTypeIsLocked;
message.api_lock = api_lock_alloc_locked();
message.bool_value = &result;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
api_lock_wait_unlock_and_free(message.api_lock);
return result.value;
}
void loader_show_menu(Loader* loader) {
LoaderMessage message;
message.type = LoaderMessageTypeShowMenu;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
}
FuriPubSub* loader_get_pubsub(Loader* loader) {
furi_assert(loader);
// it's safe to return pubsub without locking
// because it's never freed and loader is never exited
// also the loader instance cannot be obtained until the pubsub is created
return loader->pubsub;
}
// callbacks
static void loader_menu_closed_callback(void* context) {
Loader* loader = context;
LoaderMessage message;
message.type = LoaderMessageTypeMenuClosed;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
}
static void loader_menu_click_callback(const char* name, void* context) {
Loader* loader = context;
loader_start(loader, name, NULL);
}
static void loader_thread_state_callback(FuriThreadState thread_state, void* context) {
furi_assert(context);
Loader* loader = context;
LoaderEvent event;
if(thread_state == FuriThreadStateRunning) {
event.type = LoaderEventTypeApplicationStarted;
furi_pubsub_publish(loader->pubsub, &event);
} else if(thread_state == FuriThreadStateStopped) {
LoaderMessage message;
message.type = LoaderMessageTypeAppClosed;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
event.type = LoaderEventTypeApplicationStopped;
furi_pubsub_publish(loader->pubsub, &event);
} }
loader_start_application(application, NULL);
} }
static void loader_submenu_callback(void* context, uint32_t index) { // implementation
UNUSED(index);
uint32_t view_id = (uint32_t)context;
view_dispatcher_switch_to_view(loader_instance->view_dispatcher, view_id);
}
static void loader_cli_print_usage() { static Loader* loader_alloc() {
printf("Usage:\r\n"); Loader* loader = malloc(sizeof(Loader));
printf("loader <cmd> <args>\r\n"); loader->pubsub = furi_pubsub_alloc();
printf("Cmd list:\r\n"); loader->queue = furi_message_queue_alloc(1, sizeof(LoaderMessage));
printf("\tlist\t - List available applications\r\n"); loader->loader_menu = NULL;
printf("\topen <Application Name:string>\t - Open application by name\r\n"); loader->app.args = NULL;
printf("\tinfo\t - Show loader state\r\n"); loader->app.name = NULL;
loader->app.thread = NULL;
loader->app.insomniac = false;
return loader;
} }
static FlipperApplication const* loader_find_application_by_name_in_list( static FlipperApplication const* loader_find_application_by_name_in_list(
@@ -85,7 +123,7 @@ static FlipperApplication const* loader_find_application_by_name_in_list(
return NULL; return NULL;
} }
const FlipperApplication* loader_find_application_by_name(const char* name) { static const FlipperApplication* loader_find_application_by_name(const char* name) {
const FlipperApplication* application = NULL; const FlipperApplication* application = NULL;
application = loader_find_application_by_name_in_list(name, FLIPPER_APPS, FLIPPER_APPS_COUNT); application = loader_find_application_by_name_in_list(name, FLIPPER_APPS, FLIPPER_APPS_COUNT);
if(!application) { if(!application) {
@@ -100,346 +138,168 @@ const FlipperApplication* loader_find_application_by_name(const char* name) {
return application; return application;
} }
static void loader_cli_open(Cli* cli, FuriString* args, Loader* instance) { static void
UNUSED(cli); loader_start_internal_app(Loader* loader, const FlipperApplication* app, const char* args) {
if(loader_is_locked(instance)) { FURI_LOG_I(TAG, "Starting %s", app->name);
if(instance->application) {
furi_assert(instance->application->name); // store args
printf("Can't start, %s application is running", instance->application->name); furi_assert(loader->app.args == NULL);
} else { if(args && strlen(args) > 0) {
printf("Can't start, furi application is running"); loader->app.args = strdup(args);
}
return;
} }
FuriString* application_name; // store name
application_name = furi_string_alloc(); furi_assert(loader->app.name == NULL);
loader->app.name = strdup(app->name);
do { // setup app thread
if(!args_read_probably_quoted_string_and_trim(args, application_name)) { loader->app.thread =
printf("No application provided\r\n"); furi_thread_alloc_ex(app->name, app->stack_size, app->app, loader->app.args);
break; furi_thread_set_appid(loader->app.thread, app->appid);
}
const FlipperApplication* application = // setup heap trace
loader_find_application_by_name(furi_string_get_cstr(application_name)); FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode();
if(!application) { if(mode > FuriHalRtcHeapTrackModeNone) {
printf("%s doesn't exists\r\n", furi_string_get_cstr(application_name)); furi_thread_enable_heap_trace(loader->app.thread);
break;
}
furi_string_trim(args);
if(!loader_start_application(application, furi_string_get_cstr(args))) {
printf("Can't start, furi application is running");
return;
} else {
// We must to increment lock counter to keep balance
// TODO: rewrite whole thing, it's complex as hell
FURI_CRITICAL_ENTER();
instance->lock_count++;
FURI_CRITICAL_EXIT();
}
} while(false);
furi_string_free(application_name);
}
static void loader_cli_list(Cli* cli, FuriString* args, Loader* instance) {
UNUSED(cli);
UNUSED(args);
UNUSED(instance);
printf("Applications:\r\n");
for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
printf("\t%s\r\n", FLIPPER_APPS[i].name);
}
}
static void loader_cli_info(Cli* cli, FuriString* args, Loader* instance) {
UNUSED(cli);
UNUSED(args);
if(!loader_is_locked(instance)) {
printf("No application is running\r\n");
} else { } else {
printf("Running application: "); furi_thread_disable_heap_trace(loader->app.thread);
if(instance->application) { }
furi_assert(instance->application->name);
printf("%s\r\n", instance->application->name); // setup insomnia
} else { if(!(app->flags & FlipperApplicationFlagInsomniaSafe)) {
printf("unknown\r\n"); furi_hal_power_insomnia_enter();
} loader->app.insomniac = true;
} else {
loader->app.insomniac = false;
}
// setup app thread callbacks
furi_thread_set_state_context(loader->app.thread, loader);
furi_thread_set_state_callback(loader->app.thread, loader_thread_state_callback);
// start app thread
furi_thread_start(loader->app.thread);
}
// process messages
static void loader_do_menu_show(Loader* loader) {
if(!loader->loader_menu) {
loader->loader_menu = loader_menu_alloc();
loader_menu_set_closed_callback(loader->loader_menu, loader_menu_closed_callback, loader);
loader_menu_set_click_callback(loader->loader_menu, loader_menu_click_callback, loader);
loader_menu_start(loader->loader_menu);
} }
} }
static void loader_cli(Cli* cli, FuriString* args, void* _ctx) { static void loader_do_menu_closed(Loader* loader) {
furi_assert(_ctx); if(loader->loader_menu) {
Loader* instance = _ctx; loader_menu_stop(loader->loader_menu);
loader_menu_free(loader->loader_menu);
FuriString* cmd; loader->loader_menu = NULL;
cmd = furi_string_alloc(); }
do {
if(!args_read_string_and_trim(args, cmd)) {
loader_cli_print_usage();
break;
}
if(furi_string_cmp_str(cmd, "list") == 0) {
loader_cli_list(cli, args, instance);
break;
}
if(furi_string_cmp_str(cmd, "open") == 0) {
loader_cli_open(cli, args, instance);
break;
}
if(furi_string_cmp_str(cmd, "info") == 0) {
loader_cli_info(cli, args, instance);
break;
}
loader_cli_print_usage();
} while(false);
furi_string_free(cmd);
} }
LoaderStatus loader_start(Loader* instance, const char* name, const char* args) { static bool loader_do_is_locked(Loader* loader) {
UNUSED(instance); return loader->app.thread != NULL;
furi_assert(name); }
const FlipperApplication* application = loader_find_application_by_name(name); static LoaderStatus loader_do_start_by_name(Loader* loader, const char* name, const char* args) {
if(loader_do_is_locked(loader)) {
if(!application) {
FURI_LOG_E(TAG, "Can't find application with name %s", name);
return LoaderStatusErrorUnknownApp;
}
if(!loader_lock(loader_instance)) {
FURI_LOG_E(TAG, "Loader is locked");
return LoaderStatusErrorAppStarted; return LoaderStatusErrorAppStarted;
} }
if(!loader_start_application(application, args)) { const FlipperApplication* app = loader_find_application_by_name(name);
return LoaderStatusErrorInternal;
if(!app) {
return LoaderStatusErrorUnknownApp;
} }
loader_start_internal_app(loader, app, args);
return LoaderStatusOk; return LoaderStatusOk;
} }
bool loader_lock(Loader* instance) { static bool loader_do_lock(Loader* loader) {
FURI_CRITICAL_ENTER(); if(loader->app.thread) {
bool result = false; return false;
if(instance->lock_count == 0) {
instance->lock_count++;
result = true;
}
FURI_CRITICAL_EXIT();
return result;
}
void loader_unlock(Loader* instance) {
FURI_CRITICAL_ENTER();
if(instance->lock_count > 0) instance->lock_count--;
FURI_CRITICAL_EXIT();
}
bool loader_is_locked(const Loader* instance) {
return instance->lock_count > 0;
}
static void loader_thread_state_callback(FuriThreadState thread_state, void* context) {
furi_assert(context);
Loader* instance = context;
LoaderEvent event;
if(thread_state == FuriThreadStateRunning) {
event.type = LoaderEventTypeApplicationStarted;
furi_pubsub_publish(loader_instance->pubsub, &event);
if(!(loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe)) {
furi_hal_power_insomnia_enter();
}
} else if(thread_state == FuriThreadStateStopped) {
FURI_LOG_I(TAG, "Application stopped. Free heap: %zu", memmgr_get_free_heap());
if(loader_instance->application_arguments) {
free(loader_instance->application_arguments);
loader_instance->application_arguments = NULL;
}
if(!(loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe)) {
furi_hal_power_insomnia_exit();
}
loader_unlock(instance);
event.type = LoaderEventTypeApplicationStopped;
furi_pubsub_publish(loader_instance->pubsub, &event);
}
}
static uint32_t loader_hide_menu(void* context) {
UNUSED(context);
return VIEW_NONE;
}
static uint32_t loader_back_to_primary_menu(void* context) {
furi_assert(context);
Submenu* submenu = context;
submenu_set_selected_item(submenu, 0);
return LoaderMenuViewPrimary;
}
static Loader* loader_alloc() {
Loader* instance = malloc(sizeof(Loader));
instance->application_thread = furi_thread_alloc();
furi_thread_set_state_context(instance->application_thread, instance);
furi_thread_set_state_callback(instance->application_thread, loader_thread_state_callback);
instance->pubsub = furi_pubsub_alloc();
#ifdef SRV_CLI
instance->cli = furi_record_open(RECORD_CLI);
cli_add_command(
instance->cli, RECORD_LOADER, CliCommandFlagParallelSafe, loader_cli, instance);
#else
UNUSED(loader_cli);
#endif
instance->loader_thread = furi_thread_get_current_id();
// Gui
instance->gui = furi_record_open(RECORD_GUI);
instance->view_dispatcher = view_dispatcher_alloc();
view_dispatcher_attach_to_gui(
instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen);
// Primary menu
instance->primary_menu = menu_alloc();
view_set_previous_callback(menu_get_view(instance->primary_menu), loader_hide_menu);
view_dispatcher_add_view(
instance->view_dispatcher, LoaderMenuViewPrimary, menu_get_view(instance->primary_menu));
// Settings menu
instance->settings_menu = submenu_alloc();
view_set_context(submenu_get_view(instance->settings_menu), instance->settings_menu);
view_set_previous_callback(
submenu_get_view(instance->settings_menu), loader_back_to_primary_menu);
view_dispatcher_add_view(
instance->view_dispatcher,
LoaderMenuViewSettings,
submenu_get_view(instance->settings_menu));
view_dispatcher_enable_queue(instance->view_dispatcher);
return instance;
}
static void loader_free(Loader* instance) {
furi_assert(instance);
if(instance->cli) {
furi_record_close(RECORD_CLI);
} }
furi_pubsub_free(instance->pubsub); loader->app.thread = (FuriThread*)LOADER_MAGIC_THREAD_VALUE;
return true;
furi_thread_free(instance->application_thread);
menu_free(loader_instance->primary_menu);
view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewPrimary);
submenu_free(loader_instance->settings_menu);
view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewSettings);
view_dispatcher_free(loader_instance->view_dispatcher);
furi_record_close(RECORD_GUI);
free(instance);
instance = NULL;
} }
static void loader_build_menu() { static void loader_do_unlock(Loader* loader) {
FURI_LOG_I(TAG, "Building main menu"); furi_assert(loader->app.thread == (FuriThread*)LOADER_MAGIC_THREAD_VALUE);
size_t i; loader->app.thread = NULL;
for(i = 0; i < FLIPPER_APPS_COUNT; i++) { }
menu_add_item(
loader_instance->primary_menu, static void loader_do_app_closed(Loader* loader) {
FLIPPER_APPS[i].name, furi_assert(loader->app.thread);
FLIPPER_APPS[i].icon, FURI_LOG_I(TAG, "Application stopped. Free heap: %zu", memmgr_get_free_heap());
i, if(loader->app.args) {
loader_menu_callback, free(loader->app.args);
(void*)&FLIPPER_APPS[i]); loader->app.args = NULL;
} }
menu_add_item(
loader_instance->primary_menu,
"Settings",
&A_Settings_14,
i++,
loader_submenu_callback,
(void*)LoaderMenuViewSettings);
}
static void loader_build_submenu() { if(loader->app.insomniac) {
FURI_LOG_I(TAG, "Building settings menu"); furi_hal_power_insomnia_exit();
for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) {
submenu_add_item(
loader_instance->settings_menu,
FLIPPER_SETTINGS_APPS[i].name,
i,
loader_menu_callback,
(void*)&FLIPPER_SETTINGS_APPS[i]);
} }
free(loader->app.name);
loader->app.name = NULL;
furi_thread_join(loader->app.thread);
furi_thread_free(loader->app.thread);
loader->app.thread = NULL;
} }
void loader_show_menu() { // app
furi_assert(loader_instance);
furi_thread_flags_set(loader_instance->loader_thread, LOADER_THREAD_FLAG_SHOW_MENU);
}
void loader_update_menu() {
menu_reset(loader_instance->primary_menu);
loader_build_menu();
}
int32_t loader_srv(void* p) { int32_t loader_srv(void* p) {
UNUSED(p); UNUSED(p);
Loader* loader = loader_alloc();
furi_record_create(RECORD_LOADER, loader);
FURI_LOG_I(TAG, "Executing system start hooks"); FURI_LOG_I(TAG, "Executing system start hooks");
for(size_t i = 0; i < FLIPPER_ON_SYSTEM_START_COUNT; i++) { for(size_t i = 0; i < FLIPPER_ON_SYSTEM_START_COUNT; i++) {
FLIPPER_ON_SYSTEM_START[i](); FLIPPER_ON_SYSTEM_START[i]();
} }
FURI_LOG_I(TAG, "Starting");
loader_instance = loader_alloc();
loader_build_menu();
loader_build_submenu();
FURI_LOG_I(TAG, "Started");
furi_record_create(RECORD_LOADER, loader_instance);
if(FLIPPER_AUTORUN_APP_NAME && strlen(FLIPPER_AUTORUN_APP_NAME)) { if(FLIPPER_AUTORUN_APP_NAME && strlen(FLIPPER_AUTORUN_APP_NAME)) {
loader_start(loader_instance, FLIPPER_AUTORUN_APP_NAME, NULL); loader_do_start_by_name(loader, FLIPPER_AUTORUN_APP_NAME, NULL);
} }
while(1) { LoaderMessage message;
uint32_t flags = while(true) {
furi_thread_flags_wait(LOADER_THREAD_FLAG_ALL, FuriFlagWaitAny, FuriWaitForever); if(furi_message_queue_get(loader->queue, &message, FuriWaitForever) == FuriStatusOk) {
if(flags & LOADER_THREAD_FLAG_SHOW_MENU) { switch(message.type) {
menu_set_selected_item(loader_instance->primary_menu, 0); case LoaderMessageTypeStartByName:
view_dispatcher_switch_to_view( message.status_value->value =
loader_instance->view_dispatcher, LoaderMenuViewPrimary); loader_do_start_by_name(loader, message.start.name, message.start.args);
view_dispatcher_run(loader_instance->view_dispatcher); api_lock_unlock(message.api_lock);
break;
case LoaderMessageTypeShowMenu:
loader_do_menu_show(loader);
break;
case LoaderMessageTypeMenuClosed:
loader_do_menu_closed(loader);
break;
case LoaderMessageTypeIsLocked:
message.bool_value->value = loader_do_is_locked(loader);
api_lock_unlock(message.api_lock);
break;
case LoaderMessageTypeAppClosed:
loader_do_app_closed(loader);
break;
case LoaderMessageTypeLock:
message.bool_value->value = loader_do_lock(loader);
api_lock_unlock(message.api_lock);
break;
case LoaderMessageTypeUnlock:
loader_do_unlock(loader);
}
} }
} }
furi_record_destroy(RECORD_LOADER);
loader_free(loader_instance);
return 0; return 0;
} }
FuriPubSub* loader_get_pubsub(Loader* instance) {
return instance->pubsub;
}

View File

@@ -1,7 +1,5 @@
#pragma once #pragma once
#include <furi.h>
#include <core/pubsub.h>
#include <stdbool.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -43,17 +41,14 @@ bool loader_lock(Loader* instance);
void loader_unlock(Loader* instance); void loader_unlock(Loader* instance);
/** Get loader lock status */ /** Get loader lock status */
bool loader_is_locked(const Loader* instance); bool loader_is_locked(Loader* instance);
/** Show primary loader */ /** Show primary loader */
void loader_show_menu(); void loader_show_menu(Loader* instance);
/** Show primary loader */
void loader_update_menu();
/** Show primary loader */ /** Show primary loader */
FuriPubSub* loader_get_pubsub(Loader* instance); FuriPubSub* loader_get_pubsub(Loader* instance);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -0,0 +1,117 @@
#include <furi.h>
#include <cli/cli.h>
#include <applications.h>
#include <lib/toolbox/args.h>
#include "loader.h"
static void loader_cli_print_usage() {
printf("Usage:\r\n");
printf("loader <cmd> <args>\r\n");
printf("Cmd list:\r\n");
printf("\tlist\t - List available applications\r\n");
printf("\topen <Application Name:string>\t - Open application by name\r\n");
printf("\tinfo\t - Show loader state\r\n");
}
static void loader_cli_list() {
printf("Applications:\r\n");
for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
printf("\t%s\r\n", FLIPPER_APPS[i].name);
}
printf("Settings:\r\n");
for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) {
printf("\t%s\r\n", FLIPPER_SETTINGS_APPS[i].name);
}
}
static void loader_cli_info(Loader* loader) {
if(!loader_is_locked(loader)) {
printf("No application is running\r\n");
} else {
// TODO: print application name ???
printf("Application is running\r\n");
}
}
static void loader_cli_open(FuriString* args, Loader* loader) {
FuriString* app_name = furi_string_alloc();
do {
if(!args_read_probably_quoted_string_and_trim(args, app_name)) {
printf("No application provided\r\n");
break;
}
furi_string_trim(args);
const char* args_str = furi_string_get_cstr(args);
if(strlen(args_str) == 0) {
args_str = NULL;
}
const char* app_name_str = furi_string_get_cstr(app_name);
LoaderStatus status = loader_start(loader, app_name_str, args_str);
switch(status) {
case LoaderStatusOk:
break;
case LoaderStatusErrorAppStarted:
printf("Can't start, application is running");
break;
case LoaderStatusErrorUnknownApp:
printf("%s doesn't exists\r\n", app_name_str);
break;
case LoaderStatusErrorInternal:
printf("Internal error\r\n");
break;
}
} while(false);
furi_string_free(app_name);
}
static void loader_cli(Cli* cli, FuriString* args, void* context) {
UNUSED(cli);
UNUSED(context);
Loader* loader = furi_record_open(RECORD_LOADER);
FuriString* cmd;
cmd = furi_string_alloc();
do {
if(!args_read_string_and_trim(args, cmd)) {
loader_cli_print_usage();
break;
}
if(furi_string_cmp_str(cmd, "list") == 0) {
loader_cli_list();
break;
}
if(furi_string_cmp_str(cmd, "open") == 0) {
loader_cli_open(args, loader);
break;
}
if(furi_string_cmp_str(cmd, "info") == 0) {
loader_cli_info(loader);
break;
}
loader_cli_print_usage();
} while(false);
furi_string_free(cmd);
furi_record_close(RECORD_LOADER);
}
void loader_on_system_start() {
#ifdef SRV_CLI
Cli* cli = furi_record_open(RECORD_CLI);
cli_add_command(cli, RECORD_LOADER, CliCommandFlagParallelSafe, loader_cli, NULL);
furi_record_close(RECORD_CLI);
#else
UNUSED(loader_cli);
#endif
}

View File

@@ -1,39 +1,56 @@
#include "loader.h" #pragma once
#include <furi.h> #include <furi.h>
#include <furi_hal.h> #include <toolbox/api_lock.h>
#include <core/pubsub.h> #include "loader.h"
#include <cli/cli.h> #include "loader_menu.h"
#include <lib/toolbox/args.h>
#include <gui/view_dispatcher.h> typedef struct {
char* args;
#include <gui/modules/menu.h> char* name;
#include <gui/modules/submenu.h> FuriThread* thread;
bool insomniac;
#include <applications.h> } LoaderAppData;
#include <assets_icons.h>
struct Loader { struct Loader {
FuriThreadId loader_thread;
const FlipperApplication* application;
FuriThread* application_thread;
char* application_arguments;
Cli* cli;
Gui* gui;
ViewDispatcher* view_dispatcher;
Menu* primary_menu;
Submenu* settings_menu;
volatile uint8_t lock_count;
FuriPubSub* pubsub; FuriPubSub* pubsub;
FuriMessageQueue* queue;
LoaderMenu* loader_menu;
LoaderAppData app;
}; };
typedef enum { typedef enum {
LoaderMenuViewPrimary, LoaderMessageTypeStartByName,
LoaderMenuViewSettings, LoaderMessageTypeAppClosed,
} LoaderMenuView; LoaderMessageTypeShowMenu,
LoaderMessageTypeMenuClosed,
LoaderMessageTypeLock,
LoaderMessageTypeUnlock,
LoaderMessageTypeIsLocked,
} LoaderMessageType;
typedef struct {
const char* name;
const char* args;
} LoaderMessageStartByName;
typedef struct {
LoaderStatus value;
} LoaderMessageLoaderStatusResult;
typedef struct {
bool value;
} LoaderMessageBoolResult;
typedef struct {
FuriApiLock api_lock;
LoaderMessageType type;
union {
LoaderMessageStartByName start;
};
union {
LoaderMessageLoaderStatusResult* status_value;
LoaderMessageBoolResult* bool_value;
};
} LoaderMessage;

View File

@@ -0,0 +1,187 @@
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/modules/menu.h>
#include <gui/modules/submenu.h>
#include <assets_icons.h>
#include <applications.h>
#include "loader_menu.h"
#define TAG "LoaderMenu"
struct LoaderMenu {
Gui* gui;
ViewDispatcher* view_dispatcher;
Menu* primary_menu;
Submenu* settings_menu;
void (*closed_callback)(void*);
void* closed_callback_context;
void (*click_callback)(const char*, void*);
void* click_callback_context;
FuriThread* thread;
};
typedef enum {
LoaderMenuViewPrimary,
LoaderMenuViewSettings,
} LoaderMenuView;
static int32_t loader_menu_thread(void* p);
LoaderMenu* loader_menu_alloc() {
LoaderMenu* loader_menu = malloc(sizeof(LoaderMenu));
loader_menu->gui = furi_record_open(RECORD_GUI);
loader_menu->view_dispatcher = view_dispatcher_alloc();
loader_menu->primary_menu = menu_alloc();
loader_menu->settings_menu = submenu_alloc();
loader_menu->thread = NULL;
return loader_menu;
}
void loader_menu_free(LoaderMenu* loader_menu) {
furi_assert(loader_menu);
// check if thread is running
furi_assert(!loader_menu->thread);
submenu_free(loader_menu->settings_menu);
menu_free(loader_menu->primary_menu);
view_dispatcher_free(loader_menu->view_dispatcher);
furi_record_close(RECORD_GUI);
free(loader_menu);
}
void loader_menu_start(LoaderMenu* loader_menu) {
furi_assert(loader_menu);
furi_assert(!loader_menu->thread);
loader_menu->thread = furi_thread_alloc_ex(TAG, 1024, loader_menu_thread, loader_menu);
furi_thread_start(loader_menu->thread);
}
void loader_menu_stop(LoaderMenu* loader_menu) {
furi_assert(loader_menu);
furi_assert(loader_menu->thread);
view_dispatcher_stop(loader_menu->view_dispatcher);
furi_thread_join(loader_menu->thread);
furi_thread_free(loader_menu->thread);
loader_menu->thread = NULL;
}
void loader_menu_set_closed_callback(
LoaderMenu* loader_menu,
void (*callback)(void*),
void* context) {
loader_menu->closed_callback = callback;
loader_menu->closed_callback_context = context;
}
void loader_menu_set_click_callback(
LoaderMenu* loader_menu,
void (*callback)(const char*, void*),
void* context) {
loader_menu->click_callback = callback;
loader_menu->click_callback_context = context;
}
static void loader_menu_callback(void* context, uint32_t index) {
LoaderMenu* loader_menu = context;
const char* name = FLIPPER_APPS[index].name;
if(loader_menu->click_callback) {
loader_menu->click_callback(name, loader_menu->click_callback_context);
}
}
static void loader_menu_settings_menu_callback(void* context, uint32_t index) {
LoaderMenu* loader_menu = context;
const char* name = FLIPPER_SETTINGS_APPS[index].name;
if(loader_menu->click_callback) {
loader_menu->click_callback(name, loader_menu->click_callback_context);
}
}
static void loader_menu_switch_to_settings(void* context, uint32_t index) {
UNUSED(index);
LoaderMenu* loader_menu = context;
view_dispatcher_switch_to_view(loader_menu->view_dispatcher, LoaderMenuViewSettings);
}
static uint32_t loader_menu_switch_to_primary(void* context) {
UNUSED(context);
return LoaderMenuViewPrimary;
}
static uint32_t loader_menu_exit(void* context) {
UNUSED(context);
return VIEW_NONE;
}
static void loader_menu_build_menu(LoaderMenu* loader_menu) {
size_t i;
for(i = 0; i < FLIPPER_APPS_COUNT; i++) {
menu_add_item(
loader_menu->primary_menu,
FLIPPER_APPS[i].name,
FLIPPER_APPS[i].icon,
i,
loader_menu_callback,
(void*)loader_menu);
}
menu_add_item(
loader_menu->primary_menu,
"Settings",
&A_Settings_14,
i++,
loader_menu_switch_to_settings,
loader_menu);
};
static void loader_menu_build_submenu(LoaderMenu* loader_menu) {
for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) {
submenu_add_item(
loader_menu->settings_menu,
FLIPPER_SETTINGS_APPS[i].name,
i,
loader_menu_settings_menu_callback,
loader_menu);
}
}
static int32_t loader_menu_thread(void* p) {
LoaderMenu* loader_menu = p;
furi_assert(loader_menu);
loader_menu_build_menu(loader_menu);
loader_menu_build_submenu(loader_menu);
view_dispatcher_attach_to_gui(
loader_menu->view_dispatcher, loader_menu->gui, ViewDispatcherTypeFullscreen);
// Primary menu
View* primary_view = menu_get_view(loader_menu->primary_menu);
view_set_context(primary_view, loader_menu->primary_menu);
view_set_previous_callback(primary_view, loader_menu_exit);
view_dispatcher_add_view(loader_menu->view_dispatcher, LoaderMenuViewPrimary, primary_view);
// Settings menu
View* settings_view = submenu_get_view(loader_menu->settings_menu);
view_set_context(settings_view, loader_menu->settings_menu);
view_set_previous_callback(settings_view, loader_menu_switch_to_primary);
view_dispatcher_add_view(loader_menu->view_dispatcher, LoaderMenuViewSettings, settings_view);
view_dispatcher_enable_queue(loader_menu->view_dispatcher);
view_dispatcher_switch_to_view(loader_menu->view_dispatcher, LoaderMenuViewPrimary);
// run view dispatcher
view_dispatcher_run(loader_menu->view_dispatcher);
view_dispatcher_remove_view(loader_menu->view_dispatcher, LoaderMenuViewPrimary);
view_dispatcher_remove_view(loader_menu->view_dispatcher, LoaderMenuViewSettings);
if(loader_menu->closed_callback) {
loader_menu->closed_callback(loader_menu->closed_callback_context);
}
return 0;
}

View File

@@ -0,0 +1,30 @@
#pragma once
#include <furi.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct LoaderMenu LoaderMenu;
LoaderMenu* loader_menu_alloc();
void loader_menu_free(LoaderMenu* loader_menu);
void loader_menu_start(LoaderMenu* loader_menu);
void loader_menu_stop(LoaderMenu* loader_menu);
void loader_menu_set_closed_callback(
LoaderMenu* loader_menu,
void (*callback)(void*),
void* context);
void loader_menu_set_click_callback(
LoaderMenu* loader_menu,
void (*callback)(const char*, void*),
void* context);
#ifdef __cplusplus
}
#endif

View File

@@ -326,31 +326,35 @@ static int32_t rpc_session_worker(void* context) {
return 0; return 0;
} }
static void rpc_session_free_callback(FuriThreadState thread_state, void* context) { static void rpc_session_thread_pending_callback(void* context, uint32_t arg) {
furi_assert(context); UNUSED(arg);
RpcSession* session = (RpcSession*)context; RpcSession* session = (RpcSession*)context;
for(size_t i = 0; i < COUNT_OF(rpc_systems); ++i) {
if(rpc_systems[i].free) {
(rpc_systems[i].free)(session->system_contexts[i]);
}
}
free(session->system_contexts);
free(session->decoded_message);
RpcHandlerDict_clear(session->handlers);
furi_stream_buffer_free(session->stream);
furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever);
if(session->terminated_callback) {
session->terminated_callback(session->context);
}
furi_mutex_release(session->callbacks_mutex);
furi_mutex_free(session->callbacks_mutex);
furi_thread_join(session->thread);
furi_thread_free(session->thread);
free(session);
}
static void rpc_session_thread_state_callback(FuriThreadState thread_state, void* context) {
if(thread_state == FuriThreadStateStopped) { if(thread_state == FuriThreadStateStopped) {
for(size_t i = 0; i < COUNT_OF(rpc_systems); ++i) { furi_timer_pending_callback(rpc_session_thread_pending_callback, context, 0);
if(rpc_systems[i].free) {
rpc_systems[i].free(session->system_contexts[i]);
}
}
free(session->system_contexts);
free(session->decoded_message);
RpcHandlerDict_clear(session->handlers);
furi_stream_buffer_free(session->stream);
furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever);
if(session->terminated_callback) {
session->terminated_callback(session->context);
}
furi_mutex_release(session->callbacks_mutex);
furi_mutex_free(session->callbacks_mutex);
furi_thread_free(session->thread);
free(session);
} }
} }
@@ -385,7 +389,7 @@ RpcSession* rpc_session_open(Rpc* rpc, RpcOwner owner) {
session->thread = furi_thread_alloc_ex("RpcSessionWorker", 3072, rpc_session_worker, session); session->thread = furi_thread_alloc_ex("RpcSessionWorker", 3072, rpc_session_worker, session);
furi_thread_set_state_context(session->thread, session); furi_thread_set_state_context(session->thread, session);
furi_thread_set_state_callback(session->thread, rpc_session_free_callback); furi_thread_set_state_callback(session->thread, rpc_session_thread_state_callback);
furi_thread_start(session->thread); furi_thread_start(session->thread);

View File

@@ -803,6 +803,7 @@ void storage_file_free(File* file) {
} }
FuriPubSub* storage_get_pubsub(Storage* storage) { FuriPubSub* storage_get_pubsub(Storage* storage) {
furi_assert(storage);
return storage->pubsub; return storage->pubsub;
} }

View File

@@ -337,6 +337,7 @@ static bool storage_ext_file_close(void* ctx, File* file) {
file->internal_error_id = f_close(file_data); file->internal_error_id = f_close(file_data);
file->error_id = storage_ext_parse_error(file->internal_error_id); file->error_id = storage_ext_parse_error(file->internal_error_id);
free(file_data); free(file_data);
storage_set_storage_file_data(file, NULL, storage);
return (file->error_id == FSE_OK); return (file->error_id == FSE_OK);
} }

View File

@@ -53,7 +53,9 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) {
(uint32_t)(data->vbus_voltage), (uint32_t)(data->vbus_voltage),
(uint32_t)(data->vbus_voltage * 10) % 10, (uint32_t)(data->vbus_voltage * 10) % 10,
current); current);
} else if(current < 0) { } else if(current < -5) {
// Often gauge reports anything in the range 1~5ma as 5ma
// That brings confusion, so we'll treat it as Napping
snprintf( snprintf(
emote, emote,
sizeof(emote), sizeof(emote),

View File

@@ -43,7 +43,6 @@ static void debug_changed(VariableItem* item) {
} else { } else {
furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug); furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug);
} }
loader_update_menu();
} }
const char* const heap_trace_mode_text[] = { const char* const heap_trace_mode_text[] = {
@@ -137,8 +136,6 @@ static void hand_orient_changed(VariableItem* item) {
} else { } else {
furi_hal_rtc_reset_flag(FuriHalRtcFlagHandOrient); furi_hal_rtc_reset_flag(FuriHalRtcFlagHandOrient);
} }
loader_update_menu();
} }
const char* const sleep_method[] = { const char* const sleep_method[] = {

View File

@@ -85,22 +85,10 @@ static void updater_cli_ep(Cli* cli, FuriString* args, void* context) {
updater_cli_help(args); updater_cli_help(args);
} }
static int32_t updater_spawner_thread_worker(void* arg) { static void updater_start_app(void* context, uint32_t arg) {
UNUSED(context);
UNUSED(arg); UNUSED(arg);
Loader* loader = furi_record_open(RECORD_LOADER);
loader_start(loader, "UpdaterApp", NULL);
furi_record_close(RECORD_LOADER);
return 0;
}
static void updater_spawner_thread_cleanup(FuriThreadState state, void* context) {
FuriThread* thread = context;
if(state == FuriThreadStateStopped) {
furi_thread_free(thread);
}
}
static void updater_start_app() {
FuriHalRtcBootMode mode = furi_hal_rtc_get_boot_mode(); FuriHalRtcBootMode mode = furi_hal_rtc_get_boot_mode();
if((mode != FuriHalRtcBootModePreUpdate) && (mode != FuriHalRtcBootModePostUpdate)) { if((mode != FuriHalRtcBootModePreUpdate) && (mode != FuriHalRtcBootModePostUpdate)) {
return; return;
@@ -110,11 +98,9 @@ static void updater_start_app() {
* inside loader process, at startup. * inside loader process, at startup.
* So, accessing its record would cause a deadlock * So, accessing its record would cause a deadlock
*/ */
FuriThread* thread = Loader* loader = furi_record_open(RECORD_LOADER);
furi_thread_alloc_ex("UpdateAppSpawner", 768, updater_spawner_thread_worker, NULL); loader_start(loader, "UpdaterApp", NULL);
furi_thread_set_state_callback(thread, updater_spawner_thread_cleanup); furi_record_close(RECORD_LOADER);
furi_thread_set_state_context(thread, thread);
furi_thread_start(thread);
} }
void updater_on_system_start() { void updater_on_system_start() {
@@ -126,7 +112,7 @@ void updater_on_system_start() {
UNUSED(updater_cli_ep); UNUSED(updater_cli_ep);
#endif #endif
#ifndef FURI_RAM_EXEC #ifndef FURI_RAM_EXEC
updater_start_app(); furi_timer_pending_callback(updater_start_app, NULL, 0);
#else #else
UNUSED(updater_start_app); UNUSED(updater_start_app);
#endif #endif

View File

@@ -346,7 +346,11 @@ int32_t update_task_worker_flash_writer(void* context) {
furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePostUpdate); furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePostUpdate);
// Format LFS before restoring backup on next boot // Format LFS before restoring backup on next boot
furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset);
#ifdef FURI_NDEBUG
// Production
furi_hal_rtc_set_log_level(FuriLogLevelDefault);
furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug);
#endif
update_task_set_progress(update_task, UpdateTaskStageCompleted, 100); update_task_set_progress(update_task, UpdateTaskStageCompleted, 100);
success = true; success = true;
} while(false); } while(false);

View File

@@ -1,7 +1,7 @@
Import("env")
from fbt.version import get_git_commit_unix_timestamp from fbt.version import get_git_commit_unix_timestamp
Import("env")
assetsenv = env.Clone( assetsenv = env.Clone(
tools=["fbt_assets"], tools=["fbt_assets"],
FW_LIB_NAME="assets", FW_LIB_NAME="assets",

View File

@@ -20,7 +20,7 @@ DIST_SUFFIX = "local"
COPRO_OB_DATA = "scripts/ob.data" COPRO_OB_DATA = "scripts/ob.data"
# Must match lib/STM32CubeWB version # Must match lib/STM32CubeWB version
COPRO_CUBE_VERSION = "1.16.0" COPRO_CUBE_VERSION = "1.15.0"
COPRO_CUBE_DIR = "lib/STM32CubeWB" COPRO_CUBE_DIR = "lib/STM32CubeWB"

View File

@@ -1,5 +1,3 @@
Import("ENV", "fw_build_meta")
from SCons.Errors import UserError from SCons.Errors import UserError
from SCons.Node import FS from SCons.Node import FS
@@ -10,6 +8,8 @@ from fbt_extra.util import (
link_elf_dir_as_latest, link_elf_dir_as_latest,
) )
Import("ENV", "fw_build_meta")
# Building initial C environment for libs # Building initial C environment for libs
env = ENV.Clone( env = ENV.Clone(
tools=[ tools=[

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params entry,status,name,type,params
Version,+,23.0,, Version,+,26.0,,
Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli.h,,
Header,+,applications/services/cli/cli_vcp.h,, Header,+,applications/services/cli/cli_vcp.h,,
@@ -36,6 +36,7 @@ Header,+,applications/services/notification/notification_messages.h,,
Header,+,applications/services/power/power_service/power.h,, Header,+,applications/services/power/power_service/power.h,,
Header,+,applications/services/rpc/rpc_app.h,, Header,+,applications/services/rpc/rpc_app.h,,
Header,+,applications/services/storage/storage.h,, Header,+,applications/services/storage/storage.h,,
Header,-,firmware/targets/f18/furi_hal/furi_hal_power_calibration.h,,
Header,+,firmware/targets/f18/furi_hal/furi_hal_resources.h,, Header,+,firmware/targets/f18/furi_hal/furi_hal_resources.h,,
Header,+,firmware/targets/f18/furi_hal/furi_hal_spi_config.h,, Header,+,firmware/targets/f18/furi_hal/furi_hal_spi_config.h,,
Header,+,firmware/targets/f18/furi_hal/furi_hal_target_hw.h,, Header,+,firmware/targets/f18/furi_hal/furi_hal_target_hw.h,,
@@ -111,6 +112,7 @@ Header,+,lib/flipper_application/plugins/composite_resolver.h,,
Header,+,lib/flipper_application/plugins/plugin_manager.h,, Header,+,lib/flipper_application/plugins/plugin_manager.h,,
Header,+,lib/flipper_format/flipper_format.h,, Header,+,lib/flipper_format/flipper_format.h,,
Header,+,lib/flipper_format/flipper_format_i.h,, Header,+,lib/flipper_format/flipper_format_i.h,,
Header,+,lib/flipper_format/flipper_format_stream.h,,
Header,+,lib/libusb_stm32/inc/hid_usage_button.h,, Header,+,lib/libusb_stm32/inc/hid_usage_button.h,,
Header,+,lib/libusb_stm32/inc/hid_usage_consumer.h,, Header,+,lib/libusb_stm32/inc/hid_usage_consumer.h,,
Header,+,lib/libusb_stm32/inc/hid_usage_desktop.h,, Header,+,lib/libusb_stm32/inc/hid_usage_desktop.h,,
@@ -199,8 +201,8 @@ Function,-,LL_EXTI_StructInit,void,LL_EXTI_InitTypeDef*
Function,-,LL_GPIO_DeInit,ErrorStatus,GPIO_TypeDef* Function,-,LL_GPIO_DeInit,ErrorStatus,GPIO_TypeDef*
Function,+,LL_GPIO_Init,ErrorStatus,"GPIO_TypeDef*, LL_GPIO_InitTypeDef*" Function,+,LL_GPIO_Init,ErrorStatus,"GPIO_TypeDef*, LL_GPIO_InitTypeDef*"
Function,-,LL_GPIO_StructInit,void,LL_GPIO_InitTypeDef* Function,-,LL_GPIO_StructInit,void,LL_GPIO_InitTypeDef*
Function,-,LL_I2C_DeInit,ErrorStatus,const I2C_TypeDef* Function,-,LL_I2C_DeInit,ErrorStatus,I2C_TypeDef*
Function,+,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, const LL_I2C_InitTypeDef*" Function,+,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, LL_I2C_InitTypeDef*"
Function,-,LL_I2C_StructInit,void,LL_I2C_InitTypeDef* Function,-,LL_I2C_StructInit,void,LL_I2C_InitTypeDef*
Function,-,LL_Init1msTick,void,uint32_t Function,-,LL_Init1msTick,void,uint32_t
Function,+,LL_LPTIM_DeInit,ErrorStatus,LPTIM_TypeDef* Function,+,LL_LPTIM_DeInit,ErrorStatus,LPTIM_TypeDef*
@@ -754,6 +756,11 @@ Function,+,flipper_format_read_uint32,_Bool,"FlipperFormat*, const char*, uint32
Function,+,flipper_format_rewind,_Bool,FlipperFormat* Function,+,flipper_format_rewind,_Bool,FlipperFormat*
Function,+,flipper_format_seek_to_end,_Bool,FlipperFormat* Function,+,flipper_format_seek_to_end,_Bool,FlipperFormat*
Function,+,flipper_format_set_strict_mode,void,"FlipperFormat*, _Bool" Function,+,flipper_format_set_strict_mode,void,"FlipperFormat*, _Bool"
Function,+,flipper_format_stream_delete_key_and_write,_Bool,"Stream*, FlipperStreamWriteData*, _Bool"
Function,+,flipper_format_stream_get_value_count,_Bool,"Stream*, const char*, uint32_t*, _Bool"
Function,+,flipper_format_stream_read_value_line,_Bool,"Stream*, const char*, FlipperStreamValue, void*, size_t, _Bool"
Function,+,flipper_format_stream_write_comment_cstr,_Bool,"Stream*, const char*"
Function,+,flipper_format_stream_write_value_line,_Bool,"Stream*, FlipperStreamWriteData*"
Function,+,flipper_format_string_alloc,FlipperFormat*, Function,+,flipper_format_string_alloc,FlipperFormat*,
Function,+,flipper_format_update_bool,_Bool,"FlipperFormat*, const char*, const _Bool*, const uint16_t" Function,+,flipper_format_update_bool,_Bool,"FlipperFormat*, const char*, const _Bool*, const uint16_t"
Function,+,flipper_format_update_float,_Bool,"FlipperFormat*, const char*, const float*, const uint16_t" Function,+,flipper_format_update_float,_Bool,"FlipperFormat*, const char*, const float*, const uint16_t"
@@ -885,6 +892,8 @@ Function,+,furi_hal_console_puts,void,const char*
Function,+,furi_hal_console_set_tx_callback,void,"FuriHalConsoleTxCallback, void*" Function,+,furi_hal_console_set_tx_callback,void,"FuriHalConsoleTxCallback, void*"
Function,+,furi_hal_console_tx,void,"const uint8_t*, size_t" Function,+,furi_hal_console_tx,void,"const uint8_t*, size_t"
Function,+,furi_hal_console_tx_with_new_line,void,"const uint8_t*, size_t" Function,+,furi_hal_console_tx_with_new_line,void,"const uint8_t*, size_t"
Function,+,furi_hal_cortex_comp_enable,void,"FuriHalCortexComp, FuriHalCortexCompFunction, uint32_t, uint32_t, FuriHalCortexCompSize"
Function,+,furi_hal_cortex_comp_reset,void,FuriHalCortexComp
Function,+,furi_hal_cortex_delay_us,void,uint32_t Function,+,furi_hal_cortex_delay_us,void,uint32_t
Function,-,furi_hal_cortex_init_early,void, Function,-,furi_hal_cortex_init_early,void,
Function,+,furi_hal_cortex_instructions_per_microsecond,uint32_t, Function,+,furi_hal_cortex_instructions_per_microsecond,uint32_t,
@@ -959,6 +968,7 @@ Function,+,furi_hal_i2c_write_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t,
Function,+,furi_hal_i2c_write_reg_16,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t, uint32_t" Function,+,furi_hal_i2c_write_reg_16,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t, uint32_t"
Function,+,furi_hal_i2c_write_reg_8,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t, uint32_t" Function,+,furi_hal_i2c_write_reg_8,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t, uint32_t"
Function,+,furi_hal_info_get,void,"PropertyValueCallback, char, void*" Function,+,furi_hal_info_get,void,"PropertyValueCallback, char, void*"
Function,+,furi_hal_info_get_api_version,void,"uint16_t*, uint16_t*"
Function,-,furi_hal_init,void, Function,-,furi_hal_init,void,
Function,-,furi_hal_init_early,void, Function,-,furi_hal_init_early,void,
Function,-,furi_hal_interrupt_init,void, Function,-,furi_hal_interrupt_init,void,
@@ -1270,7 +1280,7 @@ Function,+,furi_thread_set_priority,void,"FuriThread*, FuriThreadPriority"
Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t" Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t"
Function,+,furi_thread_set_state_callback,void,"FuriThread*, FuriThreadStateCallback" Function,+,furi_thread_set_state_callback,void,"FuriThread*, FuriThreadStateCallback"
Function,+,furi_thread_set_state_context,void,"FuriThread*, void*" Function,+,furi_thread_set_state_context,void,"FuriThread*, void*"
Function,+,furi_thread_set_stdout_callback,_Bool,FuriThreadStdoutWriteCallback Function,+,furi_thread_set_stdout_callback,void,FuriThreadStdoutWriteCallback
Function,+,furi_thread_start,void,FuriThread* Function,+,furi_thread_start,void,FuriThread*
Function,+,furi_thread_stdout_flush,int32_t, Function,+,furi_thread_stdout_flush,int32_t,
Function,+,furi_thread_stdout_write,size_t,"const char*, size_t" Function,+,furi_thread_stdout_write,size_t,"const char*, size_t"
@@ -1279,6 +1289,7 @@ Function,+,furi_thread_yield,void,
Function,+,furi_timer_alloc,FuriTimer*,"FuriTimerCallback, FuriTimerType, void*" Function,+,furi_timer_alloc,FuriTimer*,"FuriTimerCallback, FuriTimerType, void*"
Function,+,furi_timer_free,void,FuriTimer* Function,+,furi_timer_free,void,FuriTimer*
Function,+,furi_timer_is_running,uint32_t,FuriTimer* Function,+,furi_timer_is_running,uint32_t,FuriTimer*
Function,+,furi_timer_pending_callback,void,"FuriTimerPendigCallback, void*, uint32_t"
Function,+,furi_timer_start,FuriStatus,"FuriTimer*, uint32_t" Function,+,furi_timer_start,FuriStatus,"FuriTimer*, uint32_t"
Function,+,furi_timer_stop,FuriStatus,FuriTimer* Function,+,furi_timer_stop,FuriStatus,FuriTimer*
Function,-,fwrite,size_t,"const void*, size_t, size_t, FILE*" Function,-,fwrite,size_t,"const void*, size_t, size_t, FILE*"
@@ -1367,12 +1378,11 @@ Function,-,ldiv,ldiv_t,"long, long"
Function,-,llabs,long long,long long Function,-,llabs,long long,long long
Function,-,lldiv,lldiv_t,"long long, long long" Function,-,lldiv,lldiv_t,"long long, long long"
Function,+,loader_get_pubsub,FuriPubSub*,Loader* Function,+,loader_get_pubsub,FuriPubSub*,Loader*
Function,+,loader_is_locked,_Bool,const Loader* Function,+,loader_is_locked,_Bool,Loader*
Function,+,loader_lock,_Bool,Loader* Function,+,loader_lock,_Bool,Loader*
Function,+,loader_show_menu,void, Function,+,loader_show_menu,void,Loader*
Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*" Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*"
Function,+,loader_unlock,void,Loader* Function,+,loader_unlock,void,Loader*
Function,+,loader_update_menu,void,
Function,+,loading_alloc,Loading*, Function,+,loading_alloc,Loading*,
Function,+,loading_free,void,Loading* Function,+,loading_free,void,Loading*
Function,+,loading_get_view,View*,Loading* Function,+,loading_get_view,View*,Loading*
1 entry status name type params
2 Version + 23.0 26.0
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/cli/cli.h
5 Header + applications/services/cli/cli_vcp.h
36 Header + applications/services/power/power_service/power.h
37 Header + applications/services/rpc/rpc_app.h
38 Header + applications/services/storage/storage.h
39 Header - firmware/targets/f18/furi_hal/furi_hal_power_calibration.h
40 Header + firmware/targets/f18/furi_hal/furi_hal_resources.h
41 Header + firmware/targets/f18/furi_hal/furi_hal_spi_config.h
42 Header + firmware/targets/f18/furi_hal/furi_hal_target_hw.h
112 Header + lib/flipper_application/plugins/plugin_manager.h
113 Header + lib/flipper_format/flipper_format.h
114 Header + lib/flipper_format/flipper_format_i.h
115 Header + lib/flipper_format/flipper_format_stream.h
116 Header + lib/libusb_stm32/inc/hid_usage_button.h
117 Header + lib/libusb_stm32/inc/hid_usage_consumer.h
118 Header + lib/libusb_stm32/inc/hid_usage_desktop.h
201 Function - LL_GPIO_DeInit ErrorStatus GPIO_TypeDef*
202 Function + LL_GPIO_Init ErrorStatus GPIO_TypeDef*, LL_GPIO_InitTypeDef*
203 Function - LL_GPIO_StructInit void LL_GPIO_InitTypeDef*
204 Function - LL_I2C_DeInit ErrorStatus const I2C_TypeDef* I2C_TypeDef*
205 Function + LL_I2C_Init ErrorStatus I2C_TypeDef*, const LL_I2C_InitTypeDef* I2C_TypeDef*, LL_I2C_InitTypeDef*
206 Function - LL_I2C_StructInit void LL_I2C_InitTypeDef*
207 Function - LL_Init1msTick void uint32_t
208 Function + LL_LPTIM_DeInit ErrorStatus LPTIM_TypeDef*
756 Function + flipper_format_rewind _Bool FlipperFormat*
757 Function + flipper_format_seek_to_end _Bool FlipperFormat*
758 Function + flipper_format_set_strict_mode void FlipperFormat*, _Bool
759 Function + flipper_format_stream_delete_key_and_write _Bool Stream*, FlipperStreamWriteData*, _Bool
760 Function + flipper_format_stream_get_value_count _Bool Stream*, const char*, uint32_t*, _Bool
761 Function + flipper_format_stream_read_value_line _Bool Stream*, const char*, FlipperStreamValue, void*, size_t, _Bool
762 Function + flipper_format_stream_write_comment_cstr _Bool Stream*, const char*
763 Function + flipper_format_stream_write_value_line _Bool Stream*, FlipperStreamWriteData*
764 Function + flipper_format_string_alloc FlipperFormat*
765 Function + flipper_format_update_bool _Bool FlipperFormat*, const char*, const _Bool*, const uint16_t
766 Function + flipper_format_update_float _Bool FlipperFormat*, const char*, const float*, const uint16_t
892 Function + furi_hal_console_set_tx_callback void FuriHalConsoleTxCallback, void*
893 Function + furi_hal_console_tx void const uint8_t*, size_t
894 Function + furi_hal_console_tx_with_new_line void const uint8_t*, size_t
895 Function + furi_hal_cortex_comp_enable void FuriHalCortexComp, FuriHalCortexCompFunction, uint32_t, uint32_t, FuriHalCortexCompSize
896 Function + furi_hal_cortex_comp_reset void FuriHalCortexComp
897 Function + furi_hal_cortex_delay_us void uint32_t
898 Function - furi_hal_cortex_init_early void
899 Function + furi_hal_cortex_instructions_per_microsecond uint32_t
968 Function + furi_hal_i2c_write_reg_16 _Bool FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t, uint32_t
969 Function + furi_hal_i2c_write_reg_8 _Bool FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t, uint32_t
970 Function + furi_hal_info_get void PropertyValueCallback, char, void*
971 Function + furi_hal_info_get_api_version void uint16_t*, uint16_t*
972 Function - furi_hal_init void
973 Function - furi_hal_init_early void
974 Function - furi_hal_interrupt_init void
1280 Function + furi_thread_set_stack_size void FuriThread*, size_t
1281 Function + furi_thread_set_state_callback void FuriThread*, FuriThreadStateCallback
1282 Function + furi_thread_set_state_context void FuriThread*, void*
1283 Function + furi_thread_set_stdout_callback _Bool void FuriThreadStdoutWriteCallback
1284 Function + furi_thread_start void FuriThread*
1285 Function + furi_thread_stdout_flush int32_t
1286 Function + furi_thread_stdout_write size_t const char*, size_t
1289 Function + furi_timer_alloc FuriTimer* FuriTimerCallback, FuriTimerType, void*
1290 Function + furi_timer_free void FuriTimer*
1291 Function + furi_timer_is_running uint32_t FuriTimer*
1292 Function + furi_timer_pending_callback void FuriTimerPendigCallback, void*, uint32_t
1293 Function + furi_timer_start FuriStatus FuriTimer*, uint32_t
1294 Function + furi_timer_stop FuriStatus FuriTimer*
1295 Function - fwrite size_t const void*, size_t, size_t, FILE*
1378 Function - llabs long long long long
1379 Function - lldiv lldiv_t long long, long long
1380 Function + loader_get_pubsub FuriPubSub* Loader*
1381 Function + loader_is_locked _Bool const Loader* Loader*
1382 Function + loader_lock _Bool Loader*
1383 Function + loader_show_menu void Loader*
1384 Function + loader_start LoaderStatus Loader*, const char*, const char*
1385 Function + loader_unlock void Loader*
Function + loader_update_menu void
1386 Function + loading_alloc Loading*
1387 Function + loading_free void Loading*
1388 Function + loading_get_view View* Loading*

View File

@@ -0,0 +1,37 @@
const ParamCEDV cedv = {
.cedv_conf.gauge_conf =
{
.CCT = 1,
.CSYNC = 0,
.EDV_CMP = 0,
.SC = 1,
.FIXED_EDV0 = 1,
.FCC_LIM = 1,
.FC_FOR_VDQ = 1,
.IGNORE_SD = 1,
.SME0 = 0,
},
.full_charge_cap = 1300,
.design_cap = 1300,
.EDV0 = 3300,
.EDV1 = 3321,
.EDV2 = 3355,
.EMF = 3679,
.C0 = 430,
.C1 = 0,
.R1 = 408,
.R0 = 334,
.T0 = 4626,
.TC = 11,
.DOD0 = 4044,
.DOD10 = 3905,
.DOD20 = 3807,
.DOD30 = 3718,
.DOD40 = 3642,
.DOD50 = 3585,
.DOD60 = 3546,
.DOD70 = 3514,
.DOD80 = 3477,
.DOD90 = 3411,
.DOD100 = 3299,
};

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params entry,status,name,type,params
Version,+,23.1,, Version,+,26.1,,
Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli.h,,
Header,+,applications/services/cli/cli_vcp.h,, Header,+,applications/services/cli/cli_vcp.h,,
@@ -47,6 +47,7 @@ Header,+,firmware/targets/f7/furi_hal/furi_hal_idle_timer.h,,
Header,+,firmware/targets/f7/furi_hal/furi_hal_interrupt.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_interrupt.h,,
Header,+,firmware/targets/f7/furi_hal/furi_hal_nfc.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_nfc.h,,
Header,+,firmware/targets/f7/furi_hal/furi_hal_os.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_os.h,,
Header,-,firmware/targets/f7/furi_hal/furi_hal_power_calibration.h,,
Header,+,firmware/targets/f7/furi_hal/furi_hal_pwm.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_pwm.h,,
Header,+,firmware/targets/f7/furi_hal/furi_hal_resources.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_resources.h,,
Header,+,firmware/targets/f7/furi_hal/furi_hal_rfid.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_rfid.h,,
@@ -117,6 +118,7 @@ Header,+,lib/flipper_application/plugins/composite_resolver.h,,
Header,+,lib/flipper_application/plugins/plugin_manager.h,, Header,+,lib/flipper_application/plugins/plugin_manager.h,,
Header,+,lib/flipper_format/flipper_format.h,, Header,+,lib/flipper_format/flipper_format.h,,
Header,+,lib/flipper_format/flipper_format_i.h,, Header,+,lib/flipper_format/flipper_format_i.h,,
Header,+,lib/flipper_format/flipper_format_stream.h,,
Header,+,lib/ibutton/ibutton_key.h,, Header,+,lib/ibutton/ibutton_key.h,,
Header,+,lib/ibutton/ibutton_protocols.h,, Header,+,lib/ibutton/ibutton_protocols.h,,
Header,+,lib/ibutton/ibutton_worker.h,, Header,+,lib/ibutton/ibutton_worker.h,,
@@ -231,8 +233,8 @@ Function,-,LL_EXTI_StructInit,void,LL_EXTI_InitTypeDef*
Function,-,LL_GPIO_DeInit,ErrorStatus,GPIO_TypeDef* Function,-,LL_GPIO_DeInit,ErrorStatus,GPIO_TypeDef*
Function,+,LL_GPIO_Init,ErrorStatus,"GPIO_TypeDef*, LL_GPIO_InitTypeDef*" Function,+,LL_GPIO_Init,ErrorStatus,"GPIO_TypeDef*, LL_GPIO_InitTypeDef*"
Function,-,LL_GPIO_StructInit,void,LL_GPIO_InitTypeDef* Function,-,LL_GPIO_StructInit,void,LL_GPIO_InitTypeDef*
Function,-,LL_I2C_DeInit,ErrorStatus,const I2C_TypeDef* Function,-,LL_I2C_DeInit,ErrorStatus,I2C_TypeDef*
Function,+,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, const LL_I2C_InitTypeDef*" Function,+,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, LL_I2C_InitTypeDef*"
Function,-,LL_I2C_StructInit,void,LL_I2C_InitTypeDef* Function,-,LL_I2C_StructInit,void,LL_I2C_InitTypeDef*
Function,-,LL_Init1msTick,void,uint32_t Function,-,LL_Init1msTick,void,uint32_t
Function,+,LL_LPTIM_DeInit,ErrorStatus,LPTIM_TypeDef* Function,+,LL_LPTIM_DeInit,ErrorStatus,LPTIM_TypeDef*
@@ -935,6 +937,11 @@ Function,+,flipper_format_read_uint32,_Bool,"FlipperFormat*, const char*, uint32
Function,+,flipper_format_rewind,_Bool,FlipperFormat* Function,+,flipper_format_rewind,_Bool,FlipperFormat*
Function,+,flipper_format_seek_to_end,_Bool,FlipperFormat* Function,+,flipper_format_seek_to_end,_Bool,FlipperFormat*
Function,+,flipper_format_set_strict_mode,void,"FlipperFormat*, _Bool" Function,+,flipper_format_set_strict_mode,void,"FlipperFormat*, _Bool"
Function,+,flipper_format_stream_delete_key_and_write,_Bool,"Stream*, FlipperStreamWriteData*, _Bool"
Function,+,flipper_format_stream_get_value_count,_Bool,"Stream*, const char*, uint32_t*, _Bool"
Function,+,flipper_format_stream_read_value_line,_Bool,"Stream*, const char*, FlipperStreamValue, void*, size_t, _Bool"
Function,+,flipper_format_stream_write_comment_cstr,_Bool,"Stream*, const char*"
Function,+,flipper_format_stream_write_value_line,_Bool,"Stream*, FlipperStreamWriteData*"
Function,+,flipper_format_string_alloc,FlipperFormat*, Function,+,flipper_format_string_alloc,FlipperFormat*,
Function,+,flipper_format_update_bool,_Bool,"FlipperFormat*, const char*, const _Bool*, const uint16_t" Function,+,flipper_format_update_bool,_Bool,"FlipperFormat*, const char*, const _Bool*, const uint16_t"
Function,+,flipper_format_update_float,_Bool,"FlipperFormat*, const char*, const float*, const uint16_t" Function,+,flipper_format_update_float,_Bool,"FlipperFormat*, const char*, const float*, const uint16_t"
@@ -1084,6 +1091,8 @@ Function,+,furi_hal_console_puts,void,const char*
Function,+,furi_hal_console_set_tx_callback,void,"FuriHalConsoleTxCallback, void*" Function,+,furi_hal_console_set_tx_callback,void,"FuriHalConsoleTxCallback, void*"
Function,+,furi_hal_console_tx,void,"const uint8_t*, size_t" Function,+,furi_hal_console_tx,void,"const uint8_t*, size_t"
Function,+,furi_hal_console_tx_with_new_line,void,"const uint8_t*, size_t" Function,+,furi_hal_console_tx_with_new_line,void,"const uint8_t*, size_t"
Function,+,furi_hal_cortex_comp_enable,void,"FuriHalCortexComp, FuriHalCortexCompFunction, uint32_t, uint32_t, FuriHalCortexCompSize"
Function,+,furi_hal_cortex_comp_reset,void,FuriHalCortexComp
Function,+,furi_hal_cortex_delay_us,void,uint32_t Function,+,furi_hal_cortex_delay_us,void,uint32_t
Function,-,furi_hal_cortex_init_early,void, Function,-,furi_hal_cortex_init_early,void,
Function,+,furi_hal_cortex_instructions_per_microsecond,uint32_t, Function,+,furi_hal_cortex_instructions_per_microsecond,uint32_t,
@@ -1165,6 +1174,7 @@ Function,+,furi_hal_ibutton_pin_configure,void,
Function,+,furi_hal_ibutton_pin_reset,void, Function,+,furi_hal_ibutton_pin_reset,void,
Function,+,furi_hal_ibutton_pin_write,void,const _Bool Function,+,furi_hal_ibutton_pin_write,void,const _Bool
Function,+,furi_hal_info_get,void,"PropertyValueCallback, char, void*" Function,+,furi_hal_info_get,void,"PropertyValueCallback, char, void*"
Function,+,furi_hal_info_get_api_version,void,"uint16_t*, uint16_t*"
Function,+,furi_hal_infrared_async_rx_set_capture_isr_callback,void,"FuriHalInfraredRxCaptureCallback, void*" Function,+,furi_hal_infrared_async_rx_set_capture_isr_callback,void,"FuriHalInfraredRxCaptureCallback, void*"
Function,+,furi_hal_infrared_async_rx_set_timeout,void,uint32_t Function,+,furi_hal_infrared_async_rx_set_timeout,void,uint32_t
Function,+,furi_hal_infrared_async_rx_set_timeout_isr_callback,void,"FuriHalInfraredRxTimeoutCallback, void*" Function,+,furi_hal_infrared_async_rx_set_timeout_isr_callback,void,"FuriHalInfraredRxTimeoutCallback, void*"
@@ -1572,7 +1582,7 @@ Function,+,furi_thread_set_priority,void,"FuriThread*, FuriThreadPriority"
Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t" Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t"
Function,+,furi_thread_set_state_callback,void,"FuriThread*, FuriThreadStateCallback" Function,+,furi_thread_set_state_callback,void,"FuriThread*, FuriThreadStateCallback"
Function,+,furi_thread_set_state_context,void,"FuriThread*, void*" Function,+,furi_thread_set_state_context,void,"FuriThread*, void*"
Function,+,furi_thread_set_stdout_callback,_Bool,FuriThreadStdoutWriteCallback Function,+,furi_thread_set_stdout_callback,void,FuriThreadStdoutWriteCallback
Function,+,furi_thread_start,void,FuriThread* Function,+,furi_thread_start,void,FuriThread*
Function,+,furi_thread_stdout_flush,int32_t, Function,+,furi_thread_stdout_flush,int32_t,
Function,+,furi_thread_stdout_write,size_t,"const char*, size_t" Function,+,furi_thread_stdout_write,size_t,"const char*, size_t"
@@ -1581,6 +1591,7 @@ Function,+,furi_thread_yield,void,
Function,+,furi_timer_alloc,FuriTimer*,"FuriTimerCallback, FuriTimerType, void*" Function,+,furi_timer_alloc,FuriTimer*,"FuriTimerCallback, FuriTimerType, void*"
Function,+,furi_timer_free,void,FuriTimer* Function,+,furi_timer_free,void,FuriTimer*
Function,+,furi_timer_is_running,uint32_t,FuriTimer* Function,+,furi_timer_is_running,uint32_t,FuriTimer*
Function,+,furi_timer_pending_callback,void,"FuriTimerPendigCallback, void*, uint32_t"
Function,+,furi_timer_start,FuriStatus,"FuriTimer*, uint32_t" Function,+,furi_timer_start,FuriStatus,"FuriTimer*, uint32_t"
Function,+,furi_timer_stop,FuriStatus,FuriTimer* Function,+,furi_timer_stop,FuriStatus,FuriTimer*
Function,-,fwrite,size_t,"const void*, size_t, size_t, FILE*" Function,-,fwrite,size_t,"const void*, size_t, size_t, FILE*"
@@ -1805,12 +1816,11 @@ Function,-,llround,long long int,double
Function,-,llroundf,long long int,float Function,-,llroundf,long long int,float
Function,-,llroundl,long long int,long double Function,-,llroundl,long long int,long double
Function,+,loader_get_pubsub,FuriPubSub*,Loader* Function,+,loader_get_pubsub,FuriPubSub*,Loader*
Function,+,loader_is_locked,_Bool,const Loader* Function,+,loader_is_locked,_Bool,Loader*
Function,+,loader_lock,_Bool,Loader* Function,+,loader_lock,_Bool,Loader*
Function,+,loader_show_menu,void, Function,+,loader_show_menu,void,Loader*
Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*" Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*"
Function,+,loader_unlock,void,Loader* Function,+,loader_unlock,void,Loader*
Function,+,loader_update_menu,void,
Function,+,loading_alloc,Loading*, Function,+,loading_alloc,Loading*,
Function,+,loading_free,void,Loading* Function,+,loading_free,void,Loading*
Function,+,loading_get_view,View*,Loading* Function,+,loading_get_view,View*,Loading*
1 entry status name type params
2 Version + 23.1 26.1
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/cli/cli.h
5 Header + applications/services/cli/cli_vcp.h
47 Header + firmware/targets/f7/furi_hal/furi_hal_interrupt.h
48 Header + firmware/targets/f7/furi_hal/furi_hal_nfc.h
49 Header + firmware/targets/f7/furi_hal/furi_hal_os.h
50 Header - firmware/targets/f7/furi_hal/furi_hal_power_calibration.h
51 Header + firmware/targets/f7/furi_hal/furi_hal_pwm.h
52 Header + firmware/targets/f7/furi_hal/furi_hal_resources.h
53 Header + firmware/targets/f7/furi_hal/furi_hal_rfid.h
118 Header + lib/flipper_application/plugins/plugin_manager.h
119 Header + lib/flipper_format/flipper_format.h
120 Header + lib/flipper_format/flipper_format_i.h
121 Header + lib/flipper_format/flipper_format_stream.h
122 Header + lib/ibutton/ibutton_key.h
123 Header + lib/ibutton/ibutton_protocols.h
124 Header + lib/ibutton/ibutton_worker.h
233 Function - LL_GPIO_DeInit ErrorStatus GPIO_TypeDef*
234 Function + LL_GPIO_Init ErrorStatus GPIO_TypeDef*, LL_GPIO_InitTypeDef*
235 Function - LL_GPIO_StructInit void LL_GPIO_InitTypeDef*
236 Function - LL_I2C_DeInit ErrorStatus const I2C_TypeDef* I2C_TypeDef*
237 Function + LL_I2C_Init ErrorStatus I2C_TypeDef*, const LL_I2C_InitTypeDef* I2C_TypeDef*, LL_I2C_InitTypeDef*
238 Function - LL_I2C_StructInit void LL_I2C_InitTypeDef*
239 Function - LL_Init1msTick void uint32_t
240 Function + LL_LPTIM_DeInit ErrorStatus LPTIM_TypeDef*
937 Function + flipper_format_rewind _Bool FlipperFormat*
938 Function + flipper_format_seek_to_end _Bool FlipperFormat*
939 Function + flipper_format_set_strict_mode void FlipperFormat*, _Bool
940 Function + flipper_format_stream_delete_key_and_write _Bool Stream*, FlipperStreamWriteData*, _Bool
941 Function + flipper_format_stream_get_value_count _Bool Stream*, const char*, uint32_t*, _Bool
942 Function + flipper_format_stream_read_value_line _Bool Stream*, const char*, FlipperStreamValue, void*, size_t, _Bool
943 Function + flipper_format_stream_write_comment_cstr _Bool Stream*, const char*
944 Function + flipper_format_stream_write_value_line _Bool Stream*, FlipperStreamWriteData*
945 Function + flipper_format_string_alloc FlipperFormat*
946 Function + flipper_format_update_bool _Bool FlipperFormat*, const char*, const _Bool*, const uint16_t
947 Function + flipper_format_update_float _Bool FlipperFormat*, const char*, const float*, const uint16_t
1091 Function + furi_hal_console_set_tx_callback void FuriHalConsoleTxCallback, void*
1092 Function + furi_hal_console_tx void const uint8_t*, size_t
1093 Function + furi_hal_console_tx_with_new_line void const uint8_t*, size_t
1094 Function + furi_hal_cortex_comp_enable void FuriHalCortexComp, FuriHalCortexCompFunction, uint32_t, uint32_t, FuriHalCortexCompSize
1095 Function + furi_hal_cortex_comp_reset void FuriHalCortexComp
1096 Function + furi_hal_cortex_delay_us void uint32_t
1097 Function - furi_hal_cortex_init_early void
1098 Function + furi_hal_cortex_instructions_per_microsecond uint32_t
1174 Function + furi_hal_ibutton_pin_reset void
1175 Function + furi_hal_ibutton_pin_write void const _Bool
1176 Function + furi_hal_info_get void PropertyValueCallback, char, void*
1177 Function + furi_hal_info_get_api_version void uint16_t*, uint16_t*
1178 Function + furi_hal_infrared_async_rx_set_capture_isr_callback void FuriHalInfraredRxCaptureCallback, void*
1179 Function + furi_hal_infrared_async_rx_set_timeout void uint32_t
1180 Function + furi_hal_infrared_async_rx_set_timeout_isr_callback void FuriHalInfraredRxTimeoutCallback, void*
1582 Function + furi_thread_set_stack_size void FuriThread*, size_t
1583 Function + furi_thread_set_state_callback void FuriThread*, FuriThreadStateCallback
1584 Function + furi_thread_set_state_context void FuriThread*, void*
1585 Function + furi_thread_set_stdout_callback _Bool void FuriThreadStdoutWriteCallback
1586 Function + furi_thread_start void FuriThread*
1587 Function + furi_thread_stdout_flush int32_t
1588 Function + furi_thread_stdout_write size_t const char*, size_t
1591 Function + furi_timer_alloc FuriTimer* FuriTimerCallback, FuriTimerType, void*
1592 Function + furi_timer_free void FuriTimer*
1593 Function + furi_timer_is_running uint32_t FuriTimer*
1594 Function + furi_timer_pending_callback void FuriTimerPendigCallback, void*, uint32_t
1595 Function + furi_timer_start FuriStatus FuriTimer*, uint32_t
1596 Function + furi_timer_stop FuriStatus FuriTimer*
1597 Function - fwrite size_t const void*, size_t, size_t, FILE*
1816 Function - llroundf long long int float
1817 Function - llroundl long long int long double
1818 Function + loader_get_pubsub FuriPubSub* Loader*
1819 Function + loader_is_locked _Bool const Loader* Loader*
1820 Function + loader_lock _Bool Loader*
1821 Function + loader_show_menu void Loader*
1822 Function + loader_start LoaderStatus Loader*, const char*, const char*
1823 Function + loader_unlock void Loader*
Function + loader_update_menu void
1824 Function + loading_alloc Loading*
1825 Function + loading_free void Loading*
1826 Function + loading_get_view View* Loading*

View File

@@ -18,8 +18,8 @@ PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t ble_app_cmd_buffer;
PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint32_t ble_app_nvm[BLE_NVM_SRAM_SIZE]; PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint32_t ble_app_nvm[BLE_NVM_SRAM_SIZE];
_Static_assert( _Static_assert(
sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 58, sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 57,
"Ble stack config structure size mismatch (check new config options - last updated for v.1.16.0)"); "Ble stack config structure size mismatch (check new config options - last updated for v.1.15.0)");
typedef struct { typedef struct {
FuriMutex* hci_mtx; FuriMutex* hci_mtx;
@@ -88,7 +88,7 @@ bool ble_app_init() {
.min_tx_power = 0, .min_tx_power = 0,
.max_tx_power = 0, .max_tx_power = 0,
.rx_model_config = 1, .rx_model_config = 1,
/* New stack (13.3->16.0)*/ /* New stack (13.3->15.0) */
.max_adv_set_nbr = 1, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set .max_adv_set_nbr = 1, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set
.max_adv_data_len = 31, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set .max_adv_data_len = 31, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set
.tx_path_compens = 0, // RF TX Path Compensation, * 0.1 dB .tx_path_compens = 0, // RF TX Path Compensation, * 0.1 dB

View File

@@ -84,9 +84,7 @@ void furi_hal_bt_init() {
} }
// Explicitly tell that we are in charge of CLK48 domain // Explicitly tell that we are in charge of CLK48 domain
if(!LL_HSEM_IsSemaphoreLocked(HSEM, CFG_HW_CLK48_CONFIG_SEMID)) { furi_check(LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID) == 0);
furi_check(LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID) == 0);
}
// Start Core2 // Start Core2
ble_glue_init(); ble_glue_init();
@@ -129,9 +127,7 @@ bool furi_hal_bt_start_radio_stack() {
furi_mutex_acquire(furi_hal_bt_core2_mtx, FuriWaitForever); furi_mutex_acquire(furi_hal_bt_core2_mtx, FuriWaitForever);
// Explicitly tell that we are in charge of CLK48 domain // Explicitly tell that we are in charge of CLK48 domain
if(!LL_HSEM_IsSemaphoreLocked(HSEM, CFG_HW_CLK48_CONFIG_SEMID)) { furi_check(LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID) == 0);
furi_check(LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID) == 0);
}
do { do {
// Wait until C2 is started or timeout // Wait until C2 is started or timeout

View File

@@ -144,6 +144,7 @@ void furi_hal_clock_init() {
LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_CLK48); LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_CLK48);
LL_RCC_SetUSBClockSource(LL_RCC_USB_CLKSOURCE_PLLSAI1); LL_RCC_SetUSBClockSource(LL_RCC_USB_CLKSOURCE_PLLSAI1);
LL_RCC_SetCLK48ClockSource(LL_RCC_CLK48_CLKSOURCE_PLLSAI1); LL_RCC_SetCLK48ClockSource(LL_RCC_CLK48_CLKSOURCE_PLLSAI1);
LL_RCC_HSI_EnableInStopMode(); // Ensure that MR is capable of work in STOP0
LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE); LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE);
LL_RCC_SetSMPSPrescaler(LL_RCC_SMPS_DIV_1); LL_RCC_SetSMPSPrescaler(LL_RCC_SMPS_DIV_1);
LL_RCC_SetRFWKPClockSource(LL_RCC_RFWKP_CLKSOURCE_LSE); LL_RCC_SetRFWKPClockSource(LL_RCC_RFWKP_CLKSOURCE_LSE);
@@ -207,13 +208,17 @@ void furi_hal_clock_switch_to_hsi() {
while(!LL_RCC_HSI_IsReady()) while(!LL_RCC_HSI_IsReady())
; ;
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI);
LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSI); LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSI);
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI);
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI) while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI)
; ;
LL_FLASH_SetLatency(LL_FLASH_LATENCY_1); LL_C2_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
LL_FLASH_SetLatency(LL_FLASH_LATENCY_0);
while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_0)
;
} }
void furi_hal_clock_switch_to_pll() { void furi_hal_clock_switch_to_pll() {
@@ -228,7 +233,11 @@ void furi_hal_clock_switch_to_pll() {
while(!LL_RCC_PLLSAI1_IsReady()) while(!LL_RCC_PLLSAI1_IsReady())
; ;
LL_C2_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_2);
LL_FLASH_SetLatency(LL_FLASH_LATENCY_3); LL_FLASH_SetLatency(LL_FLASH_LATENCY_3);
while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_3)
;
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE); LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE);

View File

@@ -1,11 +1,12 @@
#include <furi_hal_cortex.h> #include <furi_hal_cortex.h>
#include <furi.h>
#include <stm32wbxx.h> #include <stm32wbxx.h>
#define FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND (SystemCoreClock / 1000000) #define FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND (SystemCoreClock / 1000000)
void furi_hal_cortex_init_early() { void furi_hal_cortex_init_early() {
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; CoreDebug->DEMCR |= (CoreDebug_DEMCR_TRCENA_Msk | CoreDebug_DEMCR_MON_EN_Msk);
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
DWT->CYCCNT = 0U; DWT->CYCCNT = 0U;
@@ -38,4 +39,71 @@ bool furi_hal_cortex_timer_is_expired(FuriHalCortexTimer cortex_timer) {
void furi_hal_cortex_timer_wait(FuriHalCortexTimer cortex_timer) { void furi_hal_cortex_timer_wait(FuriHalCortexTimer cortex_timer) {
while(!furi_hal_cortex_timer_is_expired(cortex_timer)) while(!furi_hal_cortex_timer_is_expired(cortex_timer))
; ;
} }
// Duck ST
#undef COMP0
#undef COMP1
#undef COMP2
#undef COMP3
void furi_hal_cortex_comp_enable(
FuriHalCortexComp comp,
FuriHalCortexCompFunction function,
uint32_t value,
uint32_t mask,
FuriHalCortexCompSize size) {
uint32_t function_reg = (uint32_t)function | ((uint32_t)size << 10);
switch(comp) {
case FuriHalCortexComp0:
(DWT->COMP0) = value;
(DWT->MASK0) = mask;
(DWT->FUNCTION0) = function_reg;
break;
case FuriHalCortexComp1:
(DWT->COMP1) = value;
(DWT->MASK1) = mask;
(DWT->FUNCTION1) = function_reg;
break;
case FuriHalCortexComp2:
(DWT->COMP2) = value;
(DWT->MASK2) = mask;
(DWT->FUNCTION2) = function_reg;
break;
case FuriHalCortexComp3:
(DWT->COMP3) = value;
(DWT->MASK3) = mask;
(DWT->FUNCTION3) = function_reg;
break;
default:
furi_crash("Invalid parameter");
}
}
void furi_hal_cortex_comp_reset(FuriHalCortexComp comp) {
switch(comp) {
case FuriHalCortexComp0:
(DWT->COMP0) = 0;
(DWT->MASK0) = 0;
(DWT->FUNCTION0) = 0;
break;
case FuriHalCortexComp1:
(DWT->COMP1) = 0;
(DWT->MASK1) = 0;
(DWT->FUNCTION1) = 0;
break;
case FuriHalCortexComp2:
(DWT->COMP2) = 0;
(DWT->MASK2) = 0;
(DWT->FUNCTION2) = 0;
break;
case FuriHalCortexComp3:
(DWT->COMP3) = 0;
(DWT->MASK3) = 0;
(DWT->FUNCTION3) = 0;
break;
default:
furi_crash("Invalid parameter");
}
}

View File

@@ -1,6 +1,7 @@
#include <furi_hal_flash.h> #include <furi_hal_flash.h>
#include <furi_hal_bt.h> #include <furi_hal_bt.h>
#include <furi_hal_power.h> #include <furi_hal_power.h>
#include <furi_hal_cortex.h>
#include <furi.h> #include <furi.h>
#include <ble/ble.h> #include <ble/ble.h>
#include <interface/patterns/ble_thread/shci/shci.h> #include <interface/patterns/ble_thread/shci/shci.h>
@@ -26,6 +27,16 @@
#define FURI_HAL_FLASH_OPT_KEY2 0x4C5D6E7F #define FURI_HAL_FLASH_OPT_KEY2 0x4C5D6E7F
#define FURI_HAL_FLASH_OB_TOTAL_WORDS (0x80 / (sizeof(uint32_t) * 2)) #define FURI_HAL_FLASH_OB_TOTAL_WORDS (0x80 / (sizeof(uint32_t) * 2))
/* lib/STM32CubeWB/Projects/P-NUCLEO-WB55.Nucleo/Applications/BLE/BLE_RfWithFlash/Core/Src/flash_driver.c
* ProcessSingleFlashOperation, quote:
> In most BLE application, the flash should not be blocked by the CPU2 longer than FLASH_TIMEOUT_VALUE (1000ms)
> However, it could be that for some marginal application, this time is longer.
> ... there is no other way than waiting the operation to be completed.
> If for any reason this test is never passed, this means there is a failure in the system and there is no other
> way to recover than applying a device reset.
*/
#define FURI_HAL_FLASH_C2_LOCK_TIMEOUT_MS 3000u /* 3 seconds */
#define IS_ADDR_ALIGNED_64BITS(__VALUE__) (((__VALUE__)&0x7U) == (0x00UL)) #define IS_ADDR_ALIGNED_64BITS(__VALUE__) (((__VALUE__)&0x7U) == (0x00UL))
#define IS_FLASH_PROGRAM_ADDRESS(__VALUE__) \ #define IS_FLASH_PROGRAM_ADDRESS(__VALUE__) \
(((__VALUE__) >= FLASH_BASE) && ((__VALUE__) <= (FLASH_BASE + FLASH_SIZE - 8UL)) && \ (((__VALUE__) >= FLASH_BASE) && ((__VALUE__) <= (FLASH_BASE + FLASH_SIZE - 8UL)) && \
@@ -131,9 +142,11 @@ static void furi_hal_flash_begin_with_core2(bool erase_flag) {
for(volatile uint32_t i = 0; i < 35; i++) for(volatile uint32_t i = 0; i < 35; i++)
; ;
FuriHalCortexTimer timer = furi_hal_cortex_timer_get(FURI_HAL_FLASH_C2_LOCK_TIMEOUT_MS * 1000);
while(true) { while(true) {
/* Wait till flash controller become usable */ /* Wait till flash controller become usable */
while(LL_FLASH_IsActiveFlag_OperationSuspended()) { while(LL_FLASH_IsActiveFlag_OperationSuspended()) {
furi_check(!furi_hal_cortex_timer_is_expired(timer));
furi_thread_yield(); furi_thread_yield();
}; };
@@ -143,6 +156,7 @@ static void furi_hal_flash_begin_with_core2(bool erase_flag) {
/* Actually we already have mutex for it, but specification is specification */ /* Actually we already have mutex for it, but specification is specification */
if(LL_HSEM_IsSemaphoreLocked(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID)) { if(LL_HSEM_IsSemaphoreLocked(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID)) {
taskEXIT_CRITICAL(); taskEXIT_CRITICAL();
furi_check(!furi_hal_cortex_timer_is_expired(timer));
furi_thread_yield(); furi_thread_yield();
continue; continue;
} }
@@ -150,6 +164,7 @@ static void furi_hal_flash_begin_with_core2(bool erase_flag) {
/* Take sempahopre and prevent core2 from anything funky */ /* Take sempahopre and prevent core2 from anything funky */
if(LL_HSEM_1StepLock(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID) != 0) { if(LL_HSEM_1StepLock(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID) != 0) {
taskEXIT_CRITICAL(); taskEXIT_CRITICAL();
furi_check(!furi_hal_cortex_timer_is_expired(timer));
furi_thread_yield(); furi_thread_yield();
continue; continue;
} }
@@ -231,17 +246,13 @@ static void furi_hal_flush_cache(void) {
bool furi_hal_flash_wait_last_operation(uint32_t timeout) { bool furi_hal_flash_wait_last_operation(uint32_t timeout) {
uint32_t error = 0; uint32_t error = 0;
uint32_t countdown = 0;
/* Wait for the FLASH operation to complete by polling on BUSY flag to be reset. /* Wait for the FLASH operation to complete by polling on BUSY flag to be reset.
Even if the FLASH operation fails, the BUSY flag will be reset and an error Even if the FLASH operation fails, the BUSY flag will be reset and an error
flag will be set */ flag will be set */
countdown = timeout; FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout * 1000);
while(READ_BIT(FLASH->SR, FLASH_SR_BSY)) { while(READ_BIT(FLASH->SR, FLASH_SR_BSY)) {
if(LL_SYSTICK_IsActiveCounterFlag()) { if(furi_hal_cortex_timer_is_expired(timer)) {
countdown--;
}
if(countdown == 0) {
return false; return false;
} }
} }
@@ -264,12 +275,9 @@ bool furi_hal_flash_wait_last_operation(uint32_t timeout) {
CLEAR_BIT(FLASH->SR, error); CLEAR_BIT(FLASH->SR, error);
/* Wait for control register to be written */ /* Wait for control register to be written */
countdown = timeout; timer = furi_hal_cortex_timer_get(timeout * 1000);
while(READ_BIT(FLASH->SR, FLASH_SR_CFGBSY)) { while(READ_BIT(FLASH->SR, FLASH_SR_CFGBSY)) {
if(LL_SYSTICK_IsActiveCounterFlag()) { if(furi_hal_cortex_timer_is_expired(timer)) {
countdown--;
}
if(countdown == 0) {
return false; return false;
} }
} }

View File

@@ -8,6 +8,11 @@
#include <furi.h> #include <furi.h>
#include <protobuf_version.h> #include <protobuf_version.h>
FURI_WEAK void furi_hal_info_get_api_version(uint16_t* major, uint16_t* minor) {
*major = 0;
*minor = 0;
}
void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) { void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) {
FuriString* key = furi_string_alloc(); FuriString* key = furi_string_alloc();
FuriString* value = furi_string_alloc(); FuriString* value = furi_string_alloc();
@@ -18,10 +23,10 @@ void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) {
// Device Info version // Device Info version
if(sep == '.') { if(sep == '.') {
property_value_out(&property_context, NULL, 2, "format", "major", "3"); property_value_out(&property_context, NULL, 2, "format", "major", "3");
property_value_out(&property_context, NULL, 2, "format", "minor", "0"); property_value_out(&property_context, NULL, 2, "format", "minor", "1");
} else { } else {
property_value_out(&property_context, NULL, 3, "device", "info", "major", "2"); property_value_out(&property_context, NULL, 3, "device", "info", "major", "2");
property_value_out(&property_context, NULL, 3, "device", "info", "minor", "0"); property_value_out(&property_context, NULL, 3, "device", "info", "minor", "1");
} }
// Model name // Model name
@@ -161,6 +166,13 @@ void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) {
version_get_builddate(firmware_version)); version_get_builddate(firmware_version));
property_value_out( property_value_out(
&property_context, "%d", 2, "firmware", "target", version_get_target(firmware_version)); &property_context, "%d", 2, "firmware", "target", version_get_target(firmware_version));
uint16_t api_version_major, api_version_minor;
furi_hal_info_get_api_version(&api_version_major, &api_version_minor);
property_value_out(
&property_context, "%d", 3, "firmware", "api", "major", api_version_major);
property_value_out(
&property_context, "%d", 3, "firmware", "api", "minor", api_version_minor);
} }
if(furi_hal_bt_is_alive()) { if(furi_hal_bt_is_alive()) {

View File

@@ -29,10 +29,6 @@
#define FURI_HAL_POWER_DEBUG_STOP_GPIO (&gpio_ext_pc3) #define FURI_HAL_POWER_DEBUG_STOP_GPIO (&gpio_ext_pc3)
#endif #endif
#ifndef FURI_HAL_POWER_DEBUG_ABNORMAL_GPIO
#define FURI_HAL_POWER_DEBUG_ABNORMAL_GPIO (&gpio_ext_pb3)
#endif
#ifndef FURI_HAL_POWER_STOP_MODE #ifndef FURI_HAL_POWER_STOP_MODE
#define FURI_HAL_POWER_STOP_MODE (LL_PWR_MODE_STOP2) #define FURI_HAL_POWER_STOP_MODE (LL_PWR_MODE_STOP2)
#endif #endif
@@ -50,56 +46,19 @@ static volatile FuriHalPower furi_hal_power = {
.suppress_charge = 0, .suppress_charge = 0,
}; };
const ParamCEDV cedv = { #include <furi_hal_power_calibration.h>
.cedv_conf.gauge_conf =
{
.CCT = 1,
.CSYNC = 0,
.EDV_CMP = 0,
.SC = 1,
.FIXED_EDV0 = 1,
.FCC_LIM = 1,
.FC_FOR_VDQ = 1,
.IGNORE_SD = 1,
.SME0 = 0,
},
.full_charge_cap = 2101,
.design_cap = 2101,
.EDV0 = 3300,
.EDV1 = 3321,
.EDV2 = 3355,
.EMF = 3679,
.C0 = 430,
.C1 = 0,
.R1 = 408,
.R0 = 334,
.T0 = 4626,
.TC = 11,
.DOD0 = 4044,
.DOD10 = 3905,
.DOD20 = 3807,
.DOD30 = 3718,
.DOD40 = 3642,
.DOD50 = 3585,
.DOD60 = 3546,
.DOD70 = 3514,
.DOD80 = 3477,
.DOD90 = 3411,
.DOD100 = 3299,
};
void furi_hal_power_init() { void furi_hal_power_init() {
#ifdef FURI_HAL_POWER_DEBUG #ifdef FURI_HAL_POWER_DEBUG
furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_WFI_GPIO, GpioModeOutputPushPull); furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_WFI_GPIO, GpioModeOutputPushPull);
furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_STOP_GPIO, GpioModeOutputPushPull); furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_STOP_GPIO, GpioModeOutputPushPull);
furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_ABNORMAL_GPIO, GpioModeOutputPushPull);
furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 0); furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 0);
furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 0); furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 0);
furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_ABNORMAL_GPIO, 0);
#endif #endif
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1); LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
LL_PWR_SMPS_SetMode(LL_PWR_SMPS_STEP_DOWN); LL_PWR_SMPS_SetMode(LL_PWR_SMPS_STEP_DOWN);
LL_PWR_SetPowerMode(FURI_HAL_POWER_STOP_MODE); LL_PWR_SetPowerMode(FURI_HAL_POWER_STOP_MODE);
LL_C2_PWR_SetPowerMode(FURI_HAL_POWER_STOP_MODE); LL_C2_PWR_SetPowerMode(FURI_HAL_POWER_STOP_MODE);
@@ -158,9 +117,7 @@ bool furi_hal_power_sleep_available() {
static inline bool furi_hal_power_deep_sleep_available() { static inline bool furi_hal_power_deep_sleep_available() {
return furi_hal_bt_is_alive() && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagLegacySleep) && return furi_hal_bt_is_alive() && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagLegacySleep) &&
!furi_hal_debug_is_gdb_session_active() && !LL_PWR_IsActiveFlag_CRPE() && !furi_hal_debug_is_gdb_session_active();
!LL_PWR_IsActiveFlag_CRP() && !LL_PWR_IsActiveFlag_BLEA() &&
!LL_PWR_IsActiveFlag_BLEWU();
} }
static inline void furi_hal_power_light_sleep() { static inline void furi_hal_power_light_sleep() {
@@ -211,16 +168,7 @@ static inline void furi_hal_power_deep_sleep() {
__force_stores(); __force_stores();
#endif #endif
bool should_abort_sleep = LL_PWR_IsActiveFlag_CRPE() || LL_PWR_IsActiveFlag_CRP() || __WFI();
LL_PWR_IsActiveFlag_BLEA() || LL_PWR_IsActiveFlag_BLEWU();
if(should_abort_sleep) {
#ifdef FURI_HAL_POWER_DEBUG
furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_ABNORMAL_GPIO, 1);
#endif
} else {
__WFI();
}
LL_LPM_EnableSleep(); LL_LPM_EnableSleep();

View File

@@ -0,0 +1,37 @@
const ParamCEDV cedv = {
.cedv_conf.gauge_conf =
{
.CCT = 1,
.CSYNC = 0,
.EDV_CMP = 0,
.SC = 1,
.FIXED_EDV0 = 1,
.FCC_LIM = 1,
.FC_FOR_VDQ = 1,
.IGNORE_SD = 1,
.SME0 = 0,
},
.full_charge_cap = 2101,
.design_cap = 2101,
.EDV0 = 3300,
.EDV1 = 3321,
.EDV2 = 3355,
.EMF = 3679,
.C0 = 430,
.C1 = 0,
.R1 = 408,
.R0 = 334,
.T0 = 4626,
.TC = 11,
.DOD0 = 4044,
.DOD10 = 3905,
.DOD20 = 3807,
.DOD30 = 3718,
.DOD40 = 3642,
.DOD50 = 3585,
.DOD60 = 3546,
.DOD70 = 3514,
.DOD80 = 3477,
.DOD90 = 3411,
.DOD100 = 3299,
};

View File

@@ -56,6 +56,53 @@ bool furi_hal_cortex_timer_is_expired(FuriHalCortexTimer cortex_timer);
*/ */
void furi_hal_cortex_timer_wait(FuriHalCortexTimer cortex_timer); void furi_hal_cortex_timer_wait(FuriHalCortexTimer cortex_timer);
typedef enum {
FuriHalCortexComp0,
FuriHalCortexComp1,
FuriHalCortexComp2,
FuriHalCortexComp3,
} FuriHalCortexComp;
typedef enum {
FuriHalCortexCompSizeWord = 0b10,
FuriHalCortexCompSizeHalfWord = 0b01,
FuriHalCortexCompSizeByte = 0b00,
} FuriHalCortexCompSize;
typedef enum {
FuriHalCortexCompFunctionPC = 0b100,
FuriHalCortexCompFunctionRead = 0b101,
FuriHalCortexCompFunctionWrite = 0b110,
FuriHalCortexCompFunctionReadWrite = 0b110,
} FuriHalCortexCompFunction;
/** Enable DWT comparator
*
* Allows to programmatically set instruction/data breakpoints.
*
* More details on how it works can be found in armv7m official documentation:
* https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/The-Data-Watchpoint-and-Trace-unit/The-DWT-comparators
* https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/The-Data-Watchpoint-and-Trace-unit/Comparator-Function-registers--DWT-FUNCTIONn
*
* @param[in] comp The Comparator
* @param[in] function The Comparator Function to use
* @param[in] value The value
* @param[in] mask The mask
* @param[in] size The size
*/
void furi_hal_cortex_comp_enable(
FuriHalCortexComp comp,
FuriHalCortexCompFunction function,
uint32_t value,
uint32_t mask,
FuriHalCortexCompSize size);
/** Reset DWT comparator
*
* @param[in] comp The Comparator
*/
void furi_hal_cortex_comp_reset(FuriHalCortexComp comp);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -14,6 +14,8 @@
extern "C" { extern "C" {
#endif #endif
void furi_hal_info_get_api_version(uint16_t* major, uint16_t* minor);
/** Get device information /** Get device information
* *
* @param[in] callback callback to provide with new data * @param[in] callback callback to provide with new data

View File

@@ -14,7 +14,7 @@
#include <stdlib.h> #include <stdlib.h>
PLACE_IN_SECTION("MB_MEM2") const char* __furi_check_message = NULL; PLACE_IN_SECTION("MB_MEM2") const char* __furi_check_message = NULL;
PLACE_IN_SECTION("MB_MEM2") uint32_t __furi_check_registers[12] = {0}; PLACE_IN_SECTION("MB_MEM2") uint32_t __furi_check_registers[13] = {0};
/** Load r12 value to __furi_check_message and store registers to __furi_check_registers */ /** Load r12 value to __furi_check_message and store registers to __furi_check_registers */
#define GET_MESSAGE_AND_STORE_REGISTERS() \ #define GET_MESSAGE_AND_STORE_REGISTERS() \
@@ -22,6 +22,7 @@ PLACE_IN_SECTION("MB_MEM2") uint32_t __furi_check_registers[12] = {0};
"str r12, [r11] \n" \ "str r12, [r11] \n" \
"ldr r12, =__furi_check_registers \n" \ "ldr r12, =__furi_check_registers \n" \
"stm r12, {r0-r11} \n" \ "stm r12, {r0-r11} \n" \
"str lr, [r12, #48] \n" \
: \ : \
: \ : \
: "memory"); : "memory");
@@ -62,6 +63,25 @@ static void __furi_put_uint32_as_text(uint32_t data) {
furi_hal_console_puts(tmp_str); furi_hal_console_puts(tmp_str);
} }
static void __furi_put_uint32_as_hex(uint32_t data) {
char tmp_str[] = "0xFFFFFFFF";
itoa(data, tmp_str, 16);
furi_hal_console_puts(tmp_str);
}
static void __furi_print_register_info() {
// Print registers
for(uint8_t i = 0; i < 12; i++) {
furi_hal_console_puts("\r\n\tr");
__furi_put_uint32_as_text(i);
furi_hal_console_puts(" : ");
__furi_put_uint32_as_hex(__furi_check_registers[i]);
}
furi_hal_console_puts("\r\n\tlr : ");
__furi_put_uint32_as_hex(__furi_check_registers[12]);
}
static void __furi_print_stack_info() { static void __furi_print_stack_info() {
furi_hal_console_puts("\r\n\tstack watermark: "); furi_hal_console_puts("\r\n\tstack watermark: ");
__furi_put_uint32_as_text(uxTaskGetStackHighWaterMark(NULL) * 4); __furi_put_uint32_as_text(uxTaskGetStackHighWaterMark(NULL) * 4);
@@ -101,32 +121,41 @@ FURI_NORETURN void __furi_crash() {
if(__furi_check_message == NULL) { if(__furi_check_message == NULL) {
__furi_check_message = "Fatal Error"; __furi_check_message = "Fatal Error";
} else if(__furi_check_message == (void*)__FURI_ASSERT_MESSAGE_FLAG) {
__furi_check_message = "furi_assert failed";
} else if(__furi_check_message == (void*)__FURI_CHECK_MESSAGE_FLAG) {
__furi_check_message = "furi_check failed";
} }
furi_hal_console_puts("\r\n\033[0;31m[CRASH]"); furi_hal_console_puts("\r\n\033[0;31m[CRASH]");
__furi_print_name(isr); __furi_print_name(isr);
furi_hal_console_puts(__furi_check_message); furi_hal_console_puts(__furi_check_message);
__furi_print_register_info();
if(!isr) { if(!isr) {
__furi_print_stack_info(); __furi_print_stack_info();
} }
__furi_print_heap_info(); __furi_print_heap_info();
#ifndef FURI_DEBUG
// Check if debug enabled by DAP // Check if debug enabled by DAP
// https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en // https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en
bool debug = CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk; bool debug = CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk;
if(debug) { if(debug) {
#endif
furi_hal_console_puts("\r\nSystem halted. Connect debugger for more info\r\n"); furi_hal_console_puts("\r\nSystem halted. Connect debugger for more info\r\n");
furi_hal_console_puts("\033[0m\r\n"); furi_hal_console_puts("\033[0m\r\n");
furi_hal_debug_enable(); furi_hal_debug_enable();
RESTORE_REGISTERS_AND_HALT_MCU(true); RESTORE_REGISTERS_AND_HALT_MCU(true);
#ifndef FURI_DEBUG
} else { } else {
furi_hal_rtc_set_fault_data((uint32_t)__furi_check_message); furi_hal_rtc_set_fault_data((uint32_t)__furi_check_message);
furi_hal_console_puts("\r\nRebooting system.\r\n"); furi_hal_console_puts("\r\nRebooting system.\r\n");
furi_hal_console_puts("\033[0m\r\n"); furi_hal_console_puts("\033[0m\r\n");
furi_hal_power_reset(); furi_hal_power_reset();
} }
#endif
__builtin_unreachable(); __builtin_unreachable();
} }

View File

@@ -21,6 +21,10 @@ extern "C" {
#define FURI_NORETURN noreturn #define FURI_NORETURN noreturn
#endif #endif
// Flags instead of pointers will save ~4 bytes on furi_assert and furi_check calls.
#define __FURI_ASSERT_MESSAGE_FLAG (0x01)
#define __FURI_CHECK_MESSAGE_FLAG (0x02)
/** Crash system */ /** Crash system */
FURI_NORETURN void __furi_crash(); FURI_NORETURN void __furi_crash();
@@ -44,20 +48,20 @@ FURI_NORETURN void __furi_halt();
} while(0) } while(0)
/** Check condition and crash if check failed */ /** Check condition and crash if check failed */
#define furi_check(__e) \ #define furi_check(__e) \
do { \ do { \
if(!(__e)) { \ if(!(__e)) { \
furi_crash("furi_check failed\r\n"); \ furi_crash(__FURI_CHECK_MESSAGE_FLAG); \
} \ } \
} while(0) } while(0)
/** Only in debug build: Assert condition and crash if assert failed */ /** Only in debug build: Assert condition and crash if assert failed */
#ifdef FURI_DEBUG #ifdef FURI_DEBUG
#define furi_assert(__e) \ #define furi_assert(__e) \
do { \ do { \
if(!(__e)) { \ if(!(__e)) { \
furi_crash("furi_assert failed\r\n"); \ furi_crash(__FURI_ASSERT_MESSAGE_FLAG); \
} \ } \
} while(0) } while(0)
#else #else
#define furi_assert(__e) \ #define furi_assert(__e) \

View File

@@ -15,6 +15,10 @@ extern "C" {
#define FURI_WARN_UNUSED __attribute__((warn_unused_result)) #define FURI_WARN_UNUSED __attribute__((warn_unused_result))
#endif #endif
#ifndef FURI_WEAK
#define FURI_WEAK __attribute__((weak))
#endif
#ifndef FURI_IS_IRQ_MASKED #ifndef FURI_IS_IRQ_MASKED
#define FURI_IS_IRQ_MASKED() (__get_PRIMASK() != 0U) #define FURI_IS_IRQ_MASKED() (__get_PRIMASK() != 0U)
#endif #endif

View File

@@ -164,10 +164,13 @@ FuriThread* furi_thread_alloc_ex(
void furi_thread_free(FuriThread* thread) { void furi_thread_free(FuriThread* thread) {
furi_assert(thread); furi_assert(thread);
furi_assert(thread->state == FuriThreadStateStopped);
if(thread->name) free((void*)thread->name); // Ensure that use join before free
if(thread->appid) free((void*)thread->appid); furi_assert(thread->state == FuriThreadStateStopped);
furi_assert(thread->task_handle == NULL);
if(thread->name) free(thread->name);
if(thread->appid) free(thread->appid);
furi_string_free(thread->output.buffer); furi_string_free(thread->output.buffer);
free(thread); free(thread);
@@ -176,14 +179,14 @@ void furi_thread_free(FuriThread* thread) {
void furi_thread_set_name(FuriThread* thread, const char* name) { void furi_thread_set_name(FuriThread* thread, const char* name) {
furi_assert(thread); furi_assert(thread);
furi_assert(thread->state == FuriThreadStateStopped); furi_assert(thread->state == FuriThreadStateStopped);
if(thread->name) free((void*)thread->name); if(thread->name) free(thread->name);
thread->name = name ? strdup(name) : NULL; thread->name = name ? strdup(name) : NULL;
} }
void furi_thread_set_appid(FuriThread* thread, const char* appid) { void furi_thread_set_appid(FuriThread* thread, const char* appid) {
furi_assert(thread); furi_assert(thread);
furi_assert(thread->state == FuriThreadStateStopped); furi_assert(thread->state == FuriThreadStateStopped);
if(thread->appid) free((void*)thread->appid); if(thread->appid) free(thread->appid);
thread->appid = appid ? strdup(appid) : NULL; thread->appid = appid ? strdup(appid) : NULL;
} }
@@ -276,7 +279,7 @@ void furi_thread_cleanup_tcb_event(TaskHandle_t task) {
if(thread) { if(thread) {
// clear thread local storage // clear thread local storage
vTaskSetThreadLocalStoragePointer(task, 0, NULL); vTaskSetThreadLocalStoragePointer(task, 0, NULL);
furi_assert(thread->task_handle == task);
thread->task_handle = NULL; thread->task_handle = NULL;
} }
} }
@@ -332,7 +335,6 @@ FuriThreadId furi_thread_get_current_id() {
FuriThread* furi_thread_get_current() { FuriThread* furi_thread_get_current() {
FuriThread* thread = pvTaskGetThreadLocalStoragePointer(NULL, 0); FuriThread* thread = pvTaskGetThreadLocalStoragePointer(NULL, 0);
furi_assert(thread != NULL);
return thread; return thread;
} }
@@ -579,24 +581,22 @@ static int32_t __furi_thread_stdout_flush(FuriThread* thread) {
return 0; return 0;
} }
bool furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback) { void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback) {
FuriThread* thread = furi_thread_get_current(); FuriThread* thread = furi_thread_get_current();
furi_assert(thread);
__furi_thread_stdout_flush(thread); __furi_thread_stdout_flush(thread);
thread->output.write_callback = callback; thread->output.write_callback = callback;
return true;
} }
FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback() { FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback() {
FuriThread* thread = furi_thread_get_current(); FuriThread* thread = furi_thread_get_current();
furi_assert(thread);
return thread->output.write_callback; return thread->output.write_callback;
} }
size_t furi_thread_stdout_write(const char* data, size_t size) { size_t furi_thread_stdout_write(const char* data, size_t size) {
FuriThread* thread = furi_thread_get_current(); FuriThread* thread = furi_thread_get_current();
furi_assert(thread);
if(size == 0 || data == NULL) { if(size == 0 || data == NULL) {
return __furi_thread_stdout_flush(thread); return __furi_thread_stdout_flush(thread);
} else { } else {
@@ -619,7 +619,9 @@ size_t furi_thread_stdout_write(const char* data, size_t size) {
} }
int32_t furi_thread_stdout_flush() { int32_t furi_thread_stdout_flush() {
return __furi_thread_stdout_flush(furi_thread_get_current()); FuriThread* thread = furi_thread_get_current();
furi_assert(thread);
return __furi_thread_stdout_flush(thread);
} }
void furi_thread_suspend(FuriThreadId thread_id) { void furi_thread_suspend(FuriThreadId thread_id) {

View File

@@ -233,7 +233,7 @@ FuriThreadId furi_thread_get_current_id();
/** Get FuriThread instance for current thread /** Get FuriThread instance for current thread
* *
* @return FuriThread* * @return pointer to FuriThread or NULL if this thread doesn't belongs to Furi
*/ */
FuriThread* furi_thread_get_current(); FuriThread* furi_thread_get_current();
@@ -288,12 +288,10 @@ uint32_t furi_thread_get_stack_space(FuriThreadId thread_id);
FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(); FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback();
/** Set STDOUT callback for thread /** Set STDOUT callback for thread
* *
* @param callback callback or NULL to clear * @param callback callback or NULL to clear
*
* @return true on success, otherwise fail
*/ */
bool furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback); void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback);
/** Write data to buffered STDOUT /** Write data to buffered STDOUT
* *

View File

@@ -124,3 +124,13 @@ uint32_t furi_timer_is_running(FuriTimer* instance) {
/* Return 0: not running, 1: running */ /* Return 0: not running, 1: running */
return (uint32_t)xTimerIsTimerActive(hTimer); return (uint32_t)xTimerIsTimerActive(hTimer);
} }
void furi_timer_pending_callback(FuriTimerPendigCallback callback, void* context, uint32_t arg) {
BaseType_t ret = pdFAIL;
if(furi_kernel_is_irq_or_masked()) {
ret = xTimerPendFunctionCallFromISR(callback, context, arg, NULL);
} else {
ret = xTimerPendFunctionCall(callback, context, arg, FuriWaitForever);
}
furi_check(ret == pdPASS);
}

View File

@@ -56,6 +56,10 @@ FuriStatus furi_timer_stop(FuriTimer* instance);
*/ */
uint32_t furi_timer_is_running(FuriTimer* instance); uint32_t furi_timer_is_running(FuriTimer* instance);
typedef void (*FuriTimerPendigCallback)(void* context, uint32_t arg);
void furi_timer_pending_callback(FuriTimerPendigCallback callback, void* context, uint32_t arg);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -7,6 +7,7 @@ env.Append(
SDK_HEADERS=[ SDK_HEADERS=[
File("flipper_format.h"), File("flipper_format.h"),
File("flipper_format_i.h"), File("flipper_format_i.h"),
File("flipper_format_stream.h"),
], ],
) )

View File

@@ -7,9 +7,6 @@ env.Append(
"#/lib/FreeRTOS-Kernel/portable/GCC/ARM_CM4F", "#/lib/FreeRTOS-Kernel/portable/GCC/ARM_CM4F",
"#/lib/FreeRTOS-glue", "#/lib/FreeRTOS-glue",
], ],
CPPDEFINES=[
"HAVE_FREERTOS",
],
) )

Some files were not shown because too many files have changed in this diff Show More