Merge remote-tracking branch 'origin/release-candidate' into release
20
.vscode/example/tasks.json
vendored
@ -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",
|
||||
|
||||
14
applications/debug/subghz_test/application.fam
Normal 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",
|
||||
)
|
||||
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
//SubGhzTestCustomEvent
|
||||
SubGhzTestCustomEventStartId = 100,
|
||||
SubGhzTestCustomEventSceneShowOnlyRX,
|
||||
} SubGhzTestCustomEvent;
|
||||
@ -1,4 +1,4 @@
|
||||
#include "subghz_testing.h"
|
||||
#include "subghz_test_frequency.h"
|
||||
|
||||
const uint32_t subghz_frequencies_testing[] = {
|
||||
/* 300 - 348 */
|
||||
@ -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;
|
||||
18
applications/debug/subghz_test/helpers/subghz_test_types.h
Normal 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;
|
||||
BIN
applications/debug/subghz_test/images/DolphinCommon_56x48.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
244
applications/debug/subghz_test/protocol/math.c
Normal 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;
|
||||
}
|
||||
222
applications/debug/subghz_test/protocol/math.h
Normal 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
|
||||
@ -1,7 +1,7 @@
|
||||
#include "princeton_for_testing.h"
|
||||
|
||||
#include <furi_hal.h>
|
||||
#include "../blocks/math.h"
|
||||
#include "math.h"
|
||||
|
||||
/*
|
||||
* Help
|
||||
@ -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;
|
||||
30
applications/debug/subghz_test/scenes/subghz_test_scene.c
Normal 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,
|
||||
};
|
||||
29
applications/debug/subghz_test/scenes/subghz_test_scene.h
Normal 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
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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)
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
BIN
applications/debug/subghz_test/subghz_test_10px.png
Normal file
|
After Width: | Height: | Size: 181 B |
139
applications/debug/subghz_test/subghz_test_app.c
Normal 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;
|
||||
}
|
||||
5
applications/debug/subghz_test/subghz_test_app_i.c
Normal file
@ -0,0 +1,5 @@
|
||||
#include "subghz_test_app_i.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define TAG "SubGhzTest"
|
||||
32
applications/debug/subghz_test/subghz_test_app_i.h
Normal 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;
|
||||
};
|
||||
@ -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>
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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"],
|
||||
)
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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",
|
||||
)
|
||||
|
||||
BIN
applications/main/bad_usb/icon.png
Normal file
|
After Width: | Height: | Size: 576 B |
@ -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",
|
||||
)
|
||||
|
||||
BIN
applications/main/gpio/icon.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
@ -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,
|
||||
)
|
||||
|
||||
BIN
applications/main/ibutton/icon.png
Normal file
|
After Width: | Height: | Size: 304 B |
@ -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);
|
||||
|
||||
@ -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,
|
||||
)
|
||||
|
||||
BIN
applications/main/infrared/icon.png
Normal file
|
After Width: | Height: | Size: 305 B |
@ -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,
|
||||
)
|
||||
|
||||
BIN
applications/main/lfrfid/icon.png
Normal file
|
After Width: | Height: | Size: 308 B |
@ -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,
|
||||
)
|
||||
|
||||
BIN
applications/main/nfc/icon.png
Normal file
|
After Width: | Height: | Size: 304 B |
@ -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,
|
||||
)
|
||||
|
||||
@ -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,
|
||||
)
|
||||
|
||||
@ -80,9 +80,6 @@ typedef enum {
|
||||
SubGhzViewIdFrequencyAnalyzer,
|
||||
SubGhzViewIdReadRAW,
|
||||
|
||||
SubGhzViewIdStatic,
|
||||
SubGhzViewIdTestCarrier,
|
||||
SubGhzViewIdTestPacket,
|
||||
} SubGhzViewId;
|
||||
|
||||
/** SubGhz load type file */
|
||||
|
||||
BIN
applications/main/subghz/icon.png
Normal file
|
After Width: | Height: | Size: 299 B |
@ -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)
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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",
|
||||
)
|
||||
|
||||
BIN
applications/main/u2f/icon.png
Normal file
|
After Width: | Height: | Size: 583 B |
@ -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;
|
||||
|
||||
@ -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(), ¤t_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);
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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)) {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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; \
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
BIN
assets/dolphin/external/L1_My_dude_128x64/frame_0.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_My_dude_128x64/frame_1.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_My_dude_128x64/frame_10.png
vendored
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/dolphin/external/L1_My_dude_128x64/frame_11.png
vendored
Normal file
|
After Width: | Height: | Size: 990 B |
BIN
assets/dolphin/external/L1_My_dude_128x64/frame_12.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/dolphin/external/L1_My_dude_128x64/frame_13.png
vendored
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/dolphin/external/L1_My_dude_128x64/frame_14.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/dolphin/external/L1_My_dude_128x64/frame_15.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/dolphin/external/L1_My_dude_128x64/frame_16.png
vendored
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/dolphin/external/L1_My_dude_128x64/frame_17.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/dolphin/external/L1_My_dude_128x64/frame_18.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/dolphin/external/L1_My_dude_128x64/frame_19.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_My_dude_128x64/frame_2.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |