[FL-2627] Flipper applications: SDK, build and debug system (#1387)

* Added support for running applications from SD card (FAPs - Flipper Application Packages)
* Added plugin_dist target for fbt to build FAPs
* All apps of type FlipperAppType.EXTERNAL and FlipperAppType.PLUGIN are built as FAPs by default
* Updated VSCode configuration for new fbt features - re-deploy stock configuration to use them
* Added debugging support for FAPs with fbt debug & VSCode
* Added public firmware API with automated versioning

Co-authored-by: hedger <hedger@users.noreply.github.com>
Co-authored-by: SG <who.just.the.doctor@gmail.com>
Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
SG
2022-09-15 02:11:38 +10:00
committed by Aleksandr Kutuzov
parent 0f6f9ad52e
commit b9a766d909
895 changed files with 8862 additions and 1465 deletions

View File

@@ -0,0 +1,429 @@
#include "receiver.h"
#include "../subghz_i.h"
#include <math.h>
#include <input/input.h>
#include <gui/elements.h>
#include <assets_icons.h>
#include <m-string.h>
#include <m-array.h>
#define FRAME_HEIGHT 12
#define MAX_LEN_PX 100
#define MENU_ITEMS 4u
#define UNLOCK_CNT 3
typedef struct {
string_t item_str;
uint8_t type;
} SubGhzReceiverMenuItem;
ARRAY_DEF(SubGhzReceiverMenuItemArray, SubGhzReceiverMenuItem, M_POD_OPLIST)
#define M_OPL_SubGhzReceiverMenuItemArray_t() \
ARRAY_OPLIST(SubGhzReceiverMenuItemArray, M_POD_OPLIST)
struct SubGhzReceiverHistory {
SubGhzReceiverMenuItemArray_t data;
};
typedef struct SubGhzReceiverHistory SubGhzReceiverHistory;
static const Icon* ReceiverItemIcons[] = {
[SubGhzProtocolTypeUnknown] = &I_Quest_7x8,
[SubGhzProtocolTypeStatic] = &I_Unlock_7x8,
[SubGhzProtocolTypeDynamic] = &I_Lock_7x8,
};
typedef enum {
SubGhzViewReceiverBarShowDefault,
SubGhzViewReceiverBarShowLock,
SubGhzViewReceiverBarShowToUnlockPress,
SubGhzViewReceiverBarShowUnlock,
} SubGhzViewReceiverBarShow;
struct SubGhzViewReceiver {
SubGhzLock lock;
uint8_t lock_count;
FuriTimer* timer;
View* view;
SubGhzViewReceiverCallback callback;
void* context;
};
typedef struct {
string_t frequency_str;
string_t preset_str;
string_t history_stat_str;
SubGhzReceiverHistory* history;
uint16_t idx;
uint16_t list_offset;
uint16_t history_item;
SubGhzViewReceiverBarShow bar_show;
} SubGhzViewReceiverModel;
void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock lock) {
furi_assert(subghz_receiver);
subghz_receiver->lock_count = 0;
if(lock == SubGhzLockOn) {
subghz_receiver->lock = lock;
with_view_model(
subghz_receiver->view, (SubGhzViewReceiverModel * model) {
model->bar_show = SubGhzViewReceiverBarShowLock;
return true;
});
furi_timer_start(subghz_receiver->timer, pdMS_TO_TICKS(1000));
} else {
with_view_model(
subghz_receiver->view, (SubGhzViewReceiverModel * model) {
model->bar_show = SubGhzViewReceiverBarShowDefault;
return true;
});
}
}
void subghz_view_receiver_set_callback(
SubGhzViewReceiver* subghz_receiver,
SubGhzViewReceiverCallback callback,
void* context) {
furi_assert(subghz_receiver);
furi_assert(callback);
subghz_receiver->callback = callback;
subghz_receiver->context = context;
}
static void subghz_view_receiver_update_offset(SubGhzViewReceiver* subghz_receiver) {
furi_assert(subghz_receiver);
with_view_model(
subghz_receiver->view, (SubGhzViewReceiverModel * model) {
size_t history_item = model->history_item;
uint16_t bounds = history_item > 3 ? 2 : history_item;
if(history_item > 3 && model->idx >= (int16_t)(history_item - 1)) {
model->list_offset = model->idx - 3;
} else if(model->list_offset < model->idx - bounds) {
model->list_offset =
CLAMP(model->list_offset + 1, (int16_t)(history_item - bounds), 0);
} else if(model->list_offset > model->idx - bounds) {
model->list_offset = CLAMP(model->idx - 1, (int16_t)(history_item - bounds), 0);
}
return true;
});
}
void subghz_view_receiver_add_item_to_menu(
SubGhzViewReceiver* subghz_receiver,
const char* name,
uint8_t type) {
furi_assert(subghz_receiver);
with_view_model(
subghz_receiver->view, (SubGhzViewReceiverModel * model) {
SubGhzReceiverMenuItem* item_menu =
SubGhzReceiverMenuItemArray_push_raw(model->history->data);
string_init_set_str(item_menu->item_str, name);
item_menu->type = type;
if((model->idx == model->history_item - 1)) {
model->history_item++;
model->idx++;
} else {
model->history_item++;
}
return true;
});
subghz_view_receiver_update_offset(subghz_receiver);
}
void subghz_view_receiver_add_data_statusbar(
SubGhzViewReceiver* subghz_receiver,
const char* frequency_str,
const char* preset_str,
const char* history_stat_str) {
furi_assert(subghz_receiver);
with_view_model(
subghz_receiver->view, (SubGhzViewReceiverModel * model) {
string_set_str(model->frequency_str, frequency_str);
string_set_str(model->preset_str, preset_str);
string_set_str(model->history_stat_str, history_stat_str);
return true;
});
}
static void subghz_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) {
canvas_set_color(canvas, ColorBlack);
canvas_draw_box(canvas, 0, 0 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT);
canvas_set_color(canvas, ColorWhite);
canvas_draw_dot(canvas, 0, 0 + idx * FRAME_HEIGHT);
canvas_draw_dot(canvas, 1, 0 + idx * FRAME_HEIGHT);
canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 1);
canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 11);
canvas_draw_dot(canvas, scrollbar ? 121 : 126, 0 + idx * FRAME_HEIGHT);
canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11);
}
void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontSecondary);
elements_button_left(canvas, "Config");
canvas_draw_line(canvas, 46, 51, 125, 51);
bool scrollbar = model->history_item > 4;
string_t str_buff;
string_init(str_buff);
SubGhzReceiverMenuItem* item_menu;
for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) {
size_t idx = CLAMP((uint16_t)(i + model->list_offset), model->history_item, 0);
item_menu = SubGhzReceiverMenuItemArray_get(model->history->data, idx);
string_set(str_buff, item_menu->item_str);
elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX);
if(model->idx == idx) {
subghz_view_receiver_draw_frame(canvas, i, scrollbar);
} else {
canvas_set_color(canvas, ColorBlack);
}
canvas_draw_icon(canvas, 1, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]);
canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, string_get_cstr(str_buff));
string_reset(str_buff);
}
if(scrollbar) {
elements_scrollbar_pos(canvas, 128, 0, 49, model->idx, model->history_item);
}
string_clear(str_buff);
canvas_set_color(canvas, ColorBlack);
if(model->history_item == 0) {
canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 63, 46, "Scanning...");
canvas_draw_line(canvas, 46, 51, 125, 51);
canvas_set_font(canvas, FontSecondary);
}
switch(model->bar_show) {
case SubGhzViewReceiverBarShowLock:
canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8);
canvas_draw_str(canvas, 74, 62, "Locked");
break;
case SubGhzViewReceiverBarShowToUnlockPress:
canvas_draw_str(canvas, 44, 62, string_get_cstr(model->frequency_str));
canvas_draw_str(canvas, 79, 62, string_get_cstr(model->preset_str));
canvas_draw_str(canvas, 96, 62, string_get_cstr(model->history_stat_str));
canvas_set_font(canvas, FontSecondary);
elements_bold_rounded_frame(canvas, 14, 8, 99, 48);
elements_multiline_text(canvas, 65, 26, "To unlock\npress:");
canvas_draw_icon(canvas, 65, 42, &I_Pin_back_arrow_10x8);
canvas_draw_icon(canvas, 80, 42, &I_Pin_back_arrow_10x8);
canvas_draw_icon(canvas, 95, 42, &I_Pin_back_arrow_10x8);
canvas_draw_icon(canvas, 16, 13, &I_WarningDolphin_45x42);
canvas_draw_dot(canvas, 17, 61);
break;
case SubGhzViewReceiverBarShowUnlock:
canvas_draw_icon(canvas, 64, 55, &I_Unlock_7x8);
canvas_draw_str(canvas, 74, 62, "Unlocked");
break;
default:
canvas_draw_str(canvas, 44, 62, string_get_cstr(model->frequency_str));
canvas_draw_str(canvas, 79, 62, string_get_cstr(model->preset_str));
canvas_draw_str(canvas, 96, 62, string_get_cstr(model->history_stat_str));
break;
}
}
static void subghz_view_receiver_timer_callback(void* context) {
furi_assert(context);
SubGhzViewReceiver* subghz_receiver = context;
with_view_model(
subghz_receiver->view, (SubGhzViewReceiverModel * model) {
model->bar_show = SubGhzViewReceiverBarShowDefault;
return true;
});
if(subghz_receiver->lock_count < UNLOCK_CNT) {
subghz_receiver->callback(
SubGhzCustomEventViewReceiverOffDisplay, subghz_receiver->context);
} else {
subghz_receiver->lock = SubGhzLockOff;
subghz_receiver->callback(SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context);
}
subghz_receiver->lock_count = 0;
}
bool subghz_view_receiver_input(InputEvent* event, void* context) {
furi_assert(context);
SubGhzViewReceiver* subghz_receiver = context;
if(subghz_receiver->lock == SubGhzLockOn) {
with_view_model(
subghz_receiver->view, (SubGhzViewReceiverModel * model) {
model->bar_show = SubGhzViewReceiverBarShowToUnlockPress;
return true;
});
if(subghz_receiver->lock_count == 0) {
furi_timer_start(subghz_receiver->timer, pdMS_TO_TICKS(1000));
}
if(event->key == InputKeyBack && event->type == InputTypeShort) {
subghz_receiver->lock_count++;
}
if(subghz_receiver->lock_count >= UNLOCK_CNT) {
// subghz_receiver->callback(
// SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context);
with_view_model(
subghz_receiver->view, (SubGhzViewReceiverModel * model) {
model->bar_show = SubGhzViewReceiverBarShowUnlock;
return true;
});
//subghz_receiver->lock = SubGhzLockOff;
furi_timer_start(subghz_receiver->timer, pdMS_TO_TICKS(650));
}
return true;
}
if(event->key == InputKeyBack && event->type == InputTypeShort) {
subghz_receiver->callback(SubGhzCustomEventViewReceiverBack, subghz_receiver->context);
} else if(
event->key == InputKeyUp &&
(event->type == InputTypeShort || event->type == InputTypeRepeat)) {
with_view_model(
subghz_receiver->view, (SubGhzViewReceiverModel * model) {
if(model->idx != 0) model->idx--;
return true;
});
} else if(
event->key == InputKeyDown &&
(event->type == InputTypeShort || event->type == InputTypeRepeat)) {
with_view_model(
subghz_receiver->view, (SubGhzViewReceiverModel * model) {
if(model->idx != model->history_item - 1) model->idx++;
return true;
});
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
subghz_receiver->callback(SubGhzCustomEventViewReceiverConfig, subghz_receiver->context);
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
with_view_model(
subghz_receiver->view, (SubGhzViewReceiverModel * model) {
if(model->history_item != 0) {
subghz_receiver->callback(
SubGhzCustomEventViewReceiverOK, subghz_receiver->context);
}
return false;
});
}
subghz_view_receiver_update_offset(subghz_receiver);
return true;
}
void subghz_view_receiver_enter(void* context) {
furi_assert(context);
}
void subghz_view_receiver_exit(void* context) {
furi_assert(context);
SubGhzViewReceiver* subghz_receiver = context;
with_view_model(
subghz_receiver->view, (SubGhzViewReceiverModel * model) {
string_reset(model->frequency_str);
string_reset(model->preset_str);
string_reset(model->history_stat_str);
for
M_EACH(item_menu, model->history->data, SubGhzReceiverMenuItemArray_t) {
string_clear(item_menu->item_str);
item_menu->type = 0;
}
SubGhzReceiverMenuItemArray_reset(model->history->data);
model->idx = 0;
model->list_offset = 0;
model->history_item = 0;
return false;
});
furi_timer_stop(subghz_receiver->timer);
}
SubGhzViewReceiver* subghz_view_receiver_alloc() {
SubGhzViewReceiver* subghz_receiver = malloc(sizeof(SubGhzViewReceiver));
// View allocation and configuration
subghz_receiver->view = view_alloc();
subghz_receiver->lock = SubGhzLockOff;
subghz_receiver->lock_count = 0;
view_allocate_model(
subghz_receiver->view, ViewModelTypeLocking, sizeof(SubGhzViewReceiverModel));
view_set_context(subghz_receiver->view, subghz_receiver);
view_set_draw_callback(subghz_receiver->view, (ViewDrawCallback)subghz_view_receiver_draw);
view_set_input_callback(subghz_receiver->view, subghz_view_receiver_input);
view_set_enter_callback(subghz_receiver->view, subghz_view_receiver_enter);
view_set_exit_callback(subghz_receiver->view, subghz_view_receiver_exit);
with_view_model(
subghz_receiver->view, (SubGhzViewReceiverModel * model) {
string_init(model->frequency_str);
string_init(model->preset_str);
string_init(model->history_stat_str);
model->bar_show = SubGhzViewReceiverBarShowDefault;
model->history = malloc(sizeof(SubGhzReceiverHistory));
SubGhzReceiverMenuItemArray_init(model->history->data);
return true;
});
subghz_receiver->timer =
furi_timer_alloc(subghz_view_receiver_timer_callback, FuriTimerTypeOnce, subghz_receiver);
return subghz_receiver;
}
void subghz_view_receiver_free(SubGhzViewReceiver* subghz_receiver) {
furi_assert(subghz_receiver);
with_view_model(
subghz_receiver->view, (SubGhzViewReceiverModel * model) {
string_clear(model->frequency_str);
string_clear(model->preset_str);
string_clear(model->history_stat_str);
for
M_EACH(item_menu, model->history->data, SubGhzReceiverMenuItemArray_t) {
string_clear(item_menu->item_str);
item_menu->type = 0;
}
SubGhzReceiverMenuItemArray_clear(model->history->data);
free(model->history);
return false;
});
furi_timer_free(subghz_receiver->timer);
view_free(subghz_receiver->view);
free(subghz_receiver);
}
View* subghz_view_receiver_get_view(SubGhzViewReceiver* subghz_receiver) {
furi_assert(subghz_receiver);
return subghz_receiver->view;
}
uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver) {
furi_assert(subghz_receiver);
uint32_t idx = 0;
with_view_model(
subghz_receiver->view, (SubGhzViewReceiverModel * model) {
idx = model->idx;
return false;
});
return idx;
}
void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx) {
furi_assert(subghz_receiver);
with_view_model(
subghz_receiver->view, (SubGhzViewReceiverModel * model) {
model->idx = idx;
if(model->idx > 2) model->list_offset = idx - 2;
return true;
});
subghz_view_receiver_update_offset(subghz_receiver);
}

View File

@@ -0,0 +1,39 @@
#pragma once
#include <gui/view.h>
#include "../helpers/subghz_types.h"
#include "../helpers/subghz_custom_event.h"
typedef struct SubGhzViewReceiver SubGhzViewReceiver;
typedef void (*SubGhzViewReceiverCallback)(SubGhzCustomEvent event, void* context);
void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock keyboard);
void subghz_view_receiver_set_callback(
SubGhzViewReceiver* subghz_receiver,
SubGhzViewReceiverCallback callback,
void* context);
SubGhzViewReceiver* subghz_view_receiver_alloc();
void subghz_view_receiver_free(SubGhzViewReceiver* subghz_receiver);
View* subghz_view_receiver_get_view(SubGhzViewReceiver* subghz_receiver);
void subghz_view_receiver_add_data_statusbar(
SubGhzViewReceiver* subghz_receiver,
const char* frequency_str,
const char* preset_str,
const char* history_stat_str);
void subghz_view_receiver_add_item_to_menu(
SubGhzViewReceiver* subghz_receiver,
const char* name,
uint8_t type);
uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver);
void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx);
void subghz_view_receiver_exit(void* context);

View File

@@ -0,0 +1,180 @@
#include "subghz_frequency_analyzer.h"
#include "../subghz_i.h"
#include <math.h>
#include <furi.h>
#include <furi_hal.h>
#include <input/input.h>
#include <notification/notification_messages.h>
#include "../helpers/subghz_frequency_analyzer_worker.h"
#include <assets_icons.h>
typedef enum {
SubGhzFrequencyAnalyzerStatusIDLE,
} SubGhzFrequencyAnalyzerStatus;
struct SubGhzFrequencyAnalyzer {
View* view;
SubGhzFrequencyAnalyzerWorker* worker;
SubGhzFrequencyAnalyzerCallback callback;
void* context;
bool locked;
};
typedef struct {
uint32_t frequency;
float rssi;
} SubGhzFrequencyAnalyzerModel;
void subghz_frequency_analyzer_set_callback(
SubGhzFrequencyAnalyzer* subghz_frequency_analyzer,
SubGhzFrequencyAnalyzerCallback callback,
void* context) {
furi_assert(subghz_frequency_analyzer);
furi_assert(callback);
subghz_frequency_analyzer->callback = callback;
subghz_frequency_analyzer->context = context;
}
void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, float rssi) {
uint8_t x = 48;
uint8_t y = 56;
uint8_t column_number = 0;
if(rssi) {
rssi = (rssi + 90) / 3;
for(size_t i = 1; i < (uint8_t)rssi; i++) {
if(i > 20) break;
if(i % 4) {
column_number++;
canvas_draw_box(canvas, x + 2 * i, y - column_number, 2, 4 + column_number);
}
}
}
}
void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel* model) {
char buffer[64];
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 20, 8, "Frequency Analyzer");
canvas_draw_str(canvas, 28, 60, "RSSI");
subghz_frequency_analyzer_draw_rssi(canvas, model->rssi);
//Frequency
canvas_set_font(canvas, FontBigNumbers);
snprintf(
buffer,
sizeof(buffer),
"%03ld.%03ld",
model->frequency / 1000000 % 1000,
model->frequency / 1000 % 1000);
canvas_draw_str(canvas, 8, 35, buffer);
canvas_draw_icon(canvas, 96, 24, &I_MHz_25x11);
}
bool subghz_frequency_analyzer_input(InputEvent* event, void* context) {
furi_assert(context);
if(event->key == InputKeyBack) {
return false;
}
return true;
}
void subghz_frequency_analyzer_pair_callback(void* context, uint32_t frequency, float rssi) {
SubGhzFrequencyAnalyzer* instance = context;
if((rssi == 0.f) && (instance->locked)) {
if(instance->callback) {
instance->callback(SubGhzCustomEventSceneAnalyzerUnlock, instance->context);
}
} else if((rssi != 0.f) && (!instance->locked)) {
if(instance->callback) {
instance->callback(SubGhzCustomEventSceneAnalyzerLock, instance->context);
}
}
instance->locked = (rssi != 0.f);
with_view_model(
instance->view, (SubGhzFrequencyAnalyzerModel * model) {
model->rssi = rssi;
model->frequency = frequency;
return true;
});
}
void subghz_frequency_analyzer_enter(void* context) {
furi_assert(context);
SubGhzFrequencyAnalyzer* instance = context;
//Start worker
instance->worker = subghz_frequency_analyzer_worker_alloc(instance->context);
subghz_frequency_analyzer_worker_set_pair_callback(
instance->worker,
(SubGhzFrequencyAnalyzerWorkerPairCallback)subghz_frequency_analyzer_pair_callback,
instance);
subghz_frequency_analyzer_worker_start(instance->worker);
with_view_model(
instance->view, (SubGhzFrequencyAnalyzerModel * model) {
model->rssi = 0;
model->frequency = 0;
return true;
});
}
void subghz_frequency_analyzer_exit(void* context) {
furi_assert(context);
SubGhzFrequencyAnalyzer* instance = context;
//Stop worker
if(subghz_frequency_analyzer_worker_is_running(instance->worker)) {
subghz_frequency_analyzer_worker_stop(instance->worker);
}
subghz_frequency_analyzer_worker_free(instance->worker);
with_view_model(
instance->view, (SubGhzFrequencyAnalyzerModel * model) {
model->rssi = 0;
return true;
});
}
SubGhzFrequencyAnalyzer* subghz_frequency_analyzer_alloc() {
SubGhzFrequencyAnalyzer* instance = malloc(sizeof(SubGhzFrequencyAnalyzer));
// View allocation and configuration
instance->view = view_alloc();
view_allocate_model(
instance->view, ViewModelTypeLocking, sizeof(SubGhzFrequencyAnalyzerModel));
view_set_context(instance->view, instance);
view_set_draw_callback(instance->view, (ViewDrawCallback)subghz_frequency_analyzer_draw);
view_set_input_callback(instance->view, subghz_frequency_analyzer_input);
view_set_enter_callback(instance->view, subghz_frequency_analyzer_enter);
view_set_exit_callback(instance->view, subghz_frequency_analyzer_exit);
with_view_model(
instance->view, (SubGhzFrequencyAnalyzerModel * model) {
model->rssi = 0;
return true;
});
return instance;
}
void subghz_frequency_analyzer_free(SubGhzFrequencyAnalyzer* instance) {
furi_assert(instance);
view_free(instance->view);
free(instance);
}
View* subghz_frequency_analyzer_get_view(SubGhzFrequencyAnalyzer* instance) {
furi_assert(instance);
return instance->view;
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include <gui/view.h>
#include "../helpers/subghz_custom_event.h"
typedef struct SubGhzFrequencyAnalyzer SubGhzFrequencyAnalyzer;
typedef void (*SubGhzFrequencyAnalyzerCallback)(SubGhzCustomEvent event, void* context);
void subghz_frequency_analyzer_set_callback(
SubGhzFrequencyAnalyzer* subghz_frequency_analyzer,
SubGhzFrequencyAnalyzerCallback callback,
void* context);
SubGhzFrequencyAnalyzer* subghz_frequency_analyzer_alloc();
void subghz_frequency_analyzer_free(SubGhzFrequencyAnalyzer* subghz_static);
View* subghz_frequency_analyzer_get_view(SubGhzFrequencyAnalyzer* subghz_static);

View File

@@ -0,0 +1,534 @@
#include "subghz_read_raw.h"
#include "../subghz_i.h"
#include <math.h>
#include <furi.h>
#include <furi_hal.h>
#include <input/input.h>
#include <gui/elements.h>
#include <assets_icons.h>
#define SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE 100
#define TAG "SubGhzReadRAW"
struct SubGhzReadRAW {
View* view;
SubGhzReadRAWCallback callback;
void* context;
};
typedef struct {
string_t frequency_str;
string_t preset_str;
string_t sample_write;
string_t file_name;
uint8_t* rssi_history;
bool rssi_history_end;
uint8_t ind_write;
uint8_t ind_sin;
SubGhzReadRAWStatus status;
} SubGhzReadRAWModel;
void subghz_read_raw_set_callback(
SubGhzReadRAW* subghz_read_raw,
SubGhzReadRAWCallback callback,
void* context) {
furi_assert(subghz_read_raw);
furi_assert(callback);
subghz_read_raw->callback = callback;
subghz_read_raw->context = context;
}
void subghz_read_raw_add_data_statusbar(
SubGhzReadRAW* instance,
const char* frequency_str,
const char* preset_str) {
furi_assert(instance);
with_view_model(
instance->view, (SubGhzReadRAWModel * model) {
string_set_str(model->frequency_str, frequency_str);
string_set_str(model->preset_str, preset_str);
return true;
});
}
void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi) {
furi_assert(instance);
uint8_t u_rssi = 0;
if(rssi < -90) {
u_rssi = 0;
} else {
u_rssi = (uint8_t)((rssi + 90) / 2.7);
}
with_view_model(
instance->view, (SubGhzReadRAWModel * model) {
model->rssi_history[model->ind_write++] = u_rssi;
if(model->ind_write > SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE) {
model->rssi_history_end = true;
model->ind_write = 0;
}
return true;
});
}
void subghz_read_raw_update_sample_write(SubGhzReadRAW* instance, size_t sample) {
furi_assert(instance);
with_view_model(
instance->view, (SubGhzReadRAWModel * model) {
string_printf(model->sample_write, "%d spl.", sample);
return false;
});
}
void subghz_read_raw_stop_send(SubGhzReadRAW* instance) {
furi_assert(instance);
with_view_model(
instance->view, (SubGhzReadRAWModel * model) {
switch(model->status) {
case SubGhzReadRAWStatusTXRepeat:
case SubGhzReadRAWStatusLoadKeyTXRepeat:
instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context);
break;
case SubGhzReadRAWStatusTX:
model->status = SubGhzReadRAWStatusIDLE;
break;
case SubGhzReadRAWStatusLoadKeyTX:
model->status = SubGhzReadRAWStatusLoadKeyIDLE;
break;
default:
FURI_LOG_W(TAG, "unknown status");
model->status = SubGhzReadRAWStatusIDLE;
break;
}
return true;
});
}
void subghz_read_raw_update_sin(SubGhzReadRAW* instance) {
furi_assert(instance);
with_view_model(
instance->view, (SubGhzReadRAWModel * model) {
if(model->ind_sin++ > 62) {
model->ind_sin = 0;
}
return true;
});
}
static int8_t subghz_read_raw_tab_sin(uint8_t x) {
const uint8_t tab_sin[64] = {0, 3, 6, 9, 12, 16, 19, 22, 25, 28, 31, 34, 37,
40, 43, 46, 49, 51, 54, 57, 60, 63, 65, 68, 71, 73,
76, 78, 81, 83, 85, 88, 90, 92, 94, 96, 98, 100, 102,
104, 106, 107, 109, 111, 112, 113, 115, 116, 117, 118, 120, 121,
122, 122, 123, 124, 125, 125, 126, 126, 126, 127, 127, 127};
int8_t r = tab_sin[((x & 0x40) ? -x - 1 : x) & 0x3f];
if(x & 0x80) return -r;
return r;
}
void subghz_read_raw_draw_sin(Canvas* canvas, SubGhzReadRAWModel* model) {
#define SUBGHZ_RAW_SIN_AMPLITUDE 11
for(int i = 113; i > 0; i--) {
canvas_draw_line(
canvas,
i,
32 - subghz_read_raw_tab_sin(i + model->ind_sin * 16) / SUBGHZ_RAW_SIN_AMPLITUDE,
i + 1,
32 + subghz_read_raw_tab_sin((i + model->ind_sin * 16 + 1) * 2) /
SUBGHZ_RAW_SIN_AMPLITUDE);
canvas_draw_line(
canvas,
i + 1,
32 - subghz_read_raw_tab_sin((i + model->ind_sin * 16)) / SUBGHZ_RAW_SIN_AMPLITUDE,
i + 2,
32 + subghz_read_raw_tab_sin((i + model->ind_sin * 16 + 1) * 2) /
SUBGHZ_RAW_SIN_AMPLITUDE);
}
}
void subghz_read_raw_draw_scale(Canvas* canvas, SubGhzReadRAWModel* model) {
#define SUBGHZ_RAW_TOP_SCALE 14
#define SUBGHZ_RAW_END_SCALE 115
if(model->rssi_history_end == false) {
for(int i = SUBGHZ_RAW_END_SCALE; i > 0; i -= 15) {
canvas_draw_line(canvas, i, SUBGHZ_RAW_TOP_SCALE, i, SUBGHZ_RAW_TOP_SCALE + 4);
canvas_draw_line(canvas, i - 5, SUBGHZ_RAW_TOP_SCALE, i - 5, SUBGHZ_RAW_TOP_SCALE + 2);
canvas_draw_line(
canvas, i - 10, SUBGHZ_RAW_TOP_SCALE, i - 10, SUBGHZ_RAW_TOP_SCALE + 2);
}
} else {
for(int i = SUBGHZ_RAW_END_SCALE - model->ind_write % 15; i > -15; i -= 15) {
canvas_draw_line(canvas, i, SUBGHZ_RAW_TOP_SCALE, i, SUBGHZ_RAW_TOP_SCALE + 4);
if(SUBGHZ_RAW_END_SCALE > i + 5)
canvas_draw_line(
canvas, i + 5, SUBGHZ_RAW_TOP_SCALE, i + 5, SUBGHZ_RAW_TOP_SCALE + 2);
if(SUBGHZ_RAW_END_SCALE > i + 10)
canvas_draw_line(
canvas, i + 10, SUBGHZ_RAW_TOP_SCALE, i + 10, SUBGHZ_RAW_TOP_SCALE + 2);
}
}
}
void subghz_read_raw_draw_rssi(Canvas* canvas, SubGhzReadRAWModel* model) {
int ind = 0;
int base = 0;
if(model->rssi_history_end == false) {
for(int i = model->ind_write; i >= 0; i--) {
canvas_draw_line(canvas, i, 47, i, 47 - model->rssi_history[i]);
}
if(model->ind_write > 3) {
canvas_draw_line(canvas, model->ind_write, 47, model->ind_write, 13);
canvas_draw_line(canvas, model->ind_write - 2, 12, model->ind_write + 2, 12);
canvas_draw_line(canvas, model->ind_write - 1, 13, model->ind_write + 1, 13);
}
} else {
base = SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - model->ind_write;
for(int i = SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE; i >= 0; i--) {
ind = i - base;
if(ind < 0) ind += SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE;
canvas_draw_line(canvas, i, 47, i, 47 - model->rssi_history[ind]);
}
canvas_draw_line(
canvas, SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE, 47, SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE, 13);
canvas_draw_line(
canvas,
SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 2,
12,
SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 2,
12);
canvas_draw_line(
canvas,
SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1,
13,
SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1,
13);
}
}
void subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) {
uint8_t graphics_mode = 1;
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 5, 7, string_get_cstr(model->frequency_str));
canvas_draw_str(canvas, 40, 7, string_get_cstr(model->preset_str));
canvas_draw_str_aligned(
canvas, 126, 0, AlignRight, AlignTop, string_get_cstr(model->sample_write));
canvas_draw_line(canvas, 0, 14, 115, 14);
canvas_draw_line(canvas, 0, 48, 115, 48);
canvas_draw_line(canvas, 115, 14, 115, 48);
switch(model->status) {
case SubGhzReadRAWStatusIDLE:
elements_button_left(canvas, "Erase");
elements_button_center(canvas, "Send");
elements_button_right(canvas, "Save");
break;
case SubGhzReadRAWStatusLoadKeyIDLE:
elements_button_left(canvas, "New");
elements_button_center(canvas, "Send");
elements_button_right(canvas, "More");
elements_text_box(
canvas,
4,
20,
110,
30,
AlignCenter,
AlignCenter,
string_get_cstr(model->file_name),
true);
break;
case SubGhzReadRAWStatusTX:
case SubGhzReadRAWStatusTXRepeat:
case SubGhzReadRAWStatusLoadKeyTX:
case SubGhzReadRAWStatusLoadKeyTXRepeat:
graphics_mode = 0;
elements_button_center(canvas, "Send");
break;
case SubGhzReadRAWStatusStart:
elements_button_left(canvas, "Config");
elements_button_center(canvas, "REC");
break;
default:
elements_button_center(canvas, "Stop");
break;
}
if(graphics_mode == 0) {
subghz_read_raw_draw_sin(canvas, model);
} else {
subghz_read_raw_draw_rssi(canvas, model);
subghz_read_raw_draw_scale(canvas, model);
canvas_set_font_direction(canvas, CanvasDirectionBottomToTop);
canvas_draw_str(canvas, 126, 40, "RSSI");
canvas_set_font_direction(canvas, CanvasDirectionLeftToRight);
}
}
bool subghz_read_raw_input(InputEvent* event, void* context) {
furi_assert(context);
SubGhzReadRAW* instance = context;
if((event->key == InputKeyOk) &&
(event->type == InputTypeLong || event->type == InputTypeRepeat)) {
//we check that if we hold the transfer button,
//further check of events is not needed, we exit
return false;
} else if(event->key == InputKeyOk && event->type == InputTypePress) {
with_view_model(
instance->view, (SubGhzReadRAWModel * model) {
uint8_t ret = false;
switch(model->status) {
case SubGhzReadRAWStatusIDLE:
// Start TX
instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context);
model->status = SubGhzReadRAWStatusTXRepeat;
ret = true;
break;
case SubGhzReadRAWStatusTX:
// Start TXRepeat
model->status = SubGhzReadRAWStatusTXRepeat;
break;
case SubGhzReadRAWStatusLoadKeyIDLE:
// Start Load Key TX
instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context);
model->status = SubGhzReadRAWStatusLoadKeyTXRepeat;
ret = true;
break;
case SubGhzReadRAWStatusLoadKeyTX:
// Start Load Key TXRepeat
model->status = SubGhzReadRAWStatusLoadKeyTXRepeat;
break;
default:
break;
}
return ret;
});
} else if(event->key == InputKeyOk && event->type == InputTypeRelease) {
with_view_model(
instance->view, (SubGhzReadRAWModel * model) {
if(model->status == SubGhzReadRAWStatusTXRepeat) {
// Stop repeat TX
model->status = SubGhzReadRAWStatusTX;
} else if(model->status == SubGhzReadRAWStatusLoadKeyTXRepeat) {
// Stop repeat TX
model->status = SubGhzReadRAWStatusLoadKeyTX;
}
return false;
});
} else if(event->key == InputKeyBack && event->type == InputTypeShort) {
with_view_model(
instance->view, (SubGhzReadRAWModel * model) {
switch(model->status) {
case SubGhzReadRAWStatusREC:
//Stop REC
instance->callback(SubGhzCustomEventViewReadRAWIDLE, instance->context);
model->status = SubGhzReadRAWStatusIDLE;
break;
case SubGhzReadRAWStatusLoadKeyTX:
//Stop TxRx
instance->callback(SubGhzCustomEventViewReadRAWTXRXStop, instance->context);
model->status = SubGhzReadRAWStatusLoadKeyIDLE;
break;
case SubGhzReadRAWStatusTX:
//Stop TxRx
instance->callback(SubGhzCustomEventViewReadRAWTXRXStop, instance->context);
model->status = SubGhzReadRAWStatusIDLE;
break;
case SubGhzReadRAWStatusLoadKeyIDLE:
//Exit
instance->callback(SubGhzCustomEventViewReadRAWBack, instance->context);
break;
default:
//Exit
instance->callback(SubGhzCustomEventViewReadRAWBack, instance->context);
break;
}
return true;
});
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
with_view_model(
instance->view, (SubGhzReadRAWModel * model) {
if(model->status == SubGhzReadRAWStatusStart) {
//Config
instance->callback(SubGhzCustomEventViewReadRAWConfig, instance->context);
} else if(
(model->status == SubGhzReadRAWStatusIDLE) ||
(model->status == SubGhzReadRAWStatusLoadKeyIDLE)) {
//Erase
model->status = SubGhzReadRAWStatusStart;
model->rssi_history_end = false;
model->ind_write = 0;
string_set_str(model->sample_write, "0 spl.");
string_reset(model->file_name);
instance->callback(SubGhzCustomEventViewReadRAWErase, instance->context);
}
return true;
});
} else if(event->key == InputKeyRight && event->type == InputTypeShort) {
with_view_model(
instance->view, (SubGhzReadRAWModel * model) {
if(model->status == SubGhzReadRAWStatusIDLE) {
//Save
instance->callback(SubGhzCustomEventViewReadRAWSave, instance->context);
} else if(model->status == SubGhzReadRAWStatusLoadKeyIDLE) {
//More
instance->callback(SubGhzCustomEventViewReadRAWMore, instance->context);
}
return true;
});
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
with_view_model(
instance->view, (SubGhzReadRAWModel * model) {
if(model->status == SubGhzReadRAWStatusStart) {
//Record
instance->callback(SubGhzCustomEventViewReadRAWREC, instance->context);
model->status = SubGhzReadRAWStatusREC;
model->ind_write = 0;
model->rssi_history_end = false;
} else if(model->status == SubGhzReadRAWStatusREC) {
//Stop
instance->callback(SubGhzCustomEventViewReadRAWIDLE, instance->context);
model->status = SubGhzReadRAWStatusIDLE;
}
return true;
});
}
return true;
}
void subghz_read_raw_set_status(
SubGhzReadRAW* instance,
SubGhzReadRAWStatus status,
const char* file_name) {
furi_assert(instance);
switch(status) {
case SubGhzReadRAWStatusStart:
with_view_model(
instance->view, (SubGhzReadRAWModel * model) {
model->status = SubGhzReadRAWStatusStart;
model->rssi_history_end = false;
model->ind_write = 0;
string_reset(model->file_name);
string_set_str(model->sample_write, "0 spl.");
return true;
});
break;
case SubGhzReadRAWStatusIDLE:
with_view_model(
instance->view, (SubGhzReadRAWModel * model) {
model->status = SubGhzReadRAWStatusIDLE;
return true;
});
break;
case SubGhzReadRAWStatusLoadKeyTX:
with_view_model(
instance->view, (SubGhzReadRAWModel * model) {
model->status = SubGhzReadRAWStatusLoadKeyIDLE;
model->rssi_history_end = false;
model->ind_write = 0;
string_set_str(model->file_name, file_name);
string_set_str(model->sample_write, "RAW");
return true;
});
break;
case SubGhzReadRAWStatusSaveKey:
with_view_model(
instance->view, (SubGhzReadRAWModel * model) {
model->status = SubGhzReadRAWStatusLoadKeyIDLE;
if(!model->ind_write) {
string_set_str(model->file_name, file_name);
string_set_str(model->sample_write, "RAW");
} else {
string_reset(model->file_name);
}
return true;
});
break;
default:
FURI_LOG_W(TAG, "unknown status");
break;
}
}
void subghz_read_raw_enter(void* context) {
furi_assert(context);
//SubGhzReadRAW* instance = context;
}
void subghz_read_raw_exit(void* context) {
furi_assert(context);
SubGhzReadRAW* instance = context;
with_view_model(
instance->view, (SubGhzReadRAWModel * model) {
if(model->status != SubGhzReadRAWStatusIDLE &&
model->status != SubGhzReadRAWStatusStart &&
model->status != SubGhzReadRAWStatusLoadKeyIDLE) {
instance->callback(SubGhzCustomEventViewReadRAWIDLE, instance->context);
model->status = SubGhzReadRAWStatusStart;
}
return true;
});
}
SubGhzReadRAW* subghz_read_raw_alloc() {
SubGhzReadRAW* instance = malloc(sizeof(SubGhzReadRAW));
// View allocation and configuration
instance->view = view_alloc();
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(SubGhzReadRAWModel));
view_set_context(instance->view, instance);
view_set_draw_callback(instance->view, (ViewDrawCallback)subghz_read_raw_draw);
view_set_input_callback(instance->view, subghz_read_raw_input);
view_set_enter_callback(instance->view, subghz_read_raw_enter);
view_set_exit_callback(instance->view, subghz_read_raw_exit);
with_view_model(
instance->view, (SubGhzReadRAWModel * model) {
string_init(model->frequency_str);
string_init(model->preset_str);
string_init(model->sample_write);
string_init(model->file_name);
model->rssi_history = malloc(SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE * sizeof(uint8_t));
return true;
});
return instance;
}
void subghz_read_raw_free(SubGhzReadRAW* instance) {
furi_assert(instance);
with_view_model(
instance->view, (SubGhzReadRAWModel * model) {
string_clear(model->frequency_str);
string_clear(model->preset_str);
string_clear(model->sample_write);
string_clear(model->file_name);
free(model->rssi_history);
return true;
});
view_free(instance->view);
free(instance);
}
View* subghz_read_raw_get_view(SubGhzReadRAW* instance) {
furi_assert(instance);
return instance->view;
}

View File

@@ -0,0 +1,50 @@
#pragma once
#include <gui/view.h>
#include "../helpers/subghz_custom_event.h"
typedef struct SubGhzReadRAW SubGhzReadRAW;
typedef void (*SubGhzReadRAWCallback)(SubGhzCustomEvent event, void* context);
typedef enum {
SubGhzReadRAWStatusStart,
SubGhzReadRAWStatusIDLE,
SubGhzReadRAWStatusREC,
SubGhzReadRAWStatusTX,
SubGhzReadRAWStatusTXRepeat,
SubGhzReadRAWStatusLoadKeyIDLE,
SubGhzReadRAWStatusLoadKeyTX,
SubGhzReadRAWStatusLoadKeyTXRepeat,
SubGhzReadRAWStatusSaveKey,
} SubGhzReadRAWStatus;
void subghz_read_raw_set_callback(
SubGhzReadRAW* subghz_read_raw,
SubGhzReadRAWCallback callback,
void* context);
SubGhzReadRAW* subghz_read_raw_alloc();
void subghz_read_raw_free(SubGhzReadRAW* subghz_static);
void subghz_read_raw_add_data_statusbar(
SubGhzReadRAW* instance,
const char* frequency_str,
const char* preset_str);
void subghz_read_raw_update_sample_write(SubGhzReadRAW* instance, size_t sample);
void subghz_read_raw_stop_send(SubGhzReadRAW* instance);
void subghz_read_raw_update_sin(SubGhzReadRAW* instance);
void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi);
void subghz_read_raw_set_status(
SubGhzReadRAW* instance,
SubGhzReadRAWStatus status,
const char* file_name);
View* subghz_read_raw_get_view(SubGhzReadRAW* subghz_static);

View File

@@ -0,0 +1,213 @@
#include "subghz_test_carrier.h"
#include "../subghz_i.h"
#include "../helpers/subghz_testing.h"
#include <math.h>
#include <furi.h>
#include <furi_hal.h>
#include <input/input.h>
struct SubGhzTestCarrier {
View* view;
FuriTimer* timer;
SubGhzTestCarrierCallback callback;
void* context;
};
typedef enum {
SubGhzTestCarrierModelStatusRx,
SubGhzTestCarrierModelStatusTx,
} SubGhzTestCarrierModelStatus;
typedef struct {
uint8_t frequency;
uint32_t real_frequency;
FuriHalSubGhzPath path;
float rssi;
SubGhzTestCarrierModelStatus status;
} SubGhzTestCarrierModel;
void subghz_test_carrier_set_callback(
SubGhzTestCarrier* subghz_test_carrier,
SubGhzTestCarrierCallback callback,
void* context) {
furi_assert(subghz_test_carrier);
furi_assert(callback);
subghz_test_carrier->callback = callback;
subghz_test_carrier->context = context;
}
void subghz_test_carrier_draw(Canvas* canvas, SubGhzTestCarrierModel* model) {
char buffer[64];
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 0, 8, "CC1101 Basic Test");
canvas_set_font(canvas, FontSecondary);
// Frequency
snprintf(
buffer,
sizeof(buffer),
"Freq: %03ld.%03ld.%03ld Hz",
model->real_frequency / 1000000 % 1000,
model->real_frequency / 1000 % 1000,
model->real_frequency % 1000);
canvas_draw_str(canvas, 0, 20, buffer);
// Path
char* path_name = "Unknown";
if(model->path == FuriHalSubGhzPathIsolate) {
path_name = "isolate";
} else if(model->path == FuriHalSubGhzPath433) {
path_name = "433MHz";
} else if(model->path == FuriHalSubGhzPath315) {
path_name = "315MHz";
} else if(model->path == FuriHalSubGhzPath868) {
path_name = "868MHz";
}
snprintf(buffer, sizeof(buffer), "Path: %d - %s", model->path, path_name);
canvas_draw_str(canvas, 0, 31, buffer);
if(model->status == SubGhzTestCarrierModelStatusRx) {
snprintf(
buffer,
sizeof(buffer),
"RSSI: %ld.%ld dBm",
(int32_t)(model->rssi),
(int32_t)fabs(model->rssi * 10) % 10);
canvas_draw_str(canvas, 0, 42, buffer);
} else {
canvas_draw_str(canvas, 0, 42, "TX");
}
}
bool subghz_test_carrier_input(InputEvent* event, void* context) {
furi_assert(context);
SubGhzTestCarrier* subghz_test_carrier = context;
if(event->key == InputKeyBack || event->type != InputTypeShort) {
return false;
}
with_view_model(
subghz_test_carrier->view, (SubGhzTestCarrierModel * model) {
furi_hal_subghz_idle();
if(event->key == InputKeyLeft) {
if(model->frequency > 0) model->frequency--;
} else if(event->key == InputKeyRight) {
if(model->frequency < subghz_frequencies_count_testing - 1) model->frequency++;
} else if(event->key == InputKeyDown) {
if(model->path > 0) model->path--;
} else if(event->key == InputKeyUp) {
if(model->path < FuriHalSubGhzPath868) model->path++;
} else if(event->key == InputKeyOk) {
if(model->status == SubGhzTestCarrierModelStatusTx) {
model->status = SubGhzTestCarrierModelStatusRx;
} else {
model->status = SubGhzTestCarrierModelStatusTx;
}
}
model->real_frequency =
furi_hal_subghz_set_frequency(subghz_frequencies_testing[model->frequency]);
furi_hal_subghz_set_path(model->path);
if(model->status == SubGhzTestCarrierModelStatusRx) {
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
furi_hal_subghz_rx();
} else {
furi_hal_gpio_init(
&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
furi_hal_gpio_write(&gpio_cc1101_g0, true);
if(!furi_hal_subghz_tx()) {
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
subghz_test_carrier->callback(
SubGhzTestCarrierEventOnlyRx, subghz_test_carrier->context);
}
}
return true;
});
return true;
}
void subghz_test_carrier_enter(void* context) {
furi_assert(context);
SubGhzTestCarrier* subghz_test_carrier = context;
furi_hal_subghz_reset();
furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async);
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
with_view_model(
subghz_test_carrier->view, (SubGhzTestCarrierModel * model) {
model->frequency = subghz_frequencies_433_92_testing; // 433
model->real_frequency =
furi_hal_subghz_set_frequency(subghz_frequencies_testing[model->frequency]);
model->path = FuriHalSubGhzPathIsolate; // isolate
model->rssi = 0.0f;
model->status = SubGhzTestCarrierModelStatusRx;
return true;
});
furi_hal_subghz_rx();
furi_timer_start(subghz_test_carrier->timer, furi_kernel_get_tick_frequency() / 4);
}
void subghz_test_carrier_exit(void* context) {
furi_assert(context);
SubGhzTestCarrier* subghz_test_carrier = context;
furi_timer_stop(subghz_test_carrier->timer);
// Reinitialize IC to default state
furi_hal_subghz_sleep();
}
void subghz_test_carrier_rssi_timer_callback(void* context) {
furi_assert(context);
SubGhzTestCarrier* subghz_test_carrier = context;
with_view_model(
subghz_test_carrier->view, (SubGhzTestCarrierModel * model) {
if(model->status == SubGhzTestCarrierModelStatusRx) {
model->rssi = furi_hal_subghz_get_rssi();
return true;
}
return false;
});
}
SubGhzTestCarrier* subghz_test_carrier_alloc() {
SubGhzTestCarrier* subghz_test_carrier = malloc(sizeof(SubGhzTestCarrier));
// View allocation and configuration
subghz_test_carrier->view = view_alloc();
view_allocate_model(
subghz_test_carrier->view, ViewModelTypeLocking, sizeof(SubGhzTestCarrierModel));
view_set_context(subghz_test_carrier->view, subghz_test_carrier);
view_set_draw_callback(subghz_test_carrier->view, (ViewDrawCallback)subghz_test_carrier_draw);
view_set_input_callback(subghz_test_carrier->view, subghz_test_carrier_input);
view_set_enter_callback(subghz_test_carrier->view, subghz_test_carrier_enter);
view_set_exit_callback(subghz_test_carrier->view, subghz_test_carrier_exit);
subghz_test_carrier->timer = furi_timer_alloc(
subghz_test_carrier_rssi_timer_callback, FuriTimerTypePeriodic, subghz_test_carrier);
return subghz_test_carrier;
}
void subghz_test_carrier_free(SubGhzTestCarrier* subghz_test_carrier) {
furi_assert(subghz_test_carrier);
furi_timer_free(subghz_test_carrier->timer);
view_free(subghz_test_carrier->view);
free(subghz_test_carrier);
}
View* subghz_test_carrier_get_view(SubGhzTestCarrier* subghz_test_carrier) {
furi_assert(subghz_test_carrier);
return subghz_test_carrier->view;
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include <gui/view.h>
typedef enum {
SubGhzTestCarrierEventOnlyRx,
} SubGhzTestCarrierEvent;
typedef struct SubGhzTestCarrier SubGhzTestCarrier;
typedef void (*SubGhzTestCarrierCallback)(SubGhzTestCarrierEvent event, void* context);
void subghz_test_carrier_set_callback(
SubGhzTestCarrier* subghz_test_carrier,
SubGhzTestCarrierCallback callback,
void* context);
SubGhzTestCarrier* subghz_test_carrier_alloc();
void subghz_test_carrier_free(SubGhzTestCarrier* subghz_test_carrier);
View* subghz_test_carrier_get_view(SubGhzTestCarrier* subghz_test_carrier);

View File

@@ -0,0 +1,269 @@
#include "subghz_test_packet.h"
#include "../subghz_i.h"
#include "../helpers/subghz_testing.h"
#include <math.h>
#include <furi.h>
#include <furi_hal.h>
#include <input/input.h>
#include <toolbox/level_duration.h>
#include <lib/subghz/protocols/princeton_for_testing.h>
#define SUBGHZ_TEST_PACKET_COUNT 500
struct SubGhzTestPacket {
View* view;
FuriTimer* timer;
SubGhzDecoderPrinceton* decoder;
SubGhzEncoderPrinceton* encoder;
volatile size_t packet_rx;
SubGhzTestPacketCallback callback;
void* context;
};
typedef enum {
SubGhzTestPacketModelStatusRx,
SubGhzTestPacketModelStatusOnlyRx,
SubGhzTestPacketModelStatusTx,
} SubGhzTestPacketModelStatus;
typedef struct {
uint8_t frequency;
uint32_t real_frequency;
FuriHalSubGhzPath path;
float rssi;
size_t packets;
SubGhzTestPacketModelStatus status;
} SubGhzTestPacketModel;
volatile bool subghz_test_packet_overrun = false;
void subghz_test_packet_set_callback(
SubGhzTestPacket* subghz_test_packet,
SubGhzTestPacketCallback callback,
void* context) {
furi_assert(subghz_test_packet);
furi_assert(callback);
subghz_test_packet->callback = callback;
subghz_test_packet->context = context;
}
static void subghz_test_packet_rx_callback(bool level, uint32_t duration, void* context) {
furi_assert(context);
SubGhzTestPacket* instance = context;
subghz_decoder_princeton_for_testing_parse(instance->decoder, level, duration);
}
//todo
static void subghz_test_packet_rx_pt_callback(SubGhzDecoderPrinceton* parser, void* context) {
UNUSED(parser);
furi_assert(context);
SubGhzTestPacket* instance = context;
instance->packet_rx++;
}
static void subghz_test_packet_rssi_timer_callback(void* context) {
furi_assert(context);
SubGhzTestPacket* instance = context;
with_view_model(
instance->view, (SubGhzTestPacketModel * model) {
if(model->status == SubGhzTestPacketModelStatusRx) {
model->rssi = furi_hal_subghz_get_rssi();
model->packets = instance->packet_rx;
} else if(model->status == SubGhzTestPacketModelStatusTx) {
model->packets =
SUBGHZ_TEST_PACKET_COUNT -
subghz_encoder_princeton_for_testing_get_repeat_left(instance->encoder);
}
return true;
});
}
static void subghz_test_packet_draw(Canvas* canvas, SubGhzTestPacketModel* model) {
char buffer[64];
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 0, 8, "CC1101 Packet Test");
canvas_set_font(canvas, FontSecondary);
// Frequency
snprintf(
buffer,
sizeof(buffer),
"Freq: %03ld.%03ld.%03ld Hz",
model->real_frequency / 1000000 % 1000,
model->real_frequency / 1000 % 1000,
model->real_frequency % 1000);
canvas_draw_str(canvas, 0, 20, buffer);
// Path
char* path_name = "Unknown";
if(model->path == FuriHalSubGhzPathIsolate) {
path_name = "isolate";
} else if(model->path == FuriHalSubGhzPath433) {
path_name = "433MHz";
} else if(model->path == FuriHalSubGhzPath315) {
path_name = "315MHz";
} else if(model->path == FuriHalSubGhzPath868) {
path_name = "868MHz";
}
snprintf(buffer, sizeof(buffer), "Path: %d - %s", model->path, path_name);
canvas_draw_str(canvas, 0, 31, buffer);
snprintf(buffer, sizeof(buffer), "Packets: %d", model->packets);
canvas_draw_str(canvas, 0, 42, buffer);
if(model->status == SubGhzTestPacketModelStatusRx) {
snprintf(
buffer,
sizeof(buffer),
"RSSI: %ld.%ld dBm",
(int32_t)(model->rssi),
(int32_t)fabs(model->rssi * 10) % 10);
canvas_draw_str(canvas, 0, 53, buffer);
} else {
canvas_draw_str(canvas, 0, 53, "TX");
}
}
static bool subghz_test_packet_input(InputEvent* event, void* context) {
furi_assert(context);
SubGhzTestPacket* instance = context;
if(event->key == InputKeyBack || event->type != InputTypeShort) {
return false;
}
with_view_model(
instance->view, (SubGhzTestPacketModel * model) {
if(model->status == SubGhzTestPacketModelStatusRx) {
furi_hal_subghz_stop_async_rx();
} else if(model->status == SubGhzTestPacketModelStatusTx) {
subghz_encoder_princeton_for_testing_stop(instance->encoder, furi_get_tick());
furi_hal_subghz_stop_async_tx();
}
if(event->key == InputKeyLeft) {
if(model->frequency > 0) model->frequency--;
} else if(event->key == InputKeyRight) {
if(model->frequency < subghz_frequencies_count_testing - 1) model->frequency++;
} else if(event->key == InputKeyDown) {
if(model->path > 0) model->path--;
} else if(event->key == InputKeyUp) {
if(model->path < FuriHalSubGhzPath868) model->path++;
} else if(event->key == InputKeyOk) {
if(model->status == SubGhzTestPacketModelStatusRx) {
model->status = SubGhzTestPacketModelStatusTx;
} else {
model->status = SubGhzTestPacketModelStatusRx;
}
}
model->real_frequency =
furi_hal_subghz_set_frequency(subghz_frequencies_testing[model->frequency]);
furi_hal_subghz_set_path(model->path);
if(model->status == SubGhzTestPacketModelStatusRx) {
furi_hal_subghz_start_async_rx(subghz_test_packet_rx_callback, instance);
} else {
subghz_encoder_princeton_for_testing_set(
instance->encoder,
0x00AABBCC,
SUBGHZ_TEST_PACKET_COUNT,
subghz_frequencies_testing[model->frequency]);
if(!furi_hal_subghz_start_async_tx(
subghz_encoder_princeton_for_testing_yield, instance->encoder)) {
model->status = SubGhzTestPacketModelStatusOnlyRx;
instance->callback(SubGhzTestPacketEventOnlyRx, instance->context);
}
}
return true;
});
return true;
}
void subghz_test_packet_enter(void* context) {
furi_assert(context);
SubGhzTestPacket* instance = context;
furi_hal_subghz_reset();
furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async);
with_view_model(
instance->view, (SubGhzTestPacketModel * model) {
model->frequency = subghz_frequencies_433_92_testing;
model->real_frequency =
furi_hal_subghz_set_frequency(subghz_frequencies_testing[model->frequency]);
model->path = FuriHalSubGhzPathIsolate; // isolate
model->rssi = 0.0f;
model->status = SubGhzTestPacketModelStatusRx;
return true;
});
furi_hal_subghz_start_async_rx(subghz_test_packet_rx_callback, instance);
furi_timer_start(instance->timer, furi_kernel_get_tick_frequency() / 4);
}
void subghz_test_packet_exit(void* context) {
furi_assert(context);
SubGhzTestPacket* instance = context;
furi_timer_stop(instance->timer);
// Reinitialize IC to default state
with_view_model(
instance->view, (SubGhzTestPacketModel * model) {
if(model->status == SubGhzTestPacketModelStatusRx) {
furi_hal_subghz_stop_async_rx();
} else if(model->status == SubGhzTestPacketModelStatusTx) {
subghz_encoder_princeton_for_testing_stop(instance->encoder, furi_get_tick());
furi_hal_subghz_stop_async_tx();
}
return true;
});
furi_hal_subghz_sleep();
}
SubGhzTestPacket* subghz_test_packet_alloc() {
SubGhzTestPacket* instance = malloc(sizeof(SubGhzTestPacket));
// View allocation and configuration
instance->view = view_alloc();
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(SubGhzTestPacketModel));
view_set_context(instance->view, instance);
view_set_draw_callback(instance->view, (ViewDrawCallback)subghz_test_packet_draw);
view_set_input_callback(instance->view, subghz_test_packet_input);
view_set_enter_callback(instance->view, subghz_test_packet_enter);
view_set_exit_callback(instance->view, subghz_test_packet_exit);
instance->timer =
furi_timer_alloc(subghz_test_packet_rssi_timer_callback, FuriTimerTypePeriodic, instance);
instance->decoder = subghz_decoder_princeton_for_testing_alloc();
subghz_decoder_princeton_for_testing_set_callback(
instance->decoder, subghz_test_packet_rx_pt_callback, instance);
instance->encoder = subghz_encoder_princeton_for_testing_alloc();
return instance;
}
void subghz_test_packet_free(SubGhzTestPacket* instance) {
furi_assert(instance);
subghz_decoder_princeton_for_testing_free(instance->decoder);
subghz_encoder_princeton_for_testing_free(instance->encoder);
furi_timer_free(instance->timer);
view_free(instance->view);
free(instance);
}
View* subghz_test_packet_get_view(SubGhzTestPacket* instance) {
furi_assert(instance);
return instance->view;
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include <gui/view.h>
typedef enum {
SubGhzTestPacketEventOnlyRx,
} SubGhzTestPacketEvent;
typedef struct SubGhzTestPacket SubGhzTestPacket;
typedef void (*SubGhzTestPacketCallback)(SubGhzTestPacketEvent event, void* context);
void subghz_test_packet_set_callback(
SubGhzTestPacket* subghz_test_packet,
SubGhzTestPacketCallback callback,
void* context);
SubGhzTestPacket* subghz_test_packet_alloc();
void subghz_test_packet_free(SubGhzTestPacket* subghz_test_packet);
View* subghz_test_packet_get_view(SubGhzTestPacket* subghz_test_packet);

View File

@@ -0,0 +1,191 @@
#include "subghz_test_static.h"
#include "../subghz_i.h"
#include "../helpers/subghz_testing.h"
#include <math.h>
#include <furi.h>
#include <furi_hal.h>
#include <input/input.h>
#include <notification/notification_messages.h>
#include <lib/subghz/protocols/princeton_for_testing.h>
#define TAG "SubGhzTestStatic"
typedef enum {
SubGhzTestStaticStatusIDLE,
SubGhzTestStaticStatusTX,
} SubGhzTestStaticStatus;
static const uint32_t subghz_test_static_keys[] = {
0x0074BADE,
0x0074BADD,
0x0074BADB,
0x00E34A4E,
};
struct SubGhzTestStatic {
View* view;
SubGhzTestStaticStatus status_tx;
SubGhzEncoderPrinceton* encoder;
SubGhzTestStaticCallback callback;
void* context;
};
typedef struct {
uint8_t frequency;
uint32_t real_frequency;
uint8_t button;
} SubGhzTestStaticModel;
void subghz_test_static_set_callback(
SubGhzTestStatic* subghz_test_static,
SubGhzTestStaticCallback callback,
void* context) {
furi_assert(subghz_test_static);
furi_assert(callback);
subghz_test_static->callback = callback;
subghz_test_static->context = context;
}
void subghz_test_static_draw(Canvas* canvas, SubGhzTestStaticModel* model) {
char buffer[64];
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 0, 8, "CC1101 Static");
canvas_set_font(canvas, FontSecondary);
// Frequency
snprintf(
buffer,
sizeof(buffer),
"Freq: %03ld.%03ld.%03ld Hz",
model->real_frequency / 1000000 % 1000,
model->real_frequency / 1000 % 1000,
model->real_frequency % 1000);
canvas_draw_str(canvas, 0, 20, buffer);
snprintf(buffer, sizeof(buffer), "Key: %d", model->button);
canvas_draw_str(canvas, 0, 31, buffer);
}
bool subghz_test_static_input(InputEvent* event, void* context) {
furi_assert(context);
SubGhzTestStatic* instance = context;
if(event->key == InputKeyBack) {
return false;
}
with_view_model(
instance->view, (SubGhzTestStaticModel * model) {
if(event->type == InputTypeShort) {
if(event->key == InputKeyLeft) {
if(model->frequency > 0) model->frequency--;
} else if(event->key == InputKeyRight) {
if(model->frequency < subghz_frequencies_count_testing - 1) model->frequency++;
} else if(event->key == InputKeyDown) {
if(model->button > 0) model->button--;
} else if(event->key == InputKeyUp) {
if(model->button < 3) model->button++;
}
}
model->real_frequency = subghz_frequencies_testing[model->frequency];
if(event->key == InputKeyOk) {
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
if(event->type == InputTypePress) {
furi_hal_subghz_idle();
furi_hal_subghz_set_frequency_and_path(
subghz_frequencies_testing[model->frequency]);
if(!furi_hal_subghz_tx()) {
instance->callback(SubGhzTestStaticEventOnlyRx, instance->context);
} else {
notification_message_block(notification, &sequence_set_red_255);
FURI_LOG_I(TAG, "TX Start");
subghz_encoder_princeton_for_testing_set(
instance->encoder,
subghz_test_static_keys[model->button],
10000,
subghz_frequencies_testing[model->frequency]);
furi_hal_subghz_start_async_tx(
subghz_encoder_princeton_for_testing_yield, instance->encoder);
instance->status_tx = SubGhzTestStaticStatusTX;
}
} else if(event->type == InputTypeRelease) {
if(instance->status_tx == SubGhzTestStaticStatusTX) {
FURI_LOG_I(TAG, "TX Stop");
subghz_encoder_princeton_for_testing_stop(
instance->encoder, furi_get_tick());
subghz_encoder_princeton_for_testing_print_log(instance->encoder);
furi_hal_subghz_stop_async_tx();
notification_message(notification, &sequence_reset_red);
}
instance->status_tx = SubGhzTestStaticStatusIDLE;
}
furi_record_close(RECORD_NOTIFICATION);
}
return true;
});
return true;
}
void subghz_test_static_enter(void* context) {
furi_assert(context);
SubGhzTestStatic* instance = context;
furi_hal_subghz_reset();
furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async);
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
furi_hal_gpio_write(&gpio_cc1101_g0, false);
instance->status_tx = SubGhzTestStaticStatusIDLE;
with_view_model(
instance->view, (SubGhzTestStaticModel * model) {
model->frequency = subghz_frequencies_433_92_testing;
model->real_frequency = subghz_frequencies_testing[model->frequency];
model->button = 0;
return true;
});
}
void subghz_test_static_exit(void* context) {
furi_assert(context);
furi_hal_subghz_sleep();
}
SubGhzTestStatic* subghz_test_static_alloc() {
SubGhzTestStatic* instance = malloc(sizeof(SubGhzTestStatic));
// View allocation and configuration
instance->view = view_alloc();
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(SubGhzTestStaticModel));
view_set_context(instance->view, instance);
view_set_draw_callback(instance->view, (ViewDrawCallback)subghz_test_static_draw);
view_set_input_callback(instance->view, subghz_test_static_input);
view_set_enter_callback(instance->view, subghz_test_static_enter);
view_set_exit_callback(instance->view, subghz_test_static_exit);
instance->encoder = subghz_encoder_princeton_for_testing_alloc();
return instance;
}
void subghz_test_static_free(SubGhzTestStatic* instance) {
furi_assert(instance);
subghz_encoder_princeton_for_testing_free(instance->encoder);
view_free(instance->view);
free(instance);
}
View* subghz_test_static_get_view(SubGhzTestStatic* instance) {
furi_assert(instance);
return instance->view;
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include <gui/view.h>
typedef enum {
SubGhzTestStaticEventOnlyRx,
} SubGhzTestStaticEvent;
typedef struct SubGhzTestStatic SubGhzTestStatic;
typedef void (*SubGhzTestStaticCallback)(SubGhzTestStaticEvent event, void* context);
void subghz_test_static_set_callback(
SubGhzTestStatic* subghz_test_static,
SubGhzTestStaticCallback callback,
void* context);
SubGhzTestStatic* subghz_test_static_alloc();
void subghz_test_static_free(SubGhzTestStatic* subghz_static);
View* subghz_test_static_get_view(SubGhzTestStatic* subghz_static);

View File

@@ -0,0 +1,175 @@
#include "transmitter.h"
#include "../subghz_i.h"
#include <input/input.h>
#include <gui/elements.h>
struct SubGhzViewTransmitter {
View* view;
SubGhzViewTransmitterCallback callback;
void* context;
};
typedef struct {
string_t frequency_str;
string_t preset_str;
string_t key_str;
uint8_t show_button;
} SubGhzViewTransmitterModel;
void subghz_view_transmitter_set_callback(
SubGhzViewTransmitter* subghz_transmitter,
SubGhzViewTransmitterCallback callback,
void* context) {
furi_assert(subghz_transmitter);
subghz_transmitter->callback = callback;
subghz_transmitter->context = context;
}
void subghz_view_transmitter_add_data_to_show(
SubGhzViewTransmitter* subghz_transmitter,
const char* key_str,
const char* frequency_str,
const char* preset_str,
uint8_t show_button) {
furi_assert(subghz_transmitter);
with_view_model(
subghz_transmitter->view, (SubGhzViewTransmitterModel * model) {
string_set_str(model->key_str, key_str);
string_set_str(model->frequency_str, frequency_str);
string_set_str(model->preset_str, preset_str);
model->show_button = show_button;
return true;
});
}
static void subghz_view_transmitter_button_right(Canvas* canvas, const char* str) {
const uint8_t button_height = 13;
const uint8_t vertical_offset = 3;
const uint8_t horizontal_offset = 1;
const uint8_t string_width = canvas_string_width(canvas, str);
const Icon* icon = &I_ButtonCenter_7x7;
const uint8_t icon_offset = 3;
const uint8_t icon_width_with_offset = icon_get_width(icon) + icon_offset;
const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset;
const uint8_t x = (canvas_width(canvas) - button_width) / 2 + 40;
const uint8_t y = canvas_height(canvas);
canvas_draw_box(canvas, x, y - button_height, button_width, button_height);
canvas_draw_line(canvas, x - 1, y, x - 1, y - button_height + 0);
canvas_draw_line(canvas, x - 2, y, x - 2, y - button_height + 1);
canvas_draw_line(canvas, x - 3, y, x - 3, y - button_height + 2);
canvas_draw_line(canvas, x + button_width + 0, y, x + button_width + 0, y - button_height + 0);
canvas_draw_line(canvas, x + button_width + 1, y, x + button_width + 1, y - button_height + 1);
canvas_draw_line(canvas, x + button_width + 2, y, x + button_width + 2, y - button_height + 2);
canvas_invert_color(canvas);
canvas_draw_icon(
canvas, x + horizontal_offset, y - button_height + vertical_offset, &I_ButtonCenter_7x7);
canvas_draw_str(
canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str);
canvas_invert_color(canvas);
}
void subghz_view_transmitter_draw(Canvas* canvas, SubGhzViewTransmitterModel* model) {
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontSecondary);
elements_multiline_text(canvas, 0, 8, string_get_cstr(model->key_str));
canvas_draw_str(canvas, 78, 8, string_get_cstr(model->frequency_str));
canvas_draw_str(canvas, 113, 8, string_get_cstr(model->preset_str));
if(model->show_button) subghz_view_transmitter_button_right(canvas, "Send");
}
bool subghz_view_transmitter_input(InputEvent* event, void* context) {
furi_assert(context);
SubGhzViewTransmitter* subghz_transmitter = context;
bool can_be_sent = false;
if(event->key == InputKeyBack && event->type == InputTypeShort) {
with_view_model(
subghz_transmitter->view, (SubGhzViewTransmitterModel * model) {
string_reset(model->frequency_str);
string_reset(model->preset_str);
string_reset(model->key_str);
model->show_button = 0;
return false;
});
return false;
}
with_view_model(
subghz_transmitter->view, (SubGhzViewTransmitterModel * model) {
if(model->show_button) {
can_be_sent = true;
}
return true;
});
if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) {
subghz_transmitter->callback(
SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
return true;
} else if(can_be_sent && event->key == InputKeyOk && event->type == InputTypeRelease) {
subghz_transmitter->callback(
SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
return true;
}
return true;
}
void subghz_view_transmitter_enter(void* context) {
furi_assert(context);
}
void subghz_view_transmitter_exit(void* context) {
furi_assert(context);
}
SubGhzViewTransmitter* subghz_view_transmitter_alloc() {
SubGhzViewTransmitter* subghz_transmitter = malloc(sizeof(SubGhzViewTransmitter));
// View allocation and configuration
subghz_transmitter->view = view_alloc();
view_allocate_model(
subghz_transmitter->view, ViewModelTypeLocking, sizeof(SubGhzViewTransmitterModel));
view_set_context(subghz_transmitter->view, subghz_transmitter);
view_set_draw_callback(
subghz_transmitter->view, (ViewDrawCallback)subghz_view_transmitter_draw);
view_set_input_callback(subghz_transmitter->view, subghz_view_transmitter_input);
view_set_enter_callback(subghz_transmitter->view, subghz_view_transmitter_enter);
view_set_exit_callback(subghz_transmitter->view, subghz_view_transmitter_exit);
with_view_model(
subghz_transmitter->view, (SubGhzViewTransmitterModel * model) {
string_init(model->frequency_str);
string_init(model->preset_str);
string_init(model->key_str);
return true;
});
return subghz_transmitter;
}
void subghz_view_transmitter_free(SubGhzViewTransmitter* subghz_transmitter) {
furi_assert(subghz_transmitter);
with_view_model(
subghz_transmitter->view, (SubGhzViewTransmitterModel * model) {
string_clear(model->frequency_str);
string_clear(model->preset_str);
string_clear(model->key_str);
return true;
});
view_free(subghz_transmitter->view);
free(subghz_transmitter);
}
View* subghz_view_transmitter_get_view(SubGhzViewTransmitter* subghz_transmitter) {
furi_assert(subghz_transmitter);
return subghz_transmitter->view;
}

View File

@@ -0,0 +1,26 @@
#pragma once
#include <gui/view.h>
#include "../helpers/subghz_custom_event.h"
typedef struct SubGhzViewTransmitter SubGhzViewTransmitter;
typedef void (*SubGhzViewTransmitterCallback)(SubGhzCustomEvent event, void* context);
void subghz_view_transmitter_set_callback(
SubGhzViewTransmitter* subghz_transmitter,
SubGhzViewTransmitterCallback callback,
void* context);
SubGhzViewTransmitter* subghz_view_transmitter_alloc();
void subghz_view_transmitter_free(SubGhzViewTransmitter* subghz_transmitter);
View* subghz_view_transmitter_get_view(SubGhzViewTransmitter* subghz_transmitter);
void subghz_view_transmitter_add_data_to_show(
SubGhzViewTransmitter* subghz_transmitter,
const char* key_str,
const char* frequency_str,
const char* preset_str,
uint8_t show_button);