Merge remote-tracking branch 'origin/release-candidate' into release

This commit is contained in:
Aleksandr Kutuzov 2023-07-20 15:21:02 +04:00
commit bf262efed5
201 changed files with 3531 additions and 995 deletions

View File

@ -4,13 +4,13 @@
"version": "2.0.0",
"tasks": [
{
"label": "[Release] Build",
"label": "[Release] Build Firmware",
"group": "build",
"type": "shell",
"command": "./fbt COMPACT=1 DEBUG=0"
},
{
"label": "[Debug] Build",
"label": "[Debug] Build Firmware",
"group": "build",
"type": "shell",
"command": "./fbt"
@ -123,17 +123,29 @@
"type": "shell",
"command": "./fbt COMPACT=1 DEBUG=0 fap_dist"
},
{
"label": "[Debug] Build App",
"group": "build",
"type": "shell",
"command": "./fbt build APPSRC=${relativeFileDirname}"
},
{
"label": "[Release] Build App",
"group": "build",
"type": "shell",
"command": "./fbt COMPACT=1 DEBUG=0 build APPSRC=${relativeFileDirname}"
},
{
"label": "[Debug] Launch App on Flipper",
"group": "build",
"type": "shell",
"command": "./fbt launch_app APPSRC=${relativeFileDirname}"
"command": "./fbt launch APPSRC=${relativeFileDirname}"
},
{
"label": "[Release] Launch App on Flipper",
"group": "build",
"type": "shell",
"command": "./fbt COMPACT=1 DEBUG=0 launch_app APPSRC=${relativeFileDirname}"
"command": "./fbt COMPACT=1 DEBUG=0 launch APPSRC=${relativeFileDirname}"
},
{
"label": "[Debug] Launch App on Flipper with Serial Console",

View File

@ -0,0 +1,14 @@
App(
appid="subghz_test",
name="Sub-Ghz test",
apptype=FlipperAppType.DEBUG,
targets=["f7"],
entry_point="subghz_test_app",
requires=["gui"],
stack_size=4 * 1024,
order=50,
fap_icon="subghz_test_10px.png",
fap_category="Debug",
fap_icon_assets="images",
fap_version="0.1",
)

View File

@ -0,0 +1,7 @@
#pragma once
typedef enum {
//SubGhzTestCustomEvent
SubGhzTestCustomEventStartId = 100,
SubGhzTestCustomEventSceneShowOnlyRX,
} SubGhzTestCustomEvent;

View File

@ -1,4 +1,4 @@
#include "subghz_testing.h"
#include "subghz_test_frequency.h"
const uint32_t subghz_frequencies_testing[] = {
/* 300 - 348 */

View File

@ -1,5 +1,5 @@
#pragma once
#include "../subghz_i.h"
#include "../subghz_test_app_i.h"
extern const uint32_t subghz_frequencies_testing[];
extern const uint32_t subghz_frequencies_count_testing;

View File

@ -0,0 +1,18 @@
#pragma once
#include <furi.h>
#include <furi_hal.h>
#define SUBGHZ_TEST_VERSION_APP "0.1"
#define SUBGHZ_TEST_DEVELOPED "SkorP"
#define SUBGHZ_TEST_GITHUB "https://github.com/flipperdevices/flipperzero-firmware"
typedef enum {
SubGhzTestViewVariableItemList,
SubGhzTestViewSubmenu,
SubGhzTestViewStatic,
SubGhzTestViewCarrier,
SubGhzTestViewPacket,
SubGhzTestViewWidget,
SubGhzTestViewPopup,
} SubGhzTestView;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,244 @@
#include "math.h"
uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count) {
uint64_t reverse_key = 0;
for(uint8_t i = 0; i < bit_count; i++) {
reverse_key = reverse_key << 1 | bit_read(key, i);
}
return reverse_key;
}
uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count) {
uint8_t parity = 0;
for(uint8_t i = 0; i < bit_count; i++) {
parity += bit_read(key, i);
}
return parity & 0x01;
}
uint8_t subghz_protocol_blocks_crc4(
uint8_t const message[],
size_t size,
uint8_t polynomial,
uint8_t init) {
uint8_t remainder = init << 4; // LSBs are unused
uint8_t poly = polynomial << 4;
uint8_t bit;
while(size--) {
remainder ^= *message++;
for(bit = 0; bit < 8; bit++) {
if(remainder & 0x80) {
remainder = (remainder << 1) ^ poly;
} else {
remainder = (remainder << 1);
}
}
}
return remainder >> 4 & 0x0f; // discard the LSBs
}
uint8_t subghz_protocol_blocks_crc7(
uint8_t const message[],
size_t size,
uint8_t polynomial,
uint8_t init) {
uint8_t remainder = init << 1; // LSB is unused
uint8_t poly = polynomial << 1;
for(size_t byte = 0; byte < size; ++byte) {
remainder ^= message[byte];
for(uint8_t bit = 0; bit < 8; ++bit) {
if(remainder & 0x80) {
remainder = (remainder << 1) ^ poly;
} else {
remainder = (remainder << 1);
}
}
}
return remainder >> 1 & 0x7f; // discard the LSB
}
uint8_t subghz_protocol_blocks_crc8(
uint8_t const message[],
size_t size,
uint8_t polynomial,
uint8_t init) {
uint8_t remainder = init;
for(size_t byte = 0; byte < size; ++byte) {
remainder ^= message[byte];
for(uint8_t bit = 0; bit < 8; ++bit) {
if(remainder & 0x80) {
remainder = (remainder << 1) ^ polynomial;
} else {
remainder = (remainder << 1);
}
}
}
return remainder;
}
uint8_t subghz_protocol_blocks_crc8le(
uint8_t const message[],
size_t size,
uint8_t polynomial,
uint8_t init) {
uint8_t remainder = subghz_protocol_blocks_reverse_key(init, 8);
polynomial = subghz_protocol_blocks_reverse_key(polynomial, 8);
for(size_t byte = 0; byte < size; ++byte) {
remainder ^= message[byte];
for(uint8_t bit = 0; bit < 8; ++bit) {
if(remainder & 1) {
remainder = (remainder >> 1) ^ polynomial;
} else {
remainder = (remainder >> 1);
}
}
}
return remainder;
}
uint16_t subghz_protocol_blocks_crc16lsb(
uint8_t const message[],
size_t size,
uint16_t polynomial,
uint16_t init) {
uint16_t remainder = init;
for(size_t byte = 0; byte < size; ++byte) {
remainder ^= message[byte];
for(uint8_t bit = 0; bit < 8; ++bit) {
if(remainder & 1) {
remainder = (remainder >> 1) ^ polynomial;
} else {
remainder = (remainder >> 1);
}
}
}
return remainder;
}
uint16_t subghz_protocol_blocks_crc16(
uint8_t const message[],
size_t size,
uint16_t polynomial,
uint16_t init) {
uint16_t remainder = init;
for(size_t byte = 0; byte < size; ++byte) {
remainder ^= message[byte] << 8;
for(uint8_t bit = 0; bit < 8; ++bit) {
if(remainder & 0x8000) {
remainder = (remainder << 1) ^ polynomial;
} else {
remainder = (remainder << 1);
}
}
}
return remainder;
}
uint8_t subghz_protocol_blocks_lfsr_digest8(
uint8_t const message[],
size_t size,
uint8_t gen,
uint8_t key) {
uint8_t sum = 0;
for(size_t byte = 0; byte < size; ++byte) {
uint8_t data = message[byte];
for(int i = 7; i >= 0; --i) {
// XOR key into sum if data bit is set
if((data >> i) & 1) sum ^= key;
// roll the key right (actually the LSB is dropped here)
// and apply the gen (needs to include the dropped LSB as MSB)
if(key & 1)
key = (key >> 1) ^ gen;
else
key = (key >> 1);
}
}
return sum;
}
uint8_t subghz_protocol_blocks_lfsr_digest8_reflect(
uint8_t const message[],
size_t size,
uint8_t gen,
uint8_t key) {
uint8_t sum = 0;
// Process message from last byte to first byte (reflected)
for(int byte = size - 1; byte >= 0; --byte) {
uint8_t data = message[byte];
// Process individual bits of each byte (reflected)
for(uint8_t i = 0; i < 8; ++i) {
// XOR key into sum if data bit is set
if((data >> i) & 1) {
sum ^= key;
}
// roll the key left (actually the LSB is dropped here)
// and apply the gen (needs to include the dropped lsb as MSB)
if(key & 0x80)
key = (key << 1) ^ gen;
else
key = (key << 1);
}
}
return sum;
}
uint16_t subghz_protocol_blocks_lfsr_digest16(
uint8_t const message[],
size_t size,
uint16_t gen,
uint16_t key) {
uint16_t sum = 0;
for(size_t byte = 0; byte < size; ++byte) {
uint8_t data = message[byte];
for(int8_t i = 7; i >= 0; --i) {
// if data bit is set then xor with key
if((data >> i) & 1) sum ^= key;
// roll the key right (actually the LSB is dropped here)
// and apply the gen (needs to include the dropped LSB as MSB)
if(key & 1)
key = (key >> 1) ^ gen;
else
key = (key >> 1);
}
}
return sum;
}
uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size) {
uint32_t result = 0;
for(size_t i = 0; i < size; ++i) {
result += message[i];
}
return (uint8_t)result;
}
uint8_t subghz_protocol_blocks_parity8(uint8_t byte) {
byte ^= byte >> 4;
byte &= 0xf;
return (0x6996 >> byte) & 1;
}
uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size) {
uint8_t result = 0;
for(size_t i = 0; i < size; ++i) {
result ^= subghz_protocol_blocks_parity8(message[i]);
}
return result;
}
uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size) {
uint8_t result = 0;
for(size_t i = 0; i < size; ++i) {
result ^= message[i];
}
return result;
}

View File

@ -0,0 +1,222 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#define bit_read(value, bit) (((value) >> (bit)) & 0x01)
#define bit_set(value, bit) \
({ \
__typeof__(value) _one = (1); \
(value) |= (_one << (bit)); \
})
#define bit_clear(value, bit) \
({ \
__typeof__(value) _one = (1); \
(value) &= ~(_one << (bit)); \
})
#define bit_write(value, bit, bitvalue) (bitvalue ? bit_set(value, bit) : bit_clear(value, bit))
#define DURATION_DIFF(x, y) (((x) < (y)) ? ((y) - (x)) : ((x) - (y)))
#ifdef __cplusplus
extern "C" {
#endif
/** Flip the data bitwise
*
* @param key In data
* @param bit_count number of data bits
*
* @return Reverse data
*/
uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count);
/** Get parity the data bitwise
*
* @param key In data
* @param bit_count number of data bits
*
* @return parity
*/
uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count);
/** CRC-4
*
* @param message array of bytes to check
* @param size number of bytes in message
* @param polynomial CRC polynomial
* @param init starting crc value
*
* @return CRC value
*/
uint8_t subghz_protocol_blocks_crc4(
uint8_t const message[],
size_t size,
uint8_t polynomial,
uint8_t init);
/** CRC-7
*
* @param message array of bytes to check
* @param size number of bytes in message
* @param polynomial CRC polynomial
* @param init starting crc value
*
* @return CRC value
*/
uint8_t subghz_protocol_blocks_crc7(
uint8_t const message[],
size_t size,
uint8_t polynomial,
uint8_t init);
/** Generic Cyclic Redundancy Check CRC-8. Example polynomial: 0x31 = x8 + x5 +
* x4 + 1 (x8 is implicit) Example polynomial: 0x80 = x8 + x7 (a normal
* bit-by-bit parity XOR)
*
* @param message array of bytes to check
* @param size number of bytes in message
* @param polynomial byte is from x^7 to x^0 (x^8 is implicitly one)
* @param init starting crc value
*
* @return CRC value
*/
uint8_t subghz_protocol_blocks_crc8(
uint8_t const message[],
size_t size,
uint8_t polynomial,
uint8_t init);
/** "Little-endian" Cyclic Redundancy Check CRC-8 LE Input and output are
* reflected, i.e. least significant bit is shifted in first
*
* @param message array of bytes to check
* @param size number of bytes in message
* @param polynomial CRC polynomial
* @param init starting crc value
*
* @return CRC value
*/
uint8_t subghz_protocol_blocks_crc8le(
uint8_t const message[],
size_t size,
uint8_t polynomial,
uint8_t init);
/** CRC-16 LSB. Input and output are reflected, i.e. least significant bit is
* shifted in first. Note that poly and init already need to be reflected
*
* @param message array of bytes to check
* @param size number of bytes in message
* @param polynomial CRC polynomial
* @param init starting crc value
*
* @return CRC value
*/
uint16_t subghz_protocol_blocks_crc16lsb(
uint8_t const message[],
size_t size,
uint16_t polynomial,
uint16_t init);
/** CRC-16
*
* @param message array of bytes to check
* @param size number of bytes in message
* @param polynomial CRC polynomial
* @param init starting crc value
*
* @return CRC value
*/
uint16_t subghz_protocol_blocks_crc16(
uint8_t const message[],
size_t size,
uint16_t polynomial,
uint16_t init);
/** Digest-8 by "LFSR-based Toeplitz hash"
*
* @param message bytes of message data
* @param size number of bytes to digest
* @param gen key stream generator, needs to includes the MSB if the
* LFSR is rolling
* @param key initial key
*
* @return digest value
*/
uint8_t subghz_protocol_blocks_lfsr_digest8(
uint8_t const message[],
size_t size,
uint8_t gen,
uint8_t key);
/** Digest-8 by "LFSR-based Toeplitz hash", byte reflect, bit reflect
*
* @param message bytes of message data
* @param size number of bytes to digest
* @param gen key stream generator, needs to includes the MSB if the
* LFSR is rolling
* @param key initial key
*
* @return digest value
*/
uint8_t subghz_protocol_blocks_lfsr_digest8_reflect(
uint8_t const message[],
size_t size,
uint8_t gen,
uint8_t key);
/** Digest-16 by "LFSR-based Toeplitz hash"
*
* @param message bytes of message data
* @param size number of bytes to digest
* @param gen key stream generator, needs to includes the MSB if the
* LFSR is rolling
* @param key initial key
*
* @return digest value
*/
uint16_t subghz_protocol_blocks_lfsr_digest16(
uint8_t const message[],
size_t size,
uint16_t gen,
uint16_t key);
/** Compute Addition of a number of bytes
*
* @param message bytes of message data
* @param size number of bytes to sum
*
* @return summation value
*/
uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size);
/** Compute bit parity of a single byte (8 bits)
*
* @param byte single byte to check
*
* @return 1 odd parity, 0 even parity
*/
uint8_t subghz_protocol_blocks_parity8(uint8_t byte);
/** Compute bit parity of a number of bytes
*
* @param message bytes of message data
* @param size number of bytes to sum
*
* @return 1 odd parity, 0 even parity
*/
uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size);
/** Compute XOR (byte-wide parity) of a number of bytes
*
* @param message bytes of message data
* @param size number of bytes to sum
*
* @return summation value, per bit-position 1 odd parity, 0 even parity
*/
uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size);
#ifdef __cplusplus
}
#endif

View File

@ -1,7 +1,7 @@
#include "princeton_for_testing.h"
#include <furi_hal.h>
#include "../blocks/math.h"
#include "math.h"
/*
* Help

View File

@ -1,6 +1,8 @@
#pragma once
#include "base.h"
//#include "base.h"
#include <furi.h>
#include <lib/toolbox/level_duration.h>
/** SubGhzDecoderPrinceton anonymous type */
typedef struct SubGhzDecoderPrinceton SubGhzDecoderPrinceton;

View File

@ -0,0 +1,30 @@
#include "../subghz_test_app_i.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const subghz_test_scene_on_enter_handlers[])(void*) = {
#include "subghz_test_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_event handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
bool (*const subghz_test_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "subghz_test_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_exit handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
void (*const subghz_test_scene_on_exit_handlers[])(void* context) = {
#include "subghz_test_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers subghz_test_scene_handlers = {
.on_enter_handlers = subghz_test_scene_on_enter_handlers,
.on_event_handlers = subghz_test_scene_on_event_handlers,
.on_exit_handlers = subghz_test_scene_on_exit_handlers,
.scene_num = SubGhzTestSceneNum,
};

View File

@ -0,0 +1,29 @@
#pragma once
#include <gui/scene_manager.h>
// Generate scene id and total number
#define ADD_SCENE(prefix, name, id) SubGhzTestScene##id,
typedef enum {
#include "subghz_test_scene_config.h"
SubGhzTestSceneNum,
} SubGhzTestScene;
#undef ADD_SCENE
extern const SceneManagerHandlers subghz_test_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "subghz_test_scene_config.h"
#undef ADD_SCENE
// Generate scene on_event handlers declaration
#define ADD_SCENE(prefix, name, id) \
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
#include "subghz_test_scene_config.h"
#undef ADD_SCENE
// Generate scene on_exit handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
#include "subghz_test_scene_config.h"
#undef ADD_SCENE

View File

@ -0,0 +1,66 @@
#include "../subghz_test_app_i.h"
void subghz_test_scene_about_widget_callback(GuiButtonType result, InputType type, void* context) {
SubGhzTestApp* app = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
}
void subghz_test_scene_about_on_enter(void* context) {
SubGhzTestApp* app = context;
FuriString* temp_str;
temp_str = furi_string_alloc();
furi_string_printf(temp_str, "\e#%s\n", "Information");
furi_string_cat_printf(temp_str, "Version: %s\n", SUBGHZ_TEST_VERSION_APP);
furi_string_cat_printf(temp_str, "Developed by: %s\n", SUBGHZ_TEST_DEVELOPED);
furi_string_cat_printf(temp_str, "Github: %s\n\n", SUBGHZ_TEST_GITHUB);
furi_string_cat_printf(temp_str, "\e#%s\n", "Description");
furi_string_cat_printf(
temp_str,
"This application is designed\nto test the functionality of the\nbuilt-in CC1101 module.\n\n");
widget_add_text_box_element(
app->widget,
0,
0,
128,
14,
AlignCenter,
AlignBottom,
"\e#\e! \e!\n",
false);
widget_add_text_box_element(
app->widget,
0,
2,
128,
14,
AlignCenter,
AlignBottom,
"\e#\e! Sub-Ghz Test \e!\n",
false);
widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str));
furi_string_free(temp_str);
view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewWidget);
}
bool subghz_test_scene_about_on_event(void* context, SceneManagerEvent event) {
SubGhzTestApp* app = context;
bool consumed = false;
UNUSED(app);
UNUSED(event);
return consumed;
}
void subghz_test_scene_about_on_exit(void* context) {
SubGhzTestApp* app = context;
// Clear views
widget_reset(app->widget);
}

View File

@ -0,0 +1,29 @@
#include "../subghz_test_app_i.h"
void subghz_test_scene_carrier_callback(SubGhzTestCarrierEvent event, void* context) {
furi_assert(context);
SubGhzTestApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void subghz_test_scene_carrier_on_enter(void* context) {
SubGhzTestApp* app = context;
subghz_test_carrier_set_callback(
app->subghz_test_carrier, subghz_test_scene_carrier_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewCarrier);
}
bool subghz_test_scene_carrier_on_event(void* context, SceneManagerEvent event) {
SubGhzTestApp* app = context;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzTestCarrierEventOnlyRx) {
scene_manager_next_scene(app->scene_manager, SubGhzTestSceneShowOnlyRx);
return true;
}
}
return false;
}
void subghz_test_scene_carrier_on_exit(void* context) {
UNUSED(context);
}

View File

@ -0,0 +1,6 @@
ADD_SCENE(subghz_test, start, Start)
ADD_SCENE(subghz_test, about, About)
ADD_SCENE(subghz_test, carrier, Carrier)
ADD_SCENE(subghz_test, packet, Packet)
ADD_SCENE(subghz_test, static, Static)
ADD_SCENE(subghz_test, show_only_rx, ShowOnlyRx)

View File

@ -0,0 +1,29 @@
#include "../subghz_test_app_i.h"
void subghz_test_scene_packet_callback(SubGhzTestPacketEvent event, void* context) {
furi_assert(context);
SubGhzTestApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void subghz_test_scene_packet_on_enter(void* context) {
SubGhzTestApp* app = context;
subghz_test_packet_set_callback(
app->subghz_test_packet, subghz_test_scene_packet_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewPacket);
}
bool subghz_test_scene_packet_on_event(void* context, SceneManagerEvent event) {
SubGhzTestApp* app = context;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzTestPacketEventOnlyRx) {
scene_manager_next_scene(app->scene_manager, SubGhzTestSceneShowOnlyRx);
return true;
}
}
return false;
}
void subghz_test_scene_packet_on_exit(void* context) {
UNUSED(context);
}

View File

@ -0,0 +1,49 @@
#include "../subghz_test_app_i.h"
#include <subghz_test_icons.h>
void subghz_test_scene_show_only_rx_popup_callback(void* context) {
SubGhzTestApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SubGhzTestCustomEventSceneShowOnlyRX);
}
void subghz_test_scene_show_only_rx_on_enter(void* context) {
SubGhzTestApp* app = context;
// Setup view
Popup* popup = app->popup;
const char* header_text = "Transmission is blocked";
const char* message_text = "Transmission on\nthis frequency is\nrestricted in\nyour region";
if(!furi_hal_region_is_provisioned()) {
header_text = "Firmware update needed";
message_text = "Please update\nfirmware before\nusing this feature\nflipp.dev/upd";
}
popup_set_header(popup, header_text, 63, 3, AlignCenter, AlignTop);
popup_set_text(popup, message_text, 0, 17, AlignLeft, AlignTop);
popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48);
popup_set_timeout(popup, 1500);
popup_set_context(popup, app);
popup_set_callback(popup, subghz_test_scene_show_only_rx_popup_callback);
popup_enable_timeout(popup);
view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewPopup);
}
bool subghz_test_scene_show_only_rx_on_event(void* context, SceneManagerEvent event) {
SubGhzTestApp* app = context;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzTestCustomEventSceneShowOnlyRX) {
scene_manager_previous_scene(app->scene_manager);
return true;
}
}
return false;
}
void subghz_test_scene_show_only_rx_on_exit(void* context) {
SubGhzTestApp* app = context;
Popup* popup = app->popup;
popup_reset(popup);
}

View File

@ -0,0 +1,77 @@
#include "../subghz_test_app_i.h"
typedef enum {
SubmenuIndexSubGhzTestCarrier,
SubmenuIndexSubGhzTestPacket,
SubmenuIndexSubGhzTestStatic,
SubmenuIndexSubGhzTestAbout,
} SubmenuIndex;
void subghz_test_scene_start_submenu_callback(void* context, uint32_t index) {
SubGhzTestApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void subghz_test_scene_start_on_enter(void* context) {
SubGhzTestApp* app = context;
Submenu* submenu = app->submenu;
submenu_add_item(
submenu,
"Carrier",
SubmenuIndexSubGhzTestCarrier,
subghz_test_scene_start_submenu_callback,
app);
submenu_add_item(
submenu,
"Packet",
SubmenuIndexSubGhzTestPacket,
subghz_test_scene_start_submenu_callback,
app);
submenu_add_item(
submenu,
"Static",
SubmenuIndexSubGhzTestStatic,
subghz_test_scene_start_submenu_callback,
app);
submenu_add_item(
submenu,
"About",
SubmenuIndexSubGhzTestAbout,
subghz_test_scene_start_submenu_callback,
app);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(app->scene_manager, SubGhzTestSceneStart));
view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewSubmenu);
}
bool subghz_test_scene_start_on_event(void* context, SceneManagerEvent event) {
SubGhzTestApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexSubGhzTestAbout) {
scene_manager_next_scene(app->scene_manager, SubGhzTestSceneAbout);
consumed = true;
} else if(event.event == SubmenuIndexSubGhzTestCarrier) {
scene_manager_next_scene(app->scene_manager, SubGhzTestSceneCarrier);
consumed = true;
} else if(event.event == SubmenuIndexSubGhzTestPacket) {
scene_manager_next_scene(app->scene_manager, SubGhzTestScenePacket);
consumed = true;
} else if(event.event == SubmenuIndexSubGhzTestStatic) {
scene_manager_next_scene(app->scene_manager, SubGhzTestSceneStatic);
consumed = true;
}
scene_manager_set_scene_state(app->scene_manager, SubGhzTestSceneStart, event.event);
}
return consumed;
}
void subghz_test_scene_start_on_exit(void* context) {
SubGhzTestApp* app = context;
submenu_reset(app->submenu);
}

View File

@ -0,0 +1,29 @@
#include "../subghz_test_app_i.h"
void subghz_test_scene_static_callback(SubGhzTestStaticEvent event, void* context) {
furi_assert(context);
SubGhzTestApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void subghz_test_scene_static_on_enter(void* context) {
SubGhzTestApp* app = context;
subghz_test_static_set_callback(
app->subghz_test_static, subghz_test_scene_static_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewStatic);
}
bool subghz_test_scene_static_on_event(void* context, SceneManagerEvent event) {
SubGhzTestApp* app = context;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzTestStaticEventOnlyRx) {
scene_manager_next_scene(app->scene_manager, SubGhzTestSceneShowOnlyRx);
return true;
}
}
return false;
}
void subghz_test_scene_static_on_exit(void* context) {
UNUSED(context);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 B

View File

@ -0,0 +1,139 @@
#include "subghz_test_app_i.h"
#include <furi.h>
#include <furi_hal.h>
static bool subghz_test_app_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
SubGhzTestApp* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
static bool subghz_test_app_back_event_callback(void* context) {
furi_assert(context);
SubGhzTestApp* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
static void subghz_test_app_tick_event_callback(void* context) {
furi_assert(context);
SubGhzTestApp* app = context;
scene_manager_handle_tick_event(app->scene_manager);
}
SubGhzTestApp* subghz_test_app_alloc() {
SubGhzTestApp* app = malloc(sizeof(SubGhzTestApp));
// GUI
app->gui = furi_record_open(RECORD_GUI);
// View Dispatcher
app->view_dispatcher = view_dispatcher_alloc();
app->scene_manager = scene_manager_alloc(&subghz_test_scene_handlers, app);
view_dispatcher_enable_queue(app->view_dispatcher);
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
view_dispatcher_set_custom_event_callback(
app->view_dispatcher, subghz_test_app_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, subghz_test_app_back_event_callback);
view_dispatcher_set_tick_event_callback(
app->view_dispatcher, subghz_test_app_tick_event_callback, 100);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
// Open Notification record
app->notifications = furi_record_open(RECORD_NOTIFICATION);
// SubMenu
app->submenu = submenu_alloc();
view_dispatcher_add_view(
app->view_dispatcher, SubGhzTestViewSubmenu, submenu_get_view(app->submenu));
// Widget
app->widget = widget_alloc();
view_dispatcher_add_view(
app->view_dispatcher, SubGhzTestViewWidget, widget_get_view(app->widget));
// Popup
app->popup = popup_alloc();
view_dispatcher_add_view(
app->view_dispatcher, SubGhzTestViewPopup, popup_get_view(app->popup));
// Carrier Test Module
app->subghz_test_carrier = subghz_test_carrier_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
SubGhzTestViewCarrier,
subghz_test_carrier_get_view(app->subghz_test_carrier));
// Packet Test
app->subghz_test_packet = subghz_test_packet_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
SubGhzTestViewPacket,
subghz_test_packet_get_view(app->subghz_test_packet));
// Static send
app->subghz_test_static = subghz_test_static_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
SubGhzTestViewStatic,
subghz_test_static_get_view(app->subghz_test_static));
scene_manager_next_scene(app->scene_manager, SubGhzTestSceneStart);
return app;
}
void subghz_test_app_free(SubGhzTestApp* app) {
furi_assert(app);
// Submenu
view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewSubmenu);
submenu_free(app->submenu);
// Widget
view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewWidget);
widget_free(app->widget);
// Popup
view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewPopup);
popup_free(app->popup);
// Carrier Test
view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewCarrier);
subghz_test_carrier_free(app->subghz_test_carrier);
// Packet Test
view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewPacket);
subghz_test_packet_free(app->subghz_test_packet);
// Static
view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewStatic);
subghz_test_static_free(app->subghz_test_static);
// View dispatcher
view_dispatcher_free(app->view_dispatcher);
scene_manager_free(app->scene_manager);
// Notifications
furi_record_close(RECORD_NOTIFICATION);
app->notifications = NULL;
// Close records
furi_record_close(RECORD_GUI);
free(app);
}
int32_t subghz_test_app(void* p) {
UNUSED(p);
SubGhzTestApp* subghz_test_app = subghz_test_app_alloc();
view_dispatcher_run(subghz_test_app->view_dispatcher);
subghz_test_app_free(subghz_test_app);
return 0;
}

View File

@ -0,0 +1,5 @@
#include "subghz_test_app_i.h"
#include <furi.h>
#define TAG "SubGhzTest"

View File

@ -0,0 +1,32 @@
#pragma once
#include "helpers/subghz_test_types.h"
#include "helpers/subghz_test_event.h"
#include "scenes/subghz_test_scene.h"
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/modules/submenu.h>
#include <gui/modules/widget.h>
#include <gui/modules/popup.h>
#include <notification/notification_messages.h>
#include "views/subghz_test_static.h"
#include "views/subghz_test_carrier.h"
#include "views/subghz_test_packet.h"
typedef struct SubGhzTestApp SubGhzTestApp;
struct SubGhzTestApp {
Gui* gui;
ViewDispatcher* view_dispatcher;
SceneManager* scene_manager;
NotificationApp* notifications;
Submenu* submenu;
Widget* widget;
Popup* popup;
SubGhzTestStatic* subghz_test_static;
SubGhzTestCarrier* subghz_test_carrier;
SubGhzTestPacket* subghz_test_packet;
};

View File

@ -1,6 +1,6 @@
#include "subghz_test_carrier.h"
#include "../subghz_i.h"
#include "../helpers/subghz_testing.h"
#include "../subghz_test_app_i.h"
#include "../helpers/subghz_test_frequency.h"
#include <lib/subghz/devices/cc1101_configs.h>
#include <math.h>

View File

@ -1,6 +1,6 @@
#include "subghz_test_packet.h"
#include "../subghz_i.h"
#include "../helpers/subghz_testing.h"
#include "../subghz_test_app_i.h"
#include "../helpers/subghz_test_frequency.h"
#include <lib/subghz/devices/cc1101_configs.h>
#include <math.h>
@ -8,7 +8,7 @@
#include <furi_hal.h>
#include <input/input.h>
#include <toolbox/level_duration.h>
#include <lib/subghz/protocols/princeton_for_testing.h>
#include "../protocol/princeton_for_testing.h"
#define SUBGHZ_TEST_PACKET_COUNT 500

View File

@ -1,6 +1,6 @@
#include "subghz_test_static.h"
#include "../subghz_i.h"
#include "../helpers/subghz_testing.h"
#include "../subghz_test_app_i.h"
#include "../helpers/subghz_test_frequency.h"
#include <lib/subghz/devices/cc1101_configs.h>
#include <math.h>
@ -8,7 +8,7 @@
#include <furi_hal.h>
#include <input/input.h>
#include <notification/notification_messages.h>
#include <lib/subghz/protocols/princeton_for_testing.h>
#include "../protocol/princeton_for_testing.h"
#define TAG "SubGhzTestStatic"

View File

@ -425,6 +425,7 @@ MU_TEST(infrared_test_decoder_mixed) {
infrared_test_run_decoder(InfraredProtocolSamsung32, 1);
infrared_test_run_decoder(InfraredProtocolSIRC, 3);
infrared_test_run_decoder(InfraredProtocolKaseikyo, 1);
infrared_test_run_decoder(InfraredProtocolRCA, 1);
}
MU_TEST(infrared_test_decoder_nec) {
@ -499,6 +500,15 @@ MU_TEST(infrared_test_decoder_kaseikyo) {
infrared_test_run_decoder(InfraredProtocolKaseikyo, 6);
}
MU_TEST(infrared_test_decoder_rca) {
infrared_test_run_decoder(InfraredProtocolRCA, 1);
infrared_test_run_decoder(InfraredProtocolRCA, 2);
infrared_test_run_decoder(InfraredProtocolRCA, 3);
infrared_test_run_decoder(InfraredProtocolRCA, 4);
infrared_test_run_decoder(InfraredProtocolRCA, 5);
infrared_test_run_decoder(InfraredProtocolRCA, 6);
}
MU_TEST(infrared_test_encoder_decoder_all) {
infrared_test_run_encoder_decoder(InfraredProtocolNEC, 1);
infrared_test_run_encoder_decoder(InfraredProtocolNECext, 1);
@ -509,6 +519,7 @@ MU_TEST(infrared_test_encoder_decoder_all) {
infrared_test_run_encoder_decoder(InfraredProtocolRC5, 1);
infrared_test_run_encoder_decoder(InfraredProtocolSIRC, 1);
infrared_test_run_encoder_decoder(InfraredProtocolKaseikyo, 1);
infrared_test_run_encoder_decoder(InfraredProtocolRCA, 1);
}
MU_TEST_SUITE(infrared_test) {
@ -527,6 +538,7 @@ MU_TEST_SUITE(infrared_test) {
MU_RUN_TEST(infrared_test_decoder_samsung32);
MU_RUN_TEST(infrared_test_decoder_necext1);
MU_RUN_TEST(infrared_test_decoder_kaseikyo);
MU_RUN_TEST(infrared_test_decoder_rca);
MU_RUN_TEST(infrared_test_decoder_mixed);
MU_RUN_TEST(infrared_test_encoder_decoder_all);
}

View File

@ -4,5 +4,6 @@ App(
targets=["f7"],
entry_point="subghz_device_cc1101_ext_ep",
requires=["subghz"],
sdk_headers=["cc1101_ext/cc1101_ext_interconnect.h"],
fap_libs=["hwdrivers"],
)

View File

@ -18,7 +18,7 @@ Before launching the application, connect the sensor to Flipper's external GPIO
In order to launch this demo, follow the steps below:
1. Make sure your Flipper has an SD card installed.
2. Connect your Flipper to the computer via a USB cable.
3. Run `./fbt launch_app APPSRC=example_thermo` in your terminal emulator of choice.
3. Run `./fbt launch APPSRC=example_thermo` in your terminal emulator of choice.
## Changing the data pin
It is possible to use other GPIO pin as a 1-Wire data pin. In order to change it, set the `THERMO_GPIO_PIN` macro to any of the options listed below:

View File

@ -116,9 +116,11 @@ static ManchesterEvent level_and_duration_to_event(bool level, uint32_t duration
static uint8_t oregon3_sensor_id_var_bits(uint16_t sensor_id) {
switch(sensor_id) {
case ID_THGR221:
default:
// nibbles: temp + hum + '0'
return (4 + 2 + 1) * 4;
default:
FURI_LOG_D(TAG, "Unsupported sensor id 0x%x", sensor_id);
return 0;
}
}
@ -198,10 +200,8 @@ void ws_protocol_decoder_oregon3_feed(void* context, bool level, uint32_t durati
oregon3_sensor_id_var_bits(OREGON3_SENSOR_ID(instance->generic.data));
if(!instance->var_bits) {
// sensor is not supported, stop decoding, but showing the decoded fixed part
// sensor is not supported, stop decoding
instance->decoder.parser_step = Oregon3DecoderStepReset;
if(instance->base.callback)
instance->base.callback(&instance->base, instance->base.context);
} else {
instance->decoder.parser_step = Oregon3DecoderStepVarData;
}

View File

@ -4,7 +4,6 @@ App(
apptype=FlipperAppType.METAPACKAGE,
provides=[
"gpio",
"onewire",
"ibutton",
"infrared",
"lfrfid",
@ -13,5 +12,20 @@ App(
"bad_usb",
"u2f",
"archive",
"main_apps_on_start",
],
)
App(
appid="main_apps_on_start",
name="On start hooks",
apptype=FlipperAppType.METAPACKAGE,
provides=[
"ibutton_start",
"onewire_start",
"subghz_start",
"infrared_start",
"lfrfid_start",
"nfc_start",
],
)

View File

@ -64,8 +64,20 @@ static void
if(!is_last) {
archive_add_file_item(browser, is_folder, furi_string_get_cstr(item_path));
} else {
bool load_again = false;
with_view_model(
browser->view, ArchiveBrowserViewModel * model, { model->list_loading = false; }, true);
browser->view,
ArchiveBrowserViewModel * model,
{
model->list_loading = false;
if(archive_is_file_list_load_required(model)) {
load_again = true;
}
},
true);
if(load_again) {
archive_file_array_load(browser, 0);
}
}
}
@ -111,6 +123,26 @@ bool archive_is_item_in_array(ArchiveBrowserViewModel* model, uint32_t idx) {
return true;
}
bool archive_is_file_list_load_required(ArchiveBrowserViewModel* model) {
size_t array_size = files_array_size(model->files);
if((model->list_loading) || (array_size >= model->item_cnt)) {
return false;
}
if((model->array_offset > 0) &&
(model->item_idx < (model->array_offset + FILE_LIST_BUF_LEN / 4))) {
return true;
}
if(((model->array_offset + array_size) < model->item_cnt) &&
(model->item_idx > (int32_t)(model->array_offset + array_size - FILE_LIST_BUF_LEN / 4))) {
return true;
}
return false;
}
void archive_update_offset(ArchiveBrowserView* browser) {
furi_assert(browser);

View File

@ -64,6 +64,7 @@ inline bool archive_is_known_app(ArchiveFileTypeEnum type) {
}
bool archive_is_item_in_array(ArchiveBrowserViewModel* model, uint32_t idx);
bool archive_is_file_list_load_required(ArchiveBrowserViewModel* model);
void archive_update_offset(ArchiveBrowserView* browser);
void archive_update_focus(ArchiveBrowserView* browser, const char* target);

View File

@ -5,13 +5,14 @@
#include "../helpers/archive_browser.h"
#include "../views/archive_browser_view.h"
#include "archive/scenes/archive_scene.h"
#include <applications.h>
#define TAG "ArchiveSceneBrowser"
#define SCENE_STATE_DEFAULT (0)
#define SCENE_STATE_NEED_REFRESH (1)
const char* archive_get_flipper_app_name(ArchiveFileTypeEnum file_type) {
static const char* archive_get_flipper_app_name(ArchiveFileTypeEnum file_type) {
switch(file_type) {
case ArchiveFileTypeIButton:
return "iButton";

View File

@ -248,24 +248,10 @@ View* archive_browser_get_view(ArchiveBrowserView* browser) {
return browser->view;
}
static bool is_file_list_load_required(ArchiveBrowserViewModel* model) {
size_t array_size = files_array_size(model->files);
if((model->list_loading) || (array_size >= model->item_cnt)) {
return false;
static void file_list_rollover(ArchiveBrowserViewModel* model) {
if(!model->list_loading && files_array_size(model->files) < model->item_cnt) {
files_array_reset(model->files);
}
if((model->array_offset > 0) &&
(model->item_idx < (model->array_offset + FILE_LIST_BUF_LEN / 4))) {
return true;
}
if(((model->array_offset + array_size) < model->item_cnt) &&
(model->item_idx > (int32_t)(model->array_offset + array_size - FILE_LIST_BUF_LEN / 4))) {
return true;
}
return false;
}
static bool archive_view_input(InputEvent* event, void* context) {
@ -347,12 +333,13 @@ static bool archive_view_input(InputEvent* event, void* context) {
if(model->item_idx < scroll_speed) {
model->button_held_for_ticks = 0;
model->item_idx = model->item_cnt - 1;
file_list_rollover(model);
} else {
model->item_idx =
((model->item_idx - scroll_speed) + model->item_cnt) %
model->item_cnt;
}
if(is_file_list_load_required(model)) {
if(archive_is_file_list_load_required(model)) {
model->list_loading = true;
browser->callback(ArchiveBrowserEventLoadPrevItems, browser->context);
}
@ -366,10 +353,11 @@ static bool archive_view_input(InputEvent* event, void* context) {
if(model->item_idx + scroll_speed >= count) {
model->button_held_for_ticks = 0;
model->item_idx = 0;
file_list_rollover(model);
} else {
model->item_idx = (model->item_idx + scroll_speed) % model->item_cnt;
}
if(is_file_list_load_required(model)) {
if(archive_is_file_list_load_required(model)) {
model->list_loading = true;
browser->callback(ArchiveBrowserEventLoadNextItems, browser->context);
}

View File

@ -1,15 +1,12 @@
App(
appid="bad_usb",
name="Bad USB",
apptype=FlipperAppType.APP,
apptype=FlipperAppType.MENUEXTERNAL,
entry_point="bad_usb_app",
cdefines=["APP_BAD_USB"],
requires=[
"gui",
"dialogs",
],
stack_size=2 * 1024,
icon="A_BadUsb_14",
order=70,
fap_libs=["assets"],
fap_icon="icon.png",
fap_category="USB",
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

View File

@ -1,12 +1,12 @@
App(
appid="gpio",
name="GPIO",
apptype=FlipperAppType.APP,
apptype=FlipperAppType.MENUEXTERNAL,
entry_point="gpio_app",
cdefines=["APP_GPIO"],
requires=["gui"],
stack_size=1 * 1024,
icon="A_GPIO_14",
order=50,
fap_libs=["assets"],
fap_icon="icon.png",
fap_category="GPIO",
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1,25 +1,21 @@
App(
appid="ibutton",
name="iButton",
apptype=FlipperAppType.APP,
apptype=FlipperAppType.MENUEXTERNAL,
targets=["f7"],
entry_point="ibutton_app",
cdefines=["APP_IBUTTON"],
requires=[
"gui",
"dialogs",
],
provides=["ibutton_start"],
icon="A_iButton_14",
stack_size=2 * 1024,
order=60,
fap_libs=["assets"],
fap_icon="icon.png",
fap_category="iButton",
)
App(
appid="ibutton_start",
apptype=FlipperAppType.STARTUP,
targets=["f7"],
entry_point="ibutton_on_system_start",
requires=["ibutton"],
order=60,
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 B

View File

@ -12,15 +12,15 @@ void ibutton_scene_delete_confirm_on_enter(void* context) {
widget_add_button_element(
widget, GuiButtonTypeRight, "Delete", ibutton_widget_callback, context);
furi_string_printf(tmp, "Delete %s?", ibutton->key_name);
widget_add_string_element(
widget, 128 / 2, 0, AlignCenter, AlignTop, FontPrimary, furi_string_get_cstr(tmp));
furi_string_printf(tmp, "\e#Delete %s?\e#", ibutton->key_name);
widget_add_text_box_element(
widget, 0, 0, 128, 23, AlignCenter, AlignCenter, furi_string_get_cstr(tmp), false);
furi_string_reset(tmp);
ibutton_protocols_render_brief_data(ibutton->protocols, key, tmp);
widget_add_string_multiline_element(
widget, 128 / 2, 16, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(tmp));
widget, 128 / 2, 24, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(tmp));
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
furi_string_free(tmp);

View File

@ -1,25 +1,21 @@
App(
appid="infrared",
name="Infrared",
apptype=FlipperAppType.APP,
apptype=FlipperAppType.MENUEXTERNAL,
entry_point="infrared_app",
targets=["f7"],
cdefines=["APP_INFRARED"],
requires=[
"gui",
"dialogs",
],
provides=["infrared_start"],
icon="A_Infrared_14",
stack_size=3 * 1024,
order=40,
fap_libs=["assets"],
fap_icon="icon.png",
fap_category="Infrared",
)
App(
appid="infrared_start",
apptype=FlipperAppType.STARTUP,
targets=["f7"],
entry_point="infrared_on_system_start",
requires=["infrared"],
order=20,
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 B

View File

@ -1,27 +1,21 @@
App(
appid="lfrfid",
name="125 kHz RFID",
apptype=FlipperAppType.APP,
apptype=FlipperAppType.MENUEXTERNAL,
targets=["f7"],
entry_point="lfrfid_app",
cdefines=["APP_LF_RFID"],
requires=[
"gui",
"dialogs",
],
provides=[
"lfrfid_start",
],
icon="A_125khz_14",
stack_size=2 * 1024,
order=20,
fap_libs=["assets"],
fap_icon="icon.png",
fap_category="RFID",
)
App(
appid="lfrfid_start",
targets=["f7"],
apptype=FlipperAppType.STARTUP,
entry_point="lfrfid_on_system_start",
requires=["lfrfid"],
order=50,
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B

View File

@ -1,24 +1,21 @@
App(
appid="nfc",
name="NFC",
apptype=FlipperAppType.APP,
apptype=FlipperAppType.MENUEXTERNAL,
targets=["f7"],
entry_point="nfc_app",
cdefines=["APP_NFC"],
requires=[
"gui",
"dialogs",
],
provides=["nfc_start"],
icon="A_NFC_14",
stack_size=5 * 1024,
order=30,
fap_libs=["assets"],
fap_icon="icon.png",
fap_category="NFC",
)
App(
appid="nfc_start",
targets=["f7"],
apptype=FlipperAppType.STARTUP,
entry_point="nfc_on_system_start",
requires=["nfc"],
order=30,
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 B

View File

@ -1,14 +1,6 @@
App(
appid="onewire",
name="1-Wire",
apptype=FlipperAppType.METAPACKAGE,
provides=["onewire_start"],
)
App(
appid="onewire_start",
apptype=FlipperAppType.STARTUP,
entry_point="onewire_on_system_start",
requires=["onewire"],
order=60,
)

View File

@ -1,25 +1,21 @@
App(
appid="subghz",
name="Sub-GHz",
apptype=FlipperAppType.APP,
apptype=FlipperAppType.MENUEXTERNAL,
targets=["f7"],
entry_point="subghz_app",
cdefines=["APP_SUBGHZ"],
requires=[
"gui",
"cli",
"dialogs",
],
provides=["subghz_start"],
icon="A_Sub1ghz_14",
stack_size=3 * 1024,
order=10,
fap_libs=["assets", "hwdrivers"],
fap_icon="icon.png",
fap_category="Sub-GHz",
)
App(
appid="subghz_start",
targets=["f7"],
apptype=FlipperAppType.STARTUP,
entry_point="subghz_on_system_start",
requires=["subghz"],
order=40,
)

View File

@ -80,9 +80,6 @@ typedef enum {
SubGhzViewIdFrequencyAnalyzer,
SubGhzViewIdReadRAW,
SubGhzViewIdStatic,
SubGhzViewIdTestCarrier,
SubGhzViewIdTestPacket,
} SubGhzViewId;
/** SubGhz load type file */

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B

View File

@ -12,10 +12,6 @@ ADD_SCENE(subghz, show_only_rx, ShowOnlyRx)
ADD_SCENE(subghz, saved_menu, SavedMenu)
ADD_SCENE(subghz, delete, Delete)
ADD_SCENE(subghz, delete_success, DeleteSuccess)
ADD_SCENE(subghz, test, Test)
ADD_SCENE(subghz, test_static, TestStatic)
ADD_SCENE(subghz, test_carrier, TestCarrier)
ADD_SCENE(subghz, test_packet, TestPacket)
ADD_SCENE(subghz, set_type, SetType)
ADD_SCENE(subghz, frequency_analyzer, FrequencyAnalyzer)
ADD_SCENE(subghz, read_raw, ReadRAW)

View File

@ -4,7 +4,6 @@
enum SubmenuIndex {
SubmenuIndexRead = 10,
SubmenuIndexSaved,
SubmenuIndexTest,
SubmenuIndexAddManually,
SubmenuIndexFrequencyAnalyzer,
SubmenuIndexReadRAW,
@ -56,10 +55,6 @@ void subghz_scene_start_on_enter(void* context) {
SubmenuIndexRadioSetting,
subghz_scene_start_submenu_callback,
subghz);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
submenu_add_item(
subghz->submenu, "Test", SubmenuIndexTest, subghz_scene_start_submenu_callback, subghz);
}
submenu_set_selected_item(
subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneStart));
@ -101,11 +96,6 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneFrequencyAnalyzer);
dolphin_deed(DolphinDeedSubGhzFrequencyAnalyzer);
return true;
} else if(event.event == SubmenuIndexTest) {
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexTest);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTest);
return true;
} else if(event.event == SubmenuIndexShowRegionInfo) {
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexShowRegionInfo);

View File

@ -1,61 +0,0 @@
#include "../subghz_i.h"
enum SubmenuIndex {
SubmenuIndexCarrier,
SubmenuIndexPacket,
SubmenuIndexStatic,
};
void subghz_scene_test_submenu_callback(void* context, uint32_t index) {
SubGhz* subghz = context;
view_dispatcher_send_custom_event(subghz->view_dispatcher, index);
}
void subghz_scene_test_on_enter(void* context) {
SubGhz* subghz = context;
submenu_add_item(
subghz->submenu,
"Carrier",
SubmenuIndexCarrier,
subghz_scene_test_submenu_callback,
subghz);
submenu_add_item(
subghz->submenu, "Packet", SubmenuIndexPacket, subghz_scene_test_submenu_callback, subghz);
submenu_add_item(
subghz->submenu, "Static", SubmenuIndexStatic, subghz_scene_test_submenu_callback, subghz);
submenu_set_selected_item(
subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneTest));
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdMenu);
}
bool subghz_scene_test_on_event(void* context, SceneManagerEvent event) {
SubGhz* subghz = context;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexCarrier) {
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneTest, SubmenuIndexCarrier);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTestCarrier);
return true;
} else if(event.event == SubmenuIndexPacket) {
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneTest, SubmenuIndexPacket);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTestPacket);
return true;
} else if(event.event == SubmenuIndexStatic) {
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneTest, SubmenuIndexStatic);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTestStatic);
return true;
}
}
return false;
}
void subghz_scene_test_on_exit(void* context) {
SubGhz* subghz = context;
submenu_reset(subghz->submenu);
}

View File

@ -1,30 +0,0 @@
#include "../subghz_i.h"
#include "../views/subghz_test_carrier.h"
void subghz_scene_test_carrier_callback(SubGhzTestCarrierEvent event, void* context) {
furi_assert(context);
SubGhz* subghz = context;
view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
}
void subghz_scene_test_carrier_on_enter(void* context) {
SubGhz* subghz = context;
subghz_test_carrier_set_callback(
subghz->subghz_test_carrier, subghz_scene_test_carrier_callback, subghz);
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTestCarrier);
}
bool subghz_scene_test_carrier_on_event(void* context, SceneManagerEvent event) {
SubGhz* subghz = context;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzTestCarrierEventOnlyRx) {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
return true;
}
}
return false;
}
void subghz_scene_test_carrier_on_exit(void* context) {
UNUSED(context);
}

View File

@ -1,30 +0,0 @@
#include "../subghz_i.h"
#include "../views/subghz_test_packet.h"
void subghz_scene_test_packet_callback(SubGhzTestPacketEvent event, void* context) {
furi_assert(context);
SubGhz* subghz = context;
view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
}
void subghz_scene_test_packet_on_enter(void* context) {
SubGhz* subghz = context;
subghz_test_packet_set_callback(
subghz->subghz_test_packet, subghz_scene_test_packet_callback, subghz);
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTestPacket);
}
bool subghz_scene_test_packet_on_event(void* context, SceneManagerEvent event) {
SubGhz* subghz = context;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzTestPacketEventOnlyRx) {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
return true;
}
}
return false;
}
void subghz_scene_test_packet_on_exit(void* context) {
UNUSED(context);
}

View File

@ -1,30 +0,0 @@
#include "../subghz_i.h"
#include "../views/subghz_test_static.h"
void subghz_scene_test_static_callback(SubGhzTestStaticEvent event, void* context) {
furi_assert(context);
SubGhz* subghz = context;
view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
}
void subghz_scene_test_static_on_enter(void* context) {
SubGhz* subghz = context;
subghz_test_static_set_callback(
subghz->subghz_test_static, subghz_scene_test_static_callback, subghz);
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdStatic);
}
bool subghz_scene_test_static_on_event(void* context, SceneManagerEvent event) {
SubGhz* subghz = context;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzTestStaticEventOnlyRx) {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
return true;
}
}
return false;
}
void subghz_scene_test_static_on_exit(void* context) {
UNUSED(context);
}

View File

@ -129,27 +129,6 @@ SubGhz* subghz_alloc() {
SubGhzViewIdReadRAW,
subghz_read_raw_get_view(subghz->subghz_read_raw));
// Carrier Test Module
subghz->subghz_test_carrier = subghz_test_carrier_alloc();
view_dispatcher_add_view(
subghz->view_dispatcher,
SubGhzViewIdTestCarrier,
subghz_test_carrier_get_view(subghz->subghz_test_carrier));
// Packet Test
subghz->subghz_test_packet = subghz_test_packet_alloc();
view_dispatcher_add_view(
subghz->view_dispatcher,
SubGhzViewIdTestPacket,
subghz_test_packet_get_view(subghz->subghz_test_packet));
// Static send
subghz->subghz_test_static = subghz_test_static_alloc();
view_dispatcher_add_view(
subghz->view_dispatcher,
SubGhzViewIdStatic,
subghz_test_static_get_view(subghz->subghz_test_static));
//init threshold rssi
subghz->threshold_rssi = subghz_threshold_rssi_alloc();
@ -183,18 +162,6 @@ void subghz_free(SubGhz* subghz) {
subghz_txrx_stop(subghz->txrx);
subghz_txrx_sleep(subghz->txrx);
// Packet Test
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestPacket);
subghz_test_packet_free(subghz->subghz_test_packet);
// Carrier Test
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestCarrier);
subghz_test_carrier_free(subghz->subghz_test_carrier);
// Static
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdStatic);
subghz_test_static_free(subghz->subghz_test_static);
// Receiver
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdReceiver);
subghz_view_receiver_free(subghz->subghz_receiver);

View File

@ -9,10 +9,6 @@
#include "views/subghz_frequency_analyzer.h"
#include "views/subghz_read_raw.h"
#include "views/subghz_test_static.h"
#include "views/subghz_test_carrier.h"
#include "views/subghz_test_packet.h"
#include <gui/gui.h>
#include <assets_icons.h>
#include <dialogs/dialogs.h>
@ -64,9 +60,6 @@ struct SubGhz {
SubGhzFrequencyAnalyzer* subghz_frequency_analyzer;
SubGhzReadRAW* subghz_read_raw;
SubGhzTestStatic* subghz_test_static;
SubGhzTestCarrier* subghz_test_carrier;
SubGhzTestPacket* subghz_test_packet;
SubGhzProtocolFlag filter;
FuriString* error_str;

View File

@ -1,15 +1,12 @@
App(
appid="u2f",
name="U2F",
apptype=FlipperAppType.APP,
apptype=FlipperAppType.MENUEXTERNAL,
entry_point="u2f_app",
cdefines=["APP_U2F"],
requires=[
"gui",
"dialogs",
],
stack_size=2 * 1024,
icon="A_U2F_14",
order=80,
fap_libs=["assets"],
fap_category="USB",
fap_icon="icon.png",
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 583 B

View File

@ -17,6 +17,12 @@ typedef struct {
const FlipperInternalApplicationFlag flags;
} FlipperInternalApplication;
typedef struct {
const char* name;
const Icon* icon;
const char* path;
} FlipperExternalApplication;
typedef void (*FlipperInternalOnStartHook)(void);
extern const char* FLIPPER_AUTORUN_APP_NAME;
@ -52,3 +58,9 @@ extern const FlipperInternalApplication FLIPPER_ARCHIVE;
*/
extern const FlipperInternalApplication FLIPPER_SETTINGS_APPS[];
extern const size_t FLIPPER_SETTINGS_APPS_COUNT;
/* External Menu Apps list
* Spawned by loader
*/
extern const FlipperExternalApplication FLIPPER_EXTERNAL_APPS[];
extern const size_t FLIPPER_EXTERNAL_APPS_COUNT;

View File

@ -89,6 +89,14 @@ void cli_command_help(Cli* cli, FuriString* args, void* context) {
}
}
void cli_command_uptime(Cli* cli, FuriString* args, void* context) {
UNUSED(cli);
UNUSED(args);
UNUSED(context);
uint32_t uptime = furi_get_tick() / furi_kernel_get_tick_frequency();
printf("Uptime: %luh%lum%lus", uptime / 60 / 60, uptime / 60 % 60, uptime % 60);
}
void cli_command_date(Cli* cli, FuriString* args, void* context) {
UNUSED(cli);
UNUSED(context);
@ -165,24 +173,23 @@ void cli_command_log_tx_callback(const uint8_t* buffer, size_t size, void* conte
furi_stream_buffer_send(context, buffer, size, 0);
}
void cli_command_log_level_set_from_string(FuriString* level) {
if(furi_string_cmpi_str(level, "default") == 0) {
furi_log_set_level(FuriLogLevelDefault);
} else if(furi_string_cmpi_str(level, "none") == 0) {
furi_log_set_level(FuriLogLevelNone);
} else if(furi_string_cmpi_str(level, "error") == 0) {
furi_log_set_level(FuriLogLevelError);
} else if(furi_string_cmpi_str(level, "warn") == 0) {
furi_log_set_level(FuriLogLevelWarn);
} else if(furi_string_cmpi_str(level, "info") == 0) {
furi_log_set_level(FuriLogLevelInfo);
} else if(furi_string_cmpi_str(level, "debug") == 0) {
furi_log_set_level(FuriLogLevelDebug);
} else if(furi_string_cmpi_str(level, "trace") == 0) {
furi_log_set_level(FuriLogLevelTrace);
bool cli_command_log_level_set_from_string(FuriString* level) {
FuriLogLevel log_level;
if(furi_log_level_from_string(furi_string_get_cstr(level), &log_level)) {
furi_log_set_level(log_level);
return true;
} else {
printf("Unknown log level\r\n");
printf("<log> — start logging using the current level from the system settings\r\n");
printf("<log error> — only critical errors and other important messages\r\n");
printf("<log warn> — non-critical errors and warnings including <log error>\r\n");
printf("<log info> — non-critical information including <log warn>\r\n");
printf("<log default> — the default system log level (equivalent to <log info>)\r\n");
printf(
"<log debug> — debug information including <log info> (may impact system performance)\r\n");
printf(
"<log trace> — system traces including <log debug> (may impact system performance)\r\n");
}
return false;
}
void cli_command_log(Cli* cli, FuriString* args, void* context) {
@ -193,12 +200,20 @@ void cli_command_log(Cli* cli, FuriString* args, void* context) {
bool restore_log_level = false;
if(furi_string_size(args) > 0) {
cli_command_log_level_set_from_string(args);
if(!cli_command_log_level_set_from_string(args)) {
furi_stream_buffer_free(ring);
return;
}
restore_log_level = true;
}
const char* current_level;
furi_log_level_to_string(furi_log_get_level(), &current_level);
printf("Current log level: %s\r\n", current_level);
furi_hal_console_set_tx_callback(cli_command_log_tx_callback, ring);
printf("Use <log ?> to list available log levels\r\n");
printf("Press CTRL+C to stop...\r\n");
while(!cli_cmd_interrupt_received(cli)) {
size_t ret = furi_stream_buffer_receive(ring, buffer, CLI_COMMAND_LOG_BUFFER_SIZE, 50);
@ -444,6 +459,7 @@ void cli_commands_init(Cli* cli) {
cli_add_command(cli, "?", CliCommandFlagParallelSafe, cli_command_help, NULL);
cli_add_command(cli, "help", CliCommandFlagParallelSafe, cli_command_help, NULL);
cli_add_command(cli, "uptime", CliCommandFlagDefault, cli_command_uptime, NULL);
cli_add_command(cli, "date", CliCommandFlagParallelSafe, cli_command_date, NULL);
cli_add_command(cli, "log", CliCommandFlagParallelSafe, cli_command_log, NULL);
cli_add_command(cli, "sysctl", CliCommandFlagDefault, cli_command_sysctl, NULL);

View File

@ -8,7 +8,7 @@
#include <toolbox/saved_struct.h>
#include <storage/storage.h>
#define DESKTOP_SETTINGS_VER (7)
#define DESKTOP_SETTINGS_VER (8)
#define DESKTOP_SETTINGS_PATH INT_PATH(DESKTOP_SETTINGS_FILE_NAME)
#define DESKTOP_SETTINGS_MAGIC (0x17)
@ -42,7 +42,6 @@ typedef struct {
} PinCode;
typedef struct {
bool is_external;
char name_or_path[MAX_APP_LENGTH];
} FavoriteApp;

View File

@ -14,8 +14,6 @@ void desktop_scene_debug_callback(DesktopEvent event, void* context) {
void desktop_scene_debug_on_enter(void* context) {
Desktop* desktop = (Desktop*)context;
desktop_debug_get_dolphin_data(desktop->debug_view);
desktop_debug_set_callback(desktop->debug_view, desktop_scene_debug_callback, desktop);
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdDebug);
}
@ -32,24 +30,6 @@ bool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) {
dolphin_flush(dolphin);
consumed = true;
break;
case DesktopDebugEventDeed:
dolphin_deed(DolphinDeedTestRight);
desktop_debug_get_dolphin_data(desktop->debug_view);
consumed = true;
break;
case DesktopDebugEventWrongDeed:
dolphin_deed(DolphinDeedTestLeft);
desktop_debug_get_dolphin_data(desktop->debug_view);
consumed = true;
break;
case DesktopDebugEventSaveState:
dolphin_flush(dolphin);
consumed = true;
break;
default:
break;
}
@ -60,6 +40,5 @@ bool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) {
}
void desktop_scene_debug_on_exit(void* context) {
Desktop* desktop = (Desktop*)context;
desktop_debug_reset_screen_idx(desktop->debug_view);
UNUSED(context);
}

View File

@ -18,96 +18,71 @@ void desktop_debug_set_callback(
}
void desktop_debug_render(Canvas* canvas, void* model) {
UNUSED(model);
canvas_clear(canvas);
DesktopDebugViewModel* m = model;
const Version* ver;
char buffer[64];
static const char* headers[] = {"Device Info:", "Dolphin Info:"};
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(
canvas, 64, 1 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignTop, headers[m->screen]);
uint32_t uptime = furi_get_tick() / furi_kernel_get_tick_frequency();
snprintf(
buffer,
sizeof(buffer),
"Uptime: %luh%lum%lus",
uptime / 60 / 60,
uptime / 60 % 60,
uptime % 60);
canvas_draw_str_aligned(canvas, 64, 1 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignTop, buffer);
canvas_set_font(canvas, FontSecondary);
if(m->screen != DesktopViewStatsMeta) {
// Hardware version
const char* my_name = furi_hal_version_get_name_ptr();
snprintf(
buffer,
sizeof(buffer),
"%d.F%dB%dC%d %s:%s %s",
furi_hal_version_get_hw_version(),
furi_hal_version_get_hw_target(),
furi_hal_version_get_hw_body(),
furi_hal_version_get_hw_connect(),
furi_hal_version_get_hw_region_name(),
furi_hal_region_get_name(),
my_name ? my_name : "Unknown");
canvas_draw_str(canvas, 0, 19 + STATUS_BAR_Y_SHIFT, buffer);
// Hardware version
const char* my_name = furi_hal_version_get_name_ptr();
snprintf(
buffer,
sizeof(buffer),
"%d.F%dB%dC%d %s:%s %s",
furi_hal_version_get_hw_version(),
furi_hal_version_get_hw_target(),
furi_hal_version_get_hw_body(),
furi_hal_version_get_hw_connect(),
furi_hal_version_get_hw_region_name(),
furi_hal_region_get_name(),
my_name ? my_name : "Unknown");
canvas_draw_str(canvas, 0, 19 + STATUS_BAR_Y_SHIFT, buffer);
ver = furi_hal_version_get_firmware_version();
const BleGlueC2Info* c2_ver = NULL;
ver = furi_hal_version_get_firmware_version();
const BleGlueC2Info* c2_ver = NULL;
#ifdef SRV_BT
c2_ver = ble_glue_get_c2_info();
c2_ver = ble_glue_get_c2_info();
#endif
if(!ver) { //-V1051
canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, "No info");
return;
}
snprintf(
buffer,
sizeof(buffer),
"%s [%s]",
version_get_version(ver),
version_get_builddate(ver));
canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, buffer);
uint16_t api_major, api_minor;
furi_hal_info_get_api_version(&api_major, &api_minor);
snprintf(
buffer,
sizeof(buffer),
"%s%s [%d.%d] %s",
version_get_dirty_flag(ver) ? "[!] " : "",
version_get_githash(ver),
api_major,
api_minor,
c2_ver ? c2_ver->StackTypeString : "<none>");
canvas_draw_str(canvas, 0, 40 + STATUS_BAR_Y_SHIFT, buffer);
snprintf(
buffer, sizeof(buffer), "[%d] %s", version_get_target(ver), version_get_gitbranch(ver));
canvas_draw_str(canvas, 0, 50 + STATUS_BAR_Y_SHIFT, buffer);
} else {
Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);
DolphinStats stats = dolphin_stats(dolphin);
furi_record_close(RECORD_DOLPHIN);
uint32_t current_lvl = stats.level;
uint32_t remaining = dolphin_state_xp_to_levelup(m->icounter);
canvas_set_font(canvas, FontSecondary);
snprintf(buffer, sizeof(buffer), "Icounter: %lu Butthurt %lu", m->icounter, m->butthurt);
canvas_draw_str(canvas, 5, 19 + STATUS_BAR_Y_SHIFT, buffer);
snprintf(
buffer,
sizeof(buffer),
"Level: %lu To level up: %lu",
current_lvl,
(remaining == (uint32_t)(-1) ? remaining : 0));
canvas_draw_str(canvas, 5, 29 + STATUS_BAR_Y_SHIFT, buffer);
// even if timestamp is uint64_t, it's safe to cast it to uint32_t, because furi_hal_rtc_datetime_to_timestamp only returns uint32_t
snprintf(buffer, sizeof(buffer), "%lu", (uint32_t)m->timestamp);
canvas_draw_str(canvas, 5, 39 + STATUS_BAR_Y_SHIFT, buffer);
canvas_draw_str(canvas, 0, 49 + STATUS_BAR_Y_SHIFT, "[< >] icounter value [ok] save");
if(!ver) { //-V1051
canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, "No info");
return;
}
snprintf(
buffer, sizeof(buffer), "%s [%s]", version_get_version(ver), version_get_builddate(ver));
canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, buffer);
uint16_t api_major, api_minor;
furi_hal_info_get_api_version(&api_major, &api_minor);
snprintf(
buffer,
sizeof(buffer),
"%s%s [%d.%d] %s",
version_get_dirty_flag(ver) ? "[!] " : "",
version_get_githash(ver),
api_major,
api_minor,
c2_ver ? c2_ver->StackTypeString : "<none>");
canvas_draw_str(canvas, 0, 40 + STATUS_BAR_Y_SHIFT, buffer);
snprintf(
buffer, sizeof(buffer), "[%d] %s", version_get_target(ver), version_get_gitbranch(ver));
canvas_draw_str(canvas, 0, 50 + STATUS_BAR_Y_SHIFT, buffer);
}
View* desktop_debug_get_view(DesktopDebugView* debug_view) {
@ -115,61 +90,43 @@ View* desktop_debug_get_view(DesktopDebugView* debug_view) {
return debug_view->view;
}
bool desktop_debug_input(InputEvent* event, void* context) {
static bool desktop_debug_input(InputEvent* event, void* context) {
furi_assert(event);
furi_assert(context);
DesktopDebugView* debug_view = context;
if(event->type != InputTypeShort && event->type != InputTypeRepeat) {
return false;
}
DesktopViewStatsScreens current = 0;
with_view_model(
debug_view->view,
DesktopDebugViewModel * model,
{
#ifdef SRV_DOLPHIN_STATE_DEBUG
if((event->key == InputKeyDown) || (event->key == InputKeyUp)) {
model->screen = !model->screen;
}
#endif
current = model->screen;
},
true);
size_t count = (event->type == InputTypeRepeat) ? 10 : 1;
if(current == DesktopViewStatsMeta) {
if(event->key == InputKeyLeft) {
while(count-- > 0) {
debug_view->callback(DesktopDebugEventWrongDeed, debug_view->context);
}
} else if(event->key == InputKeyRight) {
while(count-- > 0) {
debug_view->callback(DesktopDebugEventDeed, debug_view->context);
}
} else if(event->key == InputKeyOk) {
debug_view->callback(DesktopDebugEventSaveState, debug_view->context);
} else {
return false;
}
}
if(event->key == InputKeyBack) {
if(event->key == InputKeyBack && event->type == InputTypeShort) {
debug_view->callback(DesktopDebugEventExit, debug_view->context);
}
return true;
}
static void desktop_debug_enter(void* context) {
DesktopDebugView* debug_view = context;
furi_timer_start(debug_view->timer, furi_ms_to_ticks(1000));
}
static void desktop_debug_exit(void* context) {
DesktopDebugView* debug_view = context;
furi_timer_stop(debug_view->timer);
}
void desktop_debug_timer(void* context) {
DesktopDebugView* debug_view = context;
view_get_model(debug_view->view);
view_commit_model(debug_view->view, true);
}
DesktopDebugView* desktop_debug_alloc() {
DesktopDebugView* debug_view = malloc(sizeof(DesktopDebugView));
debug_view->view = view_alloc();
view_allocate_model(debug_view->view, ViewModelTypeLocking, sizeof(DesktopDebugViewModel));
debug_view->timer = furi_timer_alloc(desktop_debug_timer, FuriTimerTypePeriodic, debug_view);
view_set_context(debug_view->view, debug_view);
view_set_draw_callback(debug_view->view, (ViewDrawCallback)desktop_debug_render);
view_set_input_callback(debug_view->view, desktop_debug_input);
view_set_enter_callback(debug_view->view, desktop_debug_enter);
view_set_exit_callback(debug_view->view, desktop_debug_exit);
return debug_view;
}
@ -177,27 +134,7 @@ DesktopDebugView* desktop_debug_alloc() {
void desktop_debug_free(DesktopDebugView* debug_view) {
furi_assert(debug_view);
furi_timer_free(debug_view->timer);
view_free(debug_view->view);
free(debug_view);
}
void desktop_debug_get_dolphin_data(DesktopDebugView* debug_view) {
Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);
DolphinStats stats = dolphin_stats(dolphin);
with_view_model(
debug_view->view,
DesktopDebugViewModel * model,
{
model->icounter = stats.icounter;
model->butthurt = stats.butthurt;
model->timestamp = stats.timestamp;
},
true);
furi_record_close(RECORD_DOLPHIN);
}
void desktop_debug_reset_screen_idx(DesktopDebugView* debug_view) {
with_view_model(
debug_view->view, DesktopDebugViewModel * model, { model->screen = 0; }, true);
}

View File

@ -8,26 +8,13 @@ typedef struct DesktopDebugView DesktopDebugView;
typedef void (*DesktopDebugViewCallback)(DesktopEvent event, void* context);
// Debug info
typedef enum {
DesktopViewStatsFw,
DesktopViewStatsMeta,
DesktopViewStatsTotalCount,
} DesktopViewStatsScreens;
struct DesktopDebugView {
View* view;
FuriTimer* timer;
DesktopDebugViewCallback callback;
void* context;
};
typedef struct {
uint32_t icounter;
uint32_t butthurt;
uint64_t timestamp;
DesktopViewStatsScreens screen;
} DesktopDebugViewModel;
void desktop_debug_set_callback(
DesktopDebugView* debug_view,
DesktopDebugViewCallback callback,
@ -36,7 +23,5 @@ void desktop_debug_set_callback(
View* desktop_debug_get_view(DesktopDebugView* debug_view);
DesktopDebugView* desktop_debug_alloc();
void desktop_debug_free(DesktopDebugView* debug_view);
void desktop_debug_get_dolphin_data(DesktopDebugView* debug_view);
void desktop_debug_reset_screen_idx(DesktopDebugView* debug_view);
void desktop_debug_free(DesktopDebugView* debug_view);

View File

@ -303,6 +303,12 @@ static bool browser_is_list_load_required(FileBrowserModel* model) {
return false;
}
static void browser_list_rollover(FileBrowserModel* model) {
if(!model->list_loading && items_array_size(model->items) < model->item_cnt) {
items_array_reset(model->items);
}
}
static void browser_update_offset(FileBrowser* browser) {
furi_assert(browser);
@ -385,7 +391,7 @@ static void browser_list_load_cb(void* context, uint32_t list_load_offset) {
}
}
},
true);
false);
BrowserItem_t_clear(&back_item);
}
@ -425,14 +431,15 @@ static void
(browser->hide_ext) && (item.type == BrowserItemTypeFile));
}
// We shouldn't update screen on each item if custom callback is not set
// Otherwise it will cause screen flickering
bool instant_update = (browser->item_callback != NULL);
with_view_model(
browser->view,
FileBrowserModel * model,
{
items_array_push_back(model->items, item);
// TODO: calculate if element is visible
},
true);
{ items_array_push_back(model->items, item); },
instant_update);
furi_string_free(item.display_name);
furi_string_free(item.path);
if(item.custom_icon_data) {
@ -440,7 +447,18 @@ static void
}
} else {
with_view_model(
browser->view, FileBrowserModel * model, { model->list_loading = false; }, true);
browser->view,
FileBrowserModel * model,
{
model->list_loading = false;
if(browser_is_list_load_required(model)) {
model->list_loading = true;
int32_t load_offset = CLAMP(
model->item_idx - ITEM_LIST_LEN_MAX / 2, (int32_t)model->item_cnt, 0);
file_browser_worker_load(browser->worker, load_offset, ITEM_LIST_LEN_MAX);
}
},
true);
}
}
@ -604,11 +622,13 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) {
if(model->item_idx < scroll_speed) {
model->button_held_for_ticks = 0;
model->item_idx = model->item_cnt - 1;
browser_list_rollover(model);
} else {
model->item_idx =
((model->item_idx - scroll_speed) + model->item_cnt) %
model->item_cnt;
}
if(browser_is_list_load_required(model)) {
model->list_loading = true;
int32_t load_offset = CLAMP(
@ -622,13 +642,14 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) {
model->button_held_for_ticks += 1;
} else if(event->key == InputKeyDown) {
int32_t count = model->item_cnt;
if(model->item_idx + scroll_speed >= count) {
if(model->item_idx + scroll_speed >= (int32_t)model->item_cnt) {
model->button_held_for_ticks = 0;
model->item_idx = 0;
browser_list_rollover(model);
} else {
model->item_idx = (model->item_idx + scroll_speed) % model->item_cnt;
}
if(browser_is_list_load_required(model)) {
model->list_loading = true;
int32_t load_offset = CLAMP(

View File

@ -3,6 +3,7 @@
#include <applications.h>
#include <storage/storage.h>
#include <furi_hal.h>
#include <assets_icons.h>
#include <dialogs/dialogs.h>
#include <toolbox/path.h>
@ -11,7 +12,20 @@
#define TAG "Loader"
#define LOADER_MAGIC_THREAD_VALUE 0xDEADBEEF
// api
// helpers
static const char* loader_find_external_application_by_name(const char* app_name) {
for(size_t i = 0; i < FLIPPER_EXTERNAL_APPS_COUNT; i++) {
if(strcmp(FLIPPER_EXTERNAL_APPS[i].name, app_name) == 0) {
return FLIPPER_EXTERNAL_APPS[i].path;
}
}
return NULL;
}
// API
LoaderStatus
loader_start(Loader* loader, const char* name, const char* args, FuriString* error_message) {
@ -33,17 +47,33 @@ LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const
FuriString* error_message = furi_string_alloc();
LoaderStatus status = loader_start(loader, name, args, error_message);
// TODO: we have many places where we can emit a double start, ex: desktop, menu
// so i prefer to not show LoaderStatusErrorAppStarted error message for now
if(status == LoaderStatusErrorUnknownApp || status == LoaderStatusErrorInternal) {
if(status == LoaderStatusErrorUnknownApp &&
loader_find_external_application_by_name(name) != NULL) {
// Special case for external apps
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
DialogMessage* message = dialog_message_alloc();
dialog_message_set_header(message, "Update needed", 64, 3, AlignCenter, AlignTop);
dialog_message_set_buttons(message, NULL, NULL, NULL);
dialog_message_set_icon(message, &I_DolphinCommon_56x48, 72, 17);
dialog_message_set_text(
message, "Update firmware\nto run this app", 3, 26, AlignLeft, AlignTop);
dialog_message_show(dialogs, message);
dialog_message_free(message);
furi_record_close(RECORD_DIALOGS);
} else if(status == LoaderStatusErrorUnknownApp || status == LoaderStatusErrorInternal) {
// TODO: we have many places where we can emit a double start, ex: desktop, menu
// so i prefer to not show LoaderStatusErrorAppStarted error message for now
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
DialogMessage* message = dialog_message_alloc();
dialog_message_set_header(message, "Error", 64, 0, AlignCenter, AlignTop);
dialog_message_set_buttons(message, NULL, NULL, NULL);
furi_string_replace(error_message, ":", "\n");
furi_string_replace(error_message, "/ext/apps/", "");
furi_string_replace(error_message, ", ", "\n");
furi_string_replace(error_message, ": ", "\n");
dialog_message_set_text(
message, furi_string_get_cstr(error_message), 64, 32, AlignCenter, AlignCenter);
message, furi_string_get_cstr(error_message), 64, 35, AlignCenter, AlignCenter);
dialog_message_show(dialogs, message);
dialog_message_free(message);
@ -268,22 +298,20 @@ static LoaderStatus loader_start_external_app(
if(preload_res != FlipperApplicationPreloadStatusSuccess) {
const char* err_msg = flipper_application_preload_status_to_string(preload_res);
status = loader_make_status_error(
LoaderStatusErrorInternal, error_message, "Preload failed %s: %s", path, err_msg);
LoaderStatusErrorInternal, error_message, "Preload failed, %s: %s", path, err_msg);
break;
}
FURI_LOG_I(TAG, "Mapping");
FlipperApplicationLoadStatus load_status =
flipper_application_map_to_memory(loader->app.fap);
if(load_status != FlipperApplicationLoadStatusSuccess) {
const char* err_msg = flipper_application_load_status_to_string(load_status);
status = loader_make_status_error(
LoaderStatusErrorInternal, error_message, "Load failed %s: %s", path, err_msg);
LoaderStatusErrorInternal, error_message, "Load failed, %s: %s", path, err_msg);
break;
}
FURI_LOG_I(TAG, "Loaded in %zums", (size_t)(furi_get_tick() - start));
FURI_LOG_I(TAG, "Starting app");
loader->app.thread = flipper_application_alloc_thread(loader->app.fap, args);
FuriString* app_name = furi_string_alloc();
@ -379,7 +407,15 @@ static LoaderStatus loader_do_start_by_name(
break;
}
// check external apps
// check External Applications
{
const char* path = loader_find_external_application_by_name(name);
if(path) {
name = path;
}
}
// check Faps
{
Storage* storage = furi_record_open(RECORD_STORAGE);
if(storage_file_exists(storage, name)) {

View File

@ -20,7 +20,7 @@ static int32_t loader_applications_thread(void* p);
LoaderApplications* loader_applications_alloc(void (*closed_cb)(void*), void* context) {
LoaderApplications* loader_applications = malloc(sizeof(LoaderApplications));
loader_applications->thread =
furi_thread_alloc_ex(TAG, 512, loader_applications_thread, (void*)loader_applications);
furi_thread_alloc_ex(TAG, 768, loader_applications_thread, (void*)loader_applications);
loader_applications->closed_cb = closed_cb;
loader_applications->context = context;
furi_thread_start(loader_applications->thread);
@ -38,6 +38,11 @@ typedef struct {
FuriString* fap_path;
DialogsApp* dialogs;
Storage* storage;
Loader* loader;
Gui* gui;
ViewHolder* view_holder;
Loading* loading;
} LoaderApplicationsApp;
static LoaderApplicationsApp* loader_applications_app_alloc() {
@ -45,15 +50,30 @@ static LoaderApplicationsApp* loader_applications_app_alloc() {
app->fap_path = furi_string_alloc_set(EXT_PATH("apps"));
app->dialogs = furi_record_open(RECORD_DIALOGS);
app->storage = furi_record_open(RECORD_STORAGE);
app->loader = furi_record_open(RECORD_LOADER);
app->gui = furi_record_open(RECORD_GUI);
app->view_holder = view_holder_alloc();
app->loading = loading_alloc();
view_holder_attach_to_gui(app->view_holder, app->gui);
view_holder_set_view(app->view_holder, loading_get_view(app->loading));
return app;
} //-V773
static void loader_applications_app_free(LoaderApplicationsApp* loader_applications_app) {
furi_assert(loader_applications_app);
static void loader_applications_app_free(LoaderApplicationsApp* app) {
furi_assert(app);
view_holder_free(app->view_holder);
loading_free(app->loading);
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_LOADER);
furi_record_close(RECORD_DIALOGS);
furi_record_close(RECORD_STORAGE);
furi_string_free(loader_applications_app->fap_path);
free(loader_applications_app);
furi_string_free(app->fap_path);
free(app);
}
static bool loader_applications_item_callback(
@ -96,47 +116,38 @@ static void loader_pubsub_callback(const void* message, void* context) {
}
}
static void loader_applications_start_app(const char* name) {
// start loading animation
Gui* gui = furi_record_open(RECORD_GUI);
ViewHolder* view_holder = view_holder_alloc();
Loading* loading = loading_alloc();
view_holder_attach_to_gui(view_holder, gui);
view_holder_set_view(view_holder, loading_get_view(loading));
view_holder_start(view_holder);
static void loader_applications_start_app(LoaderApplicationsApp* app) {
const char* name = furi_string_get_cstr(app->fap_path);
// load app
FuriThreadId thread_id = furi_thread_get_current_id();
Loader* loader = furi_record_open(RECORD_LOADER);
FuriPubSubSubscription* subscription =
furi_pubsub_subscribe(loader_get_pubsub(loader), loader_pubsub_callback, thread_id);
furi_pubsub_subscribe(loader_get_pubsub(app->loader), loader_pubsub_callback, thread_id);
LoaderStatus status = loader_start_with_gui_error(loader, name, NULL);
LoaderStatus status = loader_start_with_gui_error(app->loader, name, NULL);
if(status == LoaderStatusOk) {
furi_thread_flags_wait(APPLICATION_STOP_EVENT, FuriFlagWaitAny, FuriWaitForever);
}
furi_pubsub_unsubscribe(loader_get_pubsub(loader), subscription);
furi_record_close(RECORD_LOADER);
// stop loading animation
view_holder_stop(view_holder);
view_holder_free(view_holder);
loading_free(loading);
furi_record_close(RECORD_GUI);
furi_pubsub_unsubscribe(loader_get_pubsub(app->loader), subscription);
}
static int32_t loader_applications_thread(void* p) {
LoaderApplications* loader_applications = p;
LoaderApplicationsApp* loader_applications_app = loader_applications_app_alloc();
LoaderApplicationsApp* app = loader_applications_app_alloc();
while(loader_applications_select_app(loader_applications_app)) {
loader_applications_start_app(furi_string_get_cstr(loader_applications_app->fap_path));
// start loading animation
view_holder_start(app->view_holder);
while(loader_applications_select_app(app)) {
loader_applications_start_app(app);
}
loader_applications_app_free(loader_applications_app);
// stop loading animation
view_holder_stop(app->view_holder);
loader_applications_app_free(app);
if(loader_applications->closed_cb) {
loader_applications->closed_cb(loader_applications->context);

View File

@ -52,12 +52,18 @@ static void loader_menu_start(const char* name) {
furi_record_close(RECORD_LOADER);
}
static void loader_menu_callback(void* context, uint32_t index) {
static void loader_menu_apps_callback(void* context, uint32_t index) {
UNUSED(context);
const char* name = FLIPPER_APPS[index].name;
loader_menu_start(name);
}
static void loader_menu_external_apps_callback(void* context, uint32_t index) {
UNUSED(context);
const char* path = FLIPPER_EXTERNAL_APPS[index].name;
loader_menu_start(path);
}
static void loader_menu_applications_callback(void* context, uint32_t index) {
UNUSED(index);
UNUSED(context);
@ -89,13 +95,24 @@ static uint32_t loader_menu_exit(void* context) {
static void loader_menu_build_menu(LoaderMenuApp* app, LoaderMenu* menu) {
size_t i;
for(i = 0; i < FLIPPER_EXTERNAL_APPS_COUNT; i++) {
menu_add_item(
app->primary_menu,
FLIPPER_EXTERNAL_APPS[i].name,
FLIPPER_EXTERNAL_APPS[i].icon,
i,
loader_menu_external_apps_callback,
(void*)menu);
}
for(i = 0; i < FLIPPER_APPS_COUNT; i++) {
menu_add_item(
app->primary_menu,
FLIPPER_APPS[i].name,
FLIPPER_APPS[i].icon,
i,
loader_menu_callback,
loader_menu_apps_callback,
(void*)menu);
}
menu_add_item(

View File

@ -117,6 +117,7 @@ static bool power_update_info(Power* power) {
info.is_charging = furi_hal_power_is_charging();
info.gauge_is_ok = furi_hal_power_gauge_is_ok();
info.is_shutdown_requested = furi_hal_power_is_shutdown_requested();
info.charge = furi_hal_power_get_pct();
info.health = furi_hal_power_get_bat_health_pct();
info.capacity_remaining = furi_hal_power_get_battery_remaining_capacity();
@ -145,7 +146,7 @@ static void power_check_low_battery(Power* power) {
}
// Check battery charge and vbus voltage
if((power->info.charge == 0) && (power->info.voltage_vbus < 4.0f) &&
if((power->info.is_shutdown_requested) && (power->info.voltage_vbus < 4.0f) &&
power->show_low_bat_level_message) {
if(!power->battery_low) {
view_dispatcher_send_to_front(power->view_dispatcher);

View File

@ -37,6 +37,7 @@ typedef struct {
typedef struct {
bool gauge_is_ok;
bool is_charging;
bool is_shutdown_requested;
float current_charger;
float current_gauge;

View File

@ -430,6 +430,20 @@ FS_Error storage_common_rename(Storage* storage, const char* old_path, const cha
break;
}
if(storage_dir_exists(storage, old_path)) {
FuriString* dir_path = furi_string_alloc_set_str(old_path);
if(!furi_string_end_with_str(dir_path, "/")) {
furi_string_cat_str(dir_path, "/");
}
const char* dir_path_s = furi_string_get_cstr(dir_path);
if(strncmp(new_path, dir_path_s, strlen(dir_path_s)) == 0) {
error = FSE_INVALID_NAME;
furi_string_free(dir_path);
break;
}
furi_string_free(dir_path);
}
if(storage_file_exists(storage, new_path)) {
storage_common_remove(storage, new_path);
}

View File

@ -82,7 +82,9 @@ static DialogMessageButton icon1_screen(DialogsApp* dialogs, DialogMessage* mess
static DialogMessageButton icon2_screen(DialogsApp* dialogs, DialogMessage* message) {
DialogMessageButton result;
dialog_message_set_icon(message, &I_Certification2_98x33, 15, 10);
dialog_message_set_icon(message, &I_Certification2_46x33, 15, 10);
dialog_message_set_text(
message, furi_hal_version_get_mic_id(), 63, 27, AlignLeft, AlignCenter);
result = dialog_message_show(dialogs, message);
dialog_message_set_icon(message, NULL, 0, 0);

View File

@ -5,11 +5,26 @@
#include <storage/storage.h>
#include <dialogs/dialogs.h>
#define APPS_COUNT (FLIPPER_APPS_COUNT + FLIPPER_EXTERNAL_APPS_COUNT)
#define EXTERNAL_BROWSER_NAME ("Applications")
#define EXTERNAL_BROWSER_INDEX (FLIPPER_APPS_COUNT + 1)
#define EXTERNAL_BROWSER_INDEX (APPS_COUNT + 1)
#define EXTERNAL_APPLICATION_NAME ("[External Application]")
#define EXTERNAL_APPLICATION_INDEX (FLIPPER_APPS_COUNT + 2)
#define EXTERNAL_APPLICATION_INDEX (APPS_COUNT + 2)
#define PRESELECTED_SPECIAL 0xffffffff
static const char* favorite_fap_get_app_name(size_t i) {
const char* name;
if(i < FLIPPER_APPS_COUNT) {
name = FLIPPER_APPS[i].name;
} else {
name = FLIPPER_EXTERNAL_APPS[i - FLIPPER_APPS_COUNT].name;
}
return name;
}
static bool favorite_fap_selector_item_callback(
FuriString* file_path,
@ -42,21 +57,17 @@ void desktop_settings_scene_favorite_on_enter(void* context) {
uint32_t primary_favorite =
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite);
uint32_t pre_select_item = 0;
uint32_t pre_select_item = PRESELECTED_SPECIAL;
FavoriteApp* curr_favorite_app = primary_favorite ? &app->settings.favorite_primary :
&app->settings.favorite_secondary;
for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
submenu_add_item(
submenu,
FLIPPER_APPS[i].name,
i,
desktop_settings_scene_favorite_submenu_callback,
app);
for(size_t i = 0; i < APPS_COUNT; i++) {
const char* name = favorite_fap_get_app_name(i);
submenu_add_item(submenu, name, i, desktop_settings_scene_favorite_submenu_callback, app);
// Select favorite item in submenu
if(!curr_favorite_app->is_external &&
!strcmp(FLIPPER_APPS[i].name, curr_favorite_app->name_or_path)) {
if(!strcmp(name, curr_favorite_app->name_or_path)) {
pre_select_item = i;
}
}
@ -77,7 +88,7 @@ void desktop_settings_scene_favorite_on_enter(void* context) {
desktop_settings_scene_favorite_submenu_callback,
app);
if(curr_favorite_app->is_external) {
if(pre_select_item == PRESELECTED_SPECIAL) {
if(curr_favorite_app->name_or_path[0] == '\0') {
pre_select_item = EXTERNAL_BROWSER_INDEX;
} else {
@ -104,7 +115,6 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == EXTERNAL_BROWSER_INDEX) {
curr_favorite_app->is_external = true;
curr_favorite_app->name_or_path[0] = '\0';
consumed = true;
} else if(event.event == EXTERNAL_APPLICATION_INDEX) {
@ -125,7 +135,6 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e
if(dialog_file_browser_show(app->dialogs, temp_path, temp_path, &browser_options)) {
submenu_reset(app->submenu); // Prevent menu from being shown when we exiting scene
curr_favorite_app->is_external = true;
strncpy(
curr_favorite_app->name_or_path,
furi_string_get_cstr(temp_path),
@ -133,9 +142,8 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e
consumed = true;
}
} else {
curr_favorite_app->is_external = false;
strncpy(
curr_favorite_app->name_or_path, FLIPPER_APPS[event.event].name, MAX_APP_LENGTH);
const char* name = favorite_fap_get_app_name(event.event);
if(name) strncpy(curr_favorite_app->name_or_path, name, MAX_APP_LENGTH);
consumed = true;
}
if(consumed) {

View File

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

View File

@ -19,7 +19,7 @@ static const char* update_task_stage_descr[] = {
[UpdateTaskStageRadioErase] = "Uninstalling radio FW",
[UpdateTaskStageRadioWrite] = "Writing radio FW",
[UpdateTaskStageRadioInstall] = "Installing radio FW",
[UpdateTaskStageRadioBusy] = "Radio is updating",
[UpdateTaskStageRadioBusy] = "Core 2 busy",
[UpdateTaskStageOBValidation] = "Validating opt. bytes",
[UpdateTaskStageLfsBackup] = "Backing up LFS",
[UpdateTaskStageLfsRestore] = "Restoring LFS",
@ -30,6 +30,191 @@ static const char* update_task_stage_descr[] = {
[UpdateTaskStageOBError] = "OB, report",
};
static const struct {
UpdateTaskStage stage;
uint8_t percent_min, percent_max;
const char* descr;
} update_task_error_detail[] = {
{
.stage = UpdateTaskStageReadManifest,
.percent_min = 0,
.percent_max = 13,
.descr = "Wrong Updater HW",
},
{
.stage = UpdateTaskStageReadManifest,
.percent_min = 14,
.percent_max = 20,
.descr = "Manifest pointer error",
},
{
.stage = UpdateTaskStageReadManifest,
.percent_min = 21,
.percent_max = 30,
.descr = "Manifest load error",
},
{
.stage = UpdateTaskStageReadManifest,
.percent_min = 31,
.percent_max = 40,
.descr = "Wrong package version",
},
{
.stage = UpdateTaskStageReadManifest,
.percent_min = 41,
.percent_max = 50,
.descr = "HW Target mismatch",
},
{
.stage = UpdateTaskStageReadManifest,
.percent_min = 51,
.percent_max = 60,
.descr = "No DFU file",
},
{
.stage = UpdateTaskStageReadManifest,
.percent_min = 61,
.percent_max = 80,
.descr = "No Radio file",
},
#ifndef FURI_RAM_EXEC
{
.stage = UpdateTaskStageLfsBackup,
.percent_min = 0,
.percent_max = 100,
.descr = "FS R/W error",
},
#else
{
.stage = UpdateTaskStageRadioImageValidate,
.percent_min = 0,
.percent_max = 98,
.descr = "FS Read error",
},
{
.stage = UpdateTaskStageRadioImageValidate,
.percent_min = 99,
.percent_max = 100,
.descr = "CRC mismatch",
},
{
.stage = UpdateTaskStageRadioErase,
.percent_min = 0,
.percent_max = 30,
.descr = "Stack remove: cmd error",
},
{
.stage = UpdateTaskStageRadioErase,
.percent_min = 31,
.percent_max = 100,
.descr = "Stack remove: wait failed",
},
{
.stage = UpdateTaskStageRadioWrite,
.percent_min = 0,
.percent_max = 100,
.descr = "Stack write: error",
},
{
.stage = UpdateTaskStageRadioInstall,
.percent_min = 0,
.percent_max = 10,
.descr = "Stack install: cmd error",
},
{
.stage = UpdateTaskStageRadioInstall,
.percent_min = 11,
.percent_max = 100,
.descr = "Stack install: wait failed",
},
{
.stage = UpdateTaskStageRadioBusy,
.percent_min = 0,
.percent_max = 10,
.descr = "Failed to start C2",
},
{
.stage = UpdateTaskStageRadioBusy,
.percent_min = 11,
.percent_max = 20,
.descr = "C2 FUS swich failed",
},
{
.stage = UpdateTaskStageRadioBusy,
.percent_min = 21,
.percent_max = 30,
.descr = "FUS operation failed",
},
{
.stage = UpdateTaskStageRadioBusy,
.percent_min = 31,
.percent_max = 100,
.descr = "C2 Stach switch failed",
},
{
.stage = UpdateTaskStageOBValidation,
.percent_min = 0,
.percent_max = 100,
.descr = "Uncorr. value mismatch",
},
{
.stage = UpdateTaskStageValidateDFUImage,
.percent_min = 0,
.percent_max = 1,
.descr = "Failed to open DFU file",
},
{
.stage = UpdateTaskStageValidateDFUImage,
.percent_min = 1,
.percent_max = 97,
.descr = "DFU file read error",
},
{
.stage = UpdateTaskStageValidateDFUImage,
.percent_min = 98,
.percent_max = 100,
.descr = "DFU file CRC mismatch",
},
{
.stage = UpdateTaskStageFlashWrite,
.percent_min = 0,
.percent_max = 100,
.descr = "Flash write error",
},
{
.stage = UpdateTaskStageFlashValidate,
.percent_min = 0,
.percent_max = 100,
.descr = "Flash compare error",
},
#endif
#ifndef FURI_RAM_EXEC
{
.stage = UpdateTaskStageLfsRestore,
.percent_min = 0,
.percent_max = 100,
.descr = "LFS I/O error",
},
{
.stage = UpdateTaskStageResourcesUpdate,
.percent_min = 0,
.percent_max = 100,
.descr = "SD card I/O error",
},
#endif
};
static const char* update_task_get_error_message(UpdateTaskStage stage, uint8_t percent) {
for(size_t i = 0; i < COUNT_OF(update_task_error_detail); i++) {
if(update_task_error_detail[i].stage == stage &&
percent >= update_task_error_detail[i].percent_min &&
percent <= update_task_error_detail[i].percent_max) {
return update_task_error_detail[i].descr;
}
}
return "Unknown error";
}
typedef struct {
UpdateTaskStageGroup group;
uint8_t weight;
@ -111,8 +296,9 @@ void update_task_set_progress(UpdateTask* update_task, UpdateTaskStage stage, ui
if(stage >= UpdateTaskStageError) {
furi_string_printf(
update_task->state.status,
"%s #[%d-%d]",
update_task_stage_descr[stage],
"%s\n#[%d-%d]",
update_task_get_error_message(
update_task->state.stage, update_task->state.stage_progress),
update_task->state.stage,
update_task->state.stage_progress);
} else {

View File

@ -24,3 +24,8 @@ bool update_task_open_file(UpdateTask* update_task, FuriString* filename);
int32_t update_task_worker_flash_writer(void* context);
int32_t update_task_worker_backup_restore(void* context);
#define CHECK_RESULT(x) \
if(!(x)) { \
break; \
}

View File

@ -15,11 +15,6 @@
#define TAG "UpdWorkerBackup"
#define CHECK_RESULT(x) \
if(!(x)) { \
break; \
}
static bool update_task_pre_update(UpdateTask* update_task) {
bool success = false;
FuriString* backup_file_path;

View File

@ -13,11 +13,6 @@
#define TAG "UpdWorkerRAM"
#define CHECK_RESULT(x) \
if(!(x)) { \
break; \
}
#define STM_DFU_VENDOR_ID 0x0483
#define STM_DFU_PRODUCT_ID 0xDF11
/* Written into DFU file by build pipeline */
@ -137,7 +132,7 @@ static bool update_task_write_stack_data(UpdateTask* update_task) {
}
static void update_task_wait_for_restart(UpdateTask* update_task) {
update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 10);
update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 70);
furi_delay_ms(C2_MODE_SWITCH_TIMEOUT);
furi_crash("C2 timeout");
}
@ -153,12 +148,12 @@ static bool update_task_write_stack(UpdateTask* update_task) {
manifest->radio_crc);
CHECK_RESULT(update_task_write_stack_data(update_task));
update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 0);
update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 10);
CHECK_RESULT(
ble_glue_fus_stack_install(manifest->radio_address, 0) != BleGlueCommandResultError);
update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 80);
update_task_set_progress(update_task, UpdateTaskStageProgress, 80);
CHECK_RESULT(ble_glue_fus_wait_operation() == BleGlueCommandResultOK);
update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 100);
update_task_set_progress(update_task, UpdateTaskStageProgress, 100);
/* ...system will restart here. */
update_task_wait_for_restart(update_task);
} while(false);
@ -170,9 +165,9 @@ static bool update_task_remove_stack(UpdateTask* update_task) {
FURI_LOG_W(TAG, "Removing stack");
update_task_set_progress(update_task, UpdateTaskStageRadioErase, 30);
CHECK_RESULT(ble_glue_fus_stack_delete() != BleGlueCommandResultError);
update_task_set_progress(update_task, UpdateTaskStageRadioErase, 80);
update_task_set_progress(update_task, UpdateTaskStageProgress, 80);
CHECK_RESULT(ble_glue_fus_wait_operation() == BleGlueCommandResultOK);
update_task_set_progress(update_task, UpdateTaskStageRadioErase, 100);
update_task_set_progress(update_task, UpdateTaskStageProgress, 100);
/* ...system will restart here. */
update_task_wait_for_restart(update_task);
} while(false);
@ -180,6 +175,7 @@ static bool update_task_remove_stack(UpdateTask* update_task) {
}
static bool update_task_manage_radiostack(UpdateTask* update_task) {
update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 10);
bool success = false;
do {
CHECK_RESULT(ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT));
@ -208,15 +204,17 @@ static bool update_task_manage_radiostack(UpdateTask* update_task) {
/* Version or type mismatch. Let's boot to FUS and start updating. */
FURI_LOG_W(TAG, "Restarting to FUS");
furi_hal_rtc_set_flag(FuriHalRtcFlagC2Update);
update_task_set_progress(update_task, UpdateTaskStageProgress, 20);
CHECK_RESULT(furi_hal_bt_ensure_c2_mode(BleGlueC2ModeFUS));
/* ...system will restart here. */
update_task_wait_for_restart(update_task);
}
} else if(c2_state->mode == BleGlueC2ModeFUS) {
/* OK, we're in FUS mode. */
update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 10);
FURI_LOG_W(TAG, "Waiting for FUS to settle");
ble_glue_fus_wait_operation();
update_task_set_progress(update_task, UpdateTaskStageProgress, 30);
CHECK_RESULT(ble_glue_fus_wait_operation() == BleGlueCommandResultOK);
if(stack_version_match) {
/* We can't check StackType with FUS, but partial version matches */
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagC2Update)) {
@ -230,7 +228,7 @@ static bool update_task_manage_radiostack(UpdateTask* update_task) {
/* We might just had the stack installed.
* Let's start it up to check its version */
FURI_LOG_W(TAG, "Starting stack to check full version");
update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 40);
update_task_set_progress(update_task, UpdateTaskStageProgress, 50);
CHECK_RESULT(furi_hal_bt_ensure_c2_mode(BleGlueC2ModeStack));
/* ...system will restart here. */
update_task_wait_for_restart(update_task);

View File

@ -81,16 +81,17 @@ static void updater_main_draw_callback(Canvas* canvas, void* _model) {
canvas_set_font(canvas, FontPrimary);
if(model->failed) {
canvas_draw_str_aligned(canvas, 42, 16, AlignLeft, AlignTop, "Update Failed!");
canvas_draw_icon(canvas, 2, 22, &I_Warning_30x23);
canvas_draw_str_aligned(canvas, 40, 9, AlignLeft, AlignTop, "Update Failed!");
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(
canvas, 42, 32, AlignLeft, AlignTop, furi_string_get_cstr(model->status));
canvas_draw_icon(canvas, 7, 16, &I_Warning_30x23);
elements_multiline_text_aligned(
canvas, 75, 26, AlignCenter, AlignTop, furi_string_get_cstr(model->status));
canvas_draw_str_aligned(
canvas, 18, 51, AlignLeft, AlignTop, "to retry, hold to abort");
canvas_draw_icon(canvas, 7, 50, &I_Ok_btn_9x9);
canvas_draw_icon(canvas, 75, 51, &I_Pin_back_arrow_10x8);
canvas, 18, 55, AlignLeft, AlignTop, "to retry, hold to abort");
canvas_draw_icon(canvas, 7, 54, &I_Ok_btn_9x9);
canvas_draw_icon(canvas, 75, 55, &I_Pin_back_arrow_10x8);
} else {
canvas_draw_str_aligned(canvas, 55, 14, AlignLeft, AlignTop, "UPDATING");
canvas_set_font(canvas, FontSecondary);

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 990 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

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