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

This commit is contained in:
Aleksandr Kutuzov 2021-08-12 00:18:56 +03:00
commit 073c87f37d
253 changed files with 3203 additions and 1511 deletions

View File

@ -14,8 +14,11 @@ jobs:
- name: 'Decontaminate previous build leftovers'
run: |
git submodule status \
|| git checkout `git rev-list --max-parents=0 HEAD | tail -n 1`
if [ -d .git ]
then
git submodule status \
|| git checkout `git rev-list --max-parents=0 HEAD | tail -n 1`
fi
- name: 'Checkout code'
uses: actions/checkout@v2
@ -44,7 +47,7 @@ jobs:
- name: 'Generate branch suffix'
if: startsWith(github.ref, 'refs/tags/') != true
run: echo "SUFFIX=$(date +'%Y-%m-%d')-$(git rev-parse --short HEAD)" >> $GITHUB_ENV
run: echo "SUFFIX=$(git rev-parse --abbrev-ref HEAD)-$(date +'%d%m%Y')-$(git rev-parse --short HEAD)" >> $GITHUB_ENV
- name: 'Build bootloader in docker'
uses: ./.github/actions/docker

View File

@ -12,16 +12,19 @@ jobs:
- name: 'Cleanup workspace'
uses: AutoModality/action-clean@v1
- name: 'Decontaminate previous build leftovers'
run: |
if [ -d .git ]
then
git submodule status \
|| git checkout `git rev-list --max-parents=0 HEAD | tail -n 1`
fi
- name: 'Checkout code'
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: 'Checkout submodules: update'
run: git submodule update --init --recursive
- name: 'Checkout submodules: sync'
run: git submodule sync
submodules: true
- name: 'Docker cache'
uses: satackey/action-docker-layer-caching@v0.0.11

View File

@ -10,6 +10,24 @@ jobs:
lint_python:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- uses: psf/black@20.8b1
- name: 'Cleanup workspace'
uses: AutoModality/action-clean@v1
- name: 'Decontaminate previous build leftovers'
run: |
if [ -d .git ]
then
git submodule status \
|| git checkout `git rev-list --max-parents=0 HEAD | tail -n 1`
fi
- name: 'Checkout code'
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: 'Setup python'
uses: actions/setup-python@v2
- name: 'Check python code with black'
uses: psf/black@20.8b1

View File

@ -39,6 +39,7 @@ int32_t lfrfid_debug_app(void* p);
int32_t storage_app(void* p);
int32_t storage_app_test(void* p);
int32_t dialogs_app(void* p);
int32_t power_observer(void* p);
// On system start hooks declaration
void irda_cli_init();
@ -91,6 +92,10 @@ const FlipperApplication FLIPPER_SERVICES[] = {
{.app = power_task, .name = "power_task", .stack_size = 1024, .icon = &A_Plugins_14},
#endif
#ifdef SRV_POWER_OBSERVER
{.app = power_observer, .name = "power_observer", .stack_size = 1024, .icon = &A_Plugins_14},
#endif
#ifdef SRV_BT
{.app = bt_task, .name = "bt_task", .stack_size = 1024, .icon = &A_Plugins_14},
#endif
@ -139,7 +144,7 @@ const FlipperApplication FLIPPER_SERVICES[] = {
#endif
#ifdef SRV_KEYPAD_TEST
{.app = keypad_test, .name = "keypad_test", .icon = &A_Plugins_14},
{.app = keypad_test, .name = "keypad_test", .stack_size = 1024, .icon = &A_Plugins_14},
#endif
#ifdef SRV_ACCESSOR
@ -268,7 +273,7 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = {
#endif
#ifdef APP_KEYPAD_TEST
{.app = keypad_test, .name = "keypad_test", .icon = &A_Plugins_14},
{.app = keypad_test, .name = "keypad_test", .stack_size = 1024, .icon = &A_Plugins_14},
#endif
#ifdef APP_ACCESSOR

View File

@ -20,6 +20,7 @@ SRV_DOLPHIN = 1
SRV_NOTIFICATION = 1
SRV_STORAGE = 1
SRV_DIALOGS = 1
SRV_POWER_OBSERVER = 1
# Main Apps
APP_IRDA = 1
@ -59,6 +60,12 @@ SRV_CLI = 1
CFLAGS += -DSRV_POWER
endif
SRV_POWER_OBSERVER ?= 0
ifeq ($(SRV_POWER_OBSERVER), 1)
SRV_POWER = 1
CFLAGS += -DSRV_POWER_OBSERVER
endif
SRV_BT ?= 0
ifeq ($(SRV_BT), 1)
SRV_CLI = 1

View File

@ -297,7 +297,8 @@ static void archive_enter_text_input(ArchiveApp* archive) {
archive_text_input_callback,
archive,
archive->browser.text_input_buffer,
MAX_NAME_LEN);
MAX_NAME_LEN,
false);
view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput);
}

View File

@ -1,7 +1,6 @@
#include "cli_i.h"
#include "cli_commands.h"
#include <version.h>
#include <api-hal-version.h>
#include <loader/loader.h>

View File

@ -94,23 +94,30 @@ void cli_command_help(Cli* cli, string_t args, void* context) {
(void)args;
printf("Commands we have:");
// Get the middle element
CliCommandTree_it_t it_mid;
uint8_t cmd_num = CliCommandTree_size(cli->commands);
uint8_t i = cmd_num / 2 + cmd_num % 2;
for(CliCommandTree_it(it_mid, cli->commands); i; --i, CliCommandTree_next(it_mid))
;
// Command count
const size_t commands_count = CliCommandTree_size(cli->commands);
const size_t commands_count_mid = commands_count / 2 + commands_count % 2;
// Use 2 iterators from start and middle to show 2 columns
CliCommandTree_it_t it_i;
CliCommandTree_it_t it_j;
for(CliCommandTree_it(it_i, cli->commands), CliCommandTree_it_set(it_j, it_mid);
!CliCommandTree_it_equal_p(it_i, it_mid);
CliCommandTree_next(it_i), CliCommandTree_next(it_j)) {
CliCommandTree_itref_t* ref = CliCommandTree_ref(it_i);
CliCommandTree_it_t it_left;
CliCommandTree_it(it_left, cli->commands);
CliCommandTree_it_t it_right;
CliCommandTree_it(it_right, cli->commands);
for(size_t i = 0; i < commands_count_mid; i++) CliCommandTree_next(it_right);
// Iterate throw tree
for(size_t i = 0; i < commands_count_mid; i++) {
printf("\r\n");
printf("%-30s", string_get_cstr(ref->key_ptr[0]));
ref = CliCommandTree_ref(it_j);
printf(string_get_cstr(ref->key_ptr[0]));
// Left Column
if(!CliCommandTree_end_p(it_left)) {
printf("%-30s", string_get_cstr(*CliCommandTree_ref(it_left)->key_ptr));
CliCommandTree_next(it_left);
}
// Right Column
if(!CliCommandTree_end_p(it_right)) {
printf(string_get_cstr(*CliCommandTree_ref(it_right)->key_ptr));
CliCommandTree_next(it_right);
}
};
if(string_size(args) > 0) {

View File

@ -3,7 +3,6 @@
#include <gui/gui.h>
#include <gui/elements.h>
#include <api-hal.h>
#include <version.h>
#include <api-hal-version.h>
static char* Lockmenu_Items[3] = {"Lock", "Set PIN", "DUMB mode"};
@ -40,7 +39,7 @@ void dolphin_view_first_start_draw(Canvas* canvas, void* model) {
"I am",
my_name ? my_name : "Unknown",
",\ncyberdolphin\nliving in your\npocket >");
canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart5_45x53);
canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart5_54x49);
elements_multiline_text_framed(canvas, 60, 17, buf);
} else if(m->page == 6) {
canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart6_58x54);

View File

@ -1,15 +1,12 @@
#pragma once
static const char* emotes_list[] = {
"(O_o)", "(!_?)", "(^_^)", "(*__*)", "(@_@)", "(X_x)", "(>_<)", "(^ ^)", "(^_^)",
"(-_-)", "(~_~)", "(#^.^#)", "(^ ^)", "(^.^)", "(-.-)", "zZzZ", "(^_-)", "(^_-)",
"(+_+)", "(+o+)", "(' ')", "('-')", "('.')", "('_')", "(* > *)", "(o o)", "(^_^)",
"(^O^)", "(^o^)", "(^o^)", "(._.)", "(_^_)", "('_')", "('_;)", "(T_T)", "(;_;)",
"(ー_ー)", "(-.-)", "(^o^)", "(-_-)", "(=_=)", "(=^ ^=)", "(. .)", "(._.)", "( ^m^)",
"(?_?)", "(*^_^*)", "(^<^)", "(^.^)", "(^·^)", "(^.^)", "(^_^.)", "(^_^)", "(^^)",
"(^J^)", "(*^.^*)", "(#^.^#)", "(~o~)", "(^o^)", "(-o-)", "(^. ^)", "(^o^)", "(*^0^*)",
"(*_*)", "(~ o ~)", "(~_~)", "(p_-)", "d[-_-]b", "(^0_0^)", "- ^ -"};
static const char* dialogues_list[] = {
"Let's hack!\n\nbla bla bla\nbla bla..",
// temp
const char* console_emotes[] = {
"Run it, m8",
"Lets GOOOO",
"Click it, buddy",
"I wanna play",
"Wtf is this?",
"Just do it",
"JUST DO IT!",
};

View File

@ -1,49 +1,38 @@
#include <gui/elements.h>
#include "applications.h"
#include "items_i.h"
#include "emotes.h"
#include <gui/icon_i.h>
const Item TV = {
.layer = 7,
.timeout = 10,
.x = 160,
.y = 34,
.icon = &I_TV_20x24,
.action_name = "Use",
.draw = draw_tv,
.callback = smash_tv};
const Item Painting = {
.layer = 3,
.timeout = 20,
.x = 160,
.y = 10,
.icon = &I_Home_painting_17x20,
.action_name = "Inspect",
.draw = NULL,
.callback = inspect_painting};
const Item Sofa = {
const Item Food = {
.layer = 4,
.timeout = 100,
.x = 250,
.y = 34,
.icon = &I_Sofa_40x13,
.action_name = "Sit",
.draw = NULL,
.callback = sofa_sit};
.pos =
{
.x = 0,
.y = 90,
},
.width = 60,
.height = 50,
.draw = food_redraw,
.callback = food_callback};
const Item PC = {
const Item Console = {
.layer = 4,
.timeout = 100,
.x = 400,
.y = 10,
.icon = &I_PC_22x29,
.action_name = "Use",
.draw = NULL,
.callback = pc_callback};
.pos =
{
.x = 357,
.y = 190,
},
.width = 40,
.height = 20,
.draw = console_redraw,
.callback = console_callback};
const Item* Home[ITEMS_NUM] = {&TV, &Sofa, &Painting, &PC};
const Item** Scenes[1] = {*&Home};
const Item* Home[] = {&Food, &Console};
const Item** Scenes[] = {Home};
const Item** get_scene(SceneState* state) {
return Scenes[state->scene_id];
@ -64,6 +53,37 @@ static void dolphin_scene_start_app(SceneState* state, const FlipperApplication*
furi_thread_start(state->scene_app_thread);
}
uint16_t roll_new(uint16_t prev, uint16_t max) {
uint16_t val = 999;
while(val != prev) {
val = random() % max;
break;
}
return val;
}
static void dolphin_scene_type_text(
Canvas* canvas,
SceneState* state,
uint8_t x,
uint8_t y,
const char* text) {
char dialog_str[64];
char buf[64];
strcpy(dialog_str, (char*)text);
if(state->dialog_progress <= strlen(dialog_str)) {
if(HAL_GetTick() / 10 % 2 == 0) state->dialog_progress++;
dialog_str[state->dialog_progress] = '\0';
snprintf(buf, state->dialog_progress, dialog_str);
} else {
snprintf(buf, 64, dialog_str);
}
canvas_draw_str_aligned(canvas, x, y, AlignCenter, AlignCenter, buf);
}
const void scene_activate_item_callback(SceneState* state, Canvas* canvas) {
furi_assert(state);
furi_assert(canvas);
@ -78,16 +98,32 @@ const void scene_activate_item_callback(SceneState* state, Canvas* canvas) {
}
}
const Vec2 item_get_pos(SceneState* state, ItemsEnum item) {
const Item** current = get_scene(state);
Vec2 rel_pos = {0, 0};
rel_pos.x = DOLPHIN_WIDTH / 2 + (current[item]->pos.x * PARALLAX(current[item]->layer));
rel_pos.y = DOLPHIN_WIDTH / 4 + (current[item]->pos.y * PARALLAX(current[item]->layer));
return rel_pos;
}
const Item* is_nearby(SceneState* state) {
furi_assert(state);
uint8_t item = 0;
bool found = false;
const Item** current = get_scene(state);
while(item < ITEMS_NUM) {
int32_t rel =
while(item < ItemsEnumTotal) {
int32_t rel_x =
(DOLPHIN_CENTER + DOLPHIN_WIDTH / 2 -
(current[item]->x - state->player_global.x) * PARALLAX(current[item]->layer));
if(abs(rel) <= DOLPHIN_WIDTH / 2) {
(current[item]->pos.x - state->player_global.x) * PARALLAX(current[item]->layer));
uint8_t item_height = current[item]->height;
uint8_t item_width = current[item]->width;
int32_t rel_y = current[item]->pos.y - state->player_global.y;
if(abs(rel_x) <= item_width && abs(rel_y) <= item_height) {
found = !found;
break;
}
@ -96,50 +132,102 @@ const Item* is_nearby(SceneState* state) {
return found ? current[item] : NULL;
}
void draw_tv(Canvas* canvas, void* state) {
furi_assert(state);
SceneState* s = state;
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(
canvas, (TV.x + 3 - s->player_global.x) * PARALLAX(TV.layer), TV.y + 4, 16, 20);
canvas_set_color(canvas, ColorBlack);
canvas_set_bitmap_mode(canvas, true);
}
void food_redraw(Canvas* canvas, void* s) {
furi_assert(s);
SceneState* state = s;
const Icon* food_frames[] = {
&I_food1_61x98,
&I_food2_61x98,
&I_food3_61x98,
&I_food4_61x98,
&I_food5_61x98,
&I_food6_61x98,
&I_food7_61x98,
&I_food8_61x98,
&I_food9_61x98,
&I_food10_61x98,
&I_food11_61x98,
&I_food12_61x98,
};
uint8_t frame = ((HAL_GetTick() / 200) % SIZEOF_ARRAY(food_frames));
if(is_nearby(state) && (state->player_global.y > Food.pos.y)) {
dolphin_scene_type_text(
canvas,
state,
(Food.pos.x - state->player_global.x) * PARALLAX(Food.layer) + 90,
state->screen.y + 8,
console_emotes[state->emote_id]);
} else {
state->dialog_progress = 0;
state->emote_id = roll_new(state->previous_emote, SIZEOF_ARRAY(console_emotes));
}
void smash_tv(Canvas* canvas, void* state) {
furi_assert(state);
SceneState* s = state;
s->player_flipped = true;
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(
canvas, ((TV.x - 5) - s->player_global.x) * PARALLAX(TV.layer), TV.y - 2, &I_FX_Bang_32x6);
canvas_set_bitmap_mode(canvas, false);
if(s->action_timeout < TV.timeout - 2) {
elements_multiline_text_framed(canvas, 80, 24, "Bang!");
canvas,
(Food.pos.x - state->player_global.x) * PARALLAX(Food.layer),
Food.pos.y - state->player_global.y,
food_frames[frame]);
canvas_set_bitmap_mode(canvas, true);
}
void food_callback(Canvas* canvas, void* s) {
furi_assert(s);
SceneState* state = s;
if(state->use_pending) {
dolphin_scene_start_app(state, &FLIPPER_SCENE_APPS[1]);
}
}
void sofa_sit(Canvas* canvas, void* state) {
furi_assert(state);
SceneState* s = state;
// temp fix pos
s->player_global.x = 154;
s->dolphin_gfx = &A_FX_Sitting_40x27;
s->dolphin_gfx_b = &I_FX_SittingB_40x27;
}
void console_redraw(Canvas* canvas, void* s) {
furi_assert(s);
SceneState* state = s;
void inspect_painting(Canvas* canvas, void* state) {
furi_assert(state);
SceneState* s = state;
if(s->use_pending) {
dolphin_scene_start_app(s, &FLIPPER_SCENE_APPS[0]);
const Icon* console[] = {
&I_Console_74x67_0,
&I_Console_74x67_1,
&I_Console_74x67_2,
&I_Console_74x67_3,
&I_Console_74x67_4,
&I_Console_74x67_5,
&I_Console_74x67_6,
&I_Console_74x67_7,
&I_Console_74x67_8,
};
uint8_t frame = ((HAL_GetTick() / 100) % SIZEOF_ARRAY(console));
canvas_draw_icon(
canvas,
(Console.pos.x - state->player_global.x) * PARALLAX(Console.layer),
Console.pos.y - state->player_global.y,
console[frame]);
canvas_set_bitmap_mode(canvas, true);
if(is_nearby(state)) {
dolphin_scene_type_text(
canvas,
state,
(Console.pos.x - state->player_global.x) * PARALLAX(Console.layer) - 25,
Console.pos.y - state->player_global.y + 14,
console_emotes[state->emote_id]);
} else {
state->dialog_progress = 0;
state->emote_id = roll_new(state->previous_emote, SIZEOF_ARRAY(console_emotes));
}
}
void pc_callback(Canvas* canvas, void* state) {
furi_assert(state);
SceneState* s = state;
if(s->use_pending) {
dolphin_scene_start_app(s, &FLIPPER_SCENE_APPS[1]);
void console_callback(Canvas* canvas, void* s) {
furi_assert(s);
SceneState* state = s;
if(state->use_pending) {
dolphin_scene_start_app(state, &FLIPPER_SCENE_APPS[1]);
}
}

View File

@ -1,8 +1,14 @@
#pragma once
#include "dolphin/scenes/scene.h"
#define ITEMS_NUM 4
typedef enum {
ItemsFood,
ItemsConsole,
ItemsEnumTotal,
} ItemsEnum;
uint16_t roll_new(uint16_t prev, uint16_t max);
const Vec2 item_get_pos(SceneState* state, ItemsEnum item);
const Item* is_nearby(SceneState* state);
const Item** get_scene(SceneState* state);
const void scene_activate_item_callback(SceneState* state, Canvas* canvas);

View File

@ -1,8 +1,8 @@
#pragma once
#include "items.h"
void smash_tv(Canvas* canvas, void* state);
void draw_tv(Canvas* canvas, void* state);
void sofa_sit(Canvas* canvas, void* state);
void inspect_painting(Canvas* canvas, void* state);
void pc_callback(Canvas* canvas, void* state);
void food_redraw(Canvas* canvas, void* state);
void food_callback(Canvas* canvas, void* state);
void console_redraw(Canvas* canvas, void* state);
void console_callback(Canvas* canvas, void* state);

View File

@ -0,0 +1,276 @@
#pragma once
#include "dolphin/scenes/scene.h"
const DolphinFrame up = {
.frames =
{
{
.f = &I_up1_73x61,
.b = &I_black_up1_73x61,
},
{
.f = &I_up2_73x61,
.b = &I_black_up2_73x61,
},
},
.total = 2,
};
const DolphinFrame up_down = {
.frames =
{
{
.f = &I_updown1_73x61,
.b = &I_black_updown1_73x61,
},
{
.f = &I_updown2_73x61,
.b = &I_black_updown2_73x61,
},
{
.f = &I_updown3_73x61,
.b = &I_black_updown3_73x61,
},
},
.total = 3,
};
const DolphinFrame up_right = {
.frames =
{
{
.f = &I_upright1_73x61,
.b = &I_black_upright1_73x61,
},
{
.f = &I_upright2_73x61,
.b = &I_black_upright2_73x61,
},
},
.total = 2,
};
const DolphinFrame up_left = {
.frames =
{
{
.f = &I_upleft1_73x61,
.b = &I_black_upleft1_73x61,
},
{
.f = &I_upleft2_73x61,
.b = &I_black_upleft2_73x61,
},
},
.total = 2,
};
const DolphinFrame right = {
.frames =
{
{
.f = &I_right1_73x61,
.b = &I_black_right1_73x61,
},
{
.f = &I_right2_73x61,
.b = &I_black_right2_73x61,
},
{
.f = &I_right3_73x61,
.b = &I_black_right3_73x61,
},
},
.total = 3,
};
const DolphinFrame right_up = {
.frames =
{
{
.f = &I_rightup1_73x61,
.b = &I_black_rightup1_73x61,
},
{
.f = &I_rightup2_73x61,
.b = &I_black_rightup2_73x61,
},
},
.total = 2,
};
const DolphinFrame right_down = {
.frames =
{
{
.f = &I_rightdown1_73x61,
.b = &I_black_rightdown1_73x61,
},
{
.f = &I_rightdown2_73x61,
.b = &I_black_rightdown2_73x61,
},
},
.total = 2,
};
const DolphinFrame right_left = {
.frames =
{
{
.f = &I_rightleft1_73x61,
.b = &I_black_rightleft1_73x61,
},
{
.f = &I_rightleft2_73x61,
.b = &I_black_rightleft2_73x61,
},
},
.total = 2,
};
const DolphinFrame down = {
.frames =
{
{
.f = &I_down1_73x61,
.b = &I_black_down1_73x61,
},
{
.f = &I_down2_73x61,
.b = &I_black_down2_73x61,
},
},
.total = 2,
};
const DolphinFrame down_up = {
.frames =
{
{
.f = &I_downup1_73x61,
.b = &I_black_downup1_73x61,
},
{
.f = &I_downup2_73x61,
.b = &I_black_downup2_73x61,
},
{
.f = &I_downup3_73x61,
.b = &I_black_downup3_73x61,
},
},
.total = 3,
};
const DolphinFrame down_left = {
.frames =
{
{
.f = &I_downleft1_73x61,
.b = &I_black_downleft1_73x61,
},
{
.f = &I_downleft2_73x61,
.b = &I_black_downleft2_73x61,
},
{
.f = &I_downleft3_73x61,
.b = &I_black_downleft3_73x61,
},
},
.total = 3,
};
const DolphinFrame down_right = {
.frames =
{
{
.f = &I_downright1_73x61,
.b = &I_black_downright1_73x61,
},
{
.f = &I_downright2_73x61,
.b = &I_black_downright2_73x61,
},
{
.f = &I_downright3_73x61,
.b = &I_black_downright3_73x61,
},
},
.total = 3,
};
const DolphinFrame left = {
.frames =
{
{
.f = &I_left1_73x61,
.b = &I_black_left1_73x61,
},
{
.f = &I_left2_73x61,
.b = &I_black_left2_73x61,
},
{
.f = &I_left3_73x61,
.b = &I_black_left3_73x61,
},
},
.total = 3,
};
const DolphinFrame left_up = {
.frames =
{
{
.f = &I_leftup1_73x61,
.b = &I_black_leftup1_73x61,
},
{
.f = &I_leftup2_73x61,
.b = &I_black_leftup2_73x61,
},
},
.total = 2,
};
const DolphinFrame left_down = {
.frames =
{
{
.f = &I_leftdown1_73x61,
.b = &I_black_leftdown1_73x61,
},
{
.f = &I_leftdown2_73x61,
.b = &I_black_leftdown2_73x61,
},
},
.total = 2,
};
const DolphinFrame left_right = {
.frames =
{
{
.f = &I_rightleft1_73x61,
.b = &I_black_rightleft1_73x61,
},
{
.f = &I_rightleft2_73x61,
.b = &I_black_rightleft2_73x61,
},
},
.total = 2,
};
const DolphinFrame* frames[4][4] = {
[DirUp] = {[DirUp] = &up, [DirRight] = &up_right, [DirDown] = &up_down, [DirLeft] = &up_left},
[DirRight] =
{[DirUp] = &right_up, [DirRight] = &right, [DirDown] = &right_down, [DirLeft] = &right_left},
[DirDown] =
{[DirUp] = &down_up, [DirRight] = &down_right, [DirDown] = &down, [DirLeft] = &down_left},
[DirLeft] =
{[DirUp] = &left_up, [DirRight] = &left_right, [DirDown] = &left_down, [DirLeft] = &left},
};

View File

@ -52,7 +52,7 @@ void scene_alloc() {
// SceneAppGui
scene_app_gui = furi_alloc(sizeof(SceneAppGui));
scene_app_gui->mqueue = osMessageQueueNew(2, sizeof(AppEvent), NULL);
scene_app_gui->mqueue = osMessageQueueNew(8, sizeof(AppEvent), NULL);
scene_app_gui->gui = furi_record_open("gui");
scene_app_gui->view_port = view_port_alloc();
scene_app_gui->timer =
@ -62,10 +62,18 @@ void scene_alloc() {
SceneState* scene_state = furi_alloc(sizeof(SceneState));
scene_state->player.y = DOLPHIN_DEFAULT_Y;
scene_state->player.x = DOLPHIN_CENTER;
scene_state->player_global.x = random() % WORLD_WIDTH / 4;
scene_state->player_global.x = 160;
scene_state->player_global.y = WORLD_HEIGHT;
scene_state->frame_group = DirRight;
scene_state->frame_type = DirRight;
scene_state->frame_pending = DirRight;
scene_state->last_group = DirRight;
scene_state->screen.x = scene_state->player.x;
scene_state->screen.y = scene_state->player.y;
// scene_state->debug = true;
scene_state_mutex = furi_alloc(sizeof(ValueMutex));
furi_check(init_mutex(scene_state_mutex, scene_state, sizeof(SceneState)));
@ -73,7 +81,7 @@ void scene_alloc() {
view_port_draw_callback_set(scene_app_gui->view_port, dolphin_scene_redraw, scene_state_mutex);
view_port_input_callback_set(
scene_app_gui->view_port, scene_engine_input_callback, scene_app_gui->mqueue);
gui_add_view_port(scene_app_gui->gui, scene_app_gui->view_port, GuiLayerMain);
gui_add_view_port(scene_app_gui->gui, scene_app_gui->view_port, GuiLayerFullscreen);
view_port_enabled_set(scene_app_gui->view_port, true);
printf("scene_alloc: complete\r\n");
}

View File

@ -16,26 +16,28 @@
// player
#define DOLPHIN_WIDTH 32
#define DOLPHIN_HEIGHT 32
#define DOLPHIN_CENTER (SCREEN_WIDTH / 2 - DOLPHIN_WIDTH / 2)
#define SPEED_X 2
#define ACTIONS_NUM 5
#define DOLPHIN_DEFAULT_Y 20
#define DOLPHIN_CENTER (SCREEN_WIDTH / 2 - DOLPHIN_WIDTH)
#define SPEED_X 4
#define SPEED_Y 4
#define ACTIONS_NUM 4
#define DOLPHIN_DEFAULT_Y 2
#define MAX_FRAMES 3
// world
#define WORLD_WIDTH 2048
#define WORLD_HEIGHT 64
#define WORLD_WIDTH 256
#define WORLD_HEIGHT 192
#define LAYERS 8
#define SCENE_ZOOM 9
#define DOLPHIN_LAYER 6
#define PARALLAX_MOD 7
#define PARALLAX(layer) layer / PARALLAX_MOD - layer
#define DIALOG_PROGRESS 250
enum Actions { SLEEP = 0, IDLE, WALK, EMOTE, INTERACT, MINDCONTROL };
enum Actions { IDLE = 0, EMOTE, INTERACT, MINDCONTROL };
static const uint16_t default_timeout[] =
{[SLEEP] = 300, [IDLE] = 100, [WALK] = 100, [EMOTE] = 50, [INTERACT] = 10, [MINDCONTROL] = 50};
{[IDLE] = 100, [EMOTE] = 50, [INTERACT] = 10, [MINDCONTROL] = 50};
typedef enum {
EventTypeTick,
@ -64,47 +66,60 @@ typedef struct {
typedef struct {
uint8_t layer;
uint16_t timeout;
int32_t x;
int32_t y;
const Icon* icon;
char action_name[16];
Vec2 pos;
uint8_t width;
uint8_t height;
void (*draw)(Canvas* canvas, void* model);
void (*callback)(Canvas* canvas, void* model);
} Item;
typedef enum {
DirUp = 0,
DirRight,
DirDown,
DirLeft,
} FrameDirectionEnum;
typedef struct {
const Icon* f;
const Icon* b;
} DolphinGfxAsset;
typedef struct {
const DolphinGfxAsset frames[MAX_FRAMES];
const uint8_t total;
} DolphinFrame;
typedef struct {
///
Vec2 player;
Vec2 player_global;
Vec2 player_v;
Vec2 screen;
const Icon* dolphin_gfx;
const Icon* dolphin_gfx_b; // temp
FrameDirectionEnum frame_group;
FrameDirectionEnum last_group;
FrameDirectionEnum frame_pending;
FrameDirectionEnum frame_type;
bool player_flipped;
const DolphinFrame* current_frame;
bool transition;
bool transition_pending;
bool use_pending;
// dolphin_scene_debug
bool debug;
uint8_t player_anim;
uint8_t scene_id;
uint8_t frame_idx;
uint8_t scene_id;
uint8_t emote_id;
uint8_t previous_emote;
uint8_t dialogue_id;
uint8_t previous_dialogue;
uint32_t action_timeout;
uint8_t poi;
uint8_t action;
uint8_t next_action;
uint8_t prev_action;
int8_t zoom_v;
uint8_t scene_zoom;
uint8_t action_timeout;
uint8_t dialog_progress;
FuriThread* scene_app_thread;

View File

@ -6,43 +6,31 @@ void dolphin_scene_handle_user_input(SceneState* state, InputEvent* input) {
furi_assert(state);
furi_assert(input);
// dolphin_scene_debug
if(input->type == InputTypeShort) {
if(input->key == InputKeyUp) {
state->debug = !state->debug;
}
}
// toggle mind control on any user interaction
state->last_group = state->frame_group;
if(input->type == InputTypePress) {
if(input->key == InputKeyLeft || input->key == InputKeyRight || input->key == InputKeyOk) {
state->action = MINDCONTROL;
}
state->action = MINDCONTROL;
}
// zoom poc for tests
if(input->type == InputTypePress) {
if(input->key == InputKeyDown) {
state->zoom_v = SPEED_X;
}
} else if(input->type == InputTypeRelease) {
if(input->key == InputKeyDown) {
state->zoom_v = -SPEED_X * 2;
state->dialog_progress = 0;
}
}
// mind control
if(state->action == MINDCONTROL) {
if(input->type == InputTypePress) {
if(input->key == InputKeyRight) {
state->player_flipped = false;
state->player_v.y = 0;
state->player_v.x = SPEED_X;
} else if(input->key == InputKeyLeft) {
state->player_flipped = true;
state->player_v.y = 0;
state->player_v.x = -SPEED_X;
}
} else if(input->type == InputTypeRelease) {
if(input->key == InputKeyRight || input->key == InputKeyLeft) {
} else if(input->key == InputKeyUp) {
state->player_v.x = 0;
state->player_v.y = -SPEED_Y;
} else if(input->key == InputKeyDown) {
state->player_v.x = 0;
state->player_v.y = SPEED_Y;
}
}
if(input->type == InputTypeRelease) {
state->player_v.x = 0;
state->player_v.y = 0;
} else if(input->type == InputTypeShort) {
if(input->key == InputKeyOk) {
state->prev_action = MINDCONTROL;
@ -59,13 +47,14 @@ void dolphin_scene_coordinates(SceneState* state, uint32_t dt) {
// global pos
state->player_global.x = CLAMP(state->player_global.x + state->player_v.x, WORLD_WIDTH, 0);
state->player_global.y = CLAMP(state->player_global.y + state->player_v.y, WORLD_HEIGHT, 0);
// zoom handlers
state->scene_zoom = CLAMP(state->scene_zoom + state->zoom_v, SCENE_ZOOM, 0);
state->player.x = CLAMP(state->player.x - (state->zoom_v * (SPEED_X * 2)), DOLPHIN_CENTER, 0);
state->player.y = CLAMP(state->player.y - (state->zoom_v * SPEED_X / 2), DOLPHIN_DEFAULT_Y, 3);
//center screen
state->screen.x = state->player_global.x - state->player.x;
state->player_anim = (state->player_global.x / 10) % 2;
// nudge camera postition
if(state->player_global.x > 170) {
state->player.x =
CLAMP(state->player.x - state->player_v.x / 2, DOLPHIN_CENTER, -DOLPHIN_WIDTH / 2);
} else if(state->player_global.x < 70) {
state->player.x =
CLAMP(state->player.x - state->player_v.x / 2, DOLPHIN_WIDTH * 2, DOLPHIN_CENTER);
}
}

View File

@ -1,74 +1,32 @@
#include <furi.h>
#include "scene.h"
#include "assets/emotes.h"
static uint16_t roll_new(uint16_t prev, uint16_t max) {
uint16_t val = 999;
while(val != prev) {
val = random() % max;
break;
}
return val;
}
#include "assets/items.h"
static void scene_proceed_action(SceneState* state) {
furi_assert(state);
state->prev_action = state->action;
state->action = (state->prev_action != state->next_action) ?
state->next_action :
roll_new(state->next_action, ACTIONS_NUM);
state->action = roll_new(state->prev_action, ACTIONS_NUM);
state->action_timeout = default_timeout[state->action];
}
static void scene_dolphin_go_to_poi(SceneState* state) {
furi_assert(state);
if(state->player_global.x < state->poi) {
state->player_flipped = false;
state->player_v.x = SPEED_X / 2;
} else if(state->player_global.x > state->poi) {
state->player_flipped = true;
state->player_v.x = -SPEED_X / 2;
}
}
static void scene_action_handler(SceneState* state) {
furi_assert(state);
if(state->action == MINDCONTROL && state->player_v.x != 0) {
state->action_timeout = default_timeout[state->action];
if(state->action == MINDCONTROL) {
if(state->player_v.x != 0 || state->player_v.y != 0) {
state->action_timeout = default_timeout[state->action];
}
}
if(state->action_timeout > 0) {
state->action_timeout--;
} else {
if(random() % 1000 > 500) {
state->next_action = roll_new(state->prev_action, ACTIONS_NUM);
state->poi = roll_new(state->player_global.x, WORLD_WIDTH / 4);
}
}
}
void dolphin_scene_update_state(SceneState* state, uint32_t t, uint32_t dt) {
furi_assert(state);
scene_action_handler(state);
UNUSED(dialogues_list);
switch(state->action) {
case WALK:
if(state->player_global.x == state->poi) {
state->player_v.x = 0;
scene_proceed_action(state);
} else {
scene_dolphin_go_to_poi(state);
}
break;
case EMOTE:
state->player_flipped = false;
if(state->action_timeout == 0) {
scene_proceed_action(state);
state->emote_id = roll_new(state->previous_emote, SIZEOF_ARRAY(emotes_list));
break;
}
case INTERACT:
if(state->action_timeout == 0) {
if(state->prev_action == MINDCONTROL) {
@ -78,20 +36,9 @@ void dolphin_scene_update_state(SceneState* state, uint32_t t, uint32_t dt) {
}
}
break;
case SLEEP:
if(state->poi != 154) { // temp
state->poi = 154;
} else if(state->player_global.x != state->poi) {
scene_dolphin_go_to_poi(state);
} else {
state->player_v.x = 0;
if(state->action_timeout == 0) {
state->poi = roll_new(state->player_global.x, WORLD_WIDTH / 4);
scene_proceed_action(state);
}
break;
}
default:
if(state->action_timeout == 0) {
scene_proceed_action(state);
}

View File

@ -1,101 +1,46 @@
#include <furi.h>
#include "scene.h"
#include "assets/emotes.h"
#include "assets/items.h"
#include "assets/meta.h"
#include <gui/elements.h>
const char* action_str[] = {"Sleep", "Idle", "Walk", "Emote", "Use", "MC"};
void dolphin_scene_transition_handler(SceneState* state) {
uint8_t speed_mod = (state->player_v.x || state->player_v.y || state->transition) ? 6 : 10;
static void scene_draw_hint(SceneState* state, Canvas* canvas, bool glitching) {
furi_assert(state);
furi_assert(canvas);
char buf[32];
const Item* near = is_nearby(state);
if(near) {
int32_t hint_pos_x = (near->x - state->player_global.x) * PARALLAX(near->layer) + 25;
int8_t hint_pos_y = near->y < 15 ? near->y + 4 : near->y - 16;
strcpy(buf, near->action_name);
if(glitching) {
for(size_t g = 0; g != state->action_timeout; g++) {
buf[(g * 23) % strlen(buf)] = ' ' + (random() % g * 17) % ('z' - ' ');
}
}
canvas_draw_str(canvas, hint_pos_x, hint_pos_y, buf);
if(state->player_v.x < 0) {
state->frame_pending = DirLeft;
} else if(state->player_v.x > 0) {
state->frame_pending = DirRight;
} else if(state->player_v.y < 0) {
state->frame_pending = DirUp;
} else if(state->player_v.y > 0) {
state->frame_pending = DirDown;
}
}
state->transition_pending = state->frame_group != state->frame_pending;
static void scene_draw_current_emote(SceneState* state, Canvas* canvas) {
furi_assert(state);
furi_assert(canvas);
elements_multiline_text_framed(canvas, 80, 20, (char*)emotes_list[state->emote_id]);
}
if(*&frames[state->frame_group][state->frame_type]->frames[state->frame_idx].f) {
state->current_frame = *&frames[state->frame_group][state->frame_type];
}
static void scene_draw_sleep_emote(SceneState* state, Canvas* canvas) {
furi_assert(state);
furi_assert(canvas);
uint8_t total = state->current_frame->frames[2].f == NULL ? 2 : 3;
char dialog_str[] = "zZzZ..";
// 2do - sofa x pos getter
if(state->player_global.x == 154 && state->action_timeout % 100 < 50) {
if(state->dialog_progress < strlen(dialog_str)) {
if(state->action_timeout % 10 == 0) state->dialog_progress++;
dialog_str[state->dialog_progress + 1] = '\0';
canvas_draw_str(canvas, 80, 20, dialog_str);
}
if(state->transition_pending && !state->frame_idx) {
state->transition_pending = false;
state->transition = true;
}
if(state->transition) {
state->frame_type = state->frame_pending;
state->frame_group = state->last_group;
state->transition = !(state->frame_idx == total - 1);
} else {
state->dialog_progress = 0;
}
}
static void scene_draw_dialog(SceneState* state, Canvas* canvas) {
furi_assert(state);
furi_assert(canvas);
char dialog_str[64];
char buf[64];
strcpy(dialog_str, (char*)dialogues_list[state->dialogue_id]);
if(state->dialog_progress <= strlen(dialog_str)) {
if(state->action_timeout % 2 == 0) state->dialog_progress++;
dialog_str[state->dialog_progress] = '\0';
snprintf(buf, state->dialog_progress, dialog_str);
} else {
snprintf(buf, 64, dialog_str);
state->frame_group = state->frame_type;
}
elements_multiline_text_framed(canvas, 68, 16, buf);
}
state->player_anim++;
/*
static void draw_idle_emote(SceneState* state, Canvas* canvas){
if(state->action_timeout % 50 < 40 && state->prev_action == MINDCONTROL){
elements_multiline_text_framed(canvas, 68, 16, "WUT?!");
}
}
*/
static void draw_idle_emote(SceneState* state, Canvas* canvas) {
furi_assert(state);
furi_assert(canvas);
char dialog_str[] = "...";
if(state->action_timeout % 100 < 50) {
if(state->dialog_progress < strlen(dialog_str)) {
if(state->action_timeout % 10 == 0) state->dialog_progress++;
dialog_str[state->dialog_progress + 1] = '\0';
canvas_draw_str(canvas, 70, 15, dialog_str);
}
} else {
state->dialog_progress = 0;
if(!(state->player_anim % speed_mod)) {
state->frame_idx = (state->frame_idx + 1) % total;
}
}
@ -103,44 +48,24 @@ void dolphin_scene_render_dolphin(SceneState* state, Canvas* canvas) {
furi_assert(state);
furi_assert(canvas);
if(state->scene_zoom == SCENE_ZOOM) {
state->dolphin_gfx = &I_DolphinExcited_64x63;
} else if(state->action == SLEEP && state->player_global.x == 154) { // 2do - sofa x pos getter
state->dolphin_gfx = &A_FX_Sitting_40x27;
state->dolphin_gfx_b = &I_FX_SittingB_40x27;
} else if(state->action != INTERACT) {
if(state->player_v.x < 0 || state->player_flipped) {
if(state->player_anim == 0) {
state->dolphin_gfx = &I_WalkL1_32x32;
state->dolphin_gfx_b = &I_WalkLB1_32x32;
} else {
state->dolphin_gfx = &I_WalkL2_32x32;
state->dolphin_gfx_b = &I_WalkLB2_32x32;
}
} else if(state->player_v.x > 0 || !state->player_flipped) {
if(state->player_anim == 0) {
state->dolphin_gfx = &I_WalkR1_32x32;
state->dolphin_gfx_b = &I_WalkRB1_32x32;
} else {
state->dolphin_gfx = &I_WalkR2_32x32;
state->dolphin_gfx_b = &I_WalkRB2_32x32;
}
}
}
dolphin_scene_transition_handler(state);
canvas_set_bitmap_mode(canvas, true);
canvas_set_color(canvas, ColorWhite);
canvas_draw_icon(canvas, state->player.x, state->player.y, state->dolphin_gfx_b);
canvas_draw_icon(
canvas, state->player.x, state->player.y, state->current_frame->frames[state->frame_idx].b);
canvas_set_color(canvas, ColorBlack);
canvas_draw_icon(canvas, state->player.x, state->player.y, state->dolphin_gfx);
canvas_draw_icon(
canvas, state->player.x, state->player.y, state->current_frame->frames[state->frame_idx].f);
canvas_set_bitmap_mode(canvas, false);
}
static bool item_screen_bounds(int32_t pos) {
static bool item_screen_bounds_x(int32_t pos) {
return pos > -SCREEN_WIDTH && pos < (SCREEN_WIDTH * 2);
}
static bool item_screen_bounds_y(int32_t pos) {
return pos > -SCREEN_HEIGHT * 2 && pos < (SCREEN_HEIGHT * 2);
}
void dolphin_scene_render(SceneState* state, Canvas* canvas, uint32_t t) {
furi_assert(state);
@ -151,24 +76,17 @@ void dolphin_scene_render(SceneState* state, Canvas* canvas, uint32_t t) {
const Item** current_scene = get_scene(state);
for(uint8_t l = 0; l < LAYERS; l++) {
if(state->scene_zoom < SCENE_ZOOM) {
for(uint8_t i = 0; i < ITEMS_NUM; i++) {
int32_t item_pos = (current_scene[i]->x - state->player_global.x);
if(item_screen_bounds(item_pos)) {
if(current_scene[i]->draw) current_scene[i]->draw(canvas, state);
for(uint8_t i = 0; i < ItemsEnumTotal; i++) {
int32_t item_pos_X = (current_scene[i]->pos.x - state->player_global.x);
int32_t item_pos_Y = (current_scene[i]->pos.y - state->player_global.y);
if(l == current_scene[i]->layer) {
canvas_draw_icon(
canvas,
item_pos * PARALLAX(l),
current_scene[i]->y,
current_scene[i]->icon);
canvas_set_bitmap_mode(canvas, false);
if(item_screen_bounds_x(item_pos_X) && item_screen_bounds_y(item_pos_Y)) {
if(l == current_scene[i]->layer) {
if(current_scene[i]->draw) {
current_scene[i]->draw(canvas, state);
}
}
}
if(l == 0) canvas_draw_line(canvas, 0, 42, 128, 42);
}
if(l == DOLPHIN_LAYER) dolphin_scene_render_dolphin(state, canvas);
@ -188,24 +106,16 @@ void dolphin_scene_render_state(SceneState* state, Canvas* canvas) {
if(state->debug) {
sprintf(
buf,
"x:%ld>%d %ld %s",
state->player_global.x,
state->poi,
state->action_timeout,
action_str[state->action]);
"%d:%d %d/%dP%dL%d T%d-%d",
state->frame_idx,
state->current_frame->frames[2].f == NULL ? 2 : 3,
state->frame_group,
state->frame_type,
state->frame_pending,
state->last_group,
state->transition_pending,
state->transition);
canvas_draw_str(canvas, 0, 13, buf);
}
if(state->scene_zoom == SCENE_ZOOM)
scene_draw_dialog(state, canvas);
else if(state->action == EMOTE)
scene_draw_current_emote(state, canvas);
else if(state->action == MINDCONTROL)
scene_draw_hint(state, canvas, state->action_timeout > 45);
else if(state->action == INTERACT)
scene_activate_item_callback(state, canvas);
else if(state->action == SLEEP)
scene_draw_sleep_emote(state, canvas);
else if(state->action == IDLE)
draw_idle_emote(state, canvas);
if(state->action == INTERACT) scene_activate_item_callback(state, canvas);
}

View File

@ -192,7 +192,8 @@ int32_t gui_test(void* param) {
text_input_callback,
gui_tester,
text_input_text,
text_input_text_len);
text_input_text_len,
false);
text_input_set_header_text(gui_tester->text_input, "Name the key");
const uint8_t byte_input_bytes_len = 16;

23
applications/gui/modules/text_input.c Normal file → Executable file
View File

@ -16,6 +16,7 @@ typedef struct {
const char* header;
char* text_buffer;
size_t text_buffer_size;
bool clear_default_text;
TextInputCallback callback;
void* callback_context;
@ -128,7 +129,7 @@ static const char char_to_uppercase(const char letter) {
}
static void text_input_backspace_cb(TextInputModel* model) {
uint8_t text_length = strlen(model->text_buffer);
uint8_t text_length = model->clear_default_text ? 1 : strlen(model->text_buffer);
if(text_length > 0) {
model->text_buffer[text_length - 1] = 0;
}
@ -158,11 +159,16 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) {
text++;
}
if(model->clear_default_text) {
elements_slightly_rounded_box(
canvas, start_pos - 1, 14, canvas_string_width(canvas, text) + 2, 10);
canvas_set_color(canvas, ColorWhite);
} else {
canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 1, 22, "|");
canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 2, 22, "|");
}
canvas_draw_str(canvas, start_pos, 22, text);
canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 1, 22, "|");
canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 2, 22, "|");
canvas_set_font(canvas, FontKeyboard);
for(uint8_t row = 0; row <= keyboard_row_count; row++) {
@ -295,12 +301,16 @@ static void text_input_handle_ok(TextInput* text_input) {
} else if(selected == BACKSPACE_KEY) {
text_input_backspace_cb(model);
} else if(text_length < (model->text_buffer_size - 1)) {
if(model->clear_default_text) {
text_length = 0;
}
if(text_length == 0 && char_is_lowercase(selected)) {
selected = char_to_uppercase(selected);
}
model->text_buffer[text_length] = selected;
model->text_buffer[text_length + 1] = 0;
}
model->clear_default_text = false;
return true;
});
}
@ -365,6 +375,7 @@ TextInput* text_input_alloc() {
model->header = "";
model->selected_row = 0;
model->selected_column = 0;
model->clear_default_text = false;
return true;
});
@ -387,13 +398,15 @@ void text_input_set_result_callback(
TextInputCallback callback,
void* callback_context,
char* text_buffer,
size_t text_buffer_size) {
size_t text_buffer_size,
bool clear_default_text) {
with_view_model(
text_input->view, (TextInputModel * model) {
model->callback = callback;
model->callback_context = callback_context;
model->text_buffer = text_buffer;
model->text_buffer_size = text_buffer_size;
model->clear_default_text = clear_default_text;
return true;
});
}

View File

@ -39,13 +39,15 @@ View* text_input_get_view(TextInput* text_input);
* @param callback_context - callback context
* @param text_buffer - pointer to YOUR text buffer, that we going to modify
* @param text_buffer_size - YOUR text buffer size in bytes. Max string length will be text_buffer_size - 1.
* @param clear_default_text - clear text from text_buffer on first OK event
*/
void text_input_set_result_callback(
TextInput* text_input,
TextInputCallback callback,
void* callback_context,
char* text_buffer,
size_t text_buffer_size);
size_t text_buffer_size,
bool clear_default_text);
/**
* @brief Set text input header text

View File

@ -53,11 +53,11 @@ bool scene_manager_handle_custom_event(SceneManager* scene_manager, uint32_t cus
scene_manager->context, event);
}
bool scene_manager_handle_navigation_event(SceneManager* scene_manager) {
bool scene_manager_handle_back_event(SceneManager* scene_manager) {
furi_assert(scene_manager);
SceneManagerEvent event = {
.type = SceneManagerEventTypeNavigation,
.type = SceneManagerEventTypeBack,
};
uint32_t scene_id = *SceneManagerIdStack_back(scene_manager->scene_id_stack);
bool consumed =
@ -109,7 +109,9 @@ bool scene_manager_previous_scene(SceneManager* scene_manager) {
return true;
}
bool scene_manager_search_previous_scene(SceneManager* scene_manager, uint32_t scene_id) {
bool scene_manager_search_and_switch_to_previous_scene(
SceneManager* scene_manager,
uint32_t scene_id) {
furi_assert(scene_manager);
uint32_t prev_scene_id = 0;
@ -137,3 +139,45 @@ bool scene_manager_search_previous_scene(SceneManager* scene_manager, uint32_t s
return true;
}
bool scene_manager_has_previous_scene(SceneManager* scene_manager, uint32_t scene_id) {
furi_assert(scene_manager);
bool scene_found = false;
uint32_t prev_scene_id;
SceneManagerIdStack_it_t scene_it;
SceneManagerIdStack_it_last(scene_it, scene_manager->scene_id_stack);
// Perform search in scene stack
while(!scene_found) {
SceneManagerIdStack_previous(scene_it);
if(SceneManagerIdStack_end_p(scene_it)) {
break;
}
prev_scene_id = *SceneManagerIdStack_ref(scene_it);
if(prev_scene_id == scene_id) {
scene_found = true;
}
}
return scene_found;
}
bool scene_manager_search_and_switch_to_another_scene(
SceneManager* scene_manager,
uint32_t scene_id) {
furi_assert(scene_manager);
furi_assert(scene_id < scene_manager->scene_handlers->scene_num);
uint32_t cur_scene_id = *SceneManagerIdStack_back(scene_manager->scene_id_stack);
SceneManagerIdStack_it_t scene_it;
SceneManagerIdStack_it(scene_it, scene_manager->scene_id_stack);
SceneManagerIdStack_next(scene_it);
// Remove all scene id from navigation stack until first scene
SceneManagerIdStack_pop_until(scene_manager->scene_id_stack, scene_it);
// Add next scene
SceneManagerIdStack_push_back(scene_manager->scene_id_stack, scene_id);
scene_manager->scene_handlers->on_exit_handlers[cur_scene_id](scene_manager->context);
scene_manager->scene_handlers->on_enter_handlers[scene_id](scene_manager->context);
return true;
}

View File

@ -11,7 +11,7 @@ extern "C" {
*/
typedef enum {
SceneManagerEventTypeCustom,
SceneManagerEventTypeNavigation,
SceneManagerEventTypeBack,
SceneManagerEventTypeTick,
} SceneManagerEventType;
@ -78,12 +78,12 @@ void scene_manager_free(SceneManager* scene_manager);
*/
bool scene_manager_handle_custom_event(SceneManager* scene_manager, uint32_t custom_event);
/** Navigation event handler
* Calls Scene event handler with Navigation event parameter
/** Back event handler
* Calls Scene event handler with Back event parameter
* @param scene_manager SceneManager instance
* @return true if event was consumed, false otherwise
*/
bool scene_manager_handle_navigation_event(SceneManager* scene_manager);
bool scene_manager_handle_back_event(SceneManager* scene_manager);
/** Tick event handler
* Calls Scene event handler with Tick event parameter
@ -104,12 +104,30 @@ void scene_manager_next_scene(SceneManager* scene_manager, uint32_t next_scene_i
*/
bool scene_manager_previous_scene(SceneManager* scene_manager);
/** Search previous Scene by ID
/** Search previous Scene
* @param scene_manager SceneManager instance
* @param scene_id Scene ID
* @return true if previous scene was found, false otherwise
*/
bool scene_manager_search_previous_scene(SceneManager* scene_manager, uint32_t scene_id);
bool scene_manager_has_previous_scene(SceneManager* scene_manager, uint32_t scene_id);
/** Search and switch to previous Scene
* @param scene_manager SceneManager instance
* @param scene_id Scene ID
* @return true if previous scene was found, false otherwise
*/
bool scene_manager_search_and_switch_to_previous_scene(
SceneManager* scene_manager,
uint32_t scene_id);
/** Clear Scene stack and switch to another Scene
* @param scene_manager SceneManager instance
* @param scene_id Scene ID
* @return true if previous scene was found, false otherwise
*/
bool scene_manager_search_and_switch_to_another_scene(
SceneManager* scene_manager,
uint32_t scene_id);
#ifdef __cplusplus
}

View File

@ -3,7 +3,7 @@
#include <callback-connector.h>
#include <m-string.h>
#include <file-worker-cpp.h>
#include <path.h>
#include <lib/toolbox/path.h>
const char* iButtonApp::app_folder = "/any/ibutton";
const char* iButtonApp::app_extension = ".ibtn";
@ -40,9 +40,6 @@ iButtonApp::iButtonApp()
: notification{"notification"} {
api_hal_power_insomnia_enter();
key_worker = new KeyWorker(&ibutton_gpio);
// we need random
srand(DWT->CYCCNT);
}
iButtonApp::~iButtonApp() {
@ -186,38 +183,6 @@ uint8_t iButtonApp::get_text_store_size() {
return text_store_size;
}
void iButtonApp::generate_random_name(char* name, uint8_t max_name_size) {
const uint8_t prefix_size = 9;
const char* prefix[prefix_size] = {
"ancient",
"hollow",
"strange",
"disappeared",
"unknown",
"unthinkable",
"unnamable",
"nameless",
"my",
};
const uint8_t suffix_size = 8;
const char* suffix[suffix_size] = {
"door",
"entrance",
"doorway",
"entry",
"portal",
"entree",
"opening",
"crack",
};
sniprintf(
name, max_name_size, "%s_%s", prefix[rand() % prefix_size], suffix[rand() % suffix_size]);
// to upper
name[0] = name[0] - 0x20;
}
// file managment
bool iButtonApp::save_key(const char* key_name) {
// Create ibutton directory if necessary

View File

@ -90,8 +90,6 @@ public:
char* get_file_name();
uint8_t get_file_name_size();
void generate_random_name(char* name, uint8_t max_name_size);
bool save_key(const char* key_name);
bool load_key();
bool load_key(const char* key_name);

View File

@ -2,7 +2,7 @@
#include <api-hal.h>
#include <stdarg.h>
#include <cli/cli.h>
#include <args.h>
#include <lib/toolbox/args.h>
#include "helpers/key-info.h"
#include "helpers/key-worker.h"

View File

@ -4,6 +4,7 @@
#include "../ibutton-event.h"
#include "../ibutton-key.h"
#include <callback-connector.h>
#include <lib/toolbox/random_name.h>
void iButtonSceneSaveName::on_enter(iButtonApp* app) {
iButtonAppViewManager* view_manager = app->get_view_manager();
@ -12,16 +13,17 @@ void iButtonSceneSaveName::on_enter(iButtonApp* app) {
iButtonKey* key = app->get_key();
const char* key_name = key->get_name();
bool key_name_empty = !strcmp(key_name, "");
if(strcmp(key_name, "") == 0) {
app->generate_random_name(app->get_text_store(), app->get_text_store_size());
if(key_name_empty) {
set_random_name(app->get_text_store(), app->get_text_store_size());
} else {
app->set_text_store("%s", key_name);
}
text_input_set_header_text(text_input, "Name the key");
text_input_set_result_callback(
text_input, callback, app, app->get_text_store(), IBUTTON_KEY_NAME_SIZE);
text_input, callback, app, app->get_text_store(), IBUTTON_KEY_NAME_SIZE, key_name_empty);
view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewTextInput);
}
@ -47,7 +49,7 @@ bool iButtonSceneSaveName::on_event(iButtonApp* app, iButtonEvent* event) {
void iButtonSceneSaveName::on_exit(iButtonApp* app) {
TextInput* text_input = app->get_view_manager()->get_text_input();
text_input_set_header_text(text_input, "");
text_input_set_result_callback(text_input, NULL, NULL, NULL, 0);
text_input_set_result_callback(text_input, NULL, NULL, NULL, 0, false);
}
void iButtonSceneSaveName::text_input_callback(void* context) {

View File

@ -10,6 +10,7 @@
#include <string>
#include <m-string.h>
#include <irda_transmit.h>
#include <sys/types.h>
static void signal_received_callback(void* context, IrdaWorkerSignal* received_signal) {
furi_assert(received_signal);
@ -47,7 +48,7 @@ static void signal_received_callback(void* context, IrdaWorkerSignal* received_s
}
static void irda_cli_start_ir_rx(Cli* cli, string_t args, void* context) {
if(api_hal_irda_rx_irq_is_busy()) {
if(api_hal_irda_is_busy()) {
printf("IRDA is busy. Exit.");
return;
}
@ -105,7 +106,7 @@ static bool parse_signal_raw(
uint32_t* timings,
uint32_t* timings_cnt,
float* duty_cycle,
float* frequency) {
uint32_t* frequency) {
char frequency_str[10];
char duty_cycle_str[10];
int parsed = sscanf(str, "RAW F:%9s DC:%9s", frequency_str, duty_cycle_str);
@ -141,14 +142,14 @@ static bool parse_signal_raw(
}
static void irda_cli_start_ir_tx(Cli* cli, string_t args, void* context) {
if(api_hal_irda_rx_irq_is_busy()) {
if(api_hal_irda_is_busy()) {
printf("IRDA is busy. Exit.");
return;
}
IrdaMessage message;
const char* str = string_get_cstr(args);
float frequency;
uint32_t frequency;
float duty_cycle;
uint32_t* timings = (uint32_t*)furi_alloc(sizeof(uint32_t) * 1000);
uint32_t timings_cnt = 1000;
@ -156,7 +157,7 @@ static void irda_cli_start_ir_tx(Cli* cli, string_t args, void* context) {
if(parse_message(str, &message)) {
irda_send(&message, 1);
} else if(parse_signal_raw(str, timings, &timings_cnt, &duty_cycle, &frequency)) {
irda_send_raw_ext(timings, timings_cnt, true, duty_cycle, frequency);
irda_send_raw_ext(timings, timings_cnt, true, frequency, duty_cycle);
} else {
printf("Wrong arguments.\r\n");
irda_cli_print_usage();

View File

@ -20,7 +20,8 @@ void IrdaAppSceneEditRename::on_enter(IrdaApp* app) {
IrdaApp::text_input_callback,
app,
app->get_text_store(0),
app->get_text_store_size());
app->get_text_store_size(),
false);
view_manager->switch_to(IrdaAppViewManager::ViewType::TextInput);
}

View File

@ -26,7 +26,8 @@ void IrdaAppSceneLearnEnterName::on_enter(IrdaApp* app) {
IrdaApp::text_input_callback,
app,
app->get_text_store(0),
app->get_text_store_size());
app->get_text_store_size(),
false);
view_manager->switch_to(IrdaAppViewManager::ViewType::TextInput);
}

View File

@ -0,0 +1,15 @@
#include "decoder-gpio-out.h"
#include <furi.h>
#include <api-hal.h>
void DecoderGpioOut::process_front(bool polarity, uint32_t time) {
hal_gpio_write(&gpio_ext_pa7, polarity);
}
DecoderGpioOut::DecoderGpioOut() {
hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull);
}
DecoderGpioOut::~DecoderGpioOut() {
hal_gpio_init_simple(&gpio_ext_pa7, GpioModeAnalog);
}

View File

@ -0,0 +1,14 @@
#pragma once
#include <stdint.h>
#include <atomic>
class DecoderGpioOut {
public:
void process_front(bool polarity, uint32_t time);
DecoderGpioOut();
~DecoderGpioOut();
private:
void reset_state();
};

View File

@ -2,18 +2,24 @@
#include <api-hal.h>
constexpr uint32_t clocks_in_us = 64;
constexpr uint32_t min_time_us = 25 * clocks_in_us;
constexpr uint32_t mid_time_us = 45 * clocks_in_us;
constexpr uint32_t max_time_us = 90 * clocks_in_us;
constexpr uint32_t us_per_bit = 255;
bool DecoderIndala::read(uint8_t* data, uint8_t data_size) {
bool result = false;
if(ready) {
result = true;
printf("IND %02X %02X %02X\r\n", facility, (uint8_t)(number >> 8), (uint8_t)number);
ready = false;
if(cursed_data_valid) {
indala.decode(
reinterpret_cast<const uint8_t*>(&cursed_raw_data),
sizeof(uint64_t),
data,
data_size);
} else {
indala.decode(
reinterpret_cast<const uint8_t*>(&raw_data), sizeof(uint64_t), data, data_size);
}
reset_state();
}
return result;
@ -22,149 +28,49 @@ bool DecoderIndala::read(uint8_t* data, uint8_t data_size) {
void DecoderIndala::process_front(bool polarity, uint32_t time) {
if(ready) return;
if(polarity == false) {
last_pulse_time = time;
} else {
last_pulse_time += time;
pulse_count++;
process_internal(polarity, time, &raw_data);
if(ready) return;
if(last_pulse_time > min_time_us && last_pulse_time < max_time_us) {
if(last_pulse_time > mid_time_us) {
bool last_data = !(readed_data & 1);
pulse_count = 0;
readed_data = (readed_data << 1) | last_data;
verify();
} else if((pulse_count % 16) == 0) {
bool last_data = readed_data & 1;
pulse_count = 0;
readed_data = (readed_data << 1) | last_data;
verify();
if(polarity) {
time = time + 110;
} else {
time = time - 110;
}
process_internal(!polarity, time, &cursed_raw_data);
if(ready) {
cursed_data_valid = true;
}
}
void DecoderIndala::process_internal(bool polarity, uint32_t time, uint64_t* data) {
time /= clocks_in_us;
time += (us_per_bit / 2);
uint32_t bit_count = (time / us_per_bit);
if(bit_count < 64) {
for(uint32_t i = 0; i < bit_count; i++) {
*data = (*data << 1) | polarity;
if((*data >> 32) == 0xa0000000ULL) {
if(indala.can_be_decoded(
reinterpret_cast<const uint8_t*>(data), sizeof(uint64_t))) {
ready = true;
break;
}
}
}
}
}
DecoderIndala::DecoderIndala() {
reset_state();
}
void DecoderIndala::reset_state() {
}
void DecoderIndala::verify() {
// verify inverse
readed_data = ~readed_data;
verify_inner();
// verify normal
readed_data = ~readed_data;
verify_inner();
}
typedef union {
uint64_t raw;
struct __attribute__((packed)) {
uint8_t static0 : 3;
uint8_t checksum : 2;
uint8_t static1 : 2;
uint8_t y14 : 1;
uint8_t x8 : 1;
uint8_t x1 : 1;
uint8_t y13 : 1;
uint8_t static2 : 1;
uint8_t y12 : 1;
uint8_t x6 : 1;
uint8_t y5 : 1;
uint8_t y8 : 1;
uint8_t y15 : 1;
uint8_t x2 : 1;
uint8_t x5 : 1;
uint8_t x4 : 1;
uint8_t y9 : 1;
uint8_t y2 : 1;
uint8_t x3 : 1;
uint8_t y3 : 1;
uint8_t y1 : 1;
uint8_t y16 : 1;
uint8_t y4 : 1;
uint8_t x7 : 1;
uint8_t p2 : 1;
uint8_t y11 : 1;
uint8_t y6 : 1;
uint8_t y7 : 1;
uint8_t p1 : 1;
uint8_t y10 : 1;
uint32_t preamble : 30;
};
} IndalaFormat;
void DecoderIndala::verify_inner() {
IndalaFormat id;
id.raw = readed_data;
// preamble
//if((data >> 34) != 0b000000000000000000000000000001) return;
if(id.preamble != 1) return;
// static data bits
//if((data & 0b100001100111) != 0b101) return;
if(id.static2 != 0 && id.static1 != 0 && id.static0 != 0b101) return;
// Indala checksum
uint8_t sum_to_check = id.y2 + id.y4 + id.y7 + id.y8 + id.y10 + id.y11 + id.y14 + id.y16;
if(sum_to_check % 2 == 0) {
if(id.checksum != 0b10) return;
} else {
if(id.checksum != 0b01) return;
}
// read facility number
facility = (id.x1 << 7) + (id.x2 << 6) + (id.x3 << 5) + (id.x4 << 4) + (id.x5 << 3) +
(id.x6 << 2) + (id.x7 << 1) + (id.x8 << 0);
// read serial number
number = (id.y1 << 15) + (id.y2 << 14) + (id.y3 << 13) + (id.y4 << 12) + (id.y5 << 11) +
(id.y6 << 10) + (id.y7 << 9) + (id.y8 << 8) + (id.y9 << 7) + (id.y10 << 6) +
(id.y11 << 5) + (id.y12 << 4) + (id.y13 << 3) + (id.y14 << 2) + (id.y15 << 1) +
(id.y16 << 0);
// Wiegand checksum left
sum_to_check = 0;
for(int8_t i = 0; i < 8; i--) {
if((facility >> i) & 1) {
sum_to_check += 1;
}
}
for(int8_t i = 0; i < 4; i--) {
if((number >> i) & 1) {
sum_to_check += 1;
}
}
if(id.p1) {
sum_to_check += 1;
}
if((sum_to_check % 2) == 1) return;
// Wiegand checksum right
sum_to_check = 0;
for(int8_t i = 0; i < 12; i--) {
if((number >> (i + 4)) & 1) {
sum_to_check += 1;
}
}
if(id.p2) {
sum_to_check += 1;
}
if((sum_to_check % 2) != 1) return;
ready = true;
raw_data = 0;
cursed_raw_data = 0;
ready = false;
cursed_data_valid = false;
}

View File

@ -2,27 +2,24 @@
#include <stdint.h>
#include <limits.h>
#include <atomic>
#include "protocols/protocol-indala-40134.h"
class DecoderIndala {
public:
bool read(uint8_t* data, uint8_t data_size);
void process_front(bool polarity, uint32_t time);
void process_internal(bool polarity, uint32_t time, uint64_t* data);
DecoderIndala();
private:
void reset_state();
void verify();
void verify_inner();
uint32_t last_pulse_time = 0;
uint32_t pulse_count = 0;
uint32_t overall_pulse_count = 0;
uint64_t readed_data = 0;
uint64_t raw_data;
uint64_t cursed_raw_data;
std::atomic<bool> ready;
uint8_t facility = 0;
uint16_t number = 0;
std::atomic<bool> cursed_data_valid;
ProtocolIndala40134 indala;
};

View File

@ -7,7 +7,6 @@ void EncoderHID_H10301::init(const uint8_t* data, const uint8_t data_size) {
hid.encode(data, data_size, reinterpret_cast<uint8_t*>(&card_data), sizeof(card_data) * 3);
card_data_index = 0;
bit_index = 0;
}
void EncoderHID_H10301::write_bit(bool bit, uint8_t position) {
@ -24,39 +23,24 @@ void EncoderHID_H10301::write_raw_bit(bool bit, uint8_t position) {
}
void EncoderHID_H10301::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) {
// hid 0 is 6 cycles by 8 clocks
const uint8_t hid_0_period = 8;
const uint8_t hid_0_count = 6;
// hid 1 is 5 cycles by 10 clocks
const uint8_t hid_1_period = 10;
const uint8_t hid_1_count = 5;
uint8_t bit = (card_data[card_data_index / 32] >> (31 - (card_data_index % 32))) & 1;
bool bit = (card_data[card_data_index / 32] >> (31 - (card_data_index % 32))) & 1;
*polarity = true;
if(bit) {
*period = hid_1_period;
*pulse = hid_1_period / 2;
bit_index++;
if(bit_index >= hid_1_count) {
bit_index = 0;
card_data_index++;
if(card_data_index >= (32 * card_data_max)) {
card_data_index = 0;
}
}
} else {
*period = hid_0_period;
*pulse = hid_0_period / 2;
bit_index++;
if(bit_index >= hid_0_count) {
bit_index = 0;
card_data_index++;
if(card_data_index >= (32 * card_data_max)) {
card_data_index = 0;
}
bool advance = fsk->next(bit, period);
if(advance) {
card_data_index++;
if(card_data_index >= (32 * card_data_max)) {
card_data_index = 0;
}
}
*polarity = true;
*pulse = *period / 2;
}
EncoderHID_H10301::EncoderHID_H10301() {
fsk = new OscFSK(8, 10, 50);
}
EncoderHID_H10301::~EncoderHID_H10301() {
delete fsk;
}

View File

@ -1,5 +1,6 @@
#pragma once
#include "encoder-generic.h"
#include "osc-fsk.h"
class EncoderHID_H10301 : public EncoderGeneric {
public:
@ -10,15 +11,16 @@ public:
* @param data_size must be 3
*/
void init(const uint8_t* data, const uint8_t data_size) final;
void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final;
EncoderHID_H10301();
~EncoderHID_H10301();
private:
static const uint8_t card_data_max = 3;
uint32_t card_data[card_data_max];
uint8_t card_data_index;
uint8_t bit_index;
void write_bit(bool bit, uint8_t position);
void write_raw_bit(bool bit, uint8_t position);
OscFSK* fsk;
};

View File

@ -0,0 +1,20 @@
#include "osc-fsk.h"
OscFSK::OscFSK(uint16_t _freq_low, uint16_t _freq_hi, uint16_t _osc_phase_max)
: freq{_freq_low, _freq_hi}
, osc_phase_max(_osc_phase_max) {
osc_phase_current = 0;
}
bool OscFSK::next(bool bit, uint16_t* period) {
bool advance = false;
*period = freq[bit];
osc_phase_current += *period;
if(osc_phase_current > osc_phase_max) {
advance = true;
osc_phase_current -= osc_phase_max;
}
return advance;
}

View File

@ -0,0 +1,30 @@
#pragma once
#include <stdint.h>
/**
* This code tries to fit the periods into a given number of cycles (phases) by taking cycles from the next cycle of periods.
*/
class OscFSK {
public:
/**
* Get next period
* @param bit bit value
* @param period return period
* @return bool whether to advance to the next bit
*/
bool next(bool bit, uint16_t* period);
/**
* FSK ocillator constructor
*
* @param freq_low bit 0 freq
* @param freq_hi bit 1 freq
* @param osc_phase_max max oscillator phase
*/
OscFSK(uint16_t freq_low, uint16_t freq_hi, uint16_t osc_phase_max);
private:
const uint16_t freq[2];
const uint16_t osc_phase_max;
int32_t osc_phase_current;
};

View File

@ -12,6 +12,11 @@ static void set_bit(bool bit, uint8_t position, Indala40134CardData* card_data)
}
}
static bool get_bit(uint8_t position, const Indala40134CardData* card_data) {
position = (sizeof(Indala40134CardData) * 8) - 1 - position;
return (*card_data >> position) & 1;
}
uint8_t ProtocolIndala40134::get_encoded_data_size() {
return sizeof(Indala40134CardData);
}
@ -110,6 +115,46 @@ void ProtocolIndala40134::encode(
memcpy(encoded_data, &card_data, get_encoded_data_size());
}
// factory code
static uint8_t get_fc(const Indala40134CardData* card_data) {
uint8_t fc = 0;
fc = fc << 1 | get_bit(57, card_data);
fc = fc << 1 | get_bit(49, card_data);
fc = fc << 1 | get_bit(44, card_data);
fc = fc << 1 | get_bit(47, card_data);
fc = fc << 1 | get_bit(48, card_data);
fc = fc << 1 | get_bit(53, card_data);
fc = fc << 1 | get_bit(39, card_data);
fc = fc << 1 | get_bit(58, card_data);
return fc;
}
// card number
static uint16_t get_cn(const Indala40134CardData* card_data) {
uint16_t cn = 0;
cn = cn << 1 | get_bit(42, card_data);
cn = cn << 1 | get_bit(45, card_data);
cn = cn << 1 | get_bit(43, card_data);
cn = cn << 1 | get_bit(40, card_data);
cn = cn << 1 | get_bit(52, card_data);
cn = cn << 1 | get_bit(36, card_data);
cn = cn << 1 | get_bit(35, card_data);
cn = cn << 1 | get_bit(51, card_data);
cn = cn << 1 | get_bit(46, card_data);
cn = cn << 1 | get_bit(33, card_data);
cn = cn << 1 | get_bit(37, card_data);
cn = cn << 1 | get_bit(54, card_data);
cn = cn << 1 | get_bit(56, card_data);
cn = cn << 1 | get_bit(59, card_data);
cn = cn << 1 | get_bit(50, card_data);
cn = cn << 1 | get_bit(41, card_data);
return cn;
}
void ProtocolIndala40134::decode(
const uint8_t* encoded_data,
const uint8_t encoded_data_size,
@ -117,15 +162,76 @@ void ProtocolIndala40134::decode(
const uint8_t decoded_data_size) {
furi_check(decoded_data_size >= get_decoded_data_size());
furi_check(encoded_data_size >= get_encoded_data_size());
// TODO implement decoding
furi_check(0);
const Indala40134CardData* card_data =
reinterpret_cast<const Indala40134CardData*>(encoded_data);
uint8_t fc = get_fc(card_data);
uint16_t card = get_cn(card_data);
decoded_data[0] = fc;
decoded_data[1] = card >> 8;
decoded_data[2] = card;
}
bool ProtocolIndala40134::can_be_decoded(
const uint8_t* encoded_data,
const uint8_t encoded_data_size) {
furi_check(encoded_data_size >= get_encoded_data_size());
// TODO implement decoding
furi_check(0);
return false;
bool can_be_decoded = false;
const Indala40134CardData* card_data =
reinterpret_cast<const Indala40134CardData*>(encoded_data);
do {
// preambula
if((*card_data >> 32) != 0xa0000000UL) break;
// data
const uint32_t fc_and_card = get_fc(card_data) << 16 | get_cn(card_data);
// checksum
const uint8_t checksum = get_bit(62, card_data) << 1 | get_bit(63, card_data);
uint8_t checksum_sum = 0;
checksum_sum += ((fc_and_card >> 14) & 1);
checksum_sum += ((fc_and_card >> 12) & 1);
checksum_sum += ((fc_and_card >> 9) & 1);
checksum_sum += ((fc_and_card >> 8) & 1);
checksum_sum += ((fc_and_card >> 6) & 1);
checksum_sum += ((fc_and_card >> 5) & 1);
checksum_sum += ((fc_and_card >> 2) & 1);
checksum_sum += ((fc_and_card >> 0) & 1);
checksum_sum = checksum_sum & 0b1;
if(checksum_sum == 1 && checksum == 0b01) {
} else if(checksum_sum == 0 && checksum == 0b10) {
} else {
break;
}
// wiegand parity bits
// even parity sum calculation (high 12 bits of data)
const bool even_parity = get_bit(34, card_data);
uint8_t even_parity_sum = 0;
for(int8_t i = 12; i < 24; i++) {
if(((fc_and_card >> i) & 1) == 1) {
even_parity_sum++;
}
}
if(even_parity_sum % 2 != even_parity) break;
// odd parity sum calculation (low 12 bits of data)
const bool odd_parity = get_bit(38, card_data);
uint8_t odd_parity_sum = 1;
for(int8_t i = 0; i < 12; i++) {
if(((fc_and_card >> i) & 1) == 1) {
odd_parity_sum++;
}
}
if(odd_parity_sum % 2 != odd_parity) break;
can_be_decoded = true;
} while(false);
return can_be_decoded;
}

View File

@ -1,35 +0,0 @@
#include "rfid-name-generator.h"
#include <stdio.h>
#include <stdlib.h>
void rfid_generate_random_name(char* name, uint8_t max_name_size) {
const uint8_t prefix_size = 9;
const char* prefix[prefix_size] = {
"good",
"nice",
"best",
"some",
"strange",
"working",
"that",
"forgettable",
"easy",
};
const uint8_t suffix_size = 7;
const char* suffix[suffix_size] = {
"pass",
"card",
"key",
"fob",
"permit",
"pass",
"one",
};
sniprintf(
name, max_name_size, "%s_%s", prefix[rand() % prefix_size], suffix[rand() % suffix_size]);
// to upper
name[0] = name[0] - ('a' - 'A');
}

View File

@ -1,4 +0,0 @@
#pragma once
#include "stdint.h"
void rfid_generate_random_name(char* name, uint8_t max_name_size);

View File

@ -17,19 +17,47 @@ struct RfidReaderAccessor {
void RfidReader::decode(bool polarity) {
uint32_t current_dwt_value = DWT->CYCCNT;
uint32_t period = current_dwt_value - last_dwt_value;
last_dwt_value = current_dwt_value;
//decoder_gpio_out.process_front(polarity, period);
switch(type) {
case Type::Normal:
decoder_em.process_front(polarity, current_dwt_value - last_dwt_value);
decoder_hid26.process_front(polarity, current_dwt_value - last_dwt_value);
//decoder_indala.process_front(polarity, current_dwt_value - last_dwt_value);
//decoder_analyzer.process_front(polarity, current_dwt_value - last_dwt_value);
last_dwt_value = current_dwt_value;
decoder_em.process_front(polarity, period);
decoder_hid26.process_front(polarity, period);
break;
case Type::Indala:
decoder_em.process_front(polarity, period);
decoder_hid26.process_front(polarity, period);
decoder_indala.process_front(polarity, period);
break;
}
detect_ticks++;
}
bool RfidReader::switch_timer_elapsed() {
const uint32_t seconds_to_switch = osKernelGetTickFreq() * 2.0f;
return (osKernelGetTickCount() - switch_os_tick_last) > seconds_to_switch;
}
void RfidReader::switch_timer_reset() {
switch_os_tick_last = osKernelGetTickCount();
}
void RfidReader::switch_mode() {
switch(type) {
case Type::Normal:
type = Type::Indala;
api_hal_rfid_change_read_config(62500.0f, 0.25f);
break;
case Type::Indala:
type = Type::Normal;
api_hal_rfid_change_read_config(125000.0f, 0.5f);
break;
}
switch_timer_reset();
}
static void comparator_trigger_callback(void* hcomp, void* comp_ctx) {
@ -45,47 +73,103 @@ static void comparator_trigger_callback(void* hcomp, void* comp_ctx) {
RfidReader::RfidReader() {
}
void RfidReader::start(Type _type) {
type = _type;
void RfidReader::start() {
type = Type::Normal;
start_gpio();
api_hal_rfid_pins_read();
api_hal_rfid_tim_read(125000, 0.5);
api_hal_rfid_tim_read_start();
start_comparator();
switch_timer_reset();
last_readed_count = 0;
}
void RfidReader::start_forced(RfidReader::Type _type) {
type = _type;
switch(type) {
case Type::Normal:
start_timer();
start();
break;
case Type::Indala:
start_timer_indala();
api_hal_rfid_pins_read();
api_hal_rfid_tim_read(62500.0f, 0.25f);
api_hal_rfid_tim_read_start();
start_comparator();
switch_timer_reset();
last_readed_count = 0;
break;
}
start_comparator();
}
void RfidReader::stop() {
stop_gpio();
stop_timer();
api_hal_rfid_pins_reset();
api_hal_rfid_tim_read_stop();
api_hal_rfid_tim_reset();
stop_comparator();
}
bool RfidReader::read(LfrfidKeyType* type, uint8_t* data, uint8_t data_size) {
bool RfidReader::read(LfrfidKeyType* _type, uint8_t* data, uint8_t data_size) {
bool result = false;
bool something_readed = false;
// reading
if(decoder_em.read(data, data_size)) {
*type = LfrfidKeyType::KeyEM4100;
result = true;
*_type = LfrfidKeyType::KeyEM4100;
something_readed = true;
}
if(decoder_hid26.read(data, data_size)) {
*type = LfrfidKeyType::KeyH10301;
result = true;
*_type = LfrfidKeyType::KeyH10301;
something_readed = true;
}
//decoder_indala.read(NULL, 0);
//decoder_analyzer.read(NULL, 0);
if(decoder_indala.read(data, data_size)) {
*_type = LfrfidKeyType::KeyI40134;
something_readed = true;
}
// validation
if(something_readed) {
switch_timer_reset();
if(last_readed_type == *_type && memcmp(last_readed_data, data, data_size) == 0) {
last_readed_count = last_readed_count + 1;
if(last_readed_count > 2) {
result = true;
}
} else {
last_readed_type = *_type;
memcpy(last_readed_data, data, data_size);
last_readed_count = 0;
}
}
// mode switching
if(switch_timer_elapsed()) {
switch_mode();
last_readed_count = 0;
}
return result;
}
bool RfidReader::detect() {
bool detected = false;
if(detect_ticks > 10) {
detected = true;
}
detect_ticks = 0;
return detected;
}
bool RfidReader::any_read() {
return last_readed_count > 0;
}
void RfidReader::start_comparator(void) {
api_interrupt_add(comparator_trigger_callback, InterruptTypeComparatorTrigger, this);
last_dwt_value = DWT->CYCCNT;
@ -93,7 +177,7 @@ void RfidReader::start_comparator(void) {
hcomp1.Init.InputMinus = COMP_INPUT_MINUS_1_2VREFINT;
hcomp1.Init.InputPlus = COMP_INPUT_PLUS_IO1;
hcomp1.Init.OutputPol = COMP_OUTPUTPOL_NONINVERTED;
hcomp1.Init.Hysteresis = COMP_HYSTERESIS_LOW;
hcomp1.Init.Hysteresis = COMP_HYSTERESIS_HIGH;
hcomp1.Init.BlankingSrce = COMP_BLANKINGSRC_NONE;
hcomp1.Init.Mode = COMP_POWERMODE_MEDIUMSPEED;
hcomp1.Init.WindowMode = COMP_WINDOWMODE_DISABLE;
@ -105,30 +189,7 @@ void RfidReader::start_comparator(void) {
HAL_COMP_Start(&hcomp1);
}
void RfidReader::start_timer(void) {
api_hal_rfid_tim_read(125000, 0.5);
api_hal_rfid_tim_read_start();
}
void RfidReader::start_timer_indala(void) {
api_hal_rfid_tim_read(62500, 0.25);
api_hal_rfid_tim_read_start();
}
void RfidReader::start_gpio(void) {
api_hal_rfid_pins_read();
}
void RfidReader::stop_comparator(void) {
HAL_COMP_Stop(&hcomp1);
api_interrupt_remove(comparator_trigger_callback, InterruptTypeComparatorTrigger);
}
void RfidReader::stop_timer(void) {
api_hal_rfid_tim_read_stop();
api_hal_rfid_tim_reset();
}
void RfidReader::stop_gpio(void) {
api_hal_rfid_pins_reset();
}

View File

@ -1,5 +1,6 @@
#pragma once
#include "decoder-analyzer.h"
//#include "decoder-analyzer.h"
#include "decoder-gpio-out.h"
#include "decoder-emmarine.h"
#include "decoder-hid26.h"
#include "decoder-indala.h"
@ -13,14 +14,19 @@ public:
};
RfidReader();
void start(Type type);
void start();
void start_forced(RfidReader::Type type);
void stop();
bool read(LfrfidKeyType* type, uint8_t* data, uint8_t data_size);
bool detect();
bool any_read();
private:
friend struct RfidReaderAccessor;
//DecoderAnalyzer decoder_analyzer;
//DecoderGpioOut decoder_gpio_out;
DecoderEMMarine decoder_em;
DecoderHID26 decoder_hid26;
DecoderIndala decoder_indala;
@ -28,14 +34,20 @@ private:
uint32_t last_dwt_value;
void start_comparator(void);
void start_timer(void);
void start_timer_indala(void);
void start_gpio(void);
void stop_comparator(void);
void stop_timer(void);
void stop_gpio(void);
void decode(bool polarity);
uint32_t detect_ticks;
uint32_t switch_os_tick_last;
bool switch_timer_elapsed();
void switch_timer_reset();
void switch_mode();
LfrfidKeyType last_readed_type;
uint8_t last_readed_data[LFRFID_KEY_SIZE];
uint8_t last_readed_count;
Type type = Type::Normal;
};

View File

@ -7,7 +7,7 @@ RfidWorker::~RfidWorker() {
}
void RfidWorker::start_read() {
reader.start(RfidReader::Type::Normal);
reader.start();
}
bool RfidWorker::read() {
@ -25,6 +25,14 @@ bool RfidWorker::read() {
return result;
}
bool RfidWorker::detect() {
return reader.detect();
}
bool RfidWorker::any_read() {
return reader.any_read();
}
void RfidWorker::stop_read() {
reader.stop();
}
@ -36,7 +44,7 @@ void RfidWorker::start_write() {
write_sequence->do_every_tick(1, std::bind(&RfidWorker::sq_write, this));
write_sequence->do_after_tick(2, std::bind(&RfidWorker::sq_write_start_validate, this));
write_sequence->do_after_tick(15, std::bind(&RfidWorker::sq_write_validate, this));
write_sequence->do_every_tick(30, std::bind(&RfidWorker::sq_write_validate, this));
write_sequence->do_every_tick(1, std::bind(&RfidWorker::sq_write_stop_validate, this));
}
@ -59,26 +67,37 @@ void RfidWorker::stop_emulate() {
}
void RfidWorker::sq_write() {
// TODO expand this
switch(key.get_type()) {
case LfrfidKeyType::KeyEM4100:
writer.start();
writer.write_em(key.get_data());
writer.stop();
break;
case LfrfidKeyType::KeyH10301:
writer.start();
writer.write_hid(key.get_data());
writer.stop();
break;
default:
break;
for(size_t i = 0; i < 5; i++) {
switch(key.get_type()) {
case LfrfidKeyType::KeyEM4100:
writer.start();
writer.write_em(key.get_data());
writer.stop();
break;
case LfrfidKeyType::KeyH10301:
writer.start();
writer.write_hid(key.get_data());
writer.stop();
break;
case LfrfidKeyType::KeyI40134:
writer.start();
writer.write_indala(key.get_data());
writer.stop();
break;
}
}
}
void RfidWorker::sq_write_start_validate() {
reader.start(RfidReader::Type::Normal);
switch(key.get_type()) {
case LfrfidKeyType::KeyEM4100:
case LfrfidKeyType::KeyH10301:
reader.start_forced(RfidReader::Type::Normal);
break;
case LfrfidKeyType::KeyI40134:
reader.start_forced(RfidReader::Type::Indala);
break;
}
}
void RfidWorker::sq_write_validate() {
@ -88,7 +107,11 @@ void RfidWorker::sq_write_validate() {
bool result = reader.read(&type, data, data_size);
if(result) {
if(result && (write_result != WriteResult::Ok)) {
if(validate_counts > (5 * 60)) {
write_result = WriteResult::NotWritable;
}
if(type == key.get_type()) {
if(memcmp(data, key.get_data(), key.get_type_data_count()) == 0) {
write_result = WriteResult::Ok;
@ -99,10 +122,6 @@ void RfidWorker::sq_write_validate() {
} else {
validate_counts++;
}
if(validate_counts > 5) {
write_result = WriteResult::NotWritable;
}
};
}

View File

@ -13,6 +13,8 @@ public:
void start_read();
bool read();
bool detect();
bool any_read();
void stop_read();
enum class WriteResult : uint8_t {

View File

@ -2,6 +2,7 @@
#include <api-hal.h>
#include "protocols/protocol-emmarin.h"
#include "protocols/protocol-hid-h10301.h"
#include "protocols/protocol-indala-40134.h"
extern COMP_HandleTypeDef hcomp1;
@ -115,7 +116,7 @@ void RfidWriter::write_em(const uint8_t em_data[5]) {
ProtocolEMMarin em_card;
uint64_t em_encoded_data;
em_card.encode(em_data, 5, reinterpret_cast<uint8_t*>(&em_encoded_data), sizeof(uint64_t));
const uint32_t em_config_block_data = 0b01100000000101001000000001000000;
const uint32_t em_config_block_data = 0b00000000000101001000000001000000;
__disable_irq();
write_block(0, 0, false, em_config_block_data);
@ -140,3 +141,19 @@ void RfidWriter::write_hid(const uint8_t hid_data[3]) {
write_reset();
__enable_irq();
}
void RfidWriter::write_indala(const uint8_t indala_data[3]) {
ProtocolIndala40134 indala_card;
uint32_t card_data[2];
indala_card.encode(
indala_data, 3, reinterpret_cast<uint8_t*>(&card_data), sizeof(card_data) * 2);
const uint32_t indala_config_block_data = 0b00000000000010000001000001000000;
__disable_irq();
write_block(0, 0, false, indala_config_block_data);
write_block(0, 1, false, card_data[0]);
write_block(0, 2, false, card_data[1]);
write_reset();
__enable_irq();
}

View File

@ -9,6 +9,7 @@ public:
void stop();
void write_em(const uint8_t em_data[5]);
void write_hid(const uint8_t hid_data[3]);
void write_indala(const uint8_t indala_data[3]);
private:
void write_gap(uint32_t gap_time);

View File

@ -17,7 +17,7 @@
#include "scene/lfrfid-app-scene-delete-success.h"
#include <file-worker-cpp.h>
#include <path.h>
#include <lib/toolbox/path.h>
const char* LfRfidApp::app_folder = "/any/lfrfid";
const char* LfRfidApp::app_extension = ".rfid";
@ -27,9 +27,6 @@ LfRfidApp::LfRfidApp()
, notification{"notification"}
, text_store(40) {
api_hal_power_insomnia_enter();
// we need random
srand(DWT->CYCCNT);
}
LfRfidApp::~LfRfidApp() {

View File

@ -2,7 +2,7 @@
#include <api-hal.h>
#include <stdarg.h>
#include <cli/cli.h>
#include <args.h>
#include <lib/toolbox/args.h>
#include "helpers/rfid-reader.h"
#include "helpers/rfid-timer-emulator.h"
@ -46,7 +46,7 @@ bool lfrfid_cli_get_key_type(string_t data, LfrfidKeyType* type) {
void lfrfid_cli_read(Cli* cli) {
RfidReader reader;
reader.start(RfidReader::Type::Normal);
reader.start();
static const uint8_t data_size = LFRFID_KEY_SIZE;
uint8_t data[data_size] = {0};

View File

@ -55,6 +55,7 @@ void LfRfidAppSceneReadSuccess::on_enter(LfRfidApp* app, bool need_restore) {
string_get_cstr(string[2]), 68, 47, AlignLeft, AlignBottom, FontSecondary);
break;
case LfrfidKeyType::KeyH10301:
case LfrfidKeyType::KeyI40134:
line_1_text->set_text("HEX:", 65, 23, AlignRight, AlignBottom, FontSecondary);
line_2_text->set_text("FC:", 65, 35, AlignRight, AlignBottom, FontSecondary);
line_3_text->set_text("Card:", 65, 47, AlignRight, AlignBottom, FontSecondary);
@ -73,9 +74,6 @@ void LfRfidAppSceneReadSuccess::on_enter(LfRfidApp* app, bool need_restore) {
line_3_value->set_text(
string_get_cstr(string[2]), 68, 47, AlignLeft, AlignBottom, FontSecondary);
break;
case LfrfidKeyType::KeyI40134:
//TODO implement when we can read Indala
break;
}
app->view_controller.switch_to<ContainerVM>();

View File

@ -18,7 +18,13 @@ bool LfRfidAppSceneRead::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
notification_message(app->notification, &sequence_success);
app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::ReadSuccess);
} else {
notification_message(app->notification, &sequence_blink_red_10);
if(app->worker.any_read()) {
notification_message(app->notification, &sequence_blink_green_10);
} else if(app->worker.detect()) {
notification_message(app->notification, &sequence_blink_blue_10);
} else {
notification_message(app->notification, &sequence_blink_red_10);
}
}
}

View File

@ -1,11 +1,12 @@
#include "lfrfid-app-scene-save-name.h"
#include "../helpers/rfid-name-generator.h"
#include <lib/toolbox/random_name.h>
void LfRfidAppSceneSaveName::on_enter(LfRfidApp* app, bool need_restore) {
const char* key_name = app->worker.key.get_name();
if(strcmp(key_name, "") == 0) {
rfid_generate_random_name(app->text_store.text, app->text_store.text_size);
bool key_name_empty = !strcmp(key_name, "");
if(key_name_empty) {
set_random_name(app->text_store.text, app->text_store.text_size);
} else {
app->text_store.set("%s", key_name);
}
@ -14,7 +15,11 @@ void LfRfidAppSceneSaveName::on_enter(LfRfidApp* app, bool need_restore) {
text_input->set_header_text("Name the card");
text_input->set_result_callback(
save_callback, app, app->text_store.text, app->worker.key.get_name_length());
save_callback,
app,
app->text_store.text,
app->worker.key.get_name_length(),
key_name_empty);
app->view_controller.switch_to<TextInputVM>();
}

7
applications/nfc/nfc.c Normal file → Executable file
View File

@ -7,10 +7,10 @@ bool nfc_custom_event_callback(void* context, uint32_t event) {
return scene_manager_handle_custom_event(nfc->scene_manager, event);
}
bool nfc_navigation_event_callback(void* context) {
bool nfc_back_event_callback(void* context) {
furi_assert(context);
Nfc* nfc = (Nfc*)context;
return scene_manager_handle_navigation_event(nfc->scene_manager);
return scene_manager_handle_back_event(nfc->scene_manager);
}
void nfc_tick_event_callback(void* context) {
@ -28,8 +28,7 @@ Nfc* nfc_alloc() {
view_dispatcher_enable_queue(nfc->view_dispatcher);
view_dispatcher_set_event_callback_context(nfc->view_dispatcher, nfc);
view_dispatcher_set_custom_event_callback(nfc->view_dispatcher, nfc_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
nfc->view_dispatcher, nfc_navigation_event_callback);
view_dispatcher_set_navigation_event_callback(nfc->view_dispatcher, nfc_back_event_callback);
view_dispatcher_set_tick_event_callback(nfc->view_dispatcher, nfc_tick_event_callback, 100);
// Open GUI record

View File

@ -19,7 +19,6 @@ void nfc_cli_detect(Cli* cli, string_t args, void* context) {
rfalNfcDevice* dev_list;
uint8_t dev_cnt = 0;
bool cmd_exit = false;
api_hal_nfc_init();
api_hal_nfc_exit_sleep();
printf("Detecting nfc...\r\nPress Ctrl+C to abort\r\n");
while(!cmd_exit) {
@ -51,7 +50,6 @@ void nfc_cli_emulate(Cli* cli, string_t args, void* context) {
return;
}
api_hal_nfc_init();
api_hal_nfc_exit_sleep();
printf("Emulating NFC-A Type: T2T UID: CF72D440 SAK: 20 ATQA: 00/04\r\n");
printf("Press Ctrl+C to abort\r\n");

View File

@ -1,8 +1,8 @@
#include "nfc_device_i.h"
#include <file-worker.h>
#include <path.h>
#include <hex.h>
#include <lib/toolbox/path.h>
#include <lib/toolbox/hex.h>
#define NFC_DEVICE_MAX_DATA_LEN 14
@ -203,6 +203,10 @@ uint16_t nfc_device_prepare_bank_card_string(NfcDevice* dev, string_t bank_card_
for(uint8_t i = 0; i < sizeof(data->number); i++) {
string_cat_printf(bank_card_string, " %02X", data->number[i]);
}
if(data->exp_mon) {
string_cat_printf(
bank_card_string, "\nExp date: %02X/%02X", data->exp_mon, data->exp_year);
}
return string_size(bank_card_string);
}
@ -236,6 +240,14 @@ bool nfc_device_parse_bank_card_string(NfcDevice* dev, string_t bank_card_string
break;
}
parsed = true;
// Check expiration date presence
ws = string_search_str(bank_card_string, "Exp date: ");
if(ws != STRING_FAILURE) {
// strlen("Exp date: ") = 10
string_right(bank_card_string, 10);
nfc_device_read_hex(bank_card_string, &data->exp_mon, 1);
nfc_device_read_hex(bank_card_string, &data->exp_year, 1);
}
} while(0);
return parsed;

View File

@ -42,7 +42,7 @@ typedef struct {
uint16_t aid_len;
uint8_t number[8];
uint8_t exp_mon;
uint16_t exp_year;
uint8_t exp_year;
char cardholder[32];
} NfcEmvData;

View File

@ -15,9 +15,7 @@ NfcWorker* nfc_worker_alloc() {
nfc_worker->callback = NULL;
nfc_worker->context = NULL;
// Initialize rfal
nfc_worker->error = api_hal_nfc_init();
if(nfc_worker->error == ERR_NONE) {
api_hal_nfc_start_sleep();
if(!api_hal_nfc_is_busy()) {
nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
} else {
nfc_worker_change_state(nfc_worker, NfcWorkerStateBroken);
@ -35,10 +33,6 @@ NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker) {
return nfc_worker->state;
}
ReturnCode nfc_worker_get_error(NfcWorker* nfc_worker) {
return nfc_worker->error;
}
void nfc_worker_start(
NfcWorker* nfc_worker,
NfcWorkerState state,
@ -87,8 +81,8 @@ void nfc_worker_task(void* context) {
nfc_worker_read_emv_app(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateReadEMV) {
nfc_worker_read_emv(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateEmulateEMV) {
nfc_worker_emulate_emv(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateEmulateApdu) {
nfc_worker_emulate_apdu(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateReadMifareUl) {
nfc_worker_read_mifare_ul(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateEmulateMifareUl) {
@ -330,6 +324,10 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) {
result->emv_data.number,
emv_app.card_number,
sizeof(emv_app.card_number));
if(emv_app.exp_month) {
result->emv_data.exp_mon = emv_app.exp_month;
result->emv_data.exp_year = emv_app.exp_year;
}
// Notify caller and exit
if(nfc_worker->callback) {
nfc_worker->callback(nfc_worker->context);
@ -354,7 +352,7 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) {
}
}
void nfc_worker_emulate_emv(NfcWorker* nfc_worker) {
void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
ReturnCode err;
uint8_t tx_buff[255] = {};
uint16_t tx_len = 0;
@ -368,9 +366,47 @@ void nfc_worker_emulate_emv(NfcWorker* nfc_worker) {
.device = NfcDeviceNfca,
.protocol = NfcDeviceProtocolEMV,
};
// Test RX data
const uint8_t debug_rx[] = {
0xba, 0x0b, 0xba, 0xba, 0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca,
0xca, 0xfe, 0xfa, 0xce, 0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba,
0x0b, 0xba, 0xba, 0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca,
0xfe, 0xfa, 0xce, 0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba, 0x0b,
0xba, 0xba, 0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca, 0xfe,
0xfa, 0xce, 0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa,
0xbb, 0xcc, 0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba, 0x0b, 0xba,
0xba, 0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca, 0xfe, 0xfa,
0xce, 0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
0xcc, 0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba, 0x0b, 0xba, 0xba,
0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca, 0xfe, 0xfa, 0xce,
0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc,
0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba, 0x0b, 0xba, 0xba, 0x20,
0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca, 0xfe, 0xfa, 0xce, 0x14,
0x88, 0x00};
// Test TX data
const uint8_t debug_tx[] = {
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32,
0x10, 0x14, 0x88, 0x02, 0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad,
0xbe, 0xef, 0xce, 0xee, 0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12,
0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10,
0x14, 0x88, 0x02, 0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad, 0xbe,
0xef, 0xce, 0xee, 0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12, 0x34,
0x56, 0x78, 0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x14,
0x88, 0x02, 0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad, 0xbe, 0xef,
0xce, 0xee, 0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12, 0x34, 0x56,
0x78, 0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x14, 0x88,
0x02, 0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad, 0xbe, 0xef, 0xce,
0xee, 0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12, 0x34, 0x56, 0x78,
0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x14, 0x88, 0x02,
0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad, 0xbe, 0xef, 0xce, 0xee,
0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12, 0x34, 0x56, 0x78, 0x9a,
0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x14, 0x88, 0x02, 0x28,
0x00, 0x00};
while(nfc_worker->state == NfcWorkerStateEmulateEMV) {
if(api_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, 100)) {
while(nfc_worker->state == NfcWorkerStateEmulateApdu) {
if(api_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, 300)) {
FURI_LOG_I(NFC_WORKER_TAG, "POS terminal detected");
// Read data from POS terminal
err = api_hal_nfc_data_exchange(NULL, 0, &rx_buff, &rx_len, false);
@ -407,7 +443,23 @@ void nfc_worker_emulate_emv(NfcWorker* nfc_worker) {
tx_len = emv_get_proc_opt_ans(tx_buff);
err = api_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
if(err == ERR_NONE) {
FURI_LOG_I(NFC_WORKER_TAG, "Received PDOL");
FURI_LOG_I(NFC_WORKER_TAG, "Transive PDOL ANS");
} else {
FURI_LOG_E(NFC_WORKER_TAG, "Error in 4rd data exchange: Transive PDOL ANS");
api_hal_nfc_deactivate();
continue;
}
if(*rx_len != sizeof(debug_rx) || memcmp(rx_buff, debug_rx, sizeof(debug_rx))) {
FURI_LOG_E(NFC_WORKER_TAG, "Failed long message test");
} else {
FURI_LOG_I(NFC_WORKER_TAG, "Correct debug message received");
tx_len = sizeof(debug_tx);
err = api_hal_nfc_data_exchange(
(uint8_t*)debug_tx, tx_len, &rx_buff, &rx_len, false);
if(err == ERR_NONE) {
FURI_LOG_I(NFC_WORKER_TAG, "Transive Debug message");
}
}
api_hal_nfc_deactivate();
} else {

View File

@ -14,7 +14,7 @@ typedef enum {
NfcWorkerStateEmulate,
NfcWorkerStateReadEMVApp,
NfcWorkerStateReadEMV,
NfcWorkerStateEmulateEMV,
NfcWorkerStateEmulateApdu,
NfcWorkerStateField,
NfcWorkerStateReadMifareUl,
NfcWorkerStateEmulateMifareUl,
@ -28,8 +28,6 @@ NfcWorker* nfc_worker_alloc();
NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker);
ReturnCode nfc_worker_get_error(NfcWorker* nfc_worker);
void nfc_worker_free(NfcWorker* nfc_worker);
void nfc_worker_start(

View File

@ -26,7 +26,6 @@ struct NfcWorker {
void* context;
NfcWorkerState state;
ReturnCode error;
};
void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state);
@ -37,7 +36,7 @@ void nfc_worker_read_emv_app(NfcWorker* nfc_worker);
void nfc_worker_read_emv(NfcWorker* nfc_worker);
void nfc_worker_emulate_emv(NfcWorker* nfc_worker);
void nfc_worker_emulate_apdu(NfcWorker* nfc_worker);
void nfc_worker_detect(NfcWorker* nfc_worker);

View File

@ -70,8 +70,9 @@ const bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event)
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
return true;
}
} else if(event.type == SceneManagerEventTypeNavigation) {
return scene_manager_search_previous_scene(nfc->scene_manager, NfcSceneStart);
} else if(event.type == SceneManagerEventTypeBack) {
return scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneStart);
}
return false;

View File

@ -25,3 +25,4 @@ ADD_SCENE(nfc, delete_success, DeleteSuccess)
ADD_SCENE(nfc, run_emv_app_confirm, RunEmvAppConfirm)
ADD_SCENE(nfc, read_emv_data, ReadEmvData)
ADD_SCENE(nfc, read_emv_data_success, ReadEmvDataSuccess)
ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence)

3
applications/nfc/scenes/nfc_scene_delete.c Normal file → Executable file
View File

@ -75,7 +75,8 @@ const bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) {
if(nfc_device_delete(&nfc->dev)) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess);
} else {
scene_manager_search_previous_scene(nfc->scene_manager, NfcSceneStart);
scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneStart);
}
return true;
}

3
applications/nfc/scenes/nfc_scene_delete_success.c Normal file → Executable file
View File

@ -26,7 +26,8 @@ const bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent ev
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SCENE_SAVE_SUCCESS_CUSTOM_EVENT) {
return scene_manager_search_previous_scene(nfc->scene_manager, NfcSceneStart);
return scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneStart);
}
}
return false;

View File

@ -0,0 +1,38 @@
#include "../nfc_i.h"
const void nfc_scene_emulate_apdu_sequence_on_enter(void* context) {
Nfc* nfc = (Nfc*)context;
// Setup view
Popup* popup = nfc->popup;
popup_set_header(popup, "Run APDU reader", 64, 31, AlignCenter, AlignTop);
// Setup and start worker
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
nfc_worker_start(nfc->worker, NfcWorkerStateEmulateApdu, &nfc->dev.dev_data, NULL, nfc);
}
const bool nfc_scene_emulate_apdu_sequence_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context;
if(event.type == SceneManagerEventTypeTick) {
notification_message(nfc->notifications, &sequence_blink_blue_10);
return true;
}
return false;
}
const void nfc_scene_emulate_apdu_sequence_on_exit(void* context) {
Nfc* nfc = (Nfc*)context;
// Stop worker
nfc_worker_stop(nfc->worker);
// Clear view
Popup* popup = nfc->popup;
popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
popup_set_icon(popup, 0, 0, NULL);
}

View File

@ -6,7 +6,7 @@ const void nfc_scene_file_select_on_enter(void* context) {
if(nfc_file_select(&nfc->dev)) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneSavedMenu);
} else {
scene_manager_search_previous_scene(nfc->scene_manager, NfcSceneStart);
scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart);
}
}

5
applications/nfc/scenes/nfc_scene_mifare_ul_menu.c Normal file → Executable file
View File

@ -41,8 +41,9 @@ const bool nfc_scene_mifare_ul_menu_on_event(void* context, SceneManagerEvent ev
scene_manager_next_scene(nfc->scene_manager, NfcSceneNotImplemented);
return true;
}
} else if(event.type == SceneManagerEventTypeNavigation) {
return scene_manager_search_previous_scene(nfc->scene_manager, NfcSceneStart);
} else if(event.type == SceneManagerEventTypeBack) {
return scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneStart);
}
return false;

View File

@ -59,6 +59,12 @@ void nfc_scene_read_emv_data_success_on_enter(void* context) {
char sak_str[16];
snprintf(sak_str, sizeof(sak_str), "SAK: %02X", nfc_data->sak);
widget_add_string_element(nfc->widget, 121, 42, AlignRight, AlignTop, FontSecondary, sak_str);
if(emv_data->exp_mon) {
char exp_str[16];
snprintf(
exp_str, sizeof(exp_str), "Exp: %02X/%02X", emv_data->exp_mon, emv_data->exp_year);
widget_add_string_element(nfc->widget, 7, 32, AlignLeft, AlignTop, FontSecondary, exp_str);
}
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
}
@ -68,15 +74,16 @@ const bool nfc_scene_read_emv_data_success_on_event(void* context, SceneManagerE
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeLeft) {
return scene_manager_search_previous_scene(
return scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneReadEmvAppSuccess);
} else if(event.event == GuiButtonTypeRight) {
nfc->dev.format = NfcDeviceSaveFormatBankCard;
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
return true;
}
} else if(event.type == SceneManagerEventTypeNavigation) {
return scene_manager_search_previous_scene(nfc->scene_manager, NfcSceneReadEmvAppSuccess);
} else if(event.type == SceneManagerEventTypeBack) {
return scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneReadEmvAppSuccess);
}
return false;
}

View File

@ -1,4 +1,5 @@
#include "../nfc_i.h"
#include <lib/toolbox/random_name.h>
#define SCENE_SAVE_NAME_CUSTOM_EVENT (0UL)
@ -13,17 +14,21 @@ const void nfc_scene_save_name_on_enter(void* context) {
// Setup view
TextInput* text_input = nfc->text_input;
if(nfc->dev.dev_name) {
nfc_device_delete(&nfc->dev);
bool dev_name_empty = false;
if(!strcmp(nfc->dev.dev_name, "")) {
set_random_name(nfc->text_store, sizeof(nfc->text_store));
dev_name_empty = true;
} else {
nfc_text_store_set(nfc, nfc->dev.dev_name);
}
nfc_text_store_set(nfc, nfc->dev.dev_name);
text_input_set_header_text(text_input, "Name the card");
text_input_set_result_callback(
text_input,
nfc_scene_save_name_text_input_callback,
nfc,
nfc->text_store,
sizeof(nfc->text_store));
NFC_DEV_NAME_MAX_LEN,
dev_name_empty);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput);
}
@ -32,12 +37,16 @@ const bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event)
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SCENE_SAVE_NAME_CUSTOM_EVENT) {
if(nfc->dev.dev_name) {
nfc_device_delete(&nfc->dev);
}
memcpy(&nfc->dev.dev_name, nfc->text_store, strlen(nfc->text_store));
if(nfc_device_save(&nfc->dev, nfc->text_store)) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess);
return true;
} else {
return scene_manager_search_previous_scene(nfc->scene_manager, NfcSceneStart);
return scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneStart);
}
}
}
@ -49,4 +58,5 @@ const void nfc_scene_save_name_on_exit(void* context) {
// Clear view
text_input_set_header_text(nfc->text_input, NULL);
text_input_set_result_callback(nfc->text_input, NULL, NULL, NULL, 0, false);
}

View File

@ -23,13 +23,23 @@ const void nfc_scene_save_success_on_enter(void* context) {
const bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SCENE_SAVE_SUCCESS_CUSTOM_EVENT) {
return scene_manager_search_previous_scene(nfc->scene_manager, NfcSceneStart);
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneCardMenu)) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneCardMenu);
} else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) {
consumed = scene_manager_search_and_switch_to_another_scene(
nfc->scene_manager, NfcSceneFileSelect);
} else {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneStart);
}
}
}
return false;
return consumed;
}
const void nfc_scene_save_success_on_exit(void* context) {

View File

@ -5,6 +5,7 @@ enum SubmenuIndex {
SubmenuIndexRunScript,
SubmenuIndexSaved,
SubmenuIndexAddManualy,
SubmenuIndexDebug,
};
void nfc_scene_start_submenu_callback(void* context, uint32_t index) {
@ -29,6 +30,7 @@ const void nfc_scene_start_on_enter(void* context) {
submenu, "Saved cards", SubmenuIndexSaved, nfc_scene_start_submenu_callback, nfc);
submenu_add_item(
submenu, "Add manually", SubmenuIndexAddManualy, nfc_scene_start_submenu_callback, nfc);
submenu_add_item(submenu, "Debug", SubmenuIndexDebug, nfc_scene_start_submenu_callback, nfc);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneStart));
@ -38,29 +40,34 @@ const void nfc_scene_start_on_enter(void* context) {
const bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexRead) {
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexRead);
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCard);
return true;
consumed = true;
} else if(event.event == SubmenuIndexRunScript) {
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneStart, SubmenuIndexRunScript);
scene_manager_next_scene(nfc->scene_manager, NfcSceneScriptsMenu);
return true;
consumed = true;
} else if(event.event == SubmenuIndexSaved) {
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexSaved);
scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect);
return true;
consumed = true;
} else if(event.event == SubmenuIndexAddManualy) {
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneStart, SubmenuIndexAddManualy);
scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType);
return true;
consumed = true;
} else if(event.event == SubmenuIndexDebug) {
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug);
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateApduSequence);
consumed = true;
}
}
return false;
return consumed;
}
const void nfc_scene_start_on_exit(void* context) {

View File

@ -49,10 +49,10 @@ void bank_card_set_number(BankCard* bank_card, uint8_t* number) {
string_clear(num_str);
}
void bank_card_set_exp_date(BankCard* bank_card, uint8_t mon, uint16_t year) {
void bank_card_set_exp_date(BankCard* bank_card, uint8_t mon, uint8_t year) {
furi_assert(bank_card);
char exp_date_str[16];
snprintf(exp_date_str, sizeof(exp_date_str), "Exp: %02d/%02d", mon, year % 100);
snprintf(exp_date_str, sizeof(exp_date_str), "Exp: %02X/%02X", mon, year);
widget_add_string_element(
bank_card->widget, 122, 54, AlignRight, AlignBottom, FontSecondary, exp_date_str);
}

View File

@ -18,6 +18,6 @@ void bank_card_set_name(BankCard* bank_card, char* name);
void bank_card_set_number(BankCard* bank_card, uint8_t* number);
void bank_card_set_exp_date(BankCard* bank_card, uint8_t mon, uint16_t year);
void bank_card_set_exp_date(BankCard* bank_card, uint8_t mon, uint8_t year);
void bank_card_set_cardholder_name(BankCard* bank_card, char* name);

View File

@ -74,7 +74,7 @@ uint8_t float_value_index(const float value, const float values[], uint8_t value
const float epsilon = 0.01f;
float last_value = values[0];
uint8_t index = 0;
for(uint8_t i = 1; i < values_count; i++) {
for(uint8_t i = 0; i < values_count; i++) {
if((value >= last_value - epsilon) && (value <= values[i] + epsilon)) {
index = i;
break;
@ -85,9 +85,9 @@ uint8_t float_value_index(const float value, const float values[], uint8_t value
}
uint8_t uint32_value_index(const uint32_t value, const uint32_t values[], uint8_t values_count) {
float last_value = values[0];
int64_t last_value = INT64_MIN;
uint8_t index = 0;
for(uint8_t i = 1; i < values_count; i++) {
for(uint8_t i = 0; i < values_count; i++) {
if((value >= last_value) && (value <= values[i])) {
index = i;
break;

View File

@ -0,0 +1,33 @@
#include <furi.h>
#include <api-hal.h>
#include <notification/notification-messages.h>
const NotificationMessage message_green_110 = {
.type = NotificationMessageTypeLedGreen,
.data.led.value = 110,
};
static const NotificationSequence sequence_overconsumption = {
&message_green_110,
&message_red_255,
&message_delay_100,
NULL,
};
int32_t power_observer(void* p) {
NotificationApp* notifications = furi_record_open("notification");
const float overconsumption_limit = 0.03f;
while(true) {
float current = -api_hal_power_get_battery_current(ApiHalPowerICFuelGauge);
if(current >= overconsumption_limit) {
notification_message_block(notifications, &sequence_overconsumption);
}
delay(1000);
}
return 0;
}

View File

@ -43,6 +43,16 @@ void power_cli_otg(Cli* cli, string_t args, void* context) {
}
}
void power_cli_ext(Cli* cli, string_t args, void* context) {
if(!string_cmp(args, "0")) {
api_hal_power_disable_external_3_3v();
} else if(!string_cmp(args, "1")) {
api_hal_power_enable_external_3_3v();
} else {
cli_print_usage("power_ext", "<1|0>", string_get_cstr(args));
}
}
void power_cli_init(Cli* cli, Power* power) {
cli_add_command(cli, "poweroff", CliCommandFlagParallelSafe, power_cli_poweroff, power);
cli_add_command(cli, "reboot", CliCommandFlagParallelSafe, power_cli_reboot, power);
@ -51,4 +61,5 @@ void power_cli_init(Cli* cli, Power* power) {
cli_add_command(cli, "dfu", CliCommandFlagParallelSafe, power_cli_dfu, power);
cli_add_command(cli, "power_info", CliCommandFlagParallelSafe, power_cli_info, power);
cli_add_command(cli, "power_otg", CliCommandFlagParallelSafe, power_cli_otg, power);
cli_add_command(cli, "power_ext", CliCommandFlagParallelSafe, power_cli_ext, power);
}

View File

@ -40,12 +40,13 @@ bool storage_settings_scene_unmounted_on_event(void* context, SceneManagerEvent
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DialogExResultLeft:
consumed =
scene_manager_search_previous_scene(app->scene_manager, StorageSettingsStart);
consumed = scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, StorageSettingsStart);
break;
}
} else if(event.type == SceneManagerEventTypeNavigation) {
consumed = scene_manager_search_previous_scene(app->scene_manager, StorageSettingsStart);
} else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, StorageSettingsStart);
}
return consumed;

View File

@ -60,12 +60,13 @@ bool storage_settings_scene_formatting_on_event(void* context, SceneManagerEvent
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DialogExResultLeft:
consumed =
scene_manager_search_previous_scene(app->scene_manager, StorageSettingsStart);
consumed = scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, StorageSettingsStart);
break;
}
} else if(event.type == SceneManagerEventTypeNavigation) {
consumed = scene_manager_search_previous_scene(app->scene_manager, StorageSettingsStart);
} else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, StorageSettingsStart);
}
return consumed;

View File

@ -6,10 +6,10 @@ static bool storage_settings_custom_event_callback(void* context, uint32_t event
return scene_manager_handle_custom_event(app->scene_manager, event);
}
static bool storage_settings_navigation_event_callback(void* context) {
static bool storage_settings_back_event_callback(void* context) {
furi_assert(context);
StorageSettings* app = context;
return scene_manager_handle_navigation_event(app->scene_manager);
return scene_manager_handle_back_event(app->scene_manager);
}
static StorageSettings* storage_settings_alloc() {
@ -29,7 +29,7 @@ static StorageSettings* storage_settings_alloc() {
view_dispatcher_set_custom_event_callback(
app->view_dispatcher, storage_settings_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, storage_settings_navigation_event_callback);
app->view_dispatcher, storage_settings_back_event_callback);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);

View File

@ -1,6 +1,6 @@
#include <furi.h>
#include <cli/cli.h>
#include <args.h>
#include <lib/toolbox/args.h>
#include <storage/storage.h>
#include <storage/storage-sd-api.h>
#include <api-hal-version.h>
@ -30,6 +30,7 @@ void storage_cli_print_usage() {
"\twrite\t - read data from cli and append it to file, <args> should contain how many bytes you want to write\r\n");
printf("\tcopy\t - copy file to new file, <args> must contain new path\r\n");
printf("\trename\t - move file to new file, <args> must contain new path\r\n");
printf("\tmkdir\t - creates a new directory\r\n");
};
void storage_cli_print_error(FS_Error error) {
@ -285,6 +286,17 @@ void storage_cli_rename(Cli* cli, string_t old_path, string_t args) {
furi_record_close("storage");
}
void storage_cli_mkdir(Cli* cli, string_t path) {
Storage* api = furi_record_open("storage");
FS_Error error = storage_common_mkdir(api, string_get_cstr(path));
if(error != FSE_OK) {
storage_cli_print_error(error);
}
furi_record_close("storage");
}
void storage_cli(Cli* cli, string_t args, void* context) {
string_t cmd;
string_t path;
@ -342,6 +354,11 @@ void storage_cli(Cli* cli, string_t args, void* context) {
break;
}
if(string_cmp_str(cmd, "mkdir") == 0) {
storage_cli_mkdir(cli, path);
break;
}
storage_cli_print_usage();
} while(false);

File diff suppressed because one or more lines are too long

View File

@ -10,67 +10,67 @@ extern const Icon A_MDWRB_32x32;
extern const Icon A_MDWR_32x32;
extern const Icon A_WatchingTV_128x64;
extern const Icon A_Wink_128x64;
extern const Icon I_125_10px;
extern const Icon I_ble_10px;
extern const Icon I_dir_10px;
extern const Icon I_ibutt_10px;
extern const Icon I_ir_10px;
extern const Icon I_Nfc_10px;
extern const Icon I_sub1_10px;
extern const Icon I_ir_10px;
extern const Icon I_ibutt_10px;
extern const Icon I_unknown_10px;
extern const Icon I_ButtonCenter_7x7;
extern const Icon I_ButtonLeftSmall_3x5;
extern const Icon I_ButtonLeft_4x7;
extern const Icon I_ble_10px;
extern const Icon I_125_10px;
extern const Icon I_ButtonRightSmall_3x5;
extern const Icon I_ButtonLeft_4x7;
extern const Icon I_ButtonLeftSmall_3x5;
extern const Icon I_ButtonRight_4x7;
extern const Icon I_BigBurger_24x24;
extern const Icon I_ButtonCenter_7x7;
extern const Icon I_FX_SittingB_40x27;
extern const Icon I_BigGames_24x24;
extern const Icon I_BigProfile_24x24;
extern const Icon I_DolphinFirstStart0_70x53;
extern const Icon I_DolphinFirstStart1_59x53;
extern const Icon I_DolphinFirstStart2_59x51;
extern const Icon I_DolphinFirstStart3_57x48;
extern const Icon I_DolphinFirstStart4_67x53;
extern const Icon I_DolphinFirstStart5_45x53;
extern const Icon I_DolphinFirstStart6_58x54;
extern const Icon I_DolphinFirstStart7_61x51;
extern const Icon I_DolphinFirstStart8_56x51;
extern const Icon I_DolphinOkay_41x43;
extern const Icon I_DolphinFirstStart4_67x53;
extern const Icon I_DolphinFirstStart2_59x51;
extern const Icon I_DolphinFirstStart5_54x49;
extern const Icon I_DolphinFirstStart0_70x53;
extern const Icon I_DolphinFirstStart6_58x54;
extern const Icon I_DolphinFirstStart1_59x53;
extern const Icon I_DolphinFirstStart8_56x51;
extern const Icon I_DolphinFirstStart7_61x51;
extern const Icon I_Flipper_young_80x60;
extern const Icon I_BigBurger_24x24;
extern const Icon I_FX_Bang_32x6;
extern const Icon I_FX_SittingB_40x27;
extern const Icon I_DoorLeft_70x55;
extern const Icon I_DolphinFirstStart3_57x48;
extern const Icon I_PassportBottom_128x17;
extern const Icon I_DoorLeft_8x56;
extern const Icon I_DoorLocked_10x56;
extern const Icon I_DoorRight_70x55;
extern const Icon I_DoorRight_8x56;
extern const Icon I_LockPopup_100x49;
extern const Icon I_PassportBottom_128x17;
extern const Icon I_DoorLeft_70x55;
extern const Icon I_PassportLeft_6x47;
extern const Icon I_Back_15x10;
extern const Icon I_DoorRight_70x55;
extern const Icon I_LockPopup_100x49;
extern const Icon I_Mute_25x27;
extern const Icon I_IrdaArrowUp_4x8;
extern const Icon I_Up_hvr_25x27;
extern const Icon I_Mute_hvr_25x27;
extern const Icon I_Vol_down_25x27;
extern const Icon I_Down_25x27;
extern const Icon I_Power_hvr_25x27;
extern const Icon I_IrdaLearnShort_128x31;
extern const Icon I_IrdaArrowDown_4x8;
extern const Icon I_Vol_down_hvr_25x27;
extern const Icon I_IrdaLearn_128x64;
extern const Icon I_Down_hvr_25x27;
extern const Icon I_Fill_marker_7x7;
extern const Icon I_IrdaArrowDown_4x8;
extern const Icon I_IrdaArrowUp_4x8;
extern const Icon I_IrdaLearnShort_128x31;
extern const Icon I_IrdaLearn_128x64;
extern const Icon I_IrdaSendShort_128x34;
extern const Icon I_IrdaSend_128x64;
extern const Icon I_Mute_25x27;
extern const Icon I_Mute_hvr_25x27;
extern const Icon I_Power_25x27;
extern const Icon I_Power_hvr_25x27;
extern const Icon I_Up_25x27;
extern const Icon I_Up_hvr_25x27;
extern const Icon I_Vol_down_25x27;
extern const Icon I_Vol_down_hvr_25x27;
extern const Icon I_Vol_up_25x27;
extern const Icon I_Up_25x27;
extern const Icon I_Back_15x10;
extern const Icon I_IrdaSend_128x64;
extern const Icon I_IrdaSendShort_128x34;
extern const Icon I_Vol_up_hvr_25x27;
extern const Icon I_KeyBackspaceSelected_16x9;
extern const Icon I_KeyBackspace_16x9;
extern const Icon I_KeySaveSelected_24x11;
extern const Icon I_KeySave_24x11;
extern const Icon I_KeyBackspaceSelected_16x9;
extern const Icon I_KeySaveSelected_24x11;
extern const Icon I_KeyBackspace_16x9;
extern const Icon A_125khz_14;
extern const Icon A_Bluetooth_14;
extern const Icon A_FileManager_14;
@ -86,61 +86,141 @@ extern const Icon A_Sub1ghz_14;
extern const Icon A_Tamagotchi_14;
extern const Icon A_U2F_14;
extern const Icon A_iButton_14;
extern const Icon I_EMV_Chip_14x11;
extern const Icon I_Medium_chip_22x21;
extern const Icon I_passport_bad1_43x45;
extern const Icon I_passport_bad2_43x45;
extern const Icon I_passport_bad3_43x45;
extern const Icon I_EMV_Chip_14x11;
extern const Icon I_passport_happy1_43x45;
extern const Icon I_passport_happy2_43x45;
extern const Icon I_passport_happy3_43x45;
extern const Icon I_passport_okay1_43x45;
extern const Icon I_passport_bad3_43x45;
extern const Icon I_passport_okay2_43x45;
extern const Icon I_passport_bad2_43x45;
extern const Icon I_passport_okay3_43x45;
extern const Icon I_BatteryBody_52x28;
extern const Icon I_Battery_16x16;
extern const Icon I_passport_bad1_43x45;
extern const Icon I_passport_happy3_43x45;
extern const Icon I_passport_happy2_43x45;
extern const Icon I_passport_okay1_43x45;
extern const Icon I_Health_16x16;
extern const Icon I_FaceCharging_29x14;
extern const Icon I_FaceConfused_29x14;
extern const Icon I_BatteryBody_52x28;
extern const Icon I_Voltage_16x16;
extern const Icon I_Temperature_16x16;
extern const Icon I_FaceNopower_29x14;
extern const Icon I_FaceNormal_29x14;
extern const Icon I_Health_16x16;
extern const Icon I_Temperature_16x16;
extern const Icon I_Voltage_16x16;
extern const Icon I_RFIDBigChip_37x36;
extern const Icon I_RFIDDolphinReceive_97x61;
extern const Icon I_RFIDDolphinSend_97x61;
extern const Icon I_Battery_16x16;
extern const Icon I_FaceConfused_29x14;
extern const Icon I_RFIDDolphinSuccess_108x57;
extern const Icon I_SDError_43x35;
extern const Icon I_RFIDBigChip_37x36;
extern const Icon I_RFIDDolphinSend_97x61;
extern const Icon I_RFIDDolphinReceive_97x61;
extern const Icon I_SDQuestion_35x43;
extern const Icon I_Home_painting_17x20;
extern const Icon I_PC_22x29;
extern const Icon I_Sofa_40x13;
extern const Icon I_TV_20x20;
extern const Icon I_TV_20x24;
extern const Icon I_WalkL1_32x32;
extern const Icon I_WalkL2_32x32;
extern const Icon I_WalkLB1_32x32;
extern const Icon I_WalkLB2_32x32;
extern const Icon I_WalkR1_32x32;
extern const Icon I_WalkR2_32x32;
extern const Icon I_WalkRB1_32x32;
extern const Icon I_WalkRB2_32x32;
extern const Icon I_Background_128x11;
extern const Icon I_Background_128x8;
extern const Icon I_SDError_43x35;
extern const Icon I_Console_74x67_4;
extern const Icon I_Console_74x67_5;
extern const Icon I_Console_74x67_7;
extern const Icon I_Console_74x67_6;
extern const Icon I_Console_74x67_2;
extern const Icon I_Console_74x67_3;
extern const Icon I_Console_74x67_1;
extern const Icon I_Console_74x67_0;
extern const Icon I_Console_74x67_8;
extern const Icon I_food8_61x98;
extern const Icon I_food5_61x98;
extern const Icon I_food3_61x98;
extern const Icon I_food9_61x98;
extern const Icon I_food12_61x98;
extern const Icon I_food4_61x98;
extern const Icon I_food2_61x98;
extern const Icon I_food7_61x98;
extern const Icon I_food11_61x98;
extern const Icon I_food1_61x98;
extern const Icon I_food6_61x98;
extern const Icon I_food10_61x98;
extern const Icon I_rightdown2_73x61;
extern const Icon I_black_upright2_73x61;
extern const Icon I_black_leftup1_73x61;
extern const Icon I_black_upleft1_73x61;
extern const Icon I_black_down2_73x61;
extern const Icon I_downleft3_73x61;
extern const Icon I_down2_73x61;
extern const Icon I_black_downright2_73x61;
extern const Icon I_black_downleft2_73x61;
extern const Icon I_up2_73x61;
extern const Icon I_right1_73x61;
extern const Icon I_black_up1_73x61;
extern const Icon I_upright1_73x61;
extern const Icon I_black_rightleft2_73x61;
extern const Icon I_black_right3_73x61;
extern const Icon I_upleft2_73x61;
extern const Icon I_black_downup3_73x61;
extern const Icon I_black_updown3_73x61;
extern const Icon I_leftup2_73x61;
extern const Icon I_leftdown2_73x61;
extern const Icon I_downup1_73x61;
extern const Icon I_updown1_73x61;
extern const Icon I_rightup1_73x61;
extern const Icon I_black_rightdown1_73x61;
extern const Icon I_downleft2_73x61;
extern const Icon I_downright1_73x61;
extern const Icon I_black_downright3_73x61;
extern const Icon I_black_downleft3_73x61;
extern const Icon I_rightleft1_73x61;
extern const Icon I_black_right2_73x61;
extern const Icon I_black_rightup2_73x61;
extern const Icon I_black_downup2_73x61;
extern const Icon I_black_updown2_73x61;
extern const Icon I_black_left1_73x61;
extern const Icon I_black_leftdown2_73x61;
extern const Icon I_left1_73x61;
extern const Icon I_rightup2_73x61;
extern const Icon I_black_rightdown2_73x61;
extern const Icon I_downup2_73x61;
extern const Icon I_updown2_73x61;
extern const Icon I_right3_73x61;
extern const Icon I_downright2_73x61;
extern const Icon I_downleft1_73x61;
extern const Icon I_black_rightup1_73x61;
extern const Icon I_black_right1_73x61;
extern const Icon I_rightleft2_73x61;
extern const Icon I_left2_73x61;
extern const Icon I_black_left2_73x61;
extern const Icon I_black_downup1_73x61;
extern const Icon I_black_updown1_73x61;
extern const Icon I_black_leftdown1_73x61;
extern const Icon I_downup3_73x61;
extern const Icon I_updown3_73x61;
extern const Icon I_black_leftup2_73x61;
extern const Icon I_black_upright1_73x61;
extern const Icon I_rightdown1_73x61;
extern const Icon I_right2_73x61;
extern const Icon I_black_downleft1_73x61;
extern const Icon I_down1_73x61;
extern const Icon I_black_downright1_73x61;
extern const Icon I_up1_73x61;
extern const Icon I_downright3_73x61;
extern const Icon I_black_down1_73x61;
extern const Icon I_black_upleft2_73x61;
extern const Icon I_upleft1_73x61;
extern const Icon I_black_rightleft1_73x61;
extern const Icon I_black_up2_73x61;
extern const Icon I_upright2_73x61;
extern const Icon I_leftdown1_73x61;
extern const Icon I_left3_73x61;
extern const Icon I_leftup1_73x61;
extern const Icon I_black_left3_73x61;
extern const Icon I_BadUsb_9x8;
extern const Icon I_Battery_19x8;
extern const Icon I_Battery_26x8;
extern const Icon I_Bluetooth_5x8;
extern const Icon I_Lock_8x8;
extern const Icon I_PlaceholderL_11x13;
extern const Icon I_PlaceholderR_30x13;
extern const Icon I_SDcardFail_11x8;
extern const Icon I_Background_128x8;
extern const Icon I_Lock_8x8;
extern const Icon I_Battery_26x8;
extern const Icon I_PlaceholderL_11x13;
extern const Icon I_Battery_19x8;
extern const Icon I_SDcardMounted_11x8;
extern const Icon I_SDcardFail_11x8;
extern const Icon I_USBConnected_15x8;
extern const Icon I_DolphinExcited_64x63;
extern const Icon I_Bluetooth_5x8;
extern const Icon I_Background_128x11;
extern const Icon I_DolphinMafia_115x62;
extern const Icon I_DolphinNice_96x59;
extern const Icon I_DolphinWait_61x59;
extern const Icon I_DolphinExcited_64x63;
extern const Icon I_iButtonDolphinSuccess_109x60;
extern const Icon I_iButtonDolphinVerySuccess_108x52;
extern const Icon I_iButtonKey_49x44;
extern const Icon I_DolphinNice_96x59;
extern const Icon I_DolphinWait_61x59;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 522 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 520 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

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