mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-13 10:58:36 -07:00
Merge branch 'dev' of https://github.com/DarkFlippers/unleashed-firmware into xfw-dev
This commit is contained in:
16
applications/external/lightmeter/application.fam
vendored
16
applications/external/lightmeter/application.fam
vendored
@@ -1,6 +1,6 @@
|
||||
App(
|
||||
appid="lightmeter",
|
||||
name="[BH1750] Lightmeter",
|
||||
name="Lightmeter",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="lightmeter_app",
|
||||
requires=[
|
||||
@@ -8,6 +8,7 @@ App(
|
||||
],
|
||||
stack_size=1 * 1024,
|
||||
order=90,
|
||||
fap_version=(1, 1),
|
||||
fap_icon="lightmeter.png",
|
||||
fap_category="GPIO",
|
||||
fap_private_libs=[
|
||||
@@ -18,9 +19,16 @@ App(
|
||||
"BH1750.c",
|
||||
],
|
||||
),
|
||||
Lib(
|
||||
name="MAX44009",
|
||||
cincludes=["."],
|
||||
sources=[
|
||||
"MAX44009.c",
|
||||
],
|
||||
),
|
||||
],
|
||||
fap_description="Lightmeter app for photography",
|
||||
fap_author="Oleksii Kutuzov",
|
||||
fap_weburl="https://github.com/oleksiikutuzov/flipperzero-lightmeter",
|
||||
fap_icon_assets="icons",
|
||||
fap_author="@oleksiikutuzov",
|
||||
fap_version="1.0",
|
||||
fap_description="Lightmeter app for photography based on BH1750 sensor",
|
||||
)
|
||||
|
||||
@@ -51,12 +51,18 @@ static const char* lux_only[] = {
|
||||
[LUX_ONLY_ON] = "On",
|
||||
};
|
||||
|
||||
static const char* sensor_type[] = {
|
||||
[SENSOR_BH1750] = "BH1750",
|
||||
[SENSOR_MAX44009] = "MAX44009",
|
||||
};
|
||||
|
||||
enum LightMeterSubmenuIndex {
|
||||
LightMeterSubmenuIndexISO,
|
||||
LightMeterSubmenuIndexND,
|
||||
LightMeterSubmenuIndexDome,
|
||||
LightMeterSubmenuIndexBacklight,
|
||||
LightMeterSubmenuIndexLuxMeter,
|
||||
LightMeterSubmenuIndexSensorType,
|
||||
LightMeterSubmenuIndexHelp,
|
||||
LightMeterSubmenuIndexAbout,
|
||||
};
|
||||
@@ -127,6 +133,17 @@ static void lux_only_cb(VariableItem* item) {
|
||||
lightmeter_app_set_config(app, config);
|
||||
}
|
||||
|
||||
static void sensor_type_cb(VariableItem* item) {
|
||||
LightMeterApp* app = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, sensor_type[index]);
|
||||
|
||||
LightMeterConfig* config = app->config;
|
||||
config->sensor_type = index;
|
||||
lightmeter_app_set_config(app, config);
|
||||
}
|
||||
|
||||
static void ok_cb(void* context, uint32_t index) {
|
||||
LightMeterApp* app = context;
|
||||
UNUSED(app);
|
||||
@@ -173,6 +190,11 @@ void lightmeter_scene_config_on_enter(void* context) {
|
||||
variable_item_set_current_value_index(item, config->lux_only);
|
||||
variable_item_set_current_value_text(item, lux_only[config->lux_only]);
|
||||
|
||||
item = variable_item_list_add(
|
||||
var_item_list, "Sensor", COUNT_OF(sensor_type), sensor_type_cb, app);
|
||||
variable_item_set_current_value_index(item, config->sensor_type);
|
||||
variable_item_set_current_value_text(item, sensor_type[config->sensor_type]);
|
||||
|
||||
item = variable_item_list_add(var_item_list, "Help and Pinout", 0, NULL, NULL);
|
||||
item = variable_item_list_add(var_item_list, "About", 0, NULL, NULL);
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@ void lightmeter_scene_help_on_enter(void* context) {
|
||||
FuriString* temp_str;
|
||||
temp_str = furi_string_alloc();
|
||||
furi_string_printf(
|
||||
temp_str, "App works with BH1750\nambient light sensor\nconnected via I2C interface\n\n");
|
||||
temp_str,
|
||||
"App works with BH1750 and MAX44409 ambient light sensor connected via I2C interface\n\n");
|
||||
furi_string_cat(temp_str, "\e#Pinout:\r\n");
|
||||
furi_string_cat(
|
||||
temp_str,
|
||||
|
||||
@@ -9,6 +9,7 @@ static void lightmeter_scene_main_on_left(void* context) {
|
||||
void lightmeter_scene_main_on_enter(void* context) {
|
||||
LightMeterApp* app = context;
|
||||
|
||||
lightmeter_app_i2c_init_sensor(context);
|
||||
lightmeter_main_view_set_left_callback(app->main_view, lightmeter_scene_main_on_left, app);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, LightMeterAppViewMainView);
|
||||
}
|
||||
@@ -39,5 +40,5 @@ bool lightmeter_scene_main_on_event(void* context, SceneManagerEvent event) {
|
||||
}
|
||||
|
||||
void lightmeter_scene_main_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
lightmeter_app_i2c_deinit_sensor(context);
|
||||
}
|
||||
|
||||
27
applications/external/lightmeter/lib/MAX44009/MAX44009.c
vendored
Normal file
27
applications/external/lightmeter/lib/MAX44009/MAX44009.c
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
#include <MAX44009.h>
|
||||
#include <math.h>
|
||||
#include <furi.h>
|
||||
|
||||
void max44009_init() {
|
||||
furi_hal_i2c_acquire(I2C_BUS);
|
||||
furi_hal_i2c_write_reg_8(I2C_BUS, MAX44009_ADDR,
|
||||
MAX44009_REG_CONFIG, MAX44009_REG_CONFIG_CONT_MODE, I2C_TIMEOUT);
|
||||
furi_hal_i2c_release(I2C_BUS);
|
||||
}
|
||||
|
||||
int max44009_read_light(float* result) {
|
||||
uint8_t data_one = 0;
|
||||
uint8_t exp, mantissa;
|
||||
int status;
|
||||
|
||||
furi_hal_i2c_acquire(I2C_BUS);
|
||||
furi_hal_i2c_read_reg_8(I2C_BUS, MAX44009_ADDR, MAX44009_REG_LUX_HI, &data_one, I2C_TIMEOUT);
|
||||
exp = (data_one & MAX44009_REG_LUX_HI_EXP_MASK) >> 4;
|
||||
mantissa = (data_one & MAX44009_REG_LUX_HI_MANT_HI_MASK) << 4;
|
||||
status = furi_hal_i2c_read_reg_8(I2C_BUS, MAX44009_ADDR, MAX44009_REG_LUX_LO, &data_one, I2C_TIMEOUT);
|
||||
mantissa |= (data_one & MAX44009_REG_LUX_LO_MANT_LO_MASK);
|
||||
furi_hal_i2c_release(I2C_BUS);
|
||||
*result = (float)pow(2, exp) * mantissa * 0.045;
|
||||
FURI_LOG_D("MAX44009", "exp %d, mant %d, lux %f", exp, mantissa, (double)*result);
|
||||
return status;
|
||||
}
|
||||
26
applications/external/lightmeter/lib/MAX44009/MAX44009.h
vendored
Normal file
26
applications/external/lightmeter/lib/MAX44009/MAX44009.h
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
// I2C BUS
|
||||
#define I2C_BUS &furi_hal_i2c_handle_external
|
||||
#define I2C_TIMEOUT 10
|
||||
|
||||
#define MAX44009_ADDR (0x4A << 1)
|
||||
|
||||
#define MAX44009_REG_INT_STATUS 0x00
|
||||
#define MAX44009_REG_INT_EN 0x01
|
||||
#define MAX44009_REG_CONFIG 0x02
|
||||
#define MAX44009_REG_CONFIG_CONT_MODE (1 << 7)
|
||||
#define MAX44009_REG_LUX_HI 0x03
|
||||
#define MAX44009_REG_LUX_HI_EXP_MASK 0xF0
|
||||
#define MAX44009_REG_LUX_HI_MANT_HI_MASK 0x0F
|
||||
#define MAX44009_REG_LUX_LO 0x04
|
||||
#define MAX44009_REG_LUX_LO_MANT_LO_MASK 0x0F
|
||||
#define MAX44009_REG_THRESH_HI 0x05
|
||||
#define MAX44009_REG_THRESH_LO 0x06
|
||||
#define MAX44009_REG_INT_TIME 0x07
|
||||
|
||||
void max44009_init();
|
||||
int max44009_read_light(float* result);
|
||||
57
applications/external/lightmeter/lightmeter.c
vendored
57
applications/external/lightmeter/lightmeter.c
vendored
@@ -27,11 +27,6 @@ static void lightmeter_tick_event_callback(void* context) {
|
||||
LightMeterApp* lightmeter_app_alloc(uint32_t first_scene) {
|
||||
LightMeterApp* app = malloc(sizeof(LightMeterApp));
|
||||
|
||||
// Sensor
|
||||
bh1750_set_power_state(1);
|
||||
bh1750_init();
|
||||
bh1750_set_mode(ONETIME_HIGH_RES_MODE);
|
||||
|
||||
// Set default values to config
|
||||
app->config = malloc(sizeof(LightMeterConfig));
|
||||
app->config->iso = DEFAULT_ISO;
|
||||
@@ -117,8 +112,6 @@ void lightmeter_app_free(LightMeterApp* app) {
|
||||
}
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
|
||||
bh1750_set_power_state(0);
|
||||
|
||||
free(app->config);
|
||||
free(app);
|
||||
}
|
||||
@@ -138,6 +131,38 @@ void lightmeter_app_set_config(LightMeterApp* context, LightMeterConfig* config)
|
||||
app->config = config;
|
||||
}
|
||||
|
||||
void lightmeter_app_i2c_init_sensor(LightMeterApp* context) {
|
||||
LightMeterApp* app = context;
|
||||
switch(app->config->sensor_type) {
|
||||
case SENSOR_BH1750:
|
||||
bh1750_set_power_state(1);
|
||||
bh1750_init();
|
||||
bh1750_set_mode(ONETIME_HIGH_RES_MODE);
|
||||
break;
|
||||
case SENSOR_MAX44009:
|
||||
max44009_init();
|
||||
break;
|
||||
default:
|
||||
FURI_LOG_E(TAG, "Invalid sensor type %d", app->config->sensor_type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void lightmeter_app_i2c_deinit_sensor(LightMeterApp* context) {
|
||||
LightMeterApp* app = context;
|
||||
switch(app->config->sensor_type) {
|
||||
case SENSOR_BH1750:
|
||||
bh1750_set_power_state(0);
|
||||
break;
|
||||
case SENSOR_MAX44009:
|
||||
// nothing
|
||||
break;
|
||||
default:
|
||||
FURI_LOG_E(TAG, "Invalid sensor type %d", app->config->sensor_type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void lightmeter_app_i2c_callback(LightMeterApp* context) {
|
||||
LightMeterApp* app = context;
|
||||
|
||||
@@ -145,16 +170,18 @@ void lightmeter_app_i2c_callback(LightMeterApp* context) {
|
||||
float lux = 0;
|
||||
bool response = 0;
|
||||
|
||||
if(bh1750_trigger_manual_conversion() == BH1750_OK) response = 1;
|
||||
|
||||
if(response) {
|
||||
bh1750_read_light(&lux);
|
||||
|
||||
if(main_view_get_dome(app->main_view)) lux *= DOME_COEFFICIENT;
|
||||
|
||||
EV = lux2ev(lux);
|
||||
if(app->config->sensor_type == SENSOR_BH1750) {
|
||||
if(bh1750_trigger_manual_conversion() == BH1750_OK) {
|
||||
bh1750_read_light(&lux);
|
||||
response = 1;
|
||||
}
|
||||
} else if(app->config->sensor_type == SENSOR_MAX44009) {
|
||||
if(max44009_read_light(&lux)) response = 1;
|
||||
}
|
||||
|
||||
if(main_view_get_dome(app->main_view)) lux *= DOME_COEFFICIENT;
|
||||
EV = lux2ev(lux);
|
||||
|
||||
main_view_set_lux(app->main_view, lux);
|
||||
main_view_set_EV(app->main_view, EV);
|
||||
main_view_set_response(app->main_view, response);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "lightmeter_config.h"
|
||||
#include <BH1750.h>
|
||||
#include <MAX44009.h>
|
||||
|
||||
typedef struct {
|
||||
int iso;
|
||||
@@ -26,6 +27,7 @@ typedef struct {
|
||||
int dome;
|
||||
int backlight;
|
||||
int lux_only;
|
||||
int sensor_type;
|
||||
} LightMeterConfig;
|
||||
|
||||
typedef struct {
|
||||
@@ -55,4 +57,6 @@ typedef enum {
|
||||
|
||||
void lightmeter_app_set_config(LightMeterApp* context, LightMeterConfig* config);
|
||||
|
||||
void lightmeter_app_i2c_init_sensor(LightMeterApp* context);
|
||||
void lightmeter_app_i2c_deinit_sensor(LightMeterApp* context);
|
||||
void lightmeter_app_i2c_callback(LightMeterApp* context);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#define LM_VERSION_APP "0.7"
|
||||
#define LM_VERSION_APP "1.1"
|
||||
#define LM_DEVELOPED "Oleksii Kutuzov"
|
||||
#define LM_GITHUB "https://github.com/oleksiikutuzov/flipperzero-lightmeter"
|
||||
|
||||
@@ -105,4 +105,9 @@ typedef enum {
|
||||
LUX_ONLY_ON,
|
||||
} LightMeterLuxOnlyMode;
|
||||
|
||||
typedef enum {
|
||||
SENSOR_BH1750,
|
||||
SENSOR_MAX44009,
|
||||
} LightMeterSensorType;
|
||||
|
||||
typedef enum { BACKLIGHT_AUTO, BACKLIGHT_ON } LightMeterBacklight;
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.4 KiB |
@@ -136,8 +136,8 @@ static const WIFI_TextInputKey symbol_keyboard_keys_row_3[] = {
|
||||
{SWITCH_KEYBOARD_KEY, 1, 23},
|
||||
{'.', 15, 32},
|
||||
{',', 29, 32},
|
||||
{';', 41, 32},
|
||||
{'`', 53, 32},
|
||||
{':', 41, 32},
|
||||
{'/', 53, 32},
|
||||
{'\'', 65, 32},
|
||||
{ENTER_KEY, 74, 23},
|
||||
{'7', 100, 32},
|
||||
@@ -233,6 +233,12 @@ static bool char_is_lowercase(char letter) {
|
||||
static char char_to_uppercase(const char letter) {
|
||||
if(letter == '_') {
|
||||
return 0x20;
|
||||
} else if(letter == ':') {
|
||||
return 0x3B;
|
||||
} else if(letter == '/') {
|
||||
return 0x5C;
|
||||
} else if(letter == '\'') {
|
||||
return 0x60;
|
||||
} else if(char_is_lowercase(letter)) {
|
||||
return (letter - 0x20);
|
||||
} else {
|
||||
|
||||
@@ -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);
|
||||
@@ -463,6 +471,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);
|
||||
|
||||
@@ -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,95 +18,70 @@ 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",
|
||||
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_otp(),
|
||||
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",
|
||||
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_otp(),
|
||||
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) {
|
||||
@@ -114,61 +89,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;
|
||||
}
|
||||
@@ -176,27 +133,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);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "submenu.h"
|
||||
#include <gui/canvas_i.h>
|
||||
|
||||
#include <assets_icons.h>
|
||||
#include <gui/elements.h>
|
||||
@@ -66,23 +65,24 @@ typedef struct {
|
||||
size_t position;
|
||||
size_t window_position;
|
||||
bool locked_message_visible;
|
||||
bool is_vertical;
|
||||
} SubmenuModel;
|
||||
|
||||
static void submenu_process_up(Submenu* submenu);
|
||||
static void submenu_process_down(Submenu* submenu);
|
||||
static void submenu_process_ok(Submenu* submenu);
|
||||
|
||||
static size_t submenu_items_on_screen(bool header, bool vertical) {
|
||||
size_t res = (vertical) ? 8 : 4;
|
||||
return (header) ? res - 1 : res;
|
||||
}
|
||||
|
||||
static void submenu_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
SubmenuModel* model = _model;
|
||||
|
||||
const uint8_t item_height = 16;
|
||||
uint8_t item_width = canvas_width(canvas) - 5;
|
||||
|
||||
if(canvas->orientation == CanvasOrientationVertical ||
|
||||
canvas->orientation == CanvasOrientationVerticalFlip) {
|
||||
item_width = 60;
|
||||
}
|
||||
|
||||
canvas_clear(canvas);
|
||||
|
||||
if(!furi_string_empty(model->header)) {
|
||||
@@ -97,7 +97,8 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it);
|
||||
SubmenuItemArray_next(it)) {
|
||||
const size_t item_position = position - model->window_position;
|
||||
const size_t items_on_screen = furi_string_empty(model->header) ? 4 : 3;
|
||||
const size_t items_on_screen =
|
||||
submenu_items_on_screen(!furi_string_empty(model->header), model->is_vertical);
|
||||
uint8_t y_offset = furi_string_empty(model->header) ? 0 : 16;
|
||||
|
||||
if(item_position < items_on_screen) {
|
||||
@@ -117,7 +118,7 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
if(SubmenuItemArray_cref(it)->locked) {
|
||||
canvas_draw_icon(
|
||||
canvas,
|
||||
110,
|
||||
item_width - 10,
|
||||
y_offset + (item_position * item_height) + item_height - 12,
|
||||
&I_Lock_7x8);
|
||||
}
|
||||
@@ -125,7 +126,7 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
FuriString* disp_str;
|
||||
disp_str = furi_string_alloc_set(SubmenuItemArray_cref(it)->label);
|
||||
elements_string_fit_width(
|
||||
canvas, disp_str, item_width - (SubmenuItemArray_cref(it)->locked ? 25 : 11));
|
||||
canvas, disp_str, item_width - (SubmenuItemArray_cref(it)->locked ? 21 : 11));
|
||||
|
||||
canvas_draw_str(
|
||||
canvas,
|
||||
@@ -142,20 +143,39 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
elements_scrollbar(canvas, model->position, SubmenuItemArray_size(model->items));
|
||||
|
||||
if(model->locked_message_visible) {
|
||||
const uint8_t frame_x = 7;
|
||||
const uint8_t frame_width = canvas_width(canvas) - frame_x * 2;
|
||||
const uint8_t frame_y = 7;
|
||||
const uint8_t frame_height = canvas_height(canvas) - frame_y * 2;
|
||||
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_box(canvas, 8, 10, 110, 48);
|
||||
canvas_draw_box(canvas, frame_x + 2, frame_y + 2, frame_width - 4, frame_height - 4);
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_icon(canvas, 10, 14, &I_WarningDolphin_45x42);
|
||||
canvas_draw_rframe(canvas, 8, 8, 112, 50, 3);
|
||||
canvas_draw_rframe(canvas, 9, 9, 110, 48, 2);
|
||||
elements_multiline_text_aligned(
|
||||
canvas,
|
||||
84,
|
||||
32,
|
||||
AlignCenter,
|
||||
AlignCenter,
|
||||
furi_string_get_cstr(
|
||||
SubmenuItemArray_get(model->items, model->position)->locked_message));
|
||||
canvas_draw_icon(
|
||||
canvas, frame_x + 2, canvas_height(canvas) - frame_y - 2 - 42, &I_WarningDolphin_45x42);
|
||||
|
||||
canvas_draw_rframe(canvas, frame_x, frame_y, frame_width, frame_height, 3);
|
||||
canvas_draw_rframe(canvas, frame_x + 1, frame_y + 1, frame_width - 2, frame_height - 2, 2);
|
||||
if(model->is_vertical) {
|
||||
elements_multiline_text_aligned(
|
||||
canvas,
|
||||
32,
|
||||
42,
|
||||
AlignCenter,
|
||||
AlignCenter,
|
||||
furi_string_get_cstr(
|
||||
SubmenuItemArray_get(model->items, model->position)->locked_message));
|
||||
} else {
|
||||
elements_multiline_text_aligned(
|
||||
canvas,
|
||||
84,
|
||||
32,
|
||||
AlignCenter,
|
||||
AlignCenter,
|
||||
furi_string_get_cstr(
|
||||
SubmenuItemArray_get(model->items, model->position)->locked_message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,6 +323,7 @@ void submenu_add_lockable_item(
|
||||
|
||||
void submenu_reset(Submenu* submenu) {
|
||||
furi_assert(submenu);
|
||||
view_set_orientation(submenu->view, ViewOrientationHorizontal);
|
||||
|
||||
with_view_model(
|
||||
submenu->view,
|
||||
@@ -311,6 +332,7 @@ void submenu_reset(Submenu* submenu) {
|
||||
SubmenuItemArray_reset(model->items);
|
||||
model->position = 0;
|
||||
model->window_position = 0;
|
||||
model->is_vertical = false;
|
||||
furi_string_reset(model->header);
|
||||
},
|
||||
true);
|
||||
@@ -344,7 +366,8 @@ void submenu_set_selected_item(Submenu* submenu, uint32_t index) {
|
||||
model->window_position -= 1;
|
||||
}
|
||||
|
||||
const size_t items_on_screen = furi_string_empty(model->header) ? 4 : 3;
|
||||
const size_t items_on_screen =
|
||||
submenu_items_on_screen(!furi_string_empty(model->header), model->is_vertical);
|
||||
|
||||
if(items_size <= items_on_screen) {
|
||||
model->window_position = 0;
|
||||
@@ -363,7 +386,8 @@ void submenu_process_up(Submenu* submenu) {
|
||||
submenu->view,
|
||||
SubmenuModel * model,
|
||||
{
|
||||
const size_t items_on_screen = furi_string_empty(model->header) ? 4 : 3;
|
||||
const size_t items_on_screen =
|
||||
submenu_items_on_screen(!furi_string_empty(model->header), model->is_vertical);
|
||||
const size_t items_size = SubmenuItemArray_size(model->items);
|
||||
|
||||
if(model->position > 0) {
|
||||
@@ -386,7 +410,8 @@ void submenu_process_down(Submenu* submenu) {
|
||||
submenu->view,
|
||||
SubmenuModel * model,
|
||||
{
|
||||
const size_t items_on_screen = furi_string_empty(model->header) ? 4 : 3;
|
||||
const size_t items_on_screen =
|
||||
submenu_items_on_screen(!furi_string_empty(model->header), model->is_vertical);
|
||||
const size_t items_size = SubmenuItemArray_size(model->items);
|
||||
|
||||
if(model->position < items_size - 1) {
|
||||
@@ -441,3 +466,48 @@ void submenu_set_header(Submenu* submenu, const char* header) {
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
void submenu_set_orientation(Submenu* submenu, ViewOrientation orientation) {
|
||||
furi_assert(submenu);
|
||||
const bool is_vertical =
|
||||
(orientation == ViewOrientationVertical || orientation == ViewOrientationVerticalFlip) ?
|
||||
true :
|
||||
false;
|
||||
|
||||
view_set_orientation(submenu->view, orientation);
|
||||
|
||||
with_view_model(
|
||||
submenu->view,
|
||||
SubmenuModel * model,
|
||||
{
|
||||
model->is_vertical = is_vertical;
|
||||
|
||||
// Recalculating the position
|
||||
// Need if _set_orientation is called after _set_selected_item
|
||||
size_t position = model->position;
|
||||
const size_t items_size = SubmenuItemArray_size(model->items);
|
||||
const size_t items_on_screen =
|
||||
submenu_items_on_screen(!furi_string_empty(model->header), model->is_vertical);
|
||||
|
||||
if(position >= items_size) {
|
||||
position = 0;
|
||||
}
|
||||
|
||||
model->position = position;
|
||||
model->window_position = position;
|
||||
|
||||
if(model->window_position > 0) {
|
||||
model->window_position -= 1;
|
||||
}
|
||||
|
||||
if(items_size <= items_on_screen) {
|
||||
model->window_position = 0;
|
||||
} else {
|
||||
const size_t pos = items_size - items_on_screen;
|
||||
if(model->window_position > pos) {
|
||||
model->window_position = pos;
|
||||
}
|
||||
}
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
@@ -93,6 +93,13 @@ void submenu_set_selected_item(Submenu* submenu, uint32_t index);
|
||||
*/
|
||||
void submenu_set_header(Submenu* submenu, const char* header);
|
||||
|
||||
/** Set Orientation
|
||||
*
|
||||
* @param submenu Submenu instance
|
||||
* @param orientation either vertical or horizontal
|
||||
*/
|
||||
void submenu_set_orientation(Submenu* submenu, ViewOrientation orientation);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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; \
|
||||
}
|
||||
|
||||
@@ -17,11 +17,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);
|
||||
|
||||
Reference in New Issue
Block a user