|
@@ -11,14 +11,13 @@
|
|
|
#include <lib/subghz/protocols/protocol_items.h>
|
|
#include <lib/subghz/protocols/protocol_items.h>
|
|
|
#include <flipper_format/flipper_format_i.h>
|
|
#include <flipper_format/flipper_format_i.h>
|
|
|
|
|
|
|
|
-#include "helpers/radio_device_loader.h"
|
|
|
|
|
|
|
+#include "helpers/subghz_txrx.h"
|
|
|
|
|
+#include <lib/subghz/blocks/custom_btn.h>
|
|
|
|
|
+#include <lib/subghz/protocols/raw.h>
|
|
|
|
|
|
|
|
#include "flipper_format_stream.h"
|
|
#include "flipper_format_stream.h"
|
|
|
#include "flipper_format_stream_i.h"
|
|
#include "flipper_format_stream_i.h"
|
|
|
|
|
|
|
|
-#include <lib/subghz/transmitter.h>
|
|
|
|
|
-#include <lib/subghz/protocols/raw.h>
|
|
|
|
|
-
|
|
|
|
|
#include "playlist_file.h"
|
|
#include "playlist_file.h"
|
|
|
#include "canvas_helper.h"
|
|
#include "canvas_helper.h"
|
|
|
|
|
|
|
@@ -59,7 +58,8 @@ typedef struct {
|
|
|
DisplayMeta* meta;
|
|
DisplayMeta* meta;
|
|
|
|
|
|
|
|
FuriString* file_path; // path to the playlist file
|
|
FuriString* file_path; // path to the playlist file
|
|
|
- const SubGhzDevice* radio_device;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ SubGhzTxRx* txrx; // subghz txrx instance
|
|
|
|
|
|
|
|
bool ctl_request_exit; // can be set to true if the worker should exit
|
|
bool ctl_request_exit; // can be set to true if the worker should exit
|
|
|
bool ctl_pause; // can be set to true if the worker should pause
|
|
bool ctl_pause; // can be set to true if the worker should pause
|
|
@@ -67,6 +67,8 @@ typedef struct {
|
|
|
bool ctl_request_prev; // can be set to true if the worker should go to the previous file
|
|
bool ctl_request_prev; // can be set to true if the worker should go to the previous file
|
|
|
|
|
|
|
|
bool is_running; // indicates if the worker is running
|
|
bool is_running; // indicates if the worker is running
|
|
|
|
|
+
|
|
|
|
|
+ bool raw_file_is_tx;
|
|
|
} PlaylistWorker;
|
|
} PlaylistWorker;
|
|
|
|
|
|
|
|
typedef struct {
|
|
typedef struct {
|
|
@@ -88,26 +90,42 @@ void meta_set_state(DisplayMeta* meta, int state) {
|
|
|
view_port_update(meta->view_port);
|
|
view_port_update(meta->view_port);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-static FuriHalSubGhzPreset str_to_preset(FuriString* preset) {
|
|
|
|
|
- if(furi_string_cmp_str(preset, "FuriHalSubGhzPresetOok270Async") == 0) {
|
|
|
|
|
- return FuriHalSubGhzPresetOok270Async;
|
|
|
|
|
- }
|
|
|
|
|
- if(furi_string_cmp_str(preset, "FuriHalSubGhzPresetOok650Async") == 0) {
|
|
|
|
|
- return FuriHalSubGhzPresetOok650Async;
|
|
|
|
|
- }
|
|
|
|
|
- if(furi_string_cmp_str(preset, "FuriHalSubGhzPreset2FSKDev238Async") == 0) {
|
|
|
|
|
- return FuriHalSubGhzPreset2FSKDev238Async;
|
|
|
|
|
- }
|
|
|
|
|
- if(furi_string_cmp_str(preset, "FuriHalSubGhzPreset2FSKDev476Async") == 0) {
|
|
|
|
|
- return FuriHalSubGhzPreset2FSKDev476Async;
|
|
|
|
|
- }
|
|
|
|
|
- if(furi_string_cmp_str(preset, "FuriHalSubGhzPresetMSK99_97KbAsync") == 0) {
|
|
|
|
|
- return FuriHalSubGhzPresetMSK99_97KbAsync;
|
|
|
|
|
- }
|
|
|
|
|
- if(furi_string_cmp_str(preset, "FuriHalSubGhzPresetMSK99_97KbAsync") == 0) {
|
|
|
|
|
- return FuriHalSubGhzPresetMSK99_97KbAsync;
|
|
|
|
|
- }
|
|
|
|
|
- return FuriHalSubGhzPresetCustom;
|
|
|
|
|
|
|
+typedef struct SubGhzNeedSaveContext {
|
|
|
|
|
+ PlaylistWorker* worker;
|
|
|
|
|
+ SubGhzTxRx* txrx;
|
|
|
|
|
+ char* file_path;
|
|
|
|
|
+} SubGhzNeedSaveContext;
|
|
|
|
|
+
|
|
|
|
|
+static void playlist_subghz_need_save_callback(void* context) {
|
|
|
|
|
+ FURI_LOG_I(TAG, "Saving updated subghz signal");
|
|
|
|
|
+ SubGhzNeedSaveContext* savectx = (SubGhzNeedSaveContext*)context;
|
|
|
|
|
+
|
|
|
|
|
+ FlipperFormat* ff = subghz_txrx_get_fff_data(savectx->txrx);
|
|
|
|
|
+
|
|
|
|
|
+ Stream* ff_stream = flipper_format_get_raw_stream(ff);
|
|
|
|
|
+ flipper_format_delete_key(ff, "Repeat");
|
|
|
|
|
+
|
|
|
|
|
+ do {
|
|
|
|
|
+ if(!storage_simply_remove(savectx->worker->storage, savectx->file_path)) {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Failed to delete subghz file before re-save");
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ stream_seek(ff_stream, 0, StreamOffsetFromStart);
|
|
|
|
|
+ stream_save_to_file(
|
|
|
|
|
+ ff_stream, savectx->worker->storage, savectx->file_path, FSOM_CREATE_ALWAYS);
|
|
|
|
|
+ if(storage_common_stat(savectx->worker->storage, savectx->file_path, NULL) != FSE_OK) {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Error verifying new subghz file after re-save");
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ } while(0);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void playlist_subghz_raw_end_callback(void* context) {
|
|
|
|
|
+ FURI_LOG_I(TAG, "Stopping TX on RAW");
|
|
|
|
|
+ furi_assert(context);
|
|
|
|
|
+ PlaylistWorker* worker = context;
|
|
|
|
|
+
|
|
|
|
|
+ worker->raw_file_is_tx = false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// -4: missing protocol
|
|
// -4: missing protocol
|
|
@@ -117,146 +135,278 @@ static FuriHalSubGhzPreset str_to_preset(FuriString* preset) {
|
|
|
// 0: ok
|
|
// 0: ok
|
|
|
// 1: resend
|
|
// 1: resend
|
|
|
// 2: exited
|
|
// 2: exited
|
|
|
-static int playlist_worker_process(
|
|
|
|
|
- PlaylistWorker* worker,
|
|
|
|
|
- FlipperFormat* fff_file,
|
|
|
|
|
- FlipperFormat* fff_data,
|
|
|
|
|
- const char* path,
|
|
|
|
|
- FuriString* preset,
|
|
|
|
|
- FuriString* protocol) {
|
|
|
|
|
|
|
+static int playlist_worker_process(PlaylistWorker* worker, const char* file_name) {
|
|
|
// actual sending of .sub file
|
|
// actual sending of .sub file
|
|
|
|
|
|
|
|
- if(!flipper_format_file_open_existing(fff_file, path)) {
|
|
|
|
|
- FURI_LOG_E(TAG, " (TX) Failed to open %s", path);
|
|
|
|
|
- return -1;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ FuriString* preset_name = furi_string_alloc();
|
|
|
|
|
+ FuriString* protocol_name = furi_string_alloc();
|
|
|
|
|
+ FuriString* temp_str = furi_string_alloc();
|
|
|
|
|
|
|
|
- // read frequency or default to 433.92MHz
|
|
|
|
|
- uint32_t frequency = 0;
|
|
|
|
|
- if(!flipper_format_read_uint32(fff_file, "Frequency", &frequency, 1)) {
|
|
|
|
|
- FURI_LOG_W(TAG, " (TX) Missing Frequency, defaulting to 433.92MHz");
|
|
|
|
|
- frequency = 433920000;
|
|
|
|
|
- }
|
|
|
|
|
- if(!subghz_devices_is_frequency_valid(worker->radio_device, frequency)) {
|
|
|
|
|
- FURI_LOG_E(
|
|
|
|
|
- TAG, " (TX) The SubGhz device used does not support the frequency %lu", frequency);
|
|
|
|
|
- return -2;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ uint8_t status = 0;
|
|
|
|
|
|
|
|
- // check if preset is present
|
|
|
|
|
- if(!flipper_format_read_string(fff_file, "Preset", preset)) {
|
|
|
|
|
- FURI_LOG_E(TAG, " (TX) Missing Preset");
|
|
|
|
|
- return -3;
|
|
|
|
|
- // load preset
|
|
|
|
|
- } else {
|
|
|
|
|
- uint32_t temp_data32;
|
|
|
|
|
- uint8_t* custom_preset_data;
|
|
|
|
|
- uint32_t custom_preset_data_size;
|
|
|
|
|
-
|
|
|
|
|
- if(!strcmp(furi_string_get_cstr(preset), "FuriHalSubGhzPresetCustom")) {
|
|
|
|
|
-
|
|
|
|
|
- if(!flipper_format_get_value_count(fff_file, "Custom_preset_data", &temp_data32))
|
|
|
|
|
- return -3;
|
|
|
|
|
- if(!temp_data32 || (temp_data32 % 2)) {
|
|
|
|
|
- FURI_LOG_E(TAG, " (TX) Missing Custom preset data");
|
|
|
|
|
- return -3;
|
|
|
|
|
- }
|
|
|
|
|
- custom_preset_data_size = sizeof(uint8_t) * temp_data32;
|
|
|
|
|
- custom_preset_data = malloc(custom_preset_data_size);
|
|
|
|
|
- if(!flipper_format_read_hex(
|
|
|
|
|
- fff_file,
|
|
|
|
|
- "Custom_preset_data",
|
|
|
|
|
- custom_preset_data,
|
|
|
|
|
- custom_preset_data_size)) {
|
|
|
|
|
- FURI_LOG_E(TAG, " (TX) Custom preset data read error");
|
|
|
|
|
- return -3;
|
|
|
|
|
- }
|
|
|
|
|
- subghz_devices_load_preset(worker->radio_device,str_to_preset(preset),custom_preset_data);
|
|
|
|
|
- free(custom_preset_data);
|
|
|
|
|
- FURI_LOG_D(TAG, " (TX) Custom preset loaded ");
|
|
|
|
|
- } else {
|
|
|
|
|
- subghz_devices_load_preset(worker->radio_device, str_to_preset(preset), NULL);
|
|
|
|
|
- FURI_LOG_D(TAG, " (TX) Standart preset loaded ");
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ do {
|
|
|
|
|
+ FlipperFormat* fff_data_file = flipper_format_file_alloc(worker->storage);
|
|
|
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ SubGhzNeedSaveContext save_context = {worker, worker->txrx, (char*)file_name};
|
|
|
|
|
+ subghz_txrx_set_need_save_callback(
|
|
|
|
|
+ worker->txrx, playlist_subghz_need_save_callback, &save_context);
|
|
|
|
|
|
|
|
- // check if protocol is present
|
|
|
|
|
- if(!flipper_format_read_string(fff_file, "Protocol", protocol)) {
|
|
|
|
|
- FURI_LOG_E(TAG, " (TX) Missing Protocol");
|
|
|
|
|
- return -4;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ Stream* fff_data_stream =
|
|
|
|
|
+ flipper_format_get_raw_stream(subghz_txrx_get_fff_data(worker->txrx));
|
|
|
|
|
+ stream_clean(fff_data_stream);
|
|
|
|
|
|
|
|
- if(!furi_string_cmp_str(protocol, "RAW")) {
|
|
|
|
|
- subghz_protocol_raw_gen_fff_data(
|
|
|
|
|
- fff_data, path, subghz_devices_get_name(worker->radio_device));
|
|
|
|
|
- } else {
|
|
|
|
|
- stream_copy_full(
|
|
|
|
|
- flipper_format_get_raw_stream(fff_file), flipper_format_get_raw_stream(fff_data));
|
|
|
|
|
- }
|
|
|
|
|
- flipper_format_file_close(fff_file);
|
|
|
|
|
- flipper_format_free(fff_file);
|
|
|
|
|
|
|
+ furi_string_reset(temp_str);
|
|
|
|
|
+ furi_string_reset(preset_name);
|
|
|
|
|
+ furi_string_reset(protocol_name);
|
|
|
|
|
|
|
|
- // (try to) send file
|
|
|
|
|
- SubGhzEnvironment* environment = subghz_environment_alloc();
|
|
|
|
|
- subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
|
|
|
|
|
- SubGhzTransmitter* transmitter =
|
|
|
|
|
- subghz_transmitter_alloc_init(environment, furi_string_get_cstr(protocol));
|
|
|
|
|
|
|
+ worker->raw_file_is_tx = false;
|
|
|
|
|
|
|
|
- subghz_transmitter_deserialize(transmitter, fff_data);
|
|
|
|
|
|
|
+ subghz_custom_btns_reset();
|
|
|
|
|
|
|
|
- frequency = subghz_devices_set_frequency(worker->radio_device, frequency);
|
|
|
|
|
|
|
+ uint32_t temp_data32;
|
|
|
|
|
|
|
|
- // Set device to TX and check frequency is alowed to TX
|
|
|
|
|
- if(!subghz_devices_set_tx(worker->radio_device)) {
|
|
|
|
|
- FURI_LOG_E(
|
|
|
|
|
- TAG,
|
|
|
|
|
- " (TX) The SubGhz device used does not support the frequency for transmitеing, %lu",
|
|
|
|
|
- frequency);
|
|
|
|
|
|
|
+ uint32_t frequency = 0;
|
|
|
|
|
|
|
|
- subghz_devices_idle(worker->radio_device);
|
|
|
|
|
|
|
+ if(!flipper_format_file_open_existing(fff_data_file, file_name)) {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Error opening %s", file_name);
|
|
|
|
|
+ flipper_format_free(fff_data_file);
|
|
|
|
|
|
|
|
- subghz_transmitter_free(transmitter);
|
|
|
|
|
- subghz_environment_free(environment);
|
|
|
|
|
- return -5;
|
|
|
|
|
- }
|
|
|
|
|
- FURI_LOG_D(TAG, " (TX) Start sending ...");
|
|
|
|
|
- int status = 0;
|
|
|
|
|
|
|
+ furi_string_free(preset_name);
|
|
|
|
|
+ furi_string_free(protocol_name);
|
|
|
|
|
+ furi_string_free(temp_str);
|
|
|
|
|
|
|
|
- subghz_devices_start_async_tx(worker->radio_device, subghz_transmitter_yield, transmitter);
|
|
|
|
|
- while(!subghz_devices_is_async_complete_tx(worker->radio_device)) {
|
|
|
|
|
- if(worker->ctl_request_exit) {
|
|
|
|
|
- FURI_LOG_D(TAG, " (TX) Requested to exit. Cancelling sending...");
|
|
|
|
|
- status = 2;
|
|
|
|
|
- break;
|
|
|
|
|
|
|
+ return -1;
|
|
|
}
|
|
}
|
|
|
- if(worker->ctl_pause) {
|
|
|
|
|
- FURI_LOG_D(TAG, " (TX) Requested to pause. Cancelling and resending...");
|
|
|
|
|
- status = 1;
|
|
|
|
|
- break;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Missing or incorrect header");
|
|
|
|
|
+ flipper_format_file_close(fff_data_file);
|
|
|
|
|
+ flipper_format_free(fff_data_file);
|
|
|
|
|
+
|
|
|
|
|
+ furi_string_free(preset_name);
|
|
|
|
|
+ furi_string_free(protocol_name);
|
|
|
|
|
+ furi_string_free(temp_str);
|
|
|
|
|
+
|
|
|
|
|
+ return -1;
|
|
|
}
|
|
}
|
|
|
- if(worker->ctl_request_skip) {
|
|
|
|
|
- worker->ctl_request_skip = false;
|
|
|
|
|
- FURI_LOG_D(TAG, " (TX) Requested to skip. Cancelling and resending...");
|
|
|
|
|
- status = 0;
|
|
|
|
|
- break;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ if(((!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_KEY_FILE_TYPE)) ||
|
|
|
|
|
+ (!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE))) &&
|
|
|
|
|
+ temp_data32 == SUBGHZ_KEY_FILE_VERSION) {
|
|
|
|
|
+ } else {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Type or version mismatch");
|
|
|
|
|
+ flipper_format_file_close(fff_data_file);
|
|
|
|
|
+ flipper_format_free(fff_data_file);
|
|
|
|
|
+
|
|
|
|
|
+ furi_string_free(preset_name);
|
|
|
|
|
+ furi_string_free(protocol_name);
|
|
|
|
|
+ furi_string_free(temp_str);
|
|
|
|
|
+
|
|
|
|
|
+ return -1;
|
|
|
}
|
|
}
|
|
|
- if(worker->ctl_request_prev) {
|
|
|
|
|
- worker->ctl_request_prev = false;
|
|
|
|
|
- FURI_LOG_D(TAG, " (TX) Requested to prev. Cancelling and resending...");
|
|
|
|
|
- status = 3;
|
|
|
|
|
- break;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ SubGhzSetting* setting = subghz_txrx_get_setting(worker->txrx);
|
|
|
|
|
+ if(!flipper_format_read_uint32(fff_data_file, "Frequency", &frequency, 1)) {
|
|
|
|
|
+ FURI_LOG_W(TAG, "Missing Frequency. Setting default frequency");
|
|
|
|
|
+ frequency = subghz_setting_get_default_frequency(setting);
|
|
|
|
|
+ } else if(!subghz_txrx_radio_device_is_frequecy_valid(worker->txrx, frequency)) {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Frequency not supported on the chosen radio module");
|
|
|
|
|
+ flipper_format_file_close(fff_data_file);
|
|
|
|
|
+ flipper_format_free(fff_data_file);
|
|
|
|
|
+
|
|
|
|
|
+ furi_string_free(preset_name);
|
|
|
|
|
+ furi_string_free(protocol_name);
|
|
|
|
|
+ furi_string_free(temp_str);
|
|
|
|
|
+
|
|
|
|
|
+ return -1;
|
|
|
}
|
|
}
|
|
|
- furi_delay_ms(50);
|
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- FURI_LOG_D(TAG, " (TX) Done sending.");
|
|
|
|
|
|
|
+ if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Missing Preset");
|
|
|
|
|
+ flipper_format_file_close(fff_data_file);
|
|
|
|
|
+ flipper_format_free(fff_data_file);
|
|
|
|
|
|
|
|
- subghz_devices_stop_async_tx(worker->radio_device);
|
|
|
|
|
- subghz_devices_idle(worker->radio_device);
|
|
|
|
|
|
|
+ furi_string_free(preset_name);
|
|
|
|
|
+ furi_string_free(protocol_name);
|
|
|
|
|
+ furi_string_free(temp_str);
|
|
|
|
|
+
|
|
|
|
|
+ return -3;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- subghz_transmitter_free(transmitter);
|
|
|
|
|
- subghz_environment_free(environment);
|
|
|
|
|
|
|
+ furi_string_set_str(
|
|
|
|
|
+ temp_str, subghz_txrx_get_preset_name(worker->txrx, furi_string_get_cstr(temp_str)));
|
|
|
|
|
+ if(!strcmp(furi_string_get_cstr(temp_str), "")) {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Unknown preset");
|
|
|
|
|
+ flipper_format_file_close(fff_data_file);
|
|
|
|
|
+ flipper_format_free(fff_data_file);
|
|
|
|
|
+
|
|
|
|
|
+ furi_string_free(preset_name);
|
|
|
|
|
+ furi_string_free(protocol_name);
|
|
|
|
|
+ furi_string_free(temp_str);
|
|
|
|
|
+
|
|
|
|
|
+ return -3;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if(!strcmp(furi_string_get_cstr(temp_str), "CUSTOM")) {
|
|
|
|
|
+ subghz_setting_delete_custom_preset(setting, furi_string_get_cstr(temp_str));
|
|
|
|
|
+ if(!subghz_setting_load_custom_preset(
|
|
|
|
|
+ setting, furi_string_get_cstr(temp_str), fff_data_file)) {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Missing Custom preset");
|
|
|
|
|
+ flipper_format_file_close(fff_data_file);
|
|
|
|
|
+ flipper_format_free(fff_data_file);
|
|
|
|
|
+
|
|
|
|
|
+ furi_string_free(preset_name);
|
|
|
|
|
+ furi_string_free(protocol_name);
|
|
|
|
|
+ furi_string_free(temp_str);
|
|
|
|
|
+
|
|
|
|
|
+ return -1;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ furi_string_set(preset_name, temp_str);
|
|
|
|
|
+ size_t preset_index =
|
|
|
|
|
+ subghz_setting_get_inx_preset_by_name(setting, furi_string_get_cstr(preset_name));
|
|
|
|
|
+ subghz_txrx_set_preset(
|
|
|
|
|
+ worker->txrx,
|
|
|
|
|
+ furi_string_get_cstr(preset_name),
|
|
|
|
|
+ frequency,
|
|
|
|
|
+ subghz_setting_get_preset_data(setting, preset_index),
|
|
|
|
|
+ subghz_setting_get_preset_data_size(setting, preset_index));
|
|
|
|
|
+
|
|
|
|
|
+ // Load Protocol
|
|
|
|
|
+ if(!flipper_format_read_string(fff_data_file, "Protocol", protocol_name)) {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Missing protocol");
|
|
|
|
|
+ flipper_format_file_close(fff_data_file);
|
|
|
|
|
+ flipper_format_free(fff_data_file);
|
|
|
|
|
+
|
|
|
|
|
+ furi_string_free(preset_name);
|
|
|
|
|
+ furi_string_free(protocol_name);
|
|
|
|
|
+ furi_string_free(temp_str);
|
|
|
|
|
+
|
|
|
|
|
+ return -4;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ FlipperFormat* fff_data = subghz_txrx_get_fff_data(worker->txrx);
|
|
|
|
|
+ if(!strcmp(furi_string_get_cstr(protocol_name), "RAW")) {
|
|
|
|
|
+ subghz_protocol_raw_gen_fff_data(
|
|
|
|
|
+ fff_data, file_name, subghz_txrx_radio_device_get_name(worker->txrx));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ stream_copy_full(
|
|
|
|
|
+ flipper_format_get_raw_stream(fff_data_file),
|
|
|
|
|
+ flipper_format_get_raw_stream(fff_data));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ uint32_t repeat = 200;
|
|
|
|
|
+ if(!flipper_format_insert_or_update_uint32(fff_data, "Repeat", &repeat, 1)) {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Unable Repeat");
|
|
|
|
|
+ //
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if(subghz_txrx_load_decoder_by_name_protocol(
|
|
|
|
|
+ worker->txrx, furi_string_get_cstr(protocol_name))) {
|
|
|
|
|
+ SubGhzProtocolStatus status = subghz_protocol_decoder_base_deserialize(
|
|
|
|
|
+ subghz_txrx_get_decoder(worker->txrx), fff_data);
|
|
|
|
|
+ if(status != SubGhzProtocolStatusOk) {
|
|
|
|
|
+ flipper_format_file_close(fff_data_file);
|
|
|
|
|
+ flipper_format_free(fff_data_file);
|
|
|
|
|
+
|
|
|
|
|
+ furi_string_free(preset_name);
|
|
|
|
|
+ furi_string_free(protocol_name);
|
|
|
|
|
+ furi_string_free(temp_str);
|
|
|
|
|
+
|
|
|
|
|
+ return -2;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Protocol not found: %s", furi_string_get_cstr(protocol_name));
|
|
|
|
|
+ flipper_format_file_close(fff_data_file);
|
|
|
|
|
+ flipper_format_free(fff_data_file);
|
|
|
|
|
+
|
|
|
|
|
+ furi_string_free(preset_name);
|
|
|
|
|
+ furi_string_free(protocol_name);
|
|
|
|
|
+ furi_string_free(temp_str);
|
|
|
|
|
+
|
|
|
|
|
+ return -2;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ flipper_format_file_close(fff_data_file);
|
|
|
|
|
+ flipper_format_free(fff_data_file);
|
|
|
|
|
+
|
|
|
|
|
+ FURI_LOG_I(TAG, "Starting TX");
|
|
|
|
|
+
|
|
|
|
|
+ if(subghz_txrx_tx_start(worker->txrx, subghz_txrx_get_fff_data(worker->txrx)) !=
|
|
|
|
|
+ SubGhzTxRxStartTxStateOk) {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Failed to start TX");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ bool skip_extra_stop = false;
|
|
|
|
|
+ FURI_LOG_D(TAG, "Checking if file is RAW...");
|
|
|
|
|
+ if(!strcmp(furi_string_get_cstr(protocol_name), "RAW")) {
|
|
|
|
|
+ subghz_txrx_set_raw_file_encoder_worker_callback_end(
|
|
|
|
|
+ worker->txrx, playlist_subghz_raw_end_callback, worker);
|
|
|
|
|
+ worker->raw_file_is_tx = true;
|
|
|
|
|
+ skip_extra_stop = true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ do {
|
|
|
|
|
+ if(worker->ctl_request_exit) {
|
|
|
|
|
+ FURI_LOG_D(TAG, " (TX) Requested to exit. Cancelling sending...");
|
|
|
|
|
+ status = 2;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ if(worker->ctl_pause) {
|
|
|
|
|
+ FURI_LOG_D(TAG, " (TX) Requested to pause. Cancelling...");
|
|
|
|
|
+ status = 1;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ if(worker->ctl_request_skip) {
|
|
|
|
|
+ worker->ctl_request_skip = false;
|
|
|
|
|
+ FURI_LOG_D(TAG, " (TX) Requested to skip. Cancelling and skipping...");
|
|
|
|
|
+ status = 0;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ if(worker->ctl_request_prev) {
|
|
|
|
|
+ worker->ctl_request_prev = false;
|
|
|
|
|
+ FURI_LOG_D(TAG, " (TX) Requested to prev. Cancelling and resending...");
|
|
|
|
|
+ status = 3;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ furi_delay_ms(1);
|
|
|
|
|
+ } while(worker->raw_file_is_tx);
|
|
|
|
|
+
|
|
|
|
|
+ if(!worker->raw_file_is_tx && !skip_extra_stop) {
|
|
|
|
|
+ if(worker->ctl_request_exit) {
|
|
|
|
|
+ FURI_LOG_D(TAG, " (TX) Requested to exit. Cancelling sending...");
|
|
|
|
|
+ status = 2;
|
|
|
|
|
+ }
|
|
|
|
|
+ if(worker->ctl_pause) {
|
|
|
|
|
+ FURI_LOG_D(TAG, " (TX) Requested to pause. Cancelling...");
|
|
|
|
|
+ status = 1;
|
|
|
|
|
+ }
|
|
|
|
|
+ if(worker->ctl_request_skip) {
|
|
|
|
|
+ worker->ctl_request_skip = false;
|
|
|
|
|
+ FURI_LOG_D(TAG, " (TX) Requested to skip. Cancelling and skipping...");
|
|
|
|
|
+ status = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ if(worker->ctl_request_prev) {
|
|
|
|
|
+ worker->ctl_request_prev = false;
|
|
|
|
|
+ FURI_LOG_D(TAG, " (TX) Requested to prev. Cancelling and resending...");
|
|
|
|
|
+ status = 3;
|
|
|
|
|
+ }
|
|
|
|
|
+ furi_delay_ms(1600);
|
|
|
|
|
+ subghz_txrx_stop(worker->txrx);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ furi_delay_ms(20);
|
|
|
|
|
+ subghz_txrx_stop(worker->txrx);
|
|
|
|
|
+ }
|
|
|
|
|
+ skip_extra_stop = false;
|
|
|
|
|
+
|
|
|
|
|
+ } while(false);
|
|
|
|
|
+
|
|
|
|
|
+ FURI_LOG_D(TAG, "TX Finished");
|
|
|
|
|
+ subghz_custom_btns_reset();
|
|
|
|
|
+
|
|
|
|
|
+ furi_string_free(preset_name);
|
|
|
|
|
+ furi_string_free(protocol_name);
|
|
|
|
|
+ furi_string_free(temp_str);
|
|
|
|
|
|
|
|
return status;
|
|
return status;
|
|
|
}
|
|
}
|
|
@@ -291,12 +441,8 @@ void updatePlayListView(PlaylistWorker* worker, const char* str) {
|
|
|
|
|
|
|
|
static bool playlist_worker_play_playlist_once(
|
|
static bool playlist_worker_play_playlist_once(
|
|
|
PlaylistWorker* worker,
|
|
PlaylistWorker* worker,
|
|
|
- Storage* storage,
|
|
|
|
|
FlipperFormat* fff_head,
|
|
FlipperFormat* fff_head,
|
|
|
- FlipperFormat* fff_data,
|
|
|
|
|
- FuriString* data,
|
|
|
|
|
- FuriString* preset,
|
|
|
|
|
- FuriString* protocol) {
|
|
|
|
|
|
|
+ FuriString* data) {
|
|
|
//
|
|
//
|
|
|
if(!flipper_format_rewind(fff_head)) {
|
|
if(!flipper_format_rewind(fff_head)) {
|
|
|
FURI_LOG_E(TAG, "Failed to rewind file");
|
|
FURI_LOG_E(TAG, "Failed to rewind file");
|
|
@@ -329,17 +475,15 @@ static bool playlist_worker_play_playlist_once(
|
|
|
|
|
|
|
|
FURI_LOG_D(TAG, "(worker) Sending %s", str);
|
|
FURI_LOG_D(TAG, "(worker) Sending %s", str);
|
|
|
|
|
|
|
|
- FlipperFormat* fff_file = flipper_format_file_alloc(storage);
|
|
|
|
|
|
|
+ int status = playlist_worker_process(worker, str);
|
|
|
|
|
|
|
|
- int status =
|
|
|
|
|
- playlist_worker_process(worker, fff_file, fff_data, str, preset, protocol);
|
|
|
|
|
|
|
+ FURI_LOG_D(TAG, "Finished file, status %d", status);
|
|
|
|
|
|
|
|
// if there was an error, fff_file is not already freed
|
|
// if there was an error, fff_file is not already freed
|
|
|
// why do you do this with numbers without meaning x_x
|
|
// why do you do this with numbers without meaning x_x
|
|
|
if(status < 0) {
|
|
if(status < 0) {
|
|
|
if(status > -5) {
|
|
if(status > -5) {
|
|
|
- flipper_format_file_close(fff_file);
|
|
|
|
|
- flipper_format_free(fff_file);
|
|
|
|
|
|
|
+ //
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
furi_check(
|
|
furi_check(
|
|
@@ -382,28 +526,21 @@ static bool playlist_worker_play_playlist_once(
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static int32_t playlist_worker_thread(void* ctx) {
|
|
static int32_t playlist_worker_thread(void* ctx) {
|
|
|
- Storage* storage = furi_record_open(RECORD_STORAGE);
|
|
|
|
|
- FlipperFormat* fff_head = flipper_format_file_alloc(storage);
|
|
|
|
|
-
|
|
|
|
|
PlaylistWorker* worker = ctx;
|
|
PlaylistWorker* worker = ctx;
|
|
|
|
|
+
|
|
|
|
|
+ FlipperFormat* fff_head = flipper_format_file_alloc(worker->storage);
|
|
|
|
|
+
|
|
|
if(!flipper_format_file_open_existing(fff_head, furi_string_get_cstr(worker->file_path))) {
|
|
if(!flipper_format_file_open_existing(fff_head, furi_string_get_cstr(worker->file_path))) {
|
|
|
FURI_LOG_E(TAG, "Failed to open %s", furi_string_get_cstr(worker->file_path));
|
|
FURI_LOG_E(TAG, "Failed to open %s", furi_string_get_cstr(worker->file_path));
|
|
|
worker->is_running = false;
|
|
worker->is_running = false;
|
|
|
|
|
|
|
|
- furi_record_close(RECORD_STORAGE);
|
|
|
|
|
flipper_format_free(fff_head);
|
|
flipper_format_free(fff_head);
|
|
|
return 0;
|
|
return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
playlist_worker_wait_pause(worker);
|
|
playlist_worker_wait_pause(worker);
|
|
|
- FlipperFormat* fff_data = flipper_format_string_alloc();
|
|
|
|
|
|
|
|
|
|
- FuriString* data;
|
|
|
|
|
- FuriString* preset;
|
|
|
|
|
- FuriString* protocol;
|
|
|
|
|
- data = furi_string_alloc();
|
|
|
|
|
- preset = furi_string_alloc();
|
|
|
|
|
- protocol = furi_string_alloc();
|
|
|
|
|
|
|
+ FuriString* data = furi_string_alloc();
|
|
|
|
|
|
|
|
for(int i = 0; i < MAX(1, worker->meta->playlist_repetitions); i++) {
|
|
for(int i = 0; i < MAX(1, worker->meta->playlist_repetitions); i++) {
|
|
|
// infinite repetitions if playlist_repetitions is 0
|
|
// infinite repetitions if playlist_repetitions is 0
|
|
@@ -424,20 +561,14 @@ static int32_t playlist_worker_thread(void* ctx) {
|
|
|
worker->meta->current_playlist_repetition,
|
|
worker->meta->current_playlist_repetition,
|
|
|
worker->meta->playlist_repetitions);
|
|
worker->meta->playlist_repetitions);
|
|
|
|
|
|
|
|
- if(!playlist_worker_play_playlist_once(
|
|
|
|
|
- worker, storage, fff_head, fff_data, data, preset, protocol)) {
|
|
|
|
|
|
|
+ if(!playlist_worker_play_playlist_once(worker, fff_head, data)) {
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- furi_record_close(RECORD_STORAGE);
|
|
|
|
|
flipper_format_free(fff_head);
|
|
flipper_format_free(fff_head);
|
|
|
|
|
|
|
|
furi_string_free(data);
|
|
furi_string_free(data);
|
|
|
- furi_string_free(preset);
|
|
|
|
|
- furi_string_free(protocol);
|
|
|
|
|
-
|
|
|
|
|
- flipper_format_free(fff_data);
|
|
|
|
|
|
|
|
|
|
FURI_LOG_D(TAG, "Done reading. Read %d data lines.", worker->meta->current_count);
|
|
FURI_LOG_D(TAG, "Done reading. Read %d data lines.", worker->meta->current_count);
|
|
|
worker->is_running = false;
|
|
worker->is_running = false;
|
|
@@ -487,22 +618,18 @@ PlaylistWorker* playlist_worker_alloc(DisplayMeta* meta) {
|
|
|
|
|
|
|
|
instance->thread = furi_thread_alloc();
|
|
instance->thread = furi_thread_alloc();
|
|
|
furi_thread_set_name(instance->thread, "PlaylistWorker");
|
|
furi_thread_set_name(instance->thread, "PlaylistWorker");
|
|
|
- furi_thread_set_stack_size(instance->thread, 2048);
|
|
|
|
|
|
|
+ furi_thread_set_stack_size(instance->thread, 6 * 1024);
|
|
|
furi_thread_set_context(instance->thread, instance);
|
|
furi_thread_set_context(instance->thread, instance);
|
|
|
furi_thread_set_callback(instance->thread, playlist_worker_thread);
|
|
furi_thread_set_callback(instance->thread, playlist_worker_thread);
|
|
|
|
|
|
|
|
|
|
+ instance->storage = furi_record_open(RECORD_STORAGE);
|
|
|
|
|
+
|
|
|
instance->meta = meta;
|
|
instance->meta = meta;
|
|
|
instance->ctl_pause = true; // require the user to manually start the worker
|
|
instance->ctl_pause = true; // require the user to manually start the worker
|
|
|
|
|
|
|
|
instance->file_path = furi_string_alloc();
|
|
instance->file_path = furi_string_alloc();
|
|
|
|
|
|
|
|
- subghz_devices_init();
|
|
|
|
|
-
|
|
|
|
|
- instance->radio_device =
|
|
|
|
|
- radio_device_loader_set(instance->radio_device, SubGhzRadioDeviceTypeExternalCC1101);
|
|
|
|
|
-
|
|
|
|
|
- subghz_devices_reset(instance->radio_device);
|
|
|
|
|
- subghz_devices_idle(instance->radio_device);
|
|
|
|
|
|
|
+ instance->txrx = subghz_txrx_alloc();
|
|
|
|
|
|
|
|
return instance;
|
|
return instance;
|
|
|
}
|
|
}
|
|
@@ -512,10 +639,9 @@ void playlist_worker_free(PlaylistWorker* instance) {
|
|
|
furi_thread_free(instance->thread);
|
|
furi_thread_free(instance->thread);
|
|
|
furi_string_free(instance->file_path);
|
|
furi_string_free(instance->file_path);
|
|
|
|
|
|
|
|
- subghz_devices_sleep(instance->radio_device);
|
|
|
|
|
- radio_device_loader_end(instance->radio_device);
|
|
|
|
|
|
|
+ subghz_txrx_free(instance->txrx);
|
|
|
|
|
|
|
|
- subghz_devices_deinit();
|
|
|
|
|
|
|
+ furi_record_close(RECORD_STORAGE);
|
|
|
|
|
|
|
|
free(instance);
|
|
free(instance);
|
|
|
}
|
|
}
|
|
@@ -759,10 +885,9 @@ void playlist_start_worker(Playlist* app, DisplayMeta* meta) {
|
|
|
app->worker = playlist_worker_alloc(meta);
|
|
app->worker = playlist_worker_alloc(meta);
|
|
|
|
|
|
|
|
// count playlist items
|
|
// count playlist items
|
|
|
- Storage* storage = furi_record_open(RECORD_STORAGE);
|
|
|
|
|
|
|
+
|
|
|
app->meta->total_count =
|
|
app->meta->total_count =
|
|
|
- playlist_count_playlist_items(storage, furi_string_get_cstr(app->file_path));
|
|
|
|
|
- furi_record_close(RECORD_STORAGE);
|
|
|
|
|
|
|
+ playlist_count_playlist_items(app->worker->storage, furi_string_get_cstr(app->file_path));
|
|
|
|
|
|
|
|
// start thread
|
|
// start thread
|
|
|
playlist_worker_start(app->worker, furi_string_get_cstr(app->file_path));
|
|
playlist_worker_start(app->worker, furi_string_get_cstr(app->file_path));
|