Merge branch 'dev' into Barcode-Generator

This commit is contained in:
MX 2022-11-10 07:54:26 +03:00 committed by GitHub
commit dd601dd940
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
298 changed files with 5419 additions and 874 deletions

2
.gitignore vendored
View File

@ -55,3 +55,5 @@ openocd.log
# PVS Studio temporary files
.PVS-Studio/
PVS-Studio.log
.gdbinit

View File

@ -5,6 +5,7 @@
//-V:BPTREE_DEF2:779,1086,557,773,512
//-V:DICT_DEF2:779,524,776,760,1044,1001,729,590,568,747,685
//-V:ALGO_DEF:1048,747,1044
//-V:TUPLE_DEF2:524,590,1001,760
# Non-severe malloc/null pointer deref warnings
//-V::522:2,3

View File

@ -38,6 +38,7 @@
"postAttachCommands": [
// "compare-sections",
"source debug/flipperapps.py",
"fap-set-debug-elf-root build/latest/.extapps",
// "source debug/FreeRTOS/FreeRTOS.py",
// "svd_load debug/STM32WB55_CM4.svd"
]
@ -59,6 +60,7 @@
"set confirm off",
"set mem inaccessible-by-default off",
"source debug/flipperapps.py",
"fap-set-debug-elf-root build/latest/.extapps",
// "compare-sections",
]
// "showDevDebugOutput": "raw",
@ -76,6 +78,7 @@
"rtos": "FreeRTOS",
"postAttachCommands": [
"source debug/flipperapps.py",
"fap-set-debug-elf-root build/latest/.extapps",
]
// "showDevDebugOutput": "raw",
},
@ -95,6 +98,7 @@
],
"postAttachCommands": [
"source debug/flipperapps.py",
"fap-set-debug-elf-root build/latest/.extapps",
],
// "showDevDebugOutput": "raw",
},

View File

@ -1,6 +1,12 @@
### New changes
* Fix crash when furi_halt is called (was found when you try to power off flipper with usb cable connected)
* Fix bug when NFC keys was removed during scan
* OFW: **NFC magic cards support (gen1a) (ability to write UID)**
* Plugins -> PR: FlappyBird - draw bird via icon animation (by @an4tur0r | PR #149)
* Plugins: DHT Temp montior - Fix DHT22 timeout bug and other fixes by @quen0n
* Infrared: Universal remote assets update (by @Amec0e)
* OFW: SubGhz: fix incorrect response in rpc mode. Code cleanup
* OFW: Storage: tree timestamps
* OFW: Dolphin: add L1_Mods_128x64 animation
* OFW: Run Bad USB immediately after connection
#### [🎲 Download latest extra apps pack](https://download-directory.github.io/?url=https://github.com/xMasterX/unleashed-extra-pack/tree/main/apps)

View File

@ -56,8 +56,8 @@ Also check changelog in releases for latest updates!
- Keeloq: Normstahl
- CAME Atomo
- Nice Flor S
- FAAC SLH (Spa) [External seed calculation required (For info conatct me in Discord: Nano#8998)]
- BFT Mitto [External seed calculation required (For info conatct me in Discord: Nano#8998)]
- FAAC SLH (Spa) [External seed calculation required (For info contact me in Discord: Nano#8998)]
- BFT Mitto [External seed calculation required (For info contact me in Discord: Nano#8998)]
- Security+ v1 & v2
- Star Line (saving only)
@ -91,21 +91,24 @@ Also check changelog in releases for latest updates!
- Metronome [(by panki27)](https://github.com/panki27/Metronome)
- DTMF Dolphin [(by litui)](https://github.com/litui/dtmf_dolphin)
- **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator)
- GPS [(By ezod)](https://github.com/ezod/flipperzero-gps) works with module `NMEA 0183` via UART (13TX, 14RX, GND pins on Flipper)
- i2c Tools [(By NaejEL)](https://github.com/NaejEL/flipperzero-i2ctools) - C0 -> SCL / C1 -> SDA / GND -> GND | 3v3 logic levels only!
- Temperature Sensor Plugin - HTU21D / SI7021 [(By Mywk)](https://github.com/Mywk/FlipperTemperatureSensor) - [How to Connect](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/applications/plugins/temperature_sensor/Readme.md)
- GPS [(by ezod)](https://github.com/ezod/flipperzero-gps) works with module `NMEA 0183` via UART (13TX, 14RX, GND pins on Flipper)
- i2c Tools [(by NaejEL)](https://github.com/NaejEL/flipperzero-i2ctools) - C0 -> SCL / C1 -> SDA / GND -> GND | 3v3 logic levels only!
- Temperature Sensor Plugin - HTU21D / SI7021 [(by Mywk)](https://github.com/Mywk/FlipperTemperatureSensor) - [How to Connect](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/applications/plugins/temperature_sensor/Readme.md)
- HC-SR04 Distance sensor - Ported and modified by @xMasterX [(original by Sanqui)](https://github.com/Sanqui/flipperzero-firmware/tree/hc_sr04) - How to connect -> (5V -> VCC) / (GND -> GND) / (13|TX -> Trig) / (14|RX -> Echo)
- Morse Code [(by wh00hw)](https://github.com/wh00hw/MorseCodeFAP)
- AM2320/AM2321 Temp. Sensor plugin [(by xMasterX)](https://github.com/xMasterX/AM2320_Flipper_Plugin) - [How to Connect](https://github.com/xMasterX/AM2320_Flipper_Plugin)
- DHT11/22 Temp. Sensor Monitor [(by quen0n)](https://github.com/quen0n/FipperZero-DHT-Monitor) - How to connect -> (5V -> VCC) / (GND -> GND) / (Selected Pin -> out)
Games:
- DOOM (fixed) [(By p4nic4ttack)](https://github.com/p4nic4ttack/doom-flipper-zero/)
- DOOM (fixed) [(by p4nic4ttack)](https://github.com/p4nic4ttack/doom-flipper-zero/)
- Zombiez [(Reworked By DevMilanIan)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/240) [(Original By Dooskington)](https://github.com/Dooskington/flipperzero-zombiez)
- Flappy Bird [(By DroomOne)](https://github.com/DroomOne/flipperzero-firmware/tree/dev/applications/flappy_bird)
- Flappy Bird [(by DroomOne)](https://github.com/DroomOne/flipperzero-firmware/tree/dev/applications/flappy_bird)
- Arkanoid (refactored by xMasterX) [(by gotnull)](https://github.com/gotnull/flipperzero-firmware-wPlugins)
- Tic Tac Toe (refactored by xMasterX) [(by gotnull)](https://github.com/gotnull/flipperzero-firmware-wPlugins)
- Tetris (with fixes) [(by jeffplang)](https://github.com/jeffplang/flipperzero-firmware/tree/tetris_game/applications/tetris_game)
- Minesweeper [(by panki27)](https://github.com/panki27/minesweeper)
- Heap Defence (aka Stack Attack) - Ported to latest firmware by @xMasterX - [(original by wquinoa & Vedmein)](https://github.com/Vedmein/flipperzero-firmware/tree/hd/svisto-perdelki)
- Game15 [(by x27)](https://github.com/x27/flipperzero-game15)
### Other changes
@ -115,6 +118,7 @@ Games:
- SubGHz -> Save last used frequency and modulation [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/77)
- SubGHz -> Press OK in frequency analyzer to use detected frequency in Read modes [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/77)
- SubGHz -> Long press OK button in SubGHz Frequency analyzer to switch to Read menu [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/79)
- Lock device with pin(or regular lock if pin not set) by holding UP button on main screen [(by an4tur0r)](https://github.com/DarkFlippers/unleashed-firmware/pull/107)
# Instructions
## [- How to install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)
@ -131,7 +135,7 @@ Games:
## [- Configure Sub-GHz Remote App](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemotePlugin.md)
## [- TOTP (Authenticator) config description](https://github.com/akopachov/flipper-zero_authenticator/blob/master/.github/conf-file_description.md)
## [- TOTP (Authenticator) config description](https://github.com/akopachov/flipper-zero_authenticator/blob/master/docs/conf-file_description.md)
## [- Barcode Generator](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/BarcodeGenerator.md)

View File

@ -33,10 +33,6 @@ coreenv = SConscript(
)
SConscript("site_scons/cc.scons", exports={"ENV": coreenv})
# Store root dir in environment for certain tools
coreenv["ROOT_DIR"] = Dir(".")
# Create a separate "dist" environment and add construction envs to it
distenv = coreenv.Clone(
tools=[
@ -47,6 +43,7 @@ distenv = coreenv.Clone(
"jflash",
],
ENV=os.environ,
UPDATE_BUNDLE_DIR="dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}",
)
firmware_env = distenv.AddFwProject(
@ -144,21 +141,28 @@ distenv.Default(basic_dist)
dist_dir = distenv.GetProjetDirName()
fap_dist = [
distenv.Install(
f"#/dist/{dist_dir}/apps/debug_elf",
firmware_env["FW_EXTAPPS"]["debug"].values(),
distenv.Dir(f"#/dist/{dist_dir}/apps/debug_elf"),
list(
app_artifact.debug
for app_artifact in firmware_env["FW_EXTAPPS"].applications.values()
),
),
*(
distenv.Install(f"#/dist/{dist_dir}/apps/{dist_entry[0]}", dist_entry[1])
for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values()
distenv.Install(
f"#/dist/{dist_dir}/apps",
"#/assets/resources/apps",
),
]
Depends(fap_dist, firmware_env["FW_EXTAPPS"]["validators"].values())
Depends(
fap_dist,
list(
app_artifact.validator
for app_artifact in firmware_env["FW_EXTAPPS"].applications.values()
),
)
Alias("fap_dist", fap_dist)
# distenv.Default(fap_dist)
distenv.Depends(
firmware_env["FW_RESOURCES"], firmware_env["FW_EXTAPPS"]["resources_dist"]
)
distenv.Depends(firmware_env["FW_RESOURCES"], firmware_env["FW_EXTAPPS"].resources_dist)
# Target for bundling core2 package for qFlipper
@ -196,6 +200,9 @@ firmware_debug = distenv.PhonyTarget(
source=firmware_env["FW_ELF"],
GDBOPTS="${GDBOPTS_BASE}",
GDBREMOTE="${OPENOCD_GDB_PIPE}",
FBT_FAP_DEBUG_ELF_ROOT=firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT").replace(
"\\", "/"
),
)
distenv.Depends(firmware_debug, firmware_flash)
@ -205,6 +212,9 @@ distenv.PhonyTarget(
source=firmware_env["FW_ELF"],
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
GDBREMOTE="${BLACKMAGIC_ADDR}",
FBT_FAP_DEBUG_ELF_ROOT=firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT").replace(
"\\", "/"
),
)
# Debug alien elf
@ -213,7 +223,7 @@ distenv.PhonyTarget(
"${GDBPYCOM}",
GDBOPTS="${GDBOPTS_BASE}",
GDBREMOTE="${OPENOCD_GDB_PIPE}",
GDBPYOPTS='-ex "source debug/PyCortexMDebug/PyCortexMDebug.py" ',
GDBPYOPTS='-ex "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py" ',
)
distenv.PhonyTarget(
@ -233,13 +243,13 @@ distenv.PhonyTarget(
# Linter
distenv.PhonyTarget(
"lint",
"${PYTHON3} scripts/lint.py check ${LINT_SOURCES}",
"${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py check ${LINT_SOURCES}",
LINT_SOURCES=firmware_env["LINT_SOURCES"],
)
distenv.PhonyTarget(
"format",
"${PYTHON3} scripts/lint.py format ${LINT_SOURCES}",
"${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py format ${LINT_SOURCES}",
LINT_SOURCES=firmware_env["LINT_SOURCES"],
)
@ -280,7 +290,7 @@ distenv.PhonyTarget(
)
# Start Flipper CLI via PySerial's miniterm
distenv.PhonyTarget("cli", "${PYTHON3} scripts/serial_cli.py")
distenv.PhonyTarget("cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py")
# Find blackmagic probe

View File

@ -424,6 +424,7 @@ MU_TEST(infrared_test_decoder_mixed) {
infrared_test_run_decoder(InfraredProtocolRC5, 5);
infrared_test_run_decoder(InfraredProtocolSamsung32, 1);
infrared_test_run_decoder(InfraredProtocolSIRC, 3);
infrared_test_run_decoder(InfraredProtocolKaseikyo, 1);
}
MU_TEST(infrared_test_decoder_nec) {
@ -489,6 +490,15 @@ MU_TEST(infrared_test_encoder_rc6) {
infrared_test_run_encoder(InfraredProtocolRC6, 1);
}
MU_TEST(infrared_test_decoder_kaseikyo) {
infrared_test_run_decoder(InfraredProtocolKaseikyo, 1);
infrared_test_run_decoder(InfraredProtocolKaseikyo, 2);
infrared_test_run_decoder(InfraredProtocolKaseikyo, 3);
infrared_test_run_decoder(InfraredProtocolKaseikyo, 4);
infrared_test_run_decoder(InfraredProtocolKaseikyo, 5);
infrared_test_run_decoder(InfraredProtocolKaseikyo, 6);
}
MU_TEST(infrared_test_encoder_decoder_all) {
infrared_test_run_encoder_decoder(InfraredProtocolNEC, 1);
infrared_test_run_encoder_decoder(InfraredProtocolNECext, 1);
@ -498,6 +508,7 @@ MU_TEST(infrared_test_encoder_decoder_all) {
infrared_test_run_encoder_decoder(InfraredProtocolRC6, 1);
infrared_test_run_encoder_decoder(InfraredProtocolRC5, 1);
infrared_test_run_encoder_decoder(InfraredProtocolSIRC, 1);
infrared_test_run_encoder_decoder(InfraredProtocolKaseikyo, 1);
}
MU_TEST_SUITE(infrared_test) {
@ -515,6 +526,7 @@ MU_TEST_SUITE(infrared_test) {
MU_RUN_TEST(infrared_test_decoder_nec);
MU_RUN_TEST(infrared_test_decoder_samsung32);
MU_RUN_TEST(infrared_test_decoder_necext1);
MU_RUN_TEST(infrared_test_decoder_kaseikyo);
MU_RUN_TEST(infrared_test_decoder_mixed);
MU_RUN_TEST(infrared_test_encoder_decoder_all);
}

View File

@ -11,4 +11,5 @@ App(
stack_size=2 * 1024,
icon="A_BadUsb_14",
order=70,
fap_libs=["assets"],
)

View File

@ -86,7 +86,7 @@ static const DuckyKey ducky_keys[] = {
{"PAGEUP", HID_KEYBOARD_PAGE_UP},
{"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN},
{"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN},
{"SCROLLOCK", HID_KEYBOARD_SCROLL_LOCK},
{"SCROLLLOCK", HID_KEYBOARD_SCROLL_LOCK},
{"SPACE", HID_KEYBOARD_SPACEBAR},
{"TAB", HID_KEYBOARD_TAB},
{"MENU", HID_KEYBOARD_APPLICATION},
@ -347,10 +347,6 @@ static int32_t
furi_hal_hid_kb_release(key);
return (0);
}
if(error != NULL) {
strncpy(error, "Unknown error", error_len);
}
return SCRIPT_STATE_ERROR;
}
static bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) {
@ -533,12 +529,16 @@ static int32_t bad_usb_worker(void* context) {
} else if(worker_state == BadUsbStateNotConnected) { // State: USB not connected
uint32_t flags = furi_thread_flags_wait(
WorkerEvtEnd | WorkerEvtConnect, FuriFlagWaitAny, FuriWaitForever);
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle,
FuriFlagWaitAny,
FuriWaitForever);
furi_check((flags & FuriFlagError) == 0);
if(flags & WorkerEvtEnd) {
break;
} else if(flags & WorkerEvtConnect) {
worker_state = BadUsbStateIdle; // Ready to run
} else if(flags & WorkerEvtToggle) {
worker_state = BadUsbStateWillRun; // Will run when USB is connected
}
bad_usb->st.state = worker_state;
@ -565,6 +565,31 @@ static int32_t bad_usb_worker(void* context) {
}
bad_usb->st.state = worker_state;
} else if(worker_state == BadUsbStateWillRun) { // State: start on connection
uint32_t flags = furi_thread_flags_wait(
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle,
FuriFlagWaitAny,
FuriWaitForever);
furi_check((flags & FuriFlagError) == 0);
if(flags & WorkerEvtEnd) {
break;
} else if(flags & WorkerEvtConnect) { // Start executing script
DOLPHIN_DEED(DolphinDeedBadUsbPlayScript);
delay_val = 0;
bad_usb->buf_len = 0;
bad_usb->st.line_cur = 0;
bad_usb->defdelay = 0;
bad_usb->repeat_cnt = 0;
bad_usb->file_end = false;
storage_file_seek(script_file, 0, true);
// extra time for PC to recognize Flipper as keyboard
furi_thread_flags_wait(0, FuriFlagWaitAny, 1500);
worker_state = BadUsbStateRunning;
} else if(flags & WorkerEvtToggle) { // Cancel scheduled execution
worker_state = BadUsbStateNotConnected;
}
bad_usb->st.state = worker_state;
} else if(worker_state == BadUsbStateRunning) { // State: running
uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val);
uint32_t flags = furi_thread_flags_wait(
@ -642,7 +667,7 @@ static void bad_usb_script_set_default_keyboard_layout(BadUsbScript* bad_usb) {
BadUsbScript* bad_usb_script_open(FuriString* file_path) {
furi_assert(file_path);
BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript));
BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript)); //-V773
bad_usb->file_path = furi_string_alloc();
furi_string_set(bad_usb->file_path, file_path);
bad_usb_script_set_default_keyboard_layout(bad_usb);

View File

@ -12,6 +12,7 @@ typedef enum {
BadUsbStateInit,
BadUsbStateNotConnected,
BadUsbStateIdle,
BadUsbStateWillRun,
BadUsbStateRunning,
BadUsbStateDelay,
BadUsbStateDone,

View File

@ -45,10 +45,13 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22);
if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone)) {
if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) ||
(model->state.state == BadUsbStateNotConnected)) {
elements_button_center(canvas, "Run");
} else if((model->state.state == BadUsbStateRunning) || (model->state.state == BadUsbStateDelay)) {
elements_button_center(canvas, "Stop");
} else if(model->state.state == BadUsbStateWillRun) {
elements_button_center(canvas, "Cancel");
}
if((model->state.state == BadUsbStateNotConnected) ||
@ -61,6 +64,11 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect");
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "to USB");
} else if(model->state.state == BadUsbStateWillRun) {
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run");
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect");
} else if(model->state.state == BadUsbStateFileError) {
canvas_draw_icon(canvas, 4, 26, &I_Error_18x18);
canvas_set_font(canvas, FontPrimary);

View File

@ -216,6 +216,8 @@ int32_t clock_app(void* p) {
// Exit the plugin
processing = false;
break;
default:
break;
}
}
} /*else if(event.type == EventTypeTick) {

View File

@ -155,7 +155,7 @@ static bool fap_loader_select_app(FapLoader* loader) {
}
static FapLoader* fap_loader_alloc(const char* path) {
FapLoader* loader = malloc(sizeof(FapLoader));
FapLoader* loader = malloc(sizeof(FapLoader)); //-V773
loader->fap_path = furi_string_alloc_set(path);
loader->storage = furi_record_open(RECORD_STORAGE);
loader->dialogs = furi_record_open(RECORD_DIALOGS);

View File

@ -8,4 +8,5 @@ App(
stack_size=1 * 1024,
icon="A_GPIO_14",
order=50,
fap_libs=["assets"],
)

View File

@ -184,7 +184,7 @@ static int32_t usb_uart_worker(void* context) {
while(1) {
uint32_t events =
furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
furi_check((events & FuriFlagError) == 0);
furi_check(!(events & FuriFlagError));
if(events & WorkerEvtStop) break;
if(events & WorkerEvtRxDone) {
size_t len = furi_stream_buffer_receive(
@ -288,7 +288,7 @@ static int32_t usb_uart_tx_thread(void* context) {
while(1) {
uint32_t events =
furi_thread_flags_wait(WORKER_ALL_TX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
furi_check((events & FuriFlagError) == 0);
furi_check(!(events & FuriFlagError));
if(events & WorkerEvtTxStop) break;
if(events & WorkerEvtCdcRx) {
furi_check(furi_mutex_acquire(usb_uart->usb_mutex, FuriWaitForever) == FuriStatusOk);

View File

@ -12,6 +12,7 @@ App(
icon="A_iButton_14",
stack_size=2 * 1024,
order=60,
fap_libs=["assets"],
)
App(

View File

@ -12,6 +12,7 @@ App(
icon="A_Infrared_14",
stack_size=3 * 1024,
order=40,
fap_libs=["assets"],
)
App(

View File

@ -14,6 +14,7 @@ App(
icon="A_125khz_14",
stack_size=2 * 1024,
order=20,
fap_libs=["assets"],
)
App(

View File

@ -32,7 +32,7 @@ static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) {
}
static LfRfid* lfrfid_alloc() {
LfRfid* lfrfid = malloc(sizeof(LfRfid));
LfRfid* lfrfid = malloc(sizeof(LfRfid)); //-V773
lfrfid->storage = furi_record_open(RECORD_STORAGE);
lfrfid->dialogs = furi_record_open(RECORD_DIALOGS);

View File

@ -24,7 +24,7 @@ void nfc_scene_mf_classic_read_success_on_enter(void* context) {
widget_add_button_element(
widget, GuiButtonTypeRight, "More", nfc_scene_mf_classic_read_success_widget_callback, nfc);
FuriString* temp_str;
FuriString* temp_str = NULL;
if(furi_string_size(nfc->dev->dev_data.parsed_data)) {
temp_str = furi_string_alloc_set(nfc->dev->dev_data.parsed_data);
} else {

View File

@ -31,7 +31,7 @@ void nfc_scene_mf_ultralight_read_success_on_enter(void* context) {
nfc_scene_mf_ultralight_read_success_widget_callback,
nfc);
FuriString* temp_str;
FuriString* temp_str = NULL;
if(furi_string_size(nfc->dev->dev_data.parsed_data)) {
temp_str = furi_string_alloc_set(nfc->dev->dev_data.parsed_data);
} else {

View File

@ -170,9 +170,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
} else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) {
//CC1101 Stop RX -> Save
subghz->state_notifications = SubGhzNotificationStateIDLE;
if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) {
subghz->txrx->hopper_state = SubGhzHopperStateOFF;
}
subghz->txrx->hopper_state = SubGhzHopperStateOFF;
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
subghz_rx_end(subghz);
subghz_sleep(subghz);

View File

@ -42,9 +42,8 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {
bool result = false;
if((subghz->txrx->txrx_state == SubGhzTxRxStateSleep) &&
(state == SubGhzRpcStateLoaded)) {
subghz_blink_start(subghz);
result = subghz_tx_start(subghz, subghz->txrx->fff_data);
result = true;
if(result) subghz_blink_start(subghz);
}
rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonPress, result);
} else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) {

View File

@ -67,6 +67,8 @@ bool subghz_scene_set_seed_bft_on_event(void* context, SceneManagerEvent event)
flipper_format_write_hex(
subghz->txrx->fff_data, "Seed", seed_data, sizeof(uint32_t));
flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "BFT");
generated_protocol = true;
} else {
generated_protocol = false;

View File

@ -520,12 +520,6 @@ bool subghz_path_is_file(FuriString* path) {
}
uint32_t subghz_random_serial(void) {
static bool rand_generator_inited = false;
if(!rand_generator_inited) {
srand(DWT->CYCCNT);
rand_generator_inited = true;
}
return (uint32_t)rand();
}

View File

@ -11,4 +11,5 @@ App(
stack_size=2 * 1024,
icon="A_U2F_14",
order=80,
fap_libs=["assets"],
)

View File

@ -203,7 +203,7 @@ static int32_t u2f_hid_worker(void* context) {
WorkerEvtStop | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtRequest,
FuriFlagWaitAny,
FuriWaitForever);
furi_check((flags & FuriFlagError) == 0);
furi_check(!(flags & FuriFlagError));
if(flags & WorkerEvtStop) break;
if(flags & WorkerEvtConnect) {
u2f_set_state(u2f_hid->u2f_instance, 1);

View File

@ -1044,6 +1044,8 @@ int32_t unirfremix_app(void* p) {
unirfremix_tx_stop(app);
exit_loop = true;
break;
default:
break;
}
if(app->processing == 0) {
@ -1115,6 +1117,8 @@ int32_t unirfremix_app(void* p) {
case InputKeyBack:
exit_loop = true;
break;
default:
break;
}
if(exit_loop == true) {

View File

@ -0,0 +1,14 @@
App(
appid="am2320_temp_sensor",
name="[AM2320] Temp. Sensor",
apptype=FlipperAppType.EXTERNAL,
entry_point="am_temperature_sensor_app",
cdefines=["APP_AM_TEMPERATURE_SENSOR"],
requires=[
"gui",
],
stack_size=2 * 1024,
order=90,
fap_icon="temperature_sensor.png",
fap_category="GPIO",
)

View File

@ -0,0 +1,336 @@
/* Flipper Plugin to read the values from a AM2320/AM2321 Sensor */
/* Created by @xMasterX, original app (was used as template) by Mywk - https://github.com/Mywk */
/* Lib used as reference: https://github.com/Gozem/am2320/blob/master/am2321.c*/
#include <furi.h>
#include <furi_hal.h>
#include <furi_hal_i2c.h>
#include <math.h>
#include <gui/gui.h>
#include <input/input.h>
#include <notification/notification_messages.h>
#include <string.h>
#define TS_DEFAULT_VALUE 0xFFFF
#define AM2320_ADDRESS (0x5C << 1)
#define DATA_BUFFER_SIZE 8
// External I2C BUS
#define I2C_BUS &furi_hal_i2c_handle_external
typedef enum {
TSSInitializing,
TSSNoSensor,
TSSPendingUpdate,
} TSStatus;
typedef enum {
TSEventTypeTick,
TSEventTypeInput,
} TSEventType;
typedef struct {
TSEventType type;
InputEvent input;
} TSEvent;
extern const NotificationSequence sequence_blink_red_100;
extern const NotificationSequence sequence_blink_blue_100;
static TSStatus temperature_sensor_current_status = TSSInitializing;
// Temperature and Humidity data buffers, ready to print
char ts_data_buffer_temperature_c[DATA_BUFFER_SIZE];
char ts_data_buffer_temperature_f[DATA_BUFFER_SIZE];
char ts_data_buffer_relative_humidity[DATA_BUFFER_SIZE];
char ts_data_buffer_absolute_humidity[DATA_BUFFER_SIZE];
// CRC16 calculation
static uint16_t get_crc16(const uint8_t* buf, size_t len) {
uint16_t crc = 0xFFFF;
while(len--) {
crc ^= (uint16_t)*buf++;
for(unsigned i = 0; i < 8; i++) {
if(crc & 0x0001) {
crc >>= 1;
crc ^= 0xA001;
} else {
crc >>= 1;
}
}
}
return crc;
}
// Combine bytes
static uint16_t combine_bytes(uint8_t msb, uint8_t lsb) {
return ((uint16_t)msb << 8) | (uint16_t)lsb;
}
// Executes an I2C wake up, sends command and reads result
// true if fetch was successful, false otherwise
static bool temperature_sensor_get_data(uint8_t* buffer, uint8_t size) {
uint32_t timeout = furi_ms_to_ticks(100);
uint8_t cmdbuffer[3] = {0, 0, 0};
bool ret = false;
// Aquire I2C bus
furi_hal_i2c_acquire(I2C_BUS);
// Wake UP AM2320 (sensor goes to sleep to not warm up and affect the humidity sensor)
furi_hal_i2c_is_device_ready(I2C_BUS, (uint8_t)AM2320_ADDRESS, timeout);
// Check if device woken up then we do next stuff
if(furi_hal_i2c_is_device_ready(I2C_BUS, (uint8_t)AM2320_ADDRESS, timeout)) {
// Wait a bit
furi_delay_us(1000);
// Prepare command: Addr 0x03, start register = 0x00, number of registers to read = 0x04
cmdbuffer[0] = 0x03;
cmdbuffer[1] = 0x00;
cmdbuffer[2] = 0x04;
// Transmit command to read registers
ret = furi_hal_i2c_tx(I2C_BUS, (uint8_t)AM2320_ADDRESS, cmdbuffer, 3, timeout);
// Wait a bit
furi_delay_us(1600);
if(ret) {
/*
* Read out 8 bytes of data
* Byte 0: Should be Modbus function code 0x03
* Byte 1: Should be number of registers to read (0x04)
* Byte 2: Humidity msb
* Byte 3: Humidity lsb
* Byte 4: Temperature msb
* Byte 5: Temperature lsb
* Byte 6: CRC lsb byte
* Byte 7: CRC msb byte
*/
ret = furi_hal_i2c_rx(I2C_BUS, (uint8_t)AM2320_ADDRESS, buffer, size, timeout);
}
}
// Release i2c bus
furi_hal_i2c_release(I2C_BUS);
return ret;
}
// Fetches temperature and humidity from sensor
// Temperature and humidity must be preallocated
// true if fetch was successful, false otherwise
static bool temperature_sensor_fetch_info(double* temperature, double* humidity) {
*humidity = (float)0;
bool ret = false;
uint8_t buffer[8] = {0, 0, 0, 0, 0, 0, 0, 0};
// Fetch data from sensor
ret = temperature_sensor_get_data(buffer, 8);
// If we got no result
if(!ret) return false;
if(buffer[0] != 0x03) return false; // must be 0x03 modbus reply
if(buffer[1] != 0x04) return false; // must be 0x04 number of registers reply
// Check CRC16 sum, if not correct - return false
uint16_t crcdata = get_crc16(buffer, 6);
uint16_t crcread = combine_bytes(buffer[7], buffer[6]);
if(crcdata != crcread) return false;
// Combine bytes for temp and humidity
uint16_t temp16 = combine_bytes(buffer[4], buffer[5]);
uint16_t humi16 = combine_bytes(buffer[2], buffer[3]);
/* Temperature resolution is 16Bit,
* temperature highest bit (Bit15) is equal to 1 indicates a
* negative temperature, the temperature highest bit (Bit15)
* is equal to 0 indicates a positive temperature;
* temperature in addition to the most significant bit (Bit14 ~ Bit0)
* indicates the temperature sensor string value.
* Temperature sensor value is a string of 10 times the
* actual temperature value.
*/
if(temp16 & 0x8000) {
temp16 = -(temp16 & 0x7FFF);
}
// Prepare output data
*temperature = (float)temp16 / 10.0;
*humidity = (float)humi16 / 10.0;
return true;
}
// Draw callback
static void temperature_sensor_draw_callback(Canvas* canvas, void* ctx) {
UNUSED(ctx);
canvas_clear(canvas);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2, 10, "AM2320/AM2321 Sensor");
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 2, 62, "Press back to exit.");
switch(temperature_sensor_current_status) {
case TSSInitializing:
canvas_draw_str(canvas, 2, 30, "Initializing..");
break;
case TSSNoSensor:
canvas_draw_str(canvas, 2, 30, "No sensor found!");
break;
case TSSPendingUpdate: {
canvas_draw_str(canvas, 3, 24, "Temperature");
canvas_draw_str(canvas, 68, 24, "Humidity");
// Draw vertical lines
canvas_draw_line(canvas, 61, 16, 61, 50);
canvas_draw_line(canvas, 62, 16, 62, 50);
// Draw horizontal line
canvas_draw_line(canvas, 2, 27, 122, 27);
// Draw temperature and humidity values
canvas_draw_str(canvas, 8, 38, ts_data_buffer_temperature_c);
canvas_draw_str(canvas, 42, 38, "C");
canvas_draw_str(canvas, 8, 48, ts_data_buffer_temperature_f);
canvas_draw_str(canvas, 42, 48, "F");
canvas_draw_str(canvas, 68, 38, ts_data_buffer_relative_humidity);
canvas_draw_str(canvas, 100, 38, "%");
canvas_draw_str(canvas, 68, 48, ts_data_buffer_absolute_humidity);
canvas_draw_str(canvas, 100, 48, "g/m3");
} break;
default:
break;
}
}
// Input callback
static void temperature_sensor_input_callback(InputEvent* input_event, void* ctx) {
furi_assert(ctx);
FuriMessageQueue* event_queue = ctx;
TSEvent event = {.type = TSEventTypeInput, .input = *input_event};
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
// Timer callback
static void temperature_sensor_timer_callback(FuriMessageQueue* event_queue) {
furi_assert(event_queue);
TSEvent event = {.type = TSEventTypeTick};
furi_message_queue_put(event_queue, &event, 0);
}
// App entry point
int32_t am_temperature_sensor_app(void* p) {
UNUSED(p);
furi_hal_power_suppress_charge_enter();
// Declare our variables and assign variables a default value
TSEvent tsEvent;
bool sensorFound = false;
double celsius, fahrenheit, rel_humidity, abs_humidity = TS_DEFAULT_VALUE;
// Used for absolute humidity calculation
double vapour_pressure = 0;
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(TSEvent));
// Register callbacks
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, temperature_sensor_draw_callback, NULL);
view_port_input_callback_set(view_port, temperature_sensor_input_callback, event_queue);
// Create timer and register its callback
FuriTimer* timer =
furi_timer_alloc(temperature_sensor_timer_callback, FuriTimerTypePeriodic, event_queue);
furi_timer_start(timer, furi_kernel_get_tick_frequency());
// Register viewport
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
// Used to notify the user by blinking red (error) or blue (fetch successful)
NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);
while(1) {
furi_check(furi_message_queue_get(event_queue, &tsEvent, FuriWaitForever) == FuriStatusOk);
// Handle events
if(tsEvent.type == TSEventTypeInput) {
// Exit on back key
if(tsEvent.input.key ==
InputKeyBack) // We dont check for type here, we can check the type of keypress like: (event.input.type == InputTypeShort)
break;
} else if(tsEvent.type == TSEventTypeTick) {
// Update sensor data
// Fetch data and set the sensor current status accordingly
sensorFound = temperature_sensor_fetch_info(&celsius, &rel_humidity);
temperature_sensor_current_status = (sensorFound ? TSSPendingUpdate : TSSNoSensor);
if(sensorFound) {
// Blink blue
notification_message(notifications, &sequence_blink_blue_100);
if(celsius != TS_DEFAULT_VALUE && rel_humidity != TS_DEFAULT_VALUE) {
// Convert celsius to fahrenheit
fahrenheit = (celsius * 9 / 5) + 32;
// Calculate absolute humidity - For more info refer to https://github.com/Mywk/FlipperTemperatureSensor/issues/1
// Calculate saturation vapour pressure first
vapour_pressure =
(double)6.11 *
pow(10, (double)(((double)7.5 * celsius) / ((double)237.3 + celsius)));
// Then the vapour pressure in Pa
vapour_pressure = vapour_pressure * rel_humidity;
// Calculate absolute humidity
abs_humidity =
(double)2.16679 * (double)(vapour_pressure / ((double)273.15 + celsius));
// Fill our buffers here, not on the canvas draw callback
snprintf(ts_data_buffer_temperature_c, DATA_BUFFER_SIZE, "%.2f", celsius);
snprintf(ts_data_buffer_temperature_f, DATA_BUFFER_SIZE, "%.2f", fahrenheit);
snprintf(
ts_data_buffer_relative_humidity, DATA_BUFFER_SIZE, "%.2f", rel_humidity);
snprintf(
ts_data_buffer_absolute_humidity, DATA_BUFFER_SIZE, "%.2f", abs_humidity);
}
} else {
// Reset our variables to their default values
celsius = fahrenheit = rel_humidity = abs_humidity = TS_DEFAULT_VALUE;
// Blink red
notification_message(notifications, &sequence_blink_red_100);
}
}
uint32_t wait_ticks = furi_ms_to_ticks(!sensorFound ? 100 : 500);
furi_delay_tick(wait_ticks);
}
furi_hal_power_suppress_charge_exit();
// Dobby is freee (free our variables, Flipper will crash if we don't do this!)
furi_timer_free(timer);
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
furi_message_queue_free(event_queue);
furi_record_close(RECORD_NOTIFICATION);
furi_record_close(RECORD_GUI);
return 0;
}

View File

Before

Width:  |  Height:  |  Size: 181 B

After

Width:  |  Height:  |  Size: 181 B

View File

@ -370,8 +370,6 @@ static void arkanoid_update_timer_callback(FuriMessageQueue* event_queue) {
int32_t arkanoid_game_app(void* p) {
UNUSED(p);
int32_t return_code = 0;
// Set random seed from interrupts
srand(DWT->CYCCNT);
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(GameEvent));
@ -448,14 +446,13 @@ int32_t arkanoid_game_app(void* p) {
arkanoid_state->ball_state.dy = -1;
//start the game flag
arkanoid_state->gameStarted = true;
break;
}
break;
default:
break;
}
}
}
} else {
// Event timeout
FURI_LOG_D(TAG, "furi_message_queue: Event timeout");
}
view_port_update(view_port);

View File

@ -339,8 +339,6 @@ int32_t barcode_generator_app(void* p) {
break;
}
}
} else {
FURI_LOG_D("barcode_generator", "osMessageQueue: event timeout");
}
view_port_update(view_port);

View File

@ -13,6 +13,8 @@
#include "dap_config.h"
#include "gui/dap_gui.h"
#include "usb/dap_v2_usb.h"
#include <dialogs/dialogs.h>
#include "dap_link_icons.h"
/***************************************************************************/
/****************************** DAP COMMON *********************************/
@ -495,6 +497,24 @@ DapConfig* dap_app_get_config(DapApp* app) {
int32_t dap_link_app(void* p) {
UNUSED(p);
if(furi_hal_usb_is_locked()) {
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
DialogMessage* message = dialog_message_alloc();
dialog_message_set_header(message, "Connection\nis active!", 3, 2, AlignLeft, AlignTop);
dialog_message_set_text(
message,
"Disconnect from\nPC or phone to\nuse this function.",
3,
30,
AlignLeft,
AlignTop);
dialog_message_set_icon(message, &I_ActiveConnection_50x64, 78, 0);
dialog_message_show(dialogs, message);
dialog_message_free(message);
furi_record_close(RECORD_DIALOGS);
return -1;
}
// alloc app
DapApp* app = dap_app_alloc();
app_handle = app;

View File

@ -72,8 +72,8 @@ void dap_scene_config_on_enter(void* context) {
variable_item_set_current_value_index(item, config->uart_swap);
variable_item_set_current_value_text(item, uart_swap[config->uart_swap]);
item = variable_item_list_add(var_item_list, "Help and Pinout", 0, NULL, NULL);
item = variable_item_list_add(var_item_list, "About", 0, NULL, NULL);
variable_item_list_add(var_item_list, "Help and Pinout", 0, NULL, NULL);
variable_item_list_add(var_item_list, "About", 0, NULL, NULL);
variable_item_list_set_selected_item(
var_item_list, scene_manager_get_scene_state(app->scene_manager, DapSceneConfig));

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -8,7 +8,8 @@
#define DHT_POLLING_CONTROL 1 //Включение проверки частоты опроса датчика
#define DHT_POLLING_INTERVAL_DHT11 \
2000 //Интервал опроса DHT11 (0.5 Гц по даташиту). Можно поставить 1500, будет работать
#define DHT_POLLING_INTERVAL_DHT22 1000 //Интервал опроса DHT22 (1 Гц по даташиту)
//Костыль, временно 2 секунды для датчика AM2302
#define DHT_POLLING_INTERVAL_DHT22 2000 //Интервал опроса DHT22 (1 Гц по даташиту)
#define DHT_IRQ_CONTROL //Выключать прерывания во время обмена данных с датчиком
/* Структура возвращаемых датчиком данных */
typedef struct {

View File

@ -11,18 +11,18 @@ const GpioPin RX_14 = {.pin = LL_GPIO_PIN_7, .port = GPIOB};
//Перечень достуных портов ввода/вывода
static const GpioItem gpio_item[] = {
{2, "2", &gpio_ext_pa7},
{3, "3", &gpio_ext_pa6},
{4, "4", &gpio_ext_pa4},
{5, "5", &gpio_ext_pb3},
{6, "6", &gpio_ext_pb2},
{7, "7", &gpio_ext_pc3},
{10, "10 (SWC)", &SWC_10},
{2, "2 (A7)", &gpio_ext_pa7},
{3, "3 (A6)", &gpio_ext_pa6},
{4, "4 (A4)", &gpio_ext_pa4},
{5, "5 (B3)", &gpio_ext_pb3},
{6, "6 (B2)", &gpio_ext_pb2},
{7, "7 (C3)", &gpio_ext_pc3},
{10, " 10(SWC) ", &SWC_10},
{12, "12 (SIO)", &SIO_12},
{13, "13 (TX)", &TX_13},
{14, "14 (RX)", &RX_14},
{15, "15", &gpio_ext_pc1},
{16, "16", &gpio_ext_pc0},
{15, "15 (C1)", &gpio_ext_pc1},
{16, "16 (C0)", &gpio_ext_pc0},
{17, "17 (1W)", &ibutton_gpio}};
//Данные плагина
@ -442,6 +442,8 @@ int32_t quenon_dht_mon_app() {
case InputKeyBack:
processing = false;
break;
default:
break;
}
}
}

View File

@ -81,7 +81,7 @@ void sensorEdit_scene(PluginData* app) {
app->item =
variable_item_list_add(variable_item_list, "Type:", 2, addSensor_sensorTypeChanged, app);
variable_item_set_current_value_index(nameItem, app->currentSensorEdit->type);
variable_item_set_current_value_index(app->item, app->currentSensorEdit->type);
variable_item_set_current_value_text(app->item, sensorsTypes[app->currentSensorEdit->type]);
//GPIO
@ -93,6 +93,9 @@ void sensorEdit_scene(PluginData* app) {
app->item, DHTMon_GPIO_getName(app->currentSensorEdit->GPIO));
variable_item_list_add(variable_item_list, "Save", 1, NULL, app);
//Сброс выбранного пункта в ноль
variable_item_list_set_selected_item(variable_item_list, 0);
view_dispatcher_switch_to_view(app->view_dispatcher, ADDSENSOR_MENU_VIEW);
}
void sensorEdit_sceneRemove(void) {

View File

@ -9,4 +9,5 @@ App(
order=100,
fap_icon="flappy_10px.png",
fap_category="Games",
fap_icon_assets="assets",
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 B

View File

@ -0,0 +1 @@
3

View File

@ -1,54 +0,0 @@
#include <furi.h>
uint8_t bird_array[3][15][11] = {
{
{0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0},
{0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0},
{0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0},
{0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0},
{0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1},
{1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1},
{1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1},
{1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0},
{1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0},
{0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0},
{0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0},
{0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0},
{0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0},
},
{
{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0},
{0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0},
{0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0},
{0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1},
{1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1},
{1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1},
{1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0},
{1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0},
{0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0},
{0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0},
{0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0},
{0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0},
},
{
{0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0},
{0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0},
{0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0},
{0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1},
{1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1},
{1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1},
{1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0},
{1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0},
{0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0},
{0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0},
{0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0},
{0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0},
}};

View File

@ -1,9 +1,10 @@
#include <furi.h>
#include <gui/gui.h>
#include <input/input.h>
#include <stdlib.h>
#include "bird.h"
#include <FlappyBird_icons.h>
#include <furi.h>
#include <gui/gui.h>
#include <gui/icon_animation_i.h>
#include <input/input.h>
#define TAG "Flappy"
#define DEBUG false
@ -36,6 +37,7 @@ typedef struct {
typedef struct {
float gravity;
POINT point;
IconAnimation* sprite;
} BIRD;
typedef struct {
@ -89,6 +91,7 @@ static void flappy_game_state_init(GameState* const game_state) {
bird.gravity = 0.0f;
bird.point.x = 15;
bird.point.y = 32;
bird.sprite = icon_animation_alloc(&A_bird);
game_state->debug = DEBUG;
game_state->bird = bird;
@ -100,6 +103,11 @@ static void flappy_game_state_init(GameState* const game_state) {
flappy_game_random_pilar(game_state);
}
static void flappy_game_state_free(GameState* const game_state) {
icon_animation_free(game_state->bird.sprite);
free(game_state);
}
static void flappy_game_tick(GameState* const game_state) {
if(game_state->state == GameStateLife) {
if(!game_state->debug) {
@ -213,27 +221,16 @@ static void flappy_game_render_callback(Canvas* const canvas, void* ctx) {
FLIPPER_LCD_HEIGHT - pilar->height - FLAPPY_GAB_HEIGHT);
}
}
// Flappy
for(int h = 0; h < FLAPPY_BIRD_HEIGHT; h++) {
for(int w = 0; w < FLAPPY_BIRD_WIDTH; w++) {
// Switch animation
int bird = 0;
if(game_state->bird.gravity < -0.5)
bird = 1;
else
bird = 2;
// Draw bird pixels
if(bird_array[bird][h][w] == 1) {
int x = game_state->bird.point.x + h;
int y = game_state->bird.point.y + w;
// Switch animation
game_state->bird.sprite->frame = 1;
if(game_state->bird.gravity < -0.5)
game_state->bird.sprite->frame = 0;
else if(game_state->bird.gravity > 0.5)
game_state->bird.sprite->frame = 2;
canvas_draw_dot(canvas, x, y);
}
}
}
// Stats
canvas_draw_icon_animation(
canvas, game_state->bird.point.x, game_state->bird.point.y, game_state->bird.sprite);
canvas_set_font(canvas, FontSecondary);
char buffer[12];
@ -345,14 +342,13 @@ int32_t flappy_game_app(void* p) {
case InputKeyBack:
processing = false;
break;
default:
break;
}
}
} else if(event.type == EventTypeTick) {
flappy_game_tick(game_state);
}
} else {
//FURI_LOG_D(TAG, "osMessageQueue: event timeout");
// event timeout
}
view_port_update(view_port);
@ -367,7 +363,7 @@ int32_t flappy_game_app(void* p) {
delete_mutex(&state_mutex);
free_and_exit:
free(game_state);
flappy_game_state_free(game_state);
furi_message_queue_free(event_queue);
return return_code;

View File

@ -134,6 +134,8 @@ void flipfrid_scene_entrypoint_on_event(FlipFridEvent event, FlipFridState* cont
case InputKeyBack:
context->is_running = false;
break;
default:
break;
}
}
}

View File

@ -71,6 +71,8 @@ void flipfrid_scene_load_custom_uids_on_event(FlipFridEvent event, FlipFridState
case InputKeyBack:
context->current_scene = SceneEntryPoint;
break;
default:
break;
}
}
}

View File

@ -158,6 +158,8 @@ void flipfrid_scene_load_file_on_event(FlipFridEvent event, FlipFridState* conte
case InputKeyBack:
context->current_scene = SceneEntryPoint;
break;
default:
break;
}
}
}

View File

@ -546,6 +546,8 @@ void flipfrid_scene_run_attack_on_event(FlipFridEvent event, FlipFridState* cont
notification_message(context->notify, &sequence_blink_stop);
context->current_scene = SceneEntryPoint;
break;
default:
break;
}
}
}

View File

@ -129,6 +129,8 @@ void flipfrid_scene_select_field_on_event(FlipFridEvent event, FlipFridState* co
furi_string_reset(context->notification_msg);
context->current_scene = SceneSelectFile;
break;
default:
break;
}
FURI_LOG_D(TAG, "Position: %d/%d", context->key_index, nb_bytes);
}

View File

@ -0,0 +1,13 @@
# Game "15" for Flipper Zero
[Original link](https://github.com/x27/flipperzero-game15)
Logic game [Wikipedia](https://en.wikipedia.org/wiki/15_puzzle)
![Game screen](images/Game15.png)
![Restore game](images/Game15Restore.png)
![Popoup](images/Game15Popup.png)

View File

@ -0,0 +1,12 @@
App(
appid="game15",
name="Game 15",
apptype=FlipperAppType.EXTERNAL,
entry_point="game15_app",
cdefines=["APP_GAME15"],
requires=["gui"],
stack_size=1 * 1024,
fap_icon="game15_10px.png",
order=30,
fap_category="Games",
)

View File

@ -0,0 +1,468 @@
#include <furi.h>
#include <gui/gui.h>
#include <notification/notification.h>
#include <notification/notification_messages.h>
#include <storage/storage.h>
#include "sandbox.h"
#define FPS 20
#define CELL_WIDTH 10
#define CELL_HEIGHT 8
#define MOVE_TICKS 5
#define KEY_STACK_SIZE 16
#define SAVING_DIRECTORY "/ext/apps/Games"
#define SAVING_FILENAME SAVING_DIRECTORY "/game15.save"
#define POPUP_MENU_ITEMS 2
typedef enum {
DirectionNone,
DirectionUp,
DirectionDown,
DirectionLeft,
DirectionRight
} direction_e;
typedef enum { ScenePlay, SceneWin, ScenePopup } scene_e;
typedef struct {
uint8_t cell_index;
uint8_t zero_index;
uint8_t move_direction;
uint8_t move_ticks;
} moving_cell_t;
typedef struct {
uint16_t top_record;
scene_e scene;
uint16_t move_count;
uint32_t tick_count;
uint8_t board[16];
} game_state_t;
static game_state_t game_state;
static NotificationApp* notification;
static moving_cell_t moving_cell;
static uint8_t loaded_saving_ticks;
static uint8_t popup_menu_selected_item;
static const char* popup_menu_strings[] = {"Continue", "Reset"};
static uint8_t keys[KEY_STACK_SIZE];
static uint8_t key_stack_head = 0;
static const uint8_t pic_cells[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x30, 0xfc, 0x38, 0xfc, 0x30, 0xfc, 0x30, 0xfc, 0x30, 0xfc, 0x30, 0xfc, 0x30, 0xfc, 0x30, 0xfc,
0x78, 0xfc, 0xcc, 0xfc, 0xcc, 0xfc, 0x60, 0xfc, 0x30, 0xfc, 0x18, 0xfc, 0x0c, 0xfc, 0xfc, 0xfc,
0x78, 0xfc, 0xcc, 0xfc, 0xcc, 0xfc, 0x60, 0xfc, 0xc0, 0xfc, 0xcc, 0xfc, 0xcc, 0xfc, 0x78, 0xfc,
0x70, 0xfc, 0x78, 0xfc, 0x68, 0xfc, 0x6c, 0xfc, 0x6c, 0xfc, 0xec, 0xfc, 0xfc, 0xfc, 0x60, 0xfc,
0xfc, 0xfc, 0x0c, 0xfc, 0x0c, 0xfc, 0x7c, 0xfc, 0xc0, 0xfc, 0xcc, 0xfc, 0xcc, 0xfc, 0x78, 0xfc,
0x78, 0xfc, 0x0c, 0xfc, 0x0c, 0xfc, 0x7c, 0xfc, 0xcc, 0xfc, 0xcc, 0xfc, 0xcc, 0xfc, 0x78, 0xfc,
0xfc, 0xfc, 0xc0, 0xfc, 0xc0, 0xfc, 0xc0, 0xfc, 0xc0, 0xfc, 0xc0, 0xfc, 0xc0, 0xfc, 0xc0, 0xfc,
0x78, 0xfc, 0xcc, 0xfc, 0xcc, 0xfc, 0x78, 0xfc, 0xcc, 0xfc, 0xcc, 0xfc, 0xcc, 0xfc, 0x78, 0xfc,
0x78, 0xfc, 0xcc, 0xfc, 0xcc, 0xfc, 0xcc, 0xfc, 0xf8, 0xfc, 0xc0, 0xfc, 0xc0, 0xfc, 0x78, 0xfc,
0xe6, 0xfd, 0x37, 0xff, 0x36, 0xff, 0x36, 0xff, 0x36, 0xff, 0x36, 0xff, 0x36, 0xff, 0xe6, 0xfd,
0x8c, 0xfd, 0xce, 0xfd, 0x8c, 0xfd, 0x8c, 0xfd, 0x8c, 0xfd, 0x8c, 0xfd, 0x8c, 0xfd, 0x8c, 0xfd,
0xe6, 0xfd, 0x37, 0xff, 0x36, 0xff, 0x86, 0xfd, 0xc6, 0xfc, 0x66, 0xfc, 0x36, 0xfc, 0xf6, 0xff,
0xe6, 0xfd, 0x37, 0xff, 0x36, 0xff, 0x86, 0xfd, 0x06, 0xff, 0x36, 0xff, 0x36, 0xff, 0xe6, 0xfd,
0xc6, 0xfd, 0xe7, 0xfd, 0xa6, 0xfd, 0xb6, 0xfd, 0xb6, 0xfd, 0xb6, 0xff, 0xf6, 0xff, 0x86, 0xfd,
0xf6, 0xff, 0x37, 0xfc, 0x36, 0xfc, 0xf6, 0xfd, 0x06, 0xff, 0x36, 0xff, 0x36, 0xff, 0xe6, 0xfd,
};
static const uint8_t pic_digits[] = {
0xf0, 0xf2, 0xf2, 0xf2, 0xf2, 0xf0, 0xf9, 0xf8, 0xf9, 0xf9, 0xf9, 0xf0, 0xf0, 0xf2, 0xf3,
0xf1, 0xfc, 0xf0, 0xf0, 0xf3, 0xf1, 0xf3, 0xf2, 0xf0, 0xf3, 0xf1, 0xf2, 0xf2, 0xf0, 0xf3,
0xf0, 0xfc, 0xf0, 0xf3, 0xf2, 0xf0, 0x00, 0x0c, 0x00, 0x02, 0x02, 0x00, 0x00, 0x03, 0x03,
0x03, 0x03, 0x03, 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0x00, 0x03, 0x03, 0x00,
};
static const uint8_t pic_top[] = {11, 4, 0x88, 0xf8, 0xad, 0xfa, 0xad, 0xf8, 0x8d, 0xfe};
static const uint8_t pic_move[] =
{17, 4, 0x2e, 0x2a, 0xfe, 0xa4, 0xaa, 0xff, 0xaa, 0x2a, 0xff, 0x2e, 0x36, 0xfe};
static const uint8_t pic_time[] = {15, 4, 0xa8, 0x8b, 0x2d, 0xe9, 0xad, 0xca, 0xad, 0x8b};
static const uint8_t pic_puzzled[] = {
0xff, 0xcf, 0x00, 0xf3, 0xff, 0xfc, 0x3f, 0x03, 0xc0, 0xff, 0xf3, 0x0f, 0xdc, 0xff, 0xcf,
0x00, 0xf3, 0xff, 0xfc, 0x3f, 0x03, 0xc0, 0xff, 0xf3, 0x0f, 0xdc, 0x03, 0xcc, 0x00, 0x03,
0x38, 0x00, 0x0e, 0x03, 0xc0, 0x00, 0x30, 0x30, 0xdc, 0x03, 0xcc, 0x00, 0x03, 0x1c, 0x00,
0x07, 0x03, 0xc0, 0x00, 0x30, 0x30, 0xdc, 0xff, 0xcf, 0x00, 0x03, 0x0e, 0x80, 0x03, 0x03,
0xc0, 0xff, 0x33, 0xc0, 0xdc, 0xff, 0xcf, 0x00, 0x03, 0x07, 0xc0, 0x01, 0x03, 0xc0, 0xff,
0x33, 0xc0, 0xdc, 0x03, 0xc0, 0x00, 0x83, 0x03, 0xe0, 0x00, 0x03, 0xc0, 0x00, 0x30, 0xc0,
0xd0, 0x03, 0xc0, 0x00, 0xc3, 0x01, 0x70, 0x00, 0x03, 0xc0, 0x00, 0x30, 0xc0, 0xd0, 0x03,
0xc0, 0xff, 0xf3, 0xff, 0xfc, 0x3f, 0xff, 0xcf, 0xff, 0xf3, 0xff, 0xdc, 0x03, 0xc0, 0xff,
0xf3, 0xff, 0xfc, 0x3f, 0xff, 0xcf, 0xff, 0xf3, 0xff, 0xdc};
static void key_stack_init() {
key_stack_head = 0;
}
static uint8_t key_stack_pop() {
return keys[--key_stack_head];
}
static bool key_stack_is_empty() {
return key_stack_head == 0;
}
static int key_stack_push(uint8_t value) {
if(key_stack_head != KEY_STACK_SIZE) {
keys[key_stack_head] = value;
key_stack_head++;
return key_stack_head;
} else
return -1;
}
static bool storage_game_state_load() {
Storage* storage = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(storage);
uint16_t bytes_readed = 0;
if(storage_file_open(file, SAVING_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING))
bytes_readed = storage_file_read(file, &game_state, sizeof(game_state_t));
storage_file_close(file);
storage_file_free(file);
furi_record_close(RECORD_STORAGE);
return bytes_readed == sizeof(game_state_t);
}
static void storage_game_state_save() {
Storage* storage = furi_record_open(RECORD_STORAGE);
if(storage_common_stat(storage, SAVING_DIRECTORY, NULL) == FSE_NOT_EXIST) {
if(!storage_simply_mkdir(storage, SAVING_DIRECTORY)) {
return;
}
}
File* file = storage_file_alloc(storage);
if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
storage_file_write(file, &game_state, sizeof(game_state_t));
}
storage_file_close(file);
storage_file_free(file);
furi_record_close(RECORD_STORAGE);
}
static void set_moving_cell_by_direction(direction_e direction) {
moving_cell.move_direction = DirectionNone;
moving_cell.zero_index = 0xff;
for(int i = 0; i < 16; i++) {
if(!game_state.board[i]) {
moving_cell.zero_index = i;
break;
}
}
if(moving_cell.zero_index == 0xff) return;
uint8_t x = moving_cell.zero_index % 4;
uint8_t y = moving_cell.zero_index / 4;
moving_cell.cell_index = moving_cell.zero_index;
if(direction == DirectionUp && y < 3)
moving_cell.cell_index += 4;
else if(direction == DirectionDown && y > 0)
moving_cell.cell_index -= 4;
else if(direction == DirectionLeft && x < 3)
moving_cell.cell_index++;
else if(direction == DirectionRight && x > 0)
moving_cell.cell_index--;
else
return;
moving_cell.move_ticks = 0;
moving_cell.move_direction = direction;
}
static bool is_board_has_solution() {
uint8_t i, j, inv = 0;
for(i = 0; i < 16; ++i)
if(game_state.board[i])
for(j = 0; j < i; ++j)
if(game_state.board[j] > game_state.board[i]) ++inv;
for(i = 0; i < 16; ++i)
if(game_state.board[i] == 0) inv += 1 + i / 4;
return inv % 2 == 0;
}
static void board_init() {
for(int i = 0; i < 16; i++) {
game_state.board[i] = (i + 1) % 16;
}
do {
for(int i = 15; i >= 1; i--) {
int j = rand() % (i + 1);
uint8_t tmp = game_state.board[j];
game_state.board[j] = game_state.board[i];
game_state.board[i] = tmp;
}
} while(!is_board_has_solution());
}
static void game_init() {
game_state.scene = ScenePlay;
game_state.move_count = 0;
game_state.tick_count = 0;
moving_cell.move_direction = DirectionNone;
board_init();
key_stack_init();
popup_menu_selected_item = 0;
}
static bool is_board_solved() {
for(int i = 0; i < 16; i++)
if(((i + 1) % 16) != game_state.board[i]) return false;
return true;
}
static void game_tick() {
switch(game_state.scene) {
case ScenePlay:
game_state.tick_count++;
if(loaded_saving_ticks) loaded_saving_ticks--;
if(moving_cell.move_direction == DirectionNone && !key_stack_is_empty()) {
set_moving_cell_by_direction(key_stack_pop());
if(moving_cell.move_direction == DirectionNone) {
notification_message(notification, &sequence_single_vibro);
key_stack_init();
}
}
if(moving_cell.move_direction != DirectionNone) {
moving_cell.move_ticks++;
if(moving_cell.move_ticks == MOVE_TICKS) {
game_state.board[moving_cell.zero_index] =
game_state.board[moving_cell.cell_index];
game_state.board[moving_cell.cell_index] = 0;
moving_cell.move_direction = DirectionNone;
game_state.move_count++;
}
if(is_board_solved()) {
notification_message(notification, &sequence_double_vibro);
if(game_state.move_count < game_state.top_record || game_state.top_record == 0) {
game_state.top_record = game_state.move_count;
storage_game_state_save();
}
game_state.scene = SceneWin;
}
}
break;
case SceneWin:
if(!key_stack_is_empty()) game_init();
break;
case ScenePopup:
if(!key_stack_is_empty()) {
switch(key_stack_pop()) {
case DirectionDown:
popup_menu_selected_item++;
popup_menu_selected_item = popup_menu_selected_item % POPUP_MENU_ITEMS;
break;
case DirectionUp:
popup_menu_selected_item--;
popup_menu_selected_item = popup_menu_selected_item % POPUP_MENU_ITEMS;
break;
case DirectionNone:
if(popup_menu_selected_item == 0) {
game_state.scene = ScenePlay;
notification_message(notification, &sequence_single_vibro);
} else if(popup_menu_selected_item == 1) {
notification_message(notification, &sequence_single_vibro);
game_init();
}
break;
}
}
break;
}
}
static void draw_cell(Canvas* canvas, uint8_t x, uint8_t y, uint8_t cell_number) {
canvas_set_color(canvas, ColorBlack);
canvas_draw_rframe(canvas, x, y, 18, 14, 1);
canvas_set_color(canvas, ColorBlack);
canvas_draw_xbm(canvas, x + 4, y + 3, CELL_WIDTH, CELL_HEIGHT, pic_cells + cell_number * 16);
}
static void board_draw(Canvas* canvas) {
for(int i = 0; i < 16; i++) {
if(game_state.board[i]) {
if(moving_cell.move_direction == DirectionNone || moving_cell.cell_index != i)
draw_cell(canvas, (i % 4) * 20 + 7, (i / 4) * 16 + 1, game_state.board[i]);
if(moving_cell.move_direction != DirectionNone && moving_cell.cell_index == i) {
uint8_t from_x = (moving_cell.cell_index % 4) * 20 + 7;
uint8_t from_y = (moving_cell.cell_index / 4) * 16 + 1;
uint8_t to_x = (moving_cell.zero_index % 4) * 20 + 7;
uint8_t to_y = (moving_cell.zero_index / 4) * 16 + 1;
int now_x = from_x + (to_x - from_x) * moving_cell.move_ticks / MOVE_TICKS;
int now_y = from_y + (to_y - from_y) * moving_cell.move_ticks / MOVE_TICKS;
draw_cell(canvas, now_x, now_y, game_state.board[i]);
}
}
}
}
static void number_draw(Canvas* canvas, uint8_t y, uint32_t value) {
uint8_t x = 121;
while(true) {
uint8_t digit = value % 10;
canvas_draw_xbm(canvas, x, y, 4, 6, pic_digits + digit * 6);
x -= 5;
value = value / 10;
if(!value) break;
}
}
static void plate_draw(
Canvas* canvas,
uint8_t y,
const uint8_t* header,
uint32_t value,
bool dont_draw_zero_value) {
canvas_set_color(canvas, ColorBlack);
canvas_draw_rbox(canvas, 92, y, 35, 19, 2);
canvas_set_color(canvas, ColorBlack);
canvas_draw_xbm(canvas, 95, y + 3, header[0], header[1], &header[2]);
if((!value && !dont_draw_zero_value) || value) number_draw(canvas, y + 10, value);
}
static void info_draw(Canvas* canvas) {
plate_draw(canvas, 1, pic_top, game_state.top_record, true);
plate_draw(canvas, 22, pic_move, game_state.move_count, false);
plate_draw(canvas, 43, pic_time, game_state.tick_count / FPS, false);
}
static void gray_screen(Canvas* const canvas) {
canvas_set_color(canvas, ColorWhite);
for(int x = 0; x < 128; x += 2) {
for(int y = 0; y < 64; y++) {
canvas_draw_dot(canvas, x + (y % 2 == 1 ? 0 : 1), y);
}
}
}
static void render_callback(Canvas* const canvas) {
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, 0, 0, 128, 64);
if(game_state.scene == ScenePlay || game_state.scene == SceneWin ||
game_state.scene == ScenePopup) {
canvas_set_color(canvas, ColorBlack);
board_draw(canvas);
info_draw(canvas);
if(loaded_saving_ticks && game_state.scene != ScenePopup) {
canvas_set_color(canvas, ColorWhite);
canvas_draw_rbox(canvas, 20, 24, 88, 16, 4);
canvas_set_color(canvas, ColorBlack);
canvas_draw_rframe(canvas, 20, 24, 88, 16, 4);
canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, "Restore game ...");
}
}
if(game_state.scene == SceneWin) {
gray_screen(canvas);
canvas_draw_box(canvas, 7, 20, 114, 24);
canvas_set_color(canvas, ColorBlack);
canvas_draw_box(canvas, 8, 21, 112, 22);
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, 10, 23, 108, 18);
canvas_set_color(canvas, ColorBlack);
canvas_draw_xbm(canvas, 14, 27, 100, 10, pic_puzzled);
} else if(game_state.scene == ScenePopup) {
gray_screen(canvas);
canvas_set_color(canvas, ColorWhite);
canvas_draw_rbox(canvas, 28, 16, 72, 32, 4);
canvas_set_color(canvas, ColorBlack);
canvas_draw_rframe(canvas, 28, 16, 72, 32, 4);
for(int i = 0; i < POPUP_MENU_ITEMS; i++) {
if(i == popup_menu_selected_item) {
canvas_set_color(canvas, ColorBlack);
canvas_draw_box(canvas, 34, 20 + 12 * i, 60, 12);
}
canvas_set_color(canvas, i == popup_menu_selected_item ? ColorWhite : ColorBlack);
canvas_draw_str_aligned(
canvas, 64, 26 + 12 * i, AlignCenter, AlignCenter, popup_menu_strings[i]);
}
}
}
static void game_event_handler(GameEvent const event) {
if(event.type == EventTypeKey) {
if(event.input.type == InputTypePress) {
switch(event.input.key) {
case InputKeyUp:
key_stack_push(DirectionUp);
break;
case InputKeyDown:
key_stack_push(DirectionDown);
break;
case InputKeyRight:
key_stack_push(DirectionRight);
break;
case InputKeyLeft:
key_stack_push(DirectionLeft);
break;
case InputKeyOk:
if(game_state.scene == ScenePlay) {
game_state.scene = ScenePopup;
key_stack_init();
} else
key_stack_push(DirectionNone);
break;
case InputKeyBack:
if(game_state.scene == ScenePopup) {
game_state.scene = ScenePlay;
} else {
storage_game_state_save();
sandbox_loop_exit();
}
break;
default:
break;
}
}
} else if(event.type == EventTypeTick) {
game_tick();
}
}
static void game_alloc() {
key_stack_init();
notification = furi_record_open(RECORD_NOTIFICATION);
notification_message_block(notification, &sequence_display_backlight_enforce_on);
}
static void game_free() {
notification_message_block(notification, &sequence_display_backlight_enforce_auto);
furi_record_close(RECORD_NOTIFICATION);
}
int32_t game15_app() {
game_alloc();
game_init();
loaded_saving_ticks = 0;
if(storage_game_state_load()) {
if(game_state.scene != ScenePlay)
game_init();
else
loaded_saving_ticks = FPS;
} else
game_init();
sandbox_init(
FPS, (SandboxRenderCallback)render_callback, (SandboxEventHandler)game_event_handler);
sandbox_loop();
sandbox_free();
game_free();
return 0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,93 @@
#include <furi.h>
#include <gui/gui.h>
#include "sandbox.h"
FuriMessageQueue* sandbox_event_queue;
FuriMutex** sandbox_mutex;
ViewPort* sandbox_view_port;
Gui* sandbox_gui;
FuriTimer* sandbox_timer;
bool sandbox_loop_processing;
SandboxRenderCallback sandbox_user_render_callback;
SandboxEventHandler sandbox_user_event_handler;
static void sandbox_render_callback(Canvas* const canvas, void* context) {
UNUSED(context);
if(furi_mutex_acquire(sandbox_mutex, 25) != FuriStatusOk) return;
if(sandbox_user_render_callback) sandbox_user_render_callback(canvas);
furi_mutex_release(sandbox_mutex);
}
static void sandbox_input_callback(InputEvent* input_event, void* context) {
UNUSED(context);
GameEvent event = {.type = EventTypeKey, .input = *input_event};
furi_message_queue_put(sandbox_event_queue, &event, FuriWaitForever);
}
static void sandbox_timer_callback(void* context) {
UNUSED(context);
GameEvent event = {.type = EventTypeTick};
furi_message_queue_put(sandbox_event_queue, &event, 0);
}
void sandbox_loop() {
sandbox_loop_processing = true;
while(sandbox_loop_processing) {
GameEvent event;
FuriStatus event_status = furi_message_queue_get(sandbox_event_queue, &event, 100);
if(event_status != FuriStatusOk) {
// timeout
continue;
}
furi_mutex_acquire(sandbox_mutex, FuriWaitForever);
if(sandbox_user_event_handler) sandbox_user_event_handler(event);
view_port_update(sandbox_view_port);
furi_mutex_release(sandbox_mutex);
}
}
void sandbox_loop_exit() {
sandbox_loop_processing = false;
}
void sandbox_init(
uint8_t fps,
SandboxRenderCallback u_render_callback,
SandboxEventHandler u_event_handler) {
sandbox_user_render_callback = u_render_callback;
sandbox_user_event_handler = u_event_handler;
sandbox_event_queue = furi_message_queue_alloc(8, sizeof(GameEvent));
sandbox_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
sandbox_view_port = view_port_alloc();
view_port_draw_callback_set(sandbox_view_port, sandbox_render_callback, NULL);
view_port_input_callback_set(sandbox_view_port, sandbox_input_callback, NULL);
sandbox_gui = furi_record_open(RECORD_GUI);
gui_add_view_port(sandbox_gui, sandbox_view_port, GuiLayerFullscreen);
if(fps > 0) {
sandbox_timer = furi_timer_alloc(sandbox_timer_callback, FuriTimerTypePeriodic, NULL);
furi_timer_start(sandbox_timer, furi_kernel_get_tick_frequency() / fps);
} else
sandbox_timer = NULL;
}
void sandbox_free() {
if(sandbox_timer) furi_timer_free(sandbox_timer);
gui_remove_view_port(sandbox_gui, sandbox_view_port);
view_port_enabled_set(sandbox_view_port, false);
view_port_free(sandbox_view_port);
if(furi_mutex_acquire(sandbox_mutex, FuriWaitForever) == FuriStatusOk) {
furi_mutex_free(sandbox_mutex);
}
furi_message_queue_free(sandbox_event_queue);
}

View File

@ -0,0 +1,24 @@
#pragma once
#include <input/input.h>
typedef enum {
EventTypeTick,
EventTypeKey,
} EventType;
typedef struct {
EventType type;
InputEvent input;
} GameEvent;
typedef void (*SandboxRenderCallback)(Canvas* canvas);
typedef void (*SandboxEventHandler)(GameEvent event);
void sandbox_init(
uint8_t fps,
SandboxRenderCallback render_callback,
SandboxEventHandler event_handler);
void sandbox_loop();
void sandbox_loop_exit();
void sandbox_free();

View File

@ -1,6 +1,7 @@
# GPS for Flipper Zero
[Original link](https://github.com/ezod/flipperzero-gps)
[Adafruit Ultimate GPS Breakout].
![ui](ui.png)

View File

@ -110,11 +110,11 @@ int32_t gps_app(void* p) {
case InputKeyBack:
processing = false;
break;
default:
break;
}
}
}
} else {
FURI_LOG_D("GPS", "FuriMessageQueue: event timeout");
}
view_port_update(view_port);

View File

@ -1,6 +1,6 @@
App(
appid="hc_sr04",
name="HC-SR04 Dist. Sensor",
name="[HC-SR] Dist. Sensor",
apptype=FlipperAppType.EXTERNAL,
entry_point="hc_sr04_app",
cdefines=["APP_HC_SR04"],

View File

@ -230,6 +230,8 @@ int32_t hc_sr04_app() {
case InputKeyBack:
processing = false;
break;
default:
break;
}
}
}

View File

@ -500,7 +500,6 @@ static void heap_defense_timer_callback(FuriMessageQueue* event_queue) {
int32_t heap_defence_app(void* p) {
UNUSED(p);
srand(DWT->CYCCNT);
//FURI_LOG_W(TAG, "Heap defence start %d", __LINE__);
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(GameEvent));

View File

@ -1,6 +1,6 @@
App(
appid="temperature_sensor",
name="[HTU21D] Temp Sensor",
name="[HTU21D] Temp. Sensor",
apptype=FlipperAppType.EXTERNAL,
entry_point="temperature_sensor_app",
cdefines=["APP_TEMPERATURE_SENSOR"],

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 274 KiB

After

Width:  |  Height:  |  Size: 274 KiB

View File

Before

Width:  |  Height:  |  Size: 914 KiB

After

Width:  |  Height:  |  Size: 914 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 B

View File

@ -318,6 +318,8 @@ int32_t metronome_app() {
case InputKeyBack:
processing = false;
break;
default:
break;
}
} else if(event.input.type == InputTypeLong) {
// hold events
@ -338,6 +340,8 @@ int32_t metronome_app() {
case InputKeyBack:
processing = false;
break;
default:
break;
}
} else if(event.input.type == InputTypeRepeat) {
// repeat events
@ -357,6 +361,8 @@ int32_t metronome_app() {
case InputKeyBack:
processing = false;
break;
default:
break;
}
}
}

View File

@ -476,6 +476,8 @@ int32_t minesweeper_app(void* p) {
// Exit the plugin
processing = false;
break;
default:
break;
}
} else if(event.input.type == InputTypeLong) {
// hold events
@ -493,6 +495,8 @@ int32_t minesweeper_app(void* p) {
case InputKeyBack:
processing = false;
break;
default:
break;
}
}
}

View File

@ -27,7 +27,6 @@ typedef struct {
MorseCodeWorker* worker;
} MorseCode;
static void render_callback(Canvas* const canvas, void* ctx) {
MorseCode* morse_code = ctx;
furi_check(furi_mutex_acquire(morse_code->model_mutex, FuriWaitForever) == FuriStatusOk);
@ -35,20 +34,26 @@ static void render_callback(Canvas* const canvas, void* ctx) {
canvas_set_font(canvas, FontPrimary);
//write words
elements_multiline_text_aligned(canvas, 64, 30, AlignCenter, AlignCenter, furi_string_get_cstr(morse_code->model->words));
// volume view_port
elements_multiline_text_aligned(
canvas, 64, 30, AlignCenter, AlignCenter, furi_string_get_cstr(morse_code->model->words));
// volume view_port
uint8_t vol_bar_x_pos = 124;
uint8_t vol_bar_y_pos = 0;
const uint8_t volume_h =
(64 / (COUNT_OF(MORSE_CODE_VOLUMES) - 1)) * morse_code->model->volume;
const uint8_t volume_h = (64 / (COUNT_OF(MORSE_CODE_VOLUMES) - 1)) * morse_code->model->volume;
canvas_draw_frame(canvas, vol_bar_x_pos, vol_bar_y_pos, 4, 64);
canvas_draw_box(canvas, vol_bar_x_pos, vol_bar_y_pos + (64 - volume_h), 4, volume_h);
//dit bpm
canvas_draw_str_aligned(
canvas, 0, 10, AlignLeft, AlignCenter, furi_string_get_cstr(furi_string_alloc_printf("Dit: %ld ms", morse_code->model->dit_delta)));
canvas,
0,
10,
AlignLeft,
AlignCenter,
furi_string_get_cstr(
furi_string_alloc_printf("Dit: %ld ms", morse_code->model->dit_delta)));
//button info
elements_button_center(canvas, "Press/Hold");
furi_mutex_release(morse_code->model_mutex);
@ -59,9 +64,7 @@ static void input_callback(InputEvent* input_event, void* ctx) {
furi_message_queue_put(morse_code->input_queue, input_event, FuriWaitForever);
}
static void morse_code_worker_callback(
FuriString* words,
void* context) {
static void morse_code_worker_callback(FuriString* words, void* context) {
MorseCode* morse_code = context;
furi_check(furi_mutex_acquire(morse_code->model_mutex, FuriWaitForever) == FuriStatusOk);
morse_code->model->words = words;
@ -115,45 +118,45 @@ int32_t morse_code_app() {
InputEvent input;
morse_code_worker_start(morse_code->worker);
morse_code_worker_set_volume(
morse_code->worker, MORSE_CODE_VOLUMES[morse_code->model->volume]);
morse_code_worker_set_dit_delta(morse_code->worker, morse_code->model->dit_delta);
while(furi_message_queue_get(morse_code->input_queue, &input, FuriWaitForever) == FuriStatusOk){
morse_code->worker, MORSE_CODE_VOLUMES[morse_code->model->volume]);
morse_code_worker_set_dit_delta(morse_code->worker, morse_code->model->dit_delta);
while(furi_message_queue_get(morse_code->input_queue, &input, FuriWaitForever) ==
FuriStatusOk) {
furi_check(furi_mutex_acquire(morse_code->model_mutex, FuriWaitForever) == FuriStatusOk);
if(input.key == InputKeyBack) {
furi_mutex_release(morse_code->model_mutex);
break;
}else if(input.key == InputKeyOk){
if(input.type == InputTypePress)
morse_code_worker_play(morse_code->worker, true);
else if(input.type == InputTypeRelease)
morse_code_worker_play(morse_code->worker, false);
}else if(input.key == InputKeyUp && input.type == InputTypePress){
if(morse_code->model->volume < COUNT_OF(MORSE_CODE_VOLUMES) - 1)
morse_code->model->volume++;
morse_code_worker_set_volume(
morse_code->worker, MORSE_CODE_VOLUMES[morse_code->model->volume]);
}else if(input.key == InputKeyDown && input.type == InputTypePress){
if(morse_code->model->volume > 0)
morse_code->model->volume--;
morse_code_worker_set_volume(
morse_code->worker, MORSE_CODE_VOLUMES[morse_code->model->volume]);
}else if(input.key == InputKeyLeft && input.type == InputTypePress){
if(morse_code->model->dit_delta > 10)
morse_code->model->dit_delta-=10;
morse_code_worker_set_dit_delta(
morse_code->worker, morse_code->model->dit_delta);
}
else if(input.key == InputKeyRight && input.type == InputTypePress){
if(morse_code->model->dit_delta >= 10)
morse_code->model->dit_delta+=10;
morse_code_worker_set_dit_delta(
morse_code->worker, morse_code->model->dit_delta);
}
FURI_LOG_D("Input", "%s %s %ld", input_get_key_name(input.key), input_get_type_name(input.type), input.sequence);
furi_mutex_release(morse_code->model_mutex);
break;
} else if(input.key == InputKeyOk) {
if(input.type == InputTypePress)
morse_code_worker_play(morse_code->worker, true);
else if(input.type == InputTypeRelease)
morse_code_worker_play(morse_code->worker, false);
} else if(input.key == InputKeyUp && input.type == InputTypePress) {
if(morse_code->model->volume < COUNT_OF(MORSE_CODE_VOLUMES) - 1)
morse_code->model->volume++;
morse_code_worker_set_volume(
morse_code->worker, MORSE_CODE_VOLUMES[morse_code->model->volume]);
} else if(input.key == InputKeyDown && input.type == InputTypePress) {
if(morse_code->model->volume > 0) morse_code->model->volume--;
morse_code_worker_set_volume(
morse_code->worker, MORSE_CODE_VOLUMES[morse_code->model->volume]);
} else if(input.key == InputKeyLeft && input.type == InputTypePress) {
if(morse_code->model->dit_delta > 10) morse_code->model->dit_delta -= 10;
morse_code_worker_set_dit_delta(morse_code->worker, morse_code->model->dit_delta);
} else if(input.key == InputKeyRight && input.type == InputTypePress) {
if(morse_code->model->dit_delta >= 10) morse_code->model->dit_delta += 10;
morse_code_worker_set_dit_delta(morse_code->worker, morse_code->model->dit_delta);
}
FURI_LOG_D(
"Input",
"%s %s %ld",
input_get_key_name(input.key),
input_get_type_name(input.type),
input.sequence);
furi_mutex_release(morse_code->model_mutex);
view_port_update(morse_code->view_port);
view_port_update(morse_code->view_port);
}
morse_code_worker_stop(morse_code->worker);
morse_code_free(morse_code);

View File

@ -2,19 +2,20 @@
#include <furi_hal.h>
#include <lib/flipper_format/flipper_format.h>
#define TAG "MorseCodeWorker"
#define MORSE_CODE_VERSION 0
//A-Z0-1
const char morse_array[36][6] ={
".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.",
"--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--..", ".----", "..---", "...--", "....-", ".....",
"-....", "--...", "---..", "----.", "-----"
};
const char symbol_array[36] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};
const char morse_array[36][6] = {".-", "-...", "-.-.", "-..", ".", "..-.",
"--.", "....", "..", ".---", "-.-", ".-..",
"--", "-.", "---", ".--.", "--.-", ".-.",
"...", "-", "..-", "...-", ".--", "-..-",
"-.--", "--..", ".----", "..---", "...--", "....-",
".....", "-....", "--...", "---..", "----.", "-----"};
const char symbol_array[36] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};
struct MorseCodeWorker {
FuriThread* thread;
@ -28,31 +29,28 @@ struct MorseCodeWorker {
FuriString* words;
};
void morse_code_worker_fill_buffer(MorseCodeWorker* instance, uint32_t duration){
void morse_code_worker_fill_buffer(MorseCodeWorker* instance, uint32_t duration) {
FURI_LOG_D("MorseCode: Duration", "%ld", duration);
if( duration <= instance->dit_delta)
if(duration <= instance->dit_delta)
furi_string_push_back(instance->buffer, *DOT);
else if(duration <= (instance->dit_delta * 3))
furi_string_push_back(instance->buffer, *LINE);
if(furi_string_size(instance->buffer) > 5)
furi_string_reset(instance->buffer);
if(furi_string_size(instance->buffer) > 5) furi_string_reset(instance->buffer);
FURI_LOG_D("MorseCode: Buffer", "%s", furi_string_get_cstr(instance->buffer));
}
void morse_code_worker_fill_letter(MorseCodeWorker* instance){
if(furi_string_size(instance->words) > 63)
furi_string_reset(instance->words);
for (size_t i = 0; i < sizeof(morse_array); i++){
if(furi_string_cmp_str(instance->buffer, morse_array[i]) == 0){
furi_string_push_back(instance->words, symbol_array[i]);
furi_string_reset(instance->buffer);
break;
}
void morse_code_worker_fill_letter(MorseCodeWorker* instance) {
if(furi_string_size(instance->words) > 63) furi_string_reset(instance->words);
for(size_t i = 0; i < sizeof(morse_array); i++) {
if(furi_string_cmp_str(instance->buffer, morse_array[i]) == 0) {
furi_string_push_back(instance->words, symbol_array[i]);
furi_string_reset(instance->buffer);
break;
}
}
FURI_LOG_D("MorseCode: Words", "%s", furi_string_get_cstr(instance->words));
}
static int32_t morse_code_worker_thread_callback(void* context) {
furi_assert(context);
MorseCodeWorker* instance = context;
@ -61,16 +59,16 @@ static int32_t morse_code_worker_thread_callback(void* context) {
uint32_t end_tick = 0;
bool pushed = true;
bool spaced = true;
while(instance->is_running){
while(instance->is_running) {
furi_delay_ms(SLEEP);
if(instance->play){
if(!was_playing){
if(instance->play) {
if(!was_playing) {
start_tick = furi_get_tick();
furi_hal_speaker_start(FREQUENCY, instance->volume);
was_playing = true;
}
}else{
if(was_playing){
} else {
if(was_playing) {
pushed = false;
spaced = false;
furi_hal_speaker_stop();
@ -80,21 +78,21 @@ static int32_t morse_code_worker_thread_callback(void* context) {
start_tick = 0;
}
}
if(!pushed){
if(end_tick + (instance->dit_delta * 3) < furi_get_tick()){
if(!pushed) {
if(end_tick + (instance->dit_delta * 3) < furi_get_tick()) {
//NEW LETTER
morse_code_worker_fill_letter(instance);
if(instance->callback)
instance->callback(instance->words, instance->callback_context);
instance->callback(instance->words, instance->callback_context);
pushed = true;
}
}
if(!spaced){
if(end_tick + (instance->dit_delta * 7) < furi_get_tick()){
if(!spaced) {
if(end_tick + (instance->dit_delta * 7) < furi_get_tick()) {
//NEW WORD
furi_string_push_back(instance->words, *SPACE);
if(instance->callback)
instance->callback(instance->words, instance->callback_context);
instance->callback(instance->words, instance->callback_context);
spaced = true;
}
}
@ -132,17 +130,17 @@ void morse_code_worker_set_callback(
instance->callback_context = context;
}
void morse_code_worker_play(MorseCodeWorker* instance, bool play){
void morse_code_worker_play(MorseCodeWorker* instance, bool play) {
furi_assert(instance);
instance->play = play;
}
void morse_code_worker_set_volume(MorseCodeWorker* instance, float level){
void morse_code_worker_set_volume(MorseCodeWorker* instance, float level) {
furi_assert(instance);
instance->volume = level;
}
void morse_code_worker_set_dit_delta(MorseCodeWorker* instance, uint32_t delta){
void morse_code_worker_set_dit_delta(MorseCodeWorker* instance, uint32_t delta) {
furi_assert(instance);
instance->dit_delta = delta;
}

View File

@ -10,9 +10,7 @@
#define LINE "-"
#define SPACE " "
typedef void (*MorseCodeWorkerCallback)(
FuriString* buffer,
void* context);
typedef void (*MorseCodeWorkerCallback)(FuriString* buffer, void* context);
typedef struct MorseCodeWorker MorseCodeWorker;
@ -34,9 +32,3 @@ void morse_code_worker_play(MorseCodeWorker* instance, bool play);
void morse_code_worker_set_volume(MorseCodeWorker* instance, float level);
void morse_code_worker_set_dit_delta(MorseCodeWorker* instance, uint32_t delta);

View File

@ -372,6 +372,8 @@ int32_t mousejacker_app(void* p) {
plugin_state->close_thread_please = false;
processing = false;
break;
default:
break;
}
}
}
@ -382,7 +384,7 @@ int32_t mousejacker_app(void* p) {
}
furi_thread_free(plugin_state->mjthread);
furi_hal_spi_release(nrf24_HANDLE);
nrf24_deinit();
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
furi_record_close(RECORD_GUI);

View File

@ -0,0 +1,20 @@
App(
appid="nfc_magic",
name="NFC Magic",
apptype=FlipperAppType.EXTERNAL,
entry_point="nfc_magic_app",
requires=[
"storage",
"gui",
],
stack_size=4 * 1024,
order=30,
fap_icon="../../../assets/icons/Archive/Nfc_10px.png",
fap_category="Tools",
fap_private_libs=[
Lib(
name="magic",
),
],
fap_icon_assets="assets",
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -0,0 +1,214 @@
#include "magic.h"
#include <furi_hal_nfc.h>
#define TAG "Magic"
#define MAGIC_CMD_WUPA (0x40)
#define MAGIC_CMD_WIPE (0x41)
#define MAGIC_CMD_READ (0x43)
#define MAGIC_CMD_WRITE (0x43)
#define MAGIC_MIFARE_READ_CMD (0x30)
#define MAGIC_MIFARE_WRITE_CMD (0xA0)
#define MAGIC_ACK (0x0A)
#define MAGIC_BUFFER_SIZE (32)
bool magic_wupa() {
bool magic_activated = false;
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
uint16_t rx_len = 0;
FuriHalNfcReturn ret = 0;
do {
// Setup nfc poller
furi_hal_nfc_exit_sleep();
furi_hal_nfc_ll_txrx_on();
furi_hal_nfc_ll_poll();
ret = furi_hal_nfc_ll_set_mode(
FuriHalNfcModePollNfca, FuriHalNfcBitrate106, FuriHalNfcBitrate106);
if(ret != FuriHalNfcReturnOk) break;
furi_hal_nfc_ll_set_fdt_listen(FURI_HAL_NFC_LL_FDT_LISTEN_NFCA_POLLER);
furi_hal_nfc_ll_set_fdt_poll(FURI_HAL_NFC_LL_FDT_POLL_NFCA_POLLER);
furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandlingNfc);
furi_hal_nfc_ll_set_guard_time(FURI_HAL_NFC_LL_GT_NFCA);
// Start communication
tx_data[0] = MAGIC_CMD_WUPA;
ret = furi_hal_nfc_ll_txrx_bits(
tx_data,
7,
rx_data,
sizeof(rx_data),
&rx_len,
FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON |
FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP,
furi_hal_nfc_ll_ms2fc(20));
if(ret != FuriHalNfcReturnIncompleteByte) break;
if(rx_len != 4) break;
if(rx_data[0] != MAGIC_ACK) break;
magic_activated = true;
} while(false);
if(!magic_activated) {
furi_hal_nfc_ll_txrx_off();
furi_hal_nfc_start_sleep();
}
return magic_activated;
}
bool magic_data_access_cmd() {
bool write_cmd_success = false;
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
uint16_t rx_len = 0;
FuriHalNfcReturn ret = 0;
do {
tx_data[0] = MAGIC_CMD_WRITE;
ret = furi_hal_nfc_ll_txrx_bits(
tx_data,
8,
rx_data,
sizeof(rx_data),
&rx_len,
FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON |
FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP,
furi_hal_nfc_ll_ms2fc(20));
if(ret != FuriHalNfcReturnIncompleteByte) break;
if(rx_len != 4) break;
if(rx_data[0] != MAGIC_ACK) break;
write_cmd_success = true;
} while(false);
if(!write_cmd_success) {
furi_hal_nfc_ll_txrx_off();
furi_hal_nfc_start_sleep();
}
return write_cmd_success;
}
bool magic_read_block(uint8_t block_num, MfClassicBlock* data) {
furi_assert(data);
bool read_success = false;
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
uint16_t rx_len = 0;
FuriHalNfcReturn ret = 0;
do {
tx_data[0] = MAGIC_MIFARE_READ_CMD;
tx_data[1] = block_num;
ret = furi_hal_nfc_ll_txrx_bits(
tx_data,
2 * 8,
rx_data,
sizeof(rx_data),
&rx_len,
FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON,
furi_hal_nfc_ll_ms2fc(20));
if(ret != FuriHalNfcReturnOk) break;
if(rx_len != 16 * 8) break;
memcpy(data->value, rx_data, sizeof(data->value));
read_success = true;
} while(false);
if(!read_success) {
furi_hal_nfc_ll_txrx_off();
furi_hal_nfc_start_sleep();
}
return read_success;
}
bool magic_write_blk(uint8_t block_num, MfClassicBlock* data) {
furi_assert(data);
bool write_success = false;
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
uint16_t rx_len = 0;
FuriHalNfcReturn ret = 0;
do {
tx_data[0] = MAGIC_MIFARE_WRITE_CMD;
tx_data[1] = block_num;
ret = furi_hal_nfc_ll_txrx_bits(
tx_data,
2 * 8,
rx_data,
sizeof(rx_data),
&rx_len,
FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP,
furi_hal_nfc_ll_ms2fc(20));
if(ret != FuriHalNfcReturnIncompleteByte) break;
if(rx_len != 4) break;
if(rx_data[0] != MAGIC_ACK) break;
memcpy(tx_data, data->value, sizeof(data->value));
ret = furi_hal_nfc_ll_txrx_bits(
tx_data,
16 * 8,
rx_data,
sizeof(rx_data),
&rx_len,
FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP,
furi_hal_nfc_ll_ms2fc(20));
if(ret != FuriHalNfcReturnIncompleteByte) break;
if(rx_len != 4) break;
if(rx_data[0] != MAGIC_ACK) break;
write_success = true;
} while(false);
if(!write_success) {
furi_hal_nfc_ll_txrx_off();
furi_hal_nfc_start_sleep();
}
return write_success;
}
bool magic_wipe() {
bool wipe_success = false;
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
uint16_t rx_len = 0;
FuriHalNfcReturn ret = 0;
do {
tx_data[0] = MAGIC_CMD_WIPE;
ret = furi_hal_nfc_ll_txrx_bits(
tx_data,
8,
rx_data,
sizeof(rx_data),
&rx_len,
FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON |
FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP,
furi_hal_nfc_ll_ms2fc(2000));
if(ret != FuriHalNfcReturnIncompleteByte) break;
if(rx_len != 4) break;
if(rx_data[0] != MAGIC_ACK) break;
wipe_success = true;
} while(false);
return wipe_success;
}
void magic_deactivate() {
furi_hal_nfc_ll_txrx_off();
furi_hal_nfc_start_sleep();
}

View File

@ -0,0 +1,15 @@
#pragma once
#include <lib/nfc/protocols/mifare_classic.h>
bool magic_wupa();
bool magic_read_block(uint8_t block_num, MfClassicBlock* data);
bool magic_data_access_cmd();
bool magic_write_blk(uint8_t block_num, MfClassicBlock* data);
bool magic_wipe();
void magic_deactivate();

View File

@ -0,0 +1,169 @@
#include "nfc_magic_i.h"
bool nfc_magic_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
NfcMagic* nfc_magic = context;
return scene_manager_handle_custom_event(nfc_magic->scene_manager, event);
}
bool nfc_magic_back_event_callback(void* context) {
furi_assert(context);
NfcMagic* nfc_magic = context;
return scene_manager_handle_back_event(nfc_magic->scene_manager);
}
void nfc_magic_tick_event_callback(void* context) {
furi_assert(context);
NfcMagic* nfc_magic = context;
scene_manager_handle_tick_event(nfc_magic->scene_manager);
}
void nfc_magic_show_loading_popup(void* context, bool show) {
NfcMagic* nfc_magic = context;
TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME);
if(show) {
// Raise timer priority so that animations can play
vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1);
view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewLoading);
} else {
// Restore default timer priority
vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY);
}
}
NfcMagic* nfc_magic_alloc() {
NfcMagic* nfc_magic = malloc(sizeof(NfcMagic));
nfc_magic->worker = nfc_magic_worker_alloc();
nfc_magic->view_dispatcher = view_dispatcher_alloc();
nfc_magic->scene_manager = scene_manager_alloc(&nfc_magic_scene_handlers, nfc_magic);
view_dispatcher_enable_queue(nfc_magic->view_dispatcher);
view_dispatcher_set_event_callback_context(nfc_magic->view_dispatcher, nfc_magic);
view_dispatcher_set_custom_event_callback(
nfc_magic->view_dispatcher, nfc_magic_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
nfc_magic->view_dispatcher, nfc_magic_back_event_callback);
view_dispatcher_set_tick_event_callback(
nfc_magic->view_dispatcher, nfc_magic_tick_event_callback, 100);
// Nfc device
nfc_magic->nfc_dev = nfc_device_alloc();
// Open GUI record
nfc_magic->gui = furi_record_open(RECORD_GUI);
view_dispatcher_attach_to_gui(
nfc_magic->view_dispatcher, nfc_magic->gui, ViewDispatcherTypeFullscreen);
// Open Notification record
nfc_magic->notifications = furi_record_open(RECORD_NOTIFICATION);
// Submenu
nfc_magic->submenu = submenu_alloc();
view_dispatcher_add_view(
nfc_magic->view_dispatcher, NfcMagicViewMenu, submenu_get_view(nfc_magic->submenu));
// Popup
nfc_magic->popup = popup_alloc();
view_dispatcher_add_view(
nfc_magic->view_dispatcher, NfcMagicViewPopup, popup_get_view(nfc_magic->popup));
// Loading
nfc_magic->loading = loading_alloc();
view_dispatcher_add_view(
nfc_magic->view_dispatcher, NfcMagicViewLoading, loading_get_view(nfc_magic->loading));
// Text Input
nfc_magic->text_input = text_input_alloc();
view_dispatcher_add_view(
nfc_magic->view_dispatcher,
NfcMagicViewTextInput,
text_input_get_view(nfc_magic->text_input));
// Custom Widget
nfc_magic->widget = widget_alloc();
view_dispatcher_add_view(
nfc_magic->view_dispatcher, NfcMagicViewWidget, widget_get_view(nfc_magic->widget));
return nfc_magic;
}
void nfc_magic_free(NfcMagic* nfc_magic) {
furi_assert(nfc_magic);
// Nfc device
nfc_device_free(nfc_magic->nfc_dev);
// Submenu
view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewMenu);
submenu_free(nfc_magic->submenu);
// Popup
view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewPopup);
popup_free(nfc_magic->popup);
// Loading
view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewLoading);
loading_free(nfc_magic->loading);
// TextInput
view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewTextInput);
text_input_free(nfc_magic->text_input);
// Custom Widget
view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewWidget);
widget_free(nfc_magic->widget);
// Worker
nfc_magic_worker_stop(nfc_magic->worker);
nfc_magic_worker_free(nfc_magic->worker);
// View Dispatcher
view_dispatcher_free(nfc_magic->view_dispatcher);
// Scene Manager
scene_manager_free(nfc_magic->scene_manager);
// GUI
furi_record_close(RECORD_GUI);
nfc_magic->gui = NULL;
// Notifications
furi_record_close(RECORD_NOTIFICATION);
nfc_magic->notifications = NULL;
free(nfc_magic);
}
static const NotificationSequence nfc_magic_sequence_blink_start_blue = {
&message_blink_start_10,
&message_blink_set_color_blue,
&message_do_not_reset,
NULL,
};
static const NotificationSequence nfc_magic_sequence_blink_stop = {
&message_blink_stop,
NULL,
};
void nfc_magic_blink_start(NfcMagic* nfc_magic) {
notification_message(nfc_magic->notifications, &nfc_magic_sequence_blink_start_blue);
}
void nfc_magic_blink_stop(NfcMagic* nfc_magic) {
notification_message(nfc_magic->notifications, &nfc_magic_sequence_blink_stop);
}
int32_t nfc_magic_app(void* p) {
UNUSED(p);
NfcMagic* nfc_magic = nfc_magic_alloc();
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneStart);
view_dispatcher_run(nfc_magic->view_dispatcher);
nfc_magic_free(nfc_magic);
return 0;
}

View File

@ -0,0 +1,3 @@
#pragma once
typedef struct NfcMagic NfcMagic;

View File

@ -0,0 +1,77 @@
#pragma once
#include "nfc_magic.h"
#include "nfc_magic_worker.h"
#include "lib/magic/magic.h"
#include <furi.h>
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <notification/notification_messages.h>
#include <gui/modules/submenu.h>
#include <gui/modules/popup.h>
#include <gui/modules/loading.h>
#include <gui/modules/text_input.h>
#include <gui/modules/widget.h>
#include <input/input.h>
#include "scenes/nfc_magic_scene.h"
#include <storage/storage.h>
#include <lib/toolbox/path.h>
#include <lib/nfc/nfc_device.h>
#include "nfc_magic_icons.h"
enum NfcMagicCustomEvent {
// Reserve first 100 events for button types and indexes, starting from 0
NfcMagicCustomEventReserved = 100,
NfcMagicCustomEventViewExit,
NfcMagicCustomEventWorkerExit,
NfcMagicCustomEventByteInputDone,
NfcMagicCustomEventTextInputDone,
};
struct NfcMagic {
NfcMagicWorker* worker;
ViewDispatcher* view_dispatcher;
Gui* gui;
NotificationApp* notifications;
SceneManager* scene_manager;
// NfcMagicDevice* dev;
NfcDevice* nfc_dev;
FuriString* text_box_store;
// Common Views
Submenu* submenu;
Popup* popup;
Loading* loading;
TextInput* text_input;
Widget* widget;
};
typedef enum {
NfcMagicViewMenu,
NfcMagicViewPopup,
NfcMagicViewLoading,
NfcMagicViewTextInput,
NfcMagicViewWidget,
} NfcMagicView;
NfcMagic* nfc_magic_alloc();
void nfc_magic_text_store_set(NfcMagic* nfc_magic, const char* text, ...);
void nfc_magic_text_store_clear(NfcMagic* nfc_magic);
void nfc_magic_blink_start(NfcMagic* nfc_magic);
void nfc_magic_blink_stop(NfcMagic* nfc_magic);
void nfc_magic_show_loading_popup(void* context, bool show);

View File

@ -0,0 +1,174 @@
#include "nfc_magic_worker_i.h"
#include "lib/magic/magic.h"
#define TAG "NfcMagicWorker"
static void
nfc_magic_worker_change_state(NfcMagicWorker* nfc_magic_worker, NfcMagicWorkerState state) {
furi_assert(nfc_magic_worker);
nfc_magic_worker->state = state;
}
NfcMagicWorker* nfc_magic_worker_alloc() {
NfcMagicWorker* nfc_magic_worker = malloc(sizeof(NfcMagicWorker));
// Worker thread attributes
nfc_magic_worker->thread = furi_thread_alloc();
furi_thread_set_name(nfc_magic_worker->thread, "NfcMagicWorker");
furi_thread_set_stack_size(nfc_magic_worker->thread, 8192);
furi_thread_set_callback(nfc_magic_worker->thread, nfc_magic_worker_task);
furi_thread_set_context(nfc_magic_worker->thread, nfc_magic_worker);
nfc_magic_worker->callback = NULL;
nfc_magic_worker->context = NULL;
nfc_magic_worker_change_state(nfc_magic_worker, NfcMagicWorkerStateReady);
return nfc_magic_worker;
}
void nfc_magic_worker_free(NfcMagicWorker* nfc_magic_worker) {
furi_assert(nfc_magic_worker);
furi_thread_free(nfc_magic_worker->thread);
free(nfc_magic_worker);
}
void nfc_magic_worker_stop(NfcMagicWorker* nfc_magic_worker) {
furi_assert(nfc_magic_worker);
nfc_magic_worker_change_state(nfc_magic_worker, NfcMagicWorkerStateStop);
furi_thread_join(nfc_magic_worker->thread);
}
void nfc_magic_worker_start(
NfcMagicWorker* nfc_magic_worker,
NfcMagicWorkerState state,
NfcDeviceData* dev_data,
NfcMagicWorkerCallback callback,
void* context) {
furi_assert(nfc_magic_worker);
furi_assert(dev_data);
nfc_magic_worker->callback = callback;
nfc_magic_worker->context = context;
nfc_magic_worker->dev_data = dev_data;
nfc_magic_worker_change_state(nfc_magic_worker, state);
furi_thread_start(nfc_magic_worker->thread);
}
int32_t nfc_magic_worker_task(void* context) {
NfcMagicWorker* nfc_magic_worker = context;
if(nfc_magic_worker->state == NfcMagicWorkerStateCheck) {
nfc_magic_worker_check(nfc_magic_worker);
} else if(nfc_magic_worker->state == NfcMagicWorkerStateWrite) {
nfc_magic_worker_write(nfc_magic_worker);
} else if(nfc_magic_worker->state == NfcMagicWorkerStateWipe) {
nfc_magic_worker_wipe(nfc_magic_worker);
}
nfc_magic_worker_change_state(nfc_magic_worker, NfcMagicWorkerStateReady);
return 0;
}
void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker) {
bool card_found_notified = false;
FuriHalNfcDevData nfc_data = {};
MfClassicData* src_data = &nfc_magic_worker->dev_data->mf_classic_data;
while(nfc_magic_worker->state == NfcMagicWorkerStateWrite) {
if(furi_hal_nfc_detect(&nfc_data, 200)) {
if(!card_found_notified) {
nfc_magic_worker->callback(
NfcMagicWorkerEventCardDetected, nfc_magic_worker->context);
card_found_notified = true;
}
furi_hal_nfc_sleep();
if(!magic_wupa()) {
FURI_LOG_E(TAG, "Not Magic card");
nfc_magic_worker->callback(
NfcMagicWorkerEventWrongCard, nfc_magic_worker->context);
break;
}
if(!magic_data_access_cmd()) {
FURI_LOG_E(TAG, "Not Magic card");
nfc_magic_worker->callback(
NfcMagicWorkerEventWrongCard, nfc_magic_worker->context);
break;
}
for(size_t i = 0; i < 64; i++) {
FURI_LOG_D(TAG, "Writing block %d", i);
if(!magic_write_blk(i, &src_data->block[i])) {
FURI_LOG_E(TAG, "Failed to write %d block", i);
nfc_magic_worker->callback(NfcMagicWorkerEventFail, nfc_magic_worker->context);
break;
}
}
nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
break;
} else {
if(card_found_notified) {
nfc_magic_worker->callback(
NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context);
card_found_notified = false;
}
}
furi_delay_ms(300);
}
magic_deactivate();
}
void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker) {
bool card_found_notified = false;
while(nfc_magic_worker->state == NfcMagicWorkerStateCheck) {
if(magic_wupa()) {
if(!card_found_notified) {
nfc_magic_worker->callback(
NfcMagicWorkerEventCardDetected, nfc_magic_worker->context);
card_found_notified = true;
}
nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
break;
} else {
if(card_found_notified) {
nfc_magic_worker->callback(
NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context);
card_found_notified = false;
}
}
furi_delay_ms(300);
}
magic_deactivate();
}
void nfc_magic_worker_wipe(NfcMagicWorker* nfc_magic_worker) {
MfClassicBlock block;
memset(&block, 0, sizeof(MfClassicBlock));
block.value[0] = 0x01;
block.value[1] = 0x02;
block.value[2] = 0x03;
block.value[3] = 0x04;
block.value[4] = 0x04;
block.value[5] = 0x08;
block.value[6] = 0x04;
while(nfc_magic_worker->state == NfcMagicWorkerStateWipe) {
magic_deactivate();
furi_delay_ms(300);
if(!magic_wupa()) continue;
if(!magic_wipe()) continue;
if(!magic_data_access_cmd()) continue;
if(!magic_write_blk(0, &block)) continue;
nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
magic_deactivate();
break;
}
magic_deactivate();
}

View File

@ -0,0 +1,38 @@
#pragma once
#include <lib/nfc/nfc_device.h>
typedef struct NfcMagicWorker NfcMagicWorker;
typedef enum {
NfcMagicWorkerStateReady,
NfcMagicWorkerStateCheck,
NfcMagicWorkerStateWrite,
NfcMagicWorkerStateWipe,
NfcMagicWorkerStateStop,
} NfcMagicWorkerState;
typedef enum {
NfcMagicWorkerEventSuccess,
NfcMagicWorkerEventFail,
NfcMagicWorkerEventCardDetected,
NfcMagicWorkerEventNoCardDetected,
NfcMagicWorkerEventWrongCard,
} NfcMagicWorkerEvent;
typedef bool (*NfcMagicWorkerCallback)(NfcMagicWorkerEvent event, void* context);
NfcMagicWorker* nfc_magic_worker_alloc();
void nfc_magic_worker_free(NfcMagicWorker* nfc_magic_worker);
void nfc_magic_worker_stop(NfcMagicWorker* nfc_magic_worker);
void nfc_magic_worker_start(
NfcMagicWorker* nfc_magic_worker,
NfcMagicWorkerState state,
NfcDeviceData* dev_data,
NfcMagicWorkerCallback callback,
void* context);

View File

@ -0,0 +1,24 @@
#pragma once
#include <furi.h>
#include "nfc_magic_worker.h"
struct NfcMagicWorker {
FuriThread* thread;
NfcDeviceData* dev_data;
NfcMagicWorkerCallback callback;
void* context;
NfcMagicWorkerState state;
};
int32_t nfc_magic_worker_task(void* context);
void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker);
void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker);
void nfc_magic_worker_wipe(NfcMagicWorker* nfc_magic_worker);

View File

@ -0,0 +1,30 @@
#include "nfc_magic_scene.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const nfc_magic_on_enter_handlers[])(void*) = {
#include "nfc_magic_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_event handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
bool (*const nfc_magic_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "nfc_magic_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_exit handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
void (*const nfc_magic_on_exit_handlers[])(void* context) = {
#include "nfc_magic_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers nfc_magic_scene_handlers = {
.on_enter_handlers = nfc_magic_on_enter_handlers,
.on_event_handlers = nfc_magic_on_event_handlers,
.on_exit_handlers = nfc_magic_on_exit_handlers,
.scene_num = NfcMagicSceneNum,
};

View File

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

View File

@ -0,0 +1,87 @@
#include "../nfc_magic_i.h"
enum {
NfcMagicSceneCheckStateCardSearch,
NfcMagicSceneCheckStateCardFound,
};
bool nfc_magic_check_worker_callback(NfcMagicWorkerEvent event, void* context) {
furi_assert(context);
NfcMagic* nfc_magic = context;
view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, event);
return true;
}
static void nfc_magic_scene_check_setup_view(NfcMagic* nfc_magic) {
Popup* popup = nfc_magic->popup;
popup_reset(popup);
uint32_t state = scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneCheck);
if(state == NfcMagicSceneCheckStateCardSearch) {
popup_set_icon(nfc_magic->popup, 0, 8, &I_NFC_manual_60x50);
popup_set_text(
nfc_magic->popup, "Apply card to\nthe back", 128, 32, AlignRight, AlignCenter);
} else {
popup_set_icon(popup, 12, 23, &I_Loading_24);
popup_set_header(popup, "Checking\nDon't move...", 52, 32, AlignLeft, AlignCenter);
}
view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewPopup);
}
void nfc_magic_scene_check_on_enter(void* context) {
NfcMagic* nfc_magic = context;
scene_manager_set_scene_state(
nfc_magic->scene_manager, NfcMagicSceneCheck, NfcMagicSceneCheckStateCardSearch);
nfc_magic_scene_check_setup_view(nfc_magic);
// Setup and start worker
nfc_magic_worker_start(
nfc_magic->worker,
NfcMagicWorkerStateCheck,
&nfc_magic->nfc_dev->dev_data,
nfc_magic_check_worker_callback,
nfc_magic);
nfc_magic_blink_start(nfc_magic);
}
bool nfc_magic_scene_check_on_event(void* context, SceneManagerEvent event) {
NfcMagic* nfc_magic = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcMagicWorkerEventSuccess) {
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneMagicInfo);
consumed = true;
} else if(event.event == NfcMagicWorkerEventWrongCard) {
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneNotMagic);
consumed = true;
} else if(event.event == NfcMagicWorkerEventCardDetected) {
scene_manager_set_scene_state(
nfc_magic->scene_manager, NfcMagicSceneCheck, NfcMagicSceneCheckStateCardFound);
nfc_magic_scene_check_setup_view(nfc_magic);
consumed = true;
} else if(event.event == NfcMagicWorkerEventNoCardDetected) {
scene_manager_set_scene_state(
nfc_magic->scene_manager, NfcMagicSceneCheck, NfcMagicSceneCheckStateCardSearch);
nfc_magic_scene_check_setup_view(nfc_magic);
consumed = true;
}
}
return consumed;
}
void nfc_magic_scene_check_on_exit(void* context) {
NfcMagic* nfc_magic = context;
nfc_magic_worker_stop(nfc_magic->worker);
scene_manager_set_scene_state(
nfc_magic->scene_manager, NfcMagicSceneCheck, NfcMagicSceneCheckStateCardSearch);
// Clear view
popup_reset(nfc_magic->popup);
nfc_magic_blink_stop(nfc_magic);
}

View File

@ -0,0 +1,12 @@
ADD_SCENE(nfc_magic, start, Start)
ADD_SCENE(nfc_magic, file_select, FileSelect)
ADD_SCENE(nfc_magic, write_confirm, WriteConfirm)
ADD_SCENE(nfc_magic, wrong_card, WrongCard)
ADD_SCENE(nfc_magic, write, Write)
ADD_SCENE(nfc_magic, write_fail, WriteFail)
ADD_SCENE(nfc_magic, success, Success)
ADD_SCENE(nfc_magic, check, Check)
ADD_SCENE(nfc_magic, not_magic, NotMagic)
ADD_SCENE(nfc_magic, magic_info, MagicInfo)
ADD_SCENE(nfc_magic, wipe, Wipe)
ADD_SCENE(nfc_magic, wipe_fail, WipeFail)

View File

@ -0,0 +1,34 @@
#include "../nfc_magic_i.h"
static bool nfc_magic_scene_file_select_is_file_suitable(NfcDevice* nfc_dev) {
return (nfc_dev->format == NfcDeviceSaveFormatMifareClassic) &&
(nfc_dev->dev_data.mf_classic_data.type == MfClassicType1k) &&
(nfc_dev->dev_data.nfc_data.uid_len == 4);
}
void nfc_magic_scene_file_select_on_enter(void* context) {
NfcMagic* nfc_magic = context;
// Process file_select return
nfc_device_set_loading_callback(nfc_magic->nfc_dev, nfc_magic_show_loading_popup, nfc_magic);
if(nfc_file_select(nfc_magic->nfc_dev)) {
if(nfc_magic_scene_file_select_is_file_suitable(nfc_magic->nfc_dev)) {
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWriteConfirm);
} else {
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWrongCard);
}
} else {
scene_manager_previous_scene(nfc_magic->scene_manager);
}
}
bool nfc_magic_scene_file_select_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void nfc_magic_scene_file_select_on_exit(void* context) {
NfcMagic* nfc_magic = context;
nfc_device_set_loading_callback(nfc_magic->nfc_dev, NULL, nfc_magic);
}

View File

@ -0,0 +1,45 @@
#include "../nfc_magic_i.h"
void nfc_magic_scene_magic_info_widget_callback(
GuiButtonType result,
InputType type,
void* context) {
NfcMagic* nfc_magic = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, result);
}
}
void nfc_magic_scene_magic_info_on_enter(void* context) {
NfcMagic* nfc_magic = context;
Widget* widget = nfc_magic->widget;
notification_message(nfc_magic->notifications, &sequence_success);
widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48);
widget_add_string_element(
widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Magic card detected");
widget_add_button_element(
widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_magic_info_widget_callback, nfc_magic);
// Setup and start worker
view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget);
}
bool nfc_magic_scene_magic_info_on_event(void* context, SceneManagerEvent event) {
NfcMagic* nfc_magic = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeLeft) {
consumed = scene_manager_previous_scene(nfc_magic->scene_manager);
}
}
return consumed;
}
void nfc_magic_scene_magic_info_on_exit(void* context) {
NfcMagic* nfc_magic = context;
widget_reset(nfc_magic->widget);
}

View File

@ -0,0 +1,44 @@
#include "../nfc_magic_i.h"
void nfc_magic_scene_not_magic_widget_callback(GuiButtonType result, InputType type, void* context) {
NfcMagic* nfc_magic = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, result);
}
}
void nfc_magic_scene_not_magic_on_enter(void* context) {
NfcMagic* nfc_magic = context;
Widget* widget = nfc_magic->widget;
notification_message(nfc_magic->notifications, &sequence_error);
// widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48);
widget_add_string_element(
widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card");
widget_add_string_multiline_element(
widget, 4, 17, AlignLeft, AlignTop, FontSecondary, "Not a magic\ncard");
widget_add_button_element(
widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_not_magic_widget_callback, nfc_magic);
// Setup and start worker
view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget);
}
bool nfc_magic_scene_not_magic_on_event(void* context, SceneManagerEvent event) {
NfcMagic* nfc_magic = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeLeft) {
consumed = scene_manager_previous_scene(nfc_magic->scene_manager);
}
}
return consumed;
}
void nfc_magic_scene_not_magic_on_exit(void* context) {
NfcMagic* nfc_magic = context;
widget_reset(nfc_magic->widget);
}

View File

@ -0,0 +1,61 @@
#include "../nfc_magic_i.h"
enum SubmenuIndex {
SubmenuIndexCheck,
SubmenuIndexWriteGen1A,
SubmenuIndexWipe,
};
void nfc_magic_scene_start_submenu_callback(void* context, uint32_t index) {
NfcMagic* nfc_magic = context;
view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, index);
}
void nfc_magic_scene_start_on_enter(void* context) {
NfcMagic* nfc_magic = context;
Submenu* submenu = nfc_magic->submenu;
submenu_add_item(
submenu,
"Check Magic Tag",
SubmenuIndexCheck,
nfc_magic_scene_start_submenu_callback,
nfc_magic);
submenu_add_item(
submenu,
"Write Gen1A",
SubmenuIndexWriteGen1A,
nfc_magic_scene_start_submenu_callback,
nfc_magic);
submenu_add_item(
submenu, "Wipe", SubmenuIndexWipe, nfc_magic_scene_start_submenu_callback, nfc_magic);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneStart));
view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewMenu);
}
bool nfc_magic_scene_start_on_event(void* context, SceneManagerEvent event) {
NfcMagic* nfc_magic = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexCheck) {
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneCheck);
consumed = true;
} else if(event.event == SubmenuIndexWriteGen1A) {
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneFileSelect);
consumed = true;
} else if(event.event == SubmenuIndexWipe) {
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWipe);
consumed = true;
}
scene_manager_set_scene_state(nfc_magic->scene_manager, NfcMagicSceneStart, event.event);
}
return consumed;
}
void nfc_magic_scene_start_on_exit(void* context) {
NfcMagic* nfc_magic = context;
submenu_reset(nfc_magic->submenu);
}

View File

@ -0,0 +1,42 @@
#include "../nfc_magic_i.h"
void nfc_magic_scene_success_popup_callback(void* context) {
NfcMagic* nfc_magic = context;
view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, NfcMagicCustomEventViewExit);
}
void nfc_magic_scene_success_on_enter(void* context) {
NfcMagic* nfc_magic = context;
notification_message(nfc_magic->notifications, &sequence_success);
Popup* popup = nfc_magic->popup;
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
popup_set_header(popup, "Success!", 10, 20, AlignLeft, AlignBottom);
popup_set_timeout(popup, 1500);
popup_set_context(popup, nfc_magic);
popup_set_callback(popup, nfc_magic_scene_success_popup_callback);
popup_enable_timeout(popup);
view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewPopup);
}
bool nfc_magic_scene_success_on_event(void* context, SceneManagerEvent event) {
NfcMagic* nfc_magic = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcMagicCustomEventViewExit) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc_magic->scene_manager, NfcMagicSceneStart);
}
}
return consumed;
}
void nfc_magic_scene_success_on_exit(void* context) {
NfcMagic* nfc_magic = context;
// Clear view
popup_reset(nfc_magic->popup);
}

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