mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-09 05:49:09 -07:00
Merge branch 'dev' into feature/random_mouse_jiggler
This commit is contained in:
@@ -7,10 +7,13 @@
|
||||
#include <nfc/nfc_poller.h>
|
||||
#include <nfc/nfc_listener.h>
|
||||
#include <nfc/protocols/iso14443_3a/iso14443_3a.h>
|
||||
#include <nfc/protocols/iso14443_3a/iso14443_3a_poller.h>
|
||||
#include <nfc/protocols/iso14443_3a/iso14443_3a_poller_sync.h>
|
||||
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
|
||||
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h>
|
||||
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
|
||||
#include <nfc/protocols/mf_classic/mf_classic_poller.h>
|
||||
#include <nfc/nfc_poller.h>
|
||||
|
||||
#include <toolbox/keys_dict.h>
|
||||
#include <nfc/nfc.h>
|
||||
@@ -22,6 +25,23 @@
|
||||
#define NFC_TEST_NFC_DEV_PATH EXT_PATH("unit_tests/nfc/nfc_device_test.nfc")
|
||||
#define NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH EXT_PATH("unit_tests/mf_dict.nfc")
|
||||
|
||||
#define NFC_TEST_FLAG_WORKER_DONE (1)
|
||||
|
||||
typedef enum {
|
||||
NfcTestMfClassicSendFrameTestStateAuth,
|
||||
NfcTestMfClassicSendFrameTestStateReadBlock,
|
||||
|
||||
NfcTestMfClassicSendFrameTestStateFail,
|
||||
NfcTestMfClassicSendFrameTestStateSuccess,
|
||||
} NfcTestMfClassicSendFrameTestState;
|
||||
|
||||
typedef struct {
|
||||
NfcTestMfClassicSendFrameTestState state;
|
||||
BitBuffer* tx_buf;
|
||||
BitBuffer* rx_buf;
|
||||
FuriThreadId thread_id;
|
||||
} NfcTestMfClassicSendFrameTest;
|
||||
|
||||
typedef struct {
|
||||
Storage* storage;
|
||||
} NfcTest;
|
||||
@@ -435,6 +455,109 @@ static void mf_classic_value_block(void) {
|
||||
nfc_free(poller);
|
||||
}
|
||||
|
||||
NfcCommand mf_classic_poller_send_frame_callback(NfcGenericEventEx event, void* context) {
|
||||
furi_check(event.poller);
|
||||
furi_check(event.parent_event_data);
|
||||
furi_check(context);
|
||||
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
MfClassicPoller* instance = event.poller;
|
||||
NfcTestMfClassicSendFrameTest* frame_test = context;
|
||||
Iso14443_3aPollerEvent* iso3_event = event.parent_event_data;
|
||||
|
||||
MfClassicError error = MfClassicErrorNone;
|
||||
if(iso3_event->type == Iso14443_3aPollerEventTypeReady) {
|
||||
if(frame_test->state == NfcTestMfClassicSendFrameTestStateAuth) {
|
||||
MfClassicKey key = {
|
||||
.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||
};
|
||||
error = mf_classic_poller_auth(instance, 0, &key, MfClassicKeyTypeA, NULL);
|
||||
frame_test->state = (error == MfClassicErrorNone) ?
|
||||
NfcTestMfClassicSendFrameTestStateReadBlock :
|
||||
NfcTestMfClassicSendFrameTestStateFail;
|
||||
} else if(frame_test->state == NfcTestMfClassicSendFrameTestStateReadBlock) {
|
||||
do {
|
||||
const uint8_t read_block_cmd[] = {
|
||||
0x30,
|
||||
0x01,
|
||||
0x8b,
|
||||
0xb9,
|
||||
};
|
||||
bit_buffer_copy_bytes(frame_test->tx_buf, read_block_cmd, sizeof(read_block_cmd));
|
||||
|
||||
error = mf_classic_poller_send_encrypted_frame(
|
||||
instance, frame_test->tx_buf, frame_test->rx_buf, 200000);
|
||||
if(error != MfClassicErrorNone) break;
|
||||
if(bit_buffer_get_size_bytes(frame_test->rx_buf) != 18) {
|
||||
error = MfClassicErrorProtocol;
|
||||
break;
|
||||
}
|
||||
|
||||
const uint8_t* rx_data = bit_buffer_get_data(frame_test->rx_buf);
|
||||
const uint8_t rx_data_ref[16] = {0};
|
||||
if(memcmp(rx_data, rx_data_ref, sizeof(rx_data_ref)) != 0) {
|
||||
error = MfClassicErrorProtocol;
|
||||
break;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
frame_test->state = (error == MfClassicErrorNone) ?
|
||||
NfcTestMfClassicSendFrameTestStateSuccess :
|
||||
NfcTestMfClassicSendFrameTestStateFail;
|
||||
} else if(frame_test->state == NfcTestMfClassicSendFrameTestStateSuccess) {
|
||||
command = NfcCommandStop;
|
||||
} else if(frame_test->state == NfcTestMfClassicSendFrameTestStateFail) {
|
||||
command = NfcCommandStop;
|
||||
}
|
||||
} else {
|
||||
frame_test->state = NfcTestMfClassicSendFrameTestStateFail;
|
||||
command = NfcCommandStop;
|
||||
}
|
||||
|
||||
if(command == NfcCommandStop) {
|
||||
furi_thread_flags_set(frame_test->thread_id, NFC_TEST_FLAG_WORKER_DONE);
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
MU_TEST(mf_classic_send_frame_test) {
|
||||
Nfc* poller = nfc_alloc();
|
||||
Nfc* listener = nfc_alloc();
|
||||
|
||||
NfcDevice* nfc_device = nfc_device_alloc();
|
||||
nfc_data_generator_fill_data(NfcDataGeneratorTypeMfClassic4k_7b, nfc_device);
|
||||
NfcListener* mfc_listener = nfc_listener_alloc(
|
||||
listener, NfcProtocolMfClassic, nfc_device_get_data(nfc_device, NfcProtocolMfClassic));
|
||||
nfc_listener_start(mfc_listener, NULL, NULL);
|
||||
|
||||
NfcPoller* mfc_poller = nfc_poller_alloc(poller, NfcProtocolMfClassic);
|
||||
NfcTestMfClassicSendFrameTest context = {
|
||||
.state = NfcTestMfClassicSendFrameTestStateAuth,
|
||||
.thread_id = furi_thread_get_current_id(),
|
||||
.tx_buf = bit_buffer_alloc(32),
|
||||
.rx_buf = bit_buffer_alloc(32),
|
||||
};
|
||||
nfc_poller_start_ex(mfc_poller, mf_classic_poller_send_frame_callback, &context);
|
||||
|
||||
uint32_t flag =
|
||||
furi_thread_flags_wait(NFC_TEST_FLAG_WORKER_DONE, FuriFlagWaitAny, FuriWaitForever);
|
||||
mu_assert(flag == NFC_TEST_FLAG_WORKER_DONE, "Wrong thread flag");
|
||||
nfc_poller_stop(mfc_poller);
|
||||
nfc_poller_free(mfc_poller);
|
||||
|
||||
mu_assert(
|
||||
context.state == NfcTestMfClassicSendFrameTestStateSuccess, "Wrong test state at the end");
|
||||
|
||||
bit_buffer_free(context.tx_buf);
|
||||
bit_buffer_free(context.rx_buf);
|
||||
nfc_listener_stop(mfc_listener);
|
||||
nfc_listener_free(mfc_listener);
|
||||
nfc_device_free(nfc_device);
|
||||
nfc_free(listener);
|
||||
nfc_free(poller);
|
||||
}
|
||||
|
||||
MU_TEST(mf_classic_dict_test) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
if(storage_common_stat(storage, NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, NULL) == FSE_OK) {
|
||||
@@ -538,11 +661,11 @@ MU_TEST_SUITE(nfc) {
|
||||
MU_RUN_TEST(mf_classic_1k_7b_file_test);
|
||||
MU_RUN_TEST(mf_classic_4k_4b_file_test);
|
||||
MU_RUN_TEST(mf_classic_4k_7b_file_test);
|
||||
MU_RUN_TEST(mf_classic_reader);
|
||||
|
||||
MU_RUN_TEST(mf_classic_reader);
|
||||
MU_RUN_TEST(mf_classic_write);
|
||||
MU_RUN_TEST(mf_classic_value_block);
|
||||
|
||||
MU_RUN_TEST(mf_classic_send_frame_test);
|
||||
MU_RUN_TEST(mf_classic_dict_test);
|
||||
|
||||
nfc_test_free();
|
||||
|
||||
9
applications/examples/example_adc/application.fam
Normal file
9
applications/examples/example_adc/application.fam
Normal file
@@ -0,0 +1,9 @@
|
||||
App(
|
||||
appid="example_adc",
|
||||
name="Example: ADC",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="example_adc_main",
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
fap_category="Examples",
|
||||
)
|
||||
176
applications/examples/example_adc/example_adc.c
Normal file
176
applications/examples/example_adc/example_adc.c
Normal file
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* @file example_adc.c
|
||||
* @brief ADC example.
|
||||
*/
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/elements.h>
|
||||
#include <input/input.h>
|
||||
|
||||
const uint8_t font[] =
|
||||
"`\2\3\2\3\4\1\2\4\5\11\0\376\6\376\7\377\1M\2\263\3\370 \6\315\364\371\6!\12\315"
|
||||
"\364\201\260\35\312Q\0\42\11\315tJI\316\13\0#\14\315\264\223dP*\203R'\1$\15\315\264"
|
||||
"\262A\311\266D\251l\71\0%\15\315\264\7%\61)J\42\345 \0&\14\315\264\263$\13\223\266$"
|
||||
"\7\1'\10\315\364\201\60\347\10(\10\315\364\32[\313\0)\11\315\64\322b[\35\2*\12\315\264\263"
|
||||
"(\222j\71\15+\11\315\364I\331\226\23\1,\10\315\364\271\205Y\10-\10\315\364\31t\26\0.\10"
|
||||
"\315\364\71\346(\0/\14\315\364\221\60\13\263\60\13C\0\60\13\315\264\245Jb)E:\12\61\12\315"
|
||||
"\364\201Ll\333A\0\62\12\315\264\245bV\33r\20\63\13\315\264\245Z\232D\221\216\2\64\14\315\364"
|
||||
"\201LJ\242!\313v\20\65\14\315t\207$\134\223(\322Q\0\66\13\315\264\245p\252D\221\216\2\67"
|
||||
"\12\315t\207\60+\326a\0\70\13\315\264\245\222T\211\42\35\5\71\13\315\264\245J\24\215\221\216\2:"
|
||||
"\11\315\364i\71!G\1;\12\315\364I\71!\314B\0<\11\315\364\341\254Z\7\1=\12\315\364)"
|
||||
"C<\344$\0>\11\315\364\301\264V\207\1\77\12\315\264\245Z\35\312a\0@\14\315\264\245J\242$"
|
||||
"J\272\203\0A\15\315\264\245J\224\14I\224D\71\10B\13\315t\247\312T\211\222\35\5C\12\315\264"
|
||||
"\245JX\212t\24D\15\315t\247J\224DI\224\354(\0E\14\315t\207$\234\302p\310A\0F"
|
||||
"\12\315t\207$\234\302:\1G\14\315\264\245J\230(Q\244\243\0H\17\315t\243$J\206$J\242"
|
||||
"$\312A\0I\11\315\264\267\260m\7\1J\12\315\364\221\260%\212t\24K\14\315t\243\244\244iI"
|
||||
"T\7\1L\11\315t\303\216C\16\2M\17\315t\243dH\206$J\242$\312A\0N\16\315t\243"
|
||||
"D\251(Q\22%Q\16\2O\15\315\264\245J\224DI\24\351(\0P\12\315t\247J\224LaN"
|
||||
"Q\15\315\264\245J\224DI\42\251\61\0R\14\315t\247J\224L\225(\7\1S\13\315\264\245\222\232"
|
||||
"D\221\216\2T\10\315\264\267\260;\12U\16\315t\243$J\242$J\242HG\1V\15\315t\243$"
|
||||
"J\242$Jj\71\14W\17\315t\243$J\242dH\206$\312A\0X\15\315t\243$\212\64\251\22"
|
||||
"\345 \0Y\13\315t\243$Jja\35\6Z\12\315t\207\60k\34r\20[\10\315\264\264\260G\31"
|
||||
"\134\12\315\264\303\64L\303\64\14]\10\315t\304\276\351\0^\11\315\364\201,\311\271\1_\7\315\364y"
|
||||
"\35\4`\10\315t\322\234'\0a\14\315\364IK\224$R\222\203\0b\13\315t\303p\252D\311\216"
|
||||
"\2c\12\315\364IR%\335A\0d\14\315\364\221\60Z\242$\212v\20e\12\315\364I\322\220\244;"
|
||||
"\10f\12\315\364\221,\333\302:\12g\14\315\364IK\224D\321\30I\0h\14\315t\303p\252DI"
|
||||
"\224\203\0i\12\315\364\201\34\21k;\10j\12\315\364\201\34\21\273e\0k\13\315t\303J\244%Q"
|
||||
"\35\4l\10\315\264\305n;\10m\14\315\364)CRQ\22\245\216\1n\13\315\364)%\245\224D\71"
|
||||
"\10o\12\315\364IR%\212t\24p\13\315\364)S%J\246\60\4q\13\315\364IK\224D\321X"
|
||||
"\1r\11\315\364)%\245\230\23s\12\315\364I\313\232\354(\0t\13\315\364\201\60\333\302\64\7\1u"
|
||||
"\15\315\364)Q\22%\211\224\344 \0v\13\315\364)Q\22%\265\34\6w\13\315\364)\25%Q\272"
|
||||
"\203\0x\12\315\364)Q\244Iu\20y\15\315\364)Q\22%Q\64F\22\0z\12\315\364)CV"
|
||||
"\33r\20{\12\315\364\212\265\64\254&\0|\7\315\264\302~\7}\12\315t\322\260\232\205\265\14~\11"
|
||||
"\315\364II;\13\0\177\6\315\364\371\6\0\0\0\4\377\377\0";
|
||||
|
||||
#define FONT_HEIGHT (8u)
|
||||
|
||||
typedef float (*ValueConverter)(FuriHalAdcHandle* handle, uint16_t value);
|
||||
|
||||
typedef struct {
|
||||
const GpioPinRecord* pin;
|
||||
float value;
|
||||
ValueConverter converter;
|
||||
const char* suffix;
|
||||
} DataItem;
|
||||
|
||||
typedef struct {
|
||||
size_t count;
|
||||
DataItem* items;
|
||||
} Data;
|
||||
|
||||
const GpioPinRecord item_vref = {.name = "VREF", .channel = FuriHalAdcChannelVREFINT};
|
||||
const GpioPinRecord item_temp = {.name = "TEMP", .channel = FuriHalAdcChannelTEMPSENSOR};
|
||||
const GpioPinRecord item_vbat = {.name = "VBAT", .channel = FuriHalAdcChannelVBAT};
|
||||
|
||||
static void app_draw_callback(Canvas* canvas, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
Data* data = ctx;
|
||||
|
||||
canvas_set_custom_u8g2_font(canvas, font);
|
||||
char buffer[64];
|
||||
int32_t x = 0, y = FONT_HEIGHT;
|
||||
for(size_t i = 0; i < data->count; i++) {
|
||||
if(i == canvas_height(canvas) / FONT_HEIGHT) {
|
||||
x = 64;
|
||||
y = FONT_HEIGHT;
|
||||
}
|
||||
|
||||
snprintf(
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
"%4s: %4.0f%s\n",
|
||||
data->items[i].pin->name,
|
||||
(double)data->items[i].value,
|
||||
data->items[i].suffix);
|
||||
canvas_draw_str(canvas, x, y, buffer);
|
||||
y += FONT_HEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
static void app_input_callback(InputEvent* input_event, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
FuriMessageQueue* event_queue = ctx;
|
||||
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
|
||||
}
|
||||
|
||||
int32_t example_adc_main(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
// Data
|
||||
Data data = {};
|
||||
for(size_t i = 0; i < gpio_pins_count; i++) {
|
||||
if(gpio_pins[i].channel != FuriHalAdcChannelNone) {
|
||||
data.count++;
|
||||
}
|
||||
}
|
||||
data.count += 3; // Special channels
|
||||
data.items = malloc(data.count * sizeof(DataItem));
|
||||
size_t item_pos = 0;
|
||||
for(size_t i = 0; i < gpio_pins_count; i++) {
|
||||
if(gpio_pins[i].channel != FuriHalAdcChannelNone) {
|
||||
furi_hal_gpio_init(gpio_pins[i].pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
data.items[item_pos].pin = &gpio_pins[i];
|
||||
data.items[item_pos].converter = furi_hal_adc_convert_to_voltage;
|
||||
data.items[item_pos].suffix = "mV";
|
||||
item_pos++;
|
||||
}
|
||||
}
|
||||
data.items[item_pos].pin = &item_vref;
|
||||
data.items[item_pos].converter = furi_hal_adc_convert_vref;
|
||||
data.items[item_pos].suffix = "mV";
|
||||
item_pos++;
|
||||
data.items[item_pos].pin = &item_temp;
|
||||
data.items[item_pos].converter = furi_hal_adc_convert_temp;
|
||||
data.items[item_pos].suffix = "C";
|
||||
item_pos++;
|
||||
data.items[item_pos].pin = &item_vbat;
|
||||
data.items[item_pos].converter = furi_hal_adc_convert_vbat;
|
||||
data.items[item_pos].suffix = "mV";
|
||||
item_pos++;
|
||||
furi_assert(item_pos == data.count);
|
||||
|
||||
// Alloc message queue
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||
|
||||
// Configure view port
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, app_draw_callback, &data);
|
||||
view_port_input_callback_set(view_port, app_input_callback, event_queue);
|
||||
|
||||
// Register view port in GUI
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
// Initialize ADC
|
||||
FuriHalAdcHandle* adc_handle = furi_hal_adc_acquire();
|
||||
furi_hal_adc_configure(adc_handle);
|
||||
|
||||
// Process events
|
||||
InputEvent event;
|
||||
bool running = true;
|
||||
while(running) {
|
||||
if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {
|
||||
if(event.type == InputTypePress && event.key == InputKeyBack) {
|
||||
running = false;
|
||||
}
|
||||
} else {
|
||||
for(size_t i = 0; i < data.count; i++) {
|
||||
data.items[i].value = data.items[i].converter(
|
||||
adc_handle, furi_hal_adc_read(adc_handle, data.items[i].pin->channel));
|
||||
}
|
||||
view_port_update(view_port);
|
||||
}
|
||||
}
|
||||
|
||||
furi_hal_adc_release(adc_handle);
|
||||
view_port_enabled_set(view_port, false);
|
||||
gui_remove_view_port(gui, view_port);
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
furi_record_close(RECORD_GUI);
|
||||
free(data.items);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -94,7 +94,7 @@ int32_t example_custom_font_main(void* p) {
|
||||
|
||||
// Configure view port
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, app_draw_callback, view_port);
|
||||
view_port_draw_callback_set(view_port, app_draw_callback, NULL);
|
||||
view_port_input_callback_set(view_port, app_input_callback, event_queue);
|
||||
|
||||
// Register view port in GUI
|
||||
|
||||
@@ -39,7 +39,7 @@ int32_t example_images_main(void* p) {
|
||||
|
||||
// Configure view port
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, app_draw_callback, view_port);
|
||||
view_port_draw_callback_set(view_port, app_draw_callback, NULL);
|
||||
view_port_input_callback_set(view_port, app_input_callback, event_queue);
|
||||
|
||||
// Register view port in GUI
|
||||
|
||||
@@ -7,23 +7,40 @@ void ibutton_scene_delete_confirm_on_enter(void* context) {
|
||||
Widget* widget = ibutton->widget;
|
||||
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
FuriString* uid = furi_string_alloc();
|
||||
|
||||
widget_add_button_element(widget, GuiButtonTypeLeft, "Back", ibutton_widget_callback, context);
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeRight, "Delete", ibutton_widget_callback, context);
|
||||
|
||||
furi_string_printf(tmp, "\e#Delete %s?\e#", ibutton->key_name);
|
||||
furi_string_printf(tmp, "\e#Delete %s?\e#\n", ibutton->key_name);
|
||||
|
||||
ibutton_protocols_render_uid(ibutton->protocols, key, uid);
|
||||
|
||||
furi_string_cat(tmp, uid);
|
||||
|
||||
furi_string_push_back(tmp, '\n');
|
||||
|
||||
const char* protocol =
|
||||
ibutton_protocols_get_name(ibutton->protocols, ibutton_key_get_protocol_id(key));
|
||||
const char* manufacturer =
|
||||
ibutton_protocols_get_manufacturer(ibutton->protocols, ibutton_key_get_protocol_id(key));
|
||||
|
||||
if(strcasecmp(protocol, manufacturer) != 0 && strcasecmp(manufacturer, "N/A") != 0) {
|
||||
furi_string_cat_printf(tmp, "%s ", manufacturer);
|
||||
}
|
||||
|
||||
furi_string_cat(tmp, protocol);
|
||||
|
||||
widget_add_text_box_element(
|
||||
widget, 0, 0, 128, 23, AlignCenter, AlignCenter, furi_string_get_cstr(tmp), false);
|
||||
widget, 0, 0, 128, 64, AlignCenter, AlignTop, furi_string_get_cstr(tmp), false);
|
||||
|
||||
furi_string_reset(tmp);
|
||||
ibutton_protocols_render_brief_data(ibutton->protocols, key, tmp);
|
||||
|
||||
widget_add_string_multiline_element(
|
||||
widget, 128 / 2, 24, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(tmp));
|
||||
furi_string_reset(uid);
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
|
||||
furi_string_free(tmp);
|
||||
furi_string_free(uid);
|
||||
}
|
||||
|
||||
bool ibutton_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
@@ -28,10 +28,10 @@ void ibutton_scene_emulate_on_enter(void* context) {
|
||||
furi_string_empty(ibutton->file_path) ? "Unsaved Key" : ibutton->key_name);
|
||||
|
||||
widget_add_text_box_element(
|
||||
widget, 52, 30, 75, 40, AlignCenter, AlignCenter, furi_string_get_cstr(tmp), true);
|
||||
widget, 52, 24, 75, 40, AlignCenter, AlignTop, furi_string_get_cstr(tmp), true);
|
||||
|
||||
widget_add_string_multiline_element(
|
||||
widget, 88, 5, AlignCenter, AlignTop, FontPrimary, "iButton\nemulating");
|
||||
widget, 88, 10, AlignCenter, AlignTop, FontPrimary, "Emulating");
|
||||
|
||||
ibutton_worker_emulate_set_callback(ibutton->worker, ibutton_scene_emulate_callback, ibutton);
|
||||
ibutton_worker_emulate_start(ibutton->worker, key);
|
||||
|
||||
@@ -8,19 +8,35 @@ void ibutton_scene_info_on_enter(void* context) {
|
||||
const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key);
|
||||
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
FuriString* keynumber = furi_string_alloc();
|
||||
FuriString* brief_data = furi_string_alloc();
|
||||
|
||||
ibutton_protocols_render_brief_data(ibutton->protocols, key, keynumber);
|
||||
if((strcmp(
|
||||
ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id),
|
||||
ibutton_protocols_get_name(ibutton->protocols, protocol_id)) != 0) &&
|
||||
(strcmp(ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id), "N/A") != 0)) {
|
||||
furi_string_printf(
|
||||
tmp,
|
||||
"Name:%s\n\e#%s %s\e#\n",
|
||||
ibutton->key_name,
|
||||
ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id),
|
||||
ibutton_protocols_get_name(ibutton->protocols, protocol_id));
|
||||
} else {
|
||||
furi_string_printf(
|
||||
tmp,
|
||||
"Name:%s\n\e#%s\e#\n",
|
||||
ibutton->key_name,
|
||||
ibutton_protocols_get_name(ibutton->protocols, protocol_id));
|
||||
}
|
||||
|
||||
furi_string_printf(
|
||||
tmp,
|
||||
"\e#%s\n[%s]\e#\n%s",
|
||||
ibutton->key_name,
|
||||
ibutton_protocols_get_name(ibutton->protocols, protocol_id),
|
||||
furi_string_get_cstr(keynumber));
|
||||
ibutton_protocols_render_brief_data(ibutton->protocols, key, brief_data);
|
||||
|
||||
furi_string_cat(tmp, brief_data);
|
||||
|
||||
widget_add_text_box_element(
|
||||
widget, 0, 2, 128, 64, AlignLeft, AlignTop, furi_string_get_cstr(tmp), true);
|
||||
widget, 0, 0, 128, 64, AlignLeft, AlignTop, furi_string_get_cstr(tmp), false);
|
||||
|
||||
furi_string_reset(tmp);
|
||||
furi_string_reset(brief_data);
|
||||
|
||||
if(ibutton_protocols_get_features(ibutton->protocols, protocol_id) &
|
||||
iButtonProtocolFeatureExtData) {
|
||||
@@ -30,7 +46,7 @@ void ibutton_scene_info_on_enter(void* context) {
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
|
||||
furi_string_free(tmp);
|
||||
furi_string_free(keynumber);
|
||||
furi_string_free(brief_data);
|
||||
}
|
||||
|
||||
bool ibutton_scene_info_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
@@ -12,9 +12,9 @@ void ibutton_scene_read_on_enter(void* context) {
|
||||
iButtonKey* key = ibutton->key;
|
||||
iButtonWorker* worker = ibutton->worker;
|
||||
|
||||
popup_set_header(popup, "iButton", 95, 26, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, "Apply key to\nFlipper's back", 95, 30, AlignCenter, AlignTop);
|
||||
popup_set_icon(popup, 0, 5, &I_DolphinWait_61x59);
|
||||
popup_set_header(popup, "Reading", 95, 26, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, "Connect key\nwith pogo pins", 95, 30, AlignCenter, AlignTop);
|
||||
popup_set_icon(popup, 0, 10, &I_DolphinWait_59x54);
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);
|
||||
|
||||
|
||||
@@ -14,13 +14,10 @@ void ibutton_scene_read_error_on_enter(void* context) {
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeRight, "More", ibutton_widget_callback, context);
|
||||
|
||||
widget_add_string_element(
|
||||
widget, 128 / 2, 2, AlignCenter, AlignTop, FontPrimary, "Read Error");
|
||||
|
||||
ibutton_protocols_render_error(ibutton->protocols, key, tmp);
|
||||
|
||||
widget_add_string_multiline_element(
|
||||
widget, 128 / 2, 16, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(tmp));
|
||||
widget_add_text_box_element(
|
||||
widget, 0, 0, 128, 48, AlignCenter, AlignTop, furi_string_get_cstr(tmp), false);
|
||||
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageError);
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageRedOn);
|
||||
|
||||
@@ -30,19 +30,10 @@ void ibutton_scene_read_key_menu_on_enter(void* context) {
|
||||
ibutton_scene_read_key_menu_submenu_callback,
|
||||
ibutton);
|
||||
|
||||
if(features & iButtonProtocolFeatureExtData) {
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"View Data",
|
||||
SubmenuIndexViewData,
|
||||
ibutton_scene_read_key_menu_submenu_callback,
|
||||
ibutton);
|
||||
}
|
||||
|
||||
if(features & iButtonProtocolFeatureWriteBlank) {
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Write Blank",
|
||||
"Write ID",
|
||||
SubmenuIndexWriteBlank,
|
||||
ibutton_scene_read_key_menu_submenu_callback,
|
||||
ibutton);
|
||||
@@ -51,12 +42,21 @@ void ibutton_scene_read_key_menu_on_enter(void* context) {
|
||||
if(features & iButtonProtocolFeatureWriteCopy) {
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Write Copy",
|
||||
"Full Write on Same Type",
|
||||
SubmenuIndexWriteCopy,
|
||||
ibutton_scene_read_key_menu_submenu_callback,
|
||||
ibutton);
|
||||
}
|
||||
|
||||
if(features & iButtonProtocolFeatureExtData) {
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Data Info",
|
||||
SubmenuIndexViewData,
|
||||
ibutton_scene_read_key_menu_submenu_callback,
|
||||
ibutton);
|
||||
}
|
||||
|
||||
submenu_set_selected_item(
|
||||
submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneReadKeyMenu));
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu);
|
||||
|
||||
@@ -18,9 +18,9 @@ void ibutton_scene_read_success_on_enter(void* context) {
|
||||
|
||||
furi_string_printf(
|
||||
tmp,
|
||||
"%s[%s]",
|
||||
ibutton_protocols_get_name(ibutton->protocols, protocol_id),
|
||||
ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id));
|
||||
"%s %s",
|
||||
ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id),
|
||||
ibutton_protocols_get_name(ibutton->protocols, protocol_id));
|
||||
|
||||
widget_add_string_element(
|
||||
widget, 0, 2, AlignLeft, AlignTop, FontPrimary, furi_string_get_cstr(tmp));
|
||||
|
||||
@@ -15,7 +15,7 @@ void ibutton_scene_retry_confirm_on_enter(void* context) {
|
||||
Widget* widget = ibutton->widget;
|
||||
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeLeft, "Exit", ibutton_scene_retry_confirm_widget_callback, ibutton);
|
||||
widget, GuiButtonTypeLeft, "Retry", ibutton_scene_retry_confirm_widget_callback, ibutton);
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeRight, "Stay", ibutton_scene_retry_confirm_widget_callback, ibutton);
|
||||
widget_add_string_element(
|
||||
|
||||
@@ -21,12 +21,16 @@ void ibutton_scene_saved_key_menu_on_enter(void* context) {
|
||||
|
||||
if(features & iButtonProtocolFeatureWriteBlank) {
|
||||
submenu_add_item(
|
||||
submenu, "Write Blank", SubmenuIndexWriteBlank, ibutton_submenu_callback, ibutton);
|
||||
submenu, "Write ID", SubmenuIndexWriteBlank, ibutton_submenu_callback, ibutton);
|
||||
}
|
||||
|
||||
if(features & iButtonProtocolFeatureWriteCopy) {
|
||||
submenu_add_item(
|
||||
submenu, "Write Copy", SubmenuIndexWriteCopy, ibutton_submenu_callback, ibutton);
|
||||
submenu,
|
||||
"Full Write on Same Type",
|
||||
SubmenuIndexWriteCopy,
|
||||
ibutton_submenu_callback,
|
||||
ibutton);
|
||||
}
|
||||
|
||||
submenu_add_item(submenu, "Edit", SubmenuIndexEdit, ibutton_submenu_callback, ibutton);
|
||||
|
||||
@@ -42,23 +42,21 @@ void ibutton_scene_write_on_enter(void* context) {
|
||||
|
||||
furi_string_printf(
|
||||
tmp,
|
||||
"[%s]\n%s ",
|
||||
"[%s]\n%s",
|
||||
ibutton_protocols_get_name(ibutton->protocols, protocol_id),
|
||||
ibutton->key_name);
|
||||
furi_string_empty(ibutton->file_path) ? "Unsaved Key" : ibutton->key_name);
|
||||
|
||||
widget_add_text_box_element(
|
||||
widget, 52, 30, 75, 40, AlignCenter, AlignCenter, furi_string_get_cstr(tmp), true);
|
||||
widget, 52, 24, 75, 40, AlignCenter, AlignTop, furi_string_get_cstr(tmp), true);
|
||||
|
||||
ibutton_worker_write_set_callback(worker, ibutton_scene_write_callback, ibutton);
|
||||
|
||||
furi_string_set(tmp, "iButton\nwriting ");
|
||||
|
||||
if(ibutton->write_mode == iButtonWriteModeBlank) {
|
||||
furi_string_cat(tmp, "Blank");
|
||||
furi_string_set(tmp, "Writing ID");
|
||||
ibutton_worker_write_blank_start(worker, key);
|
||||
|
||||
} else if(ibutton->write_mode == iButtonWriteModeCopy) {
|
||||
furi_string_cat(tmp, "Copy");
|
||||
furi_string_set(tmp, "Full Writing");
|
||||
ibutton_worker_write_copy_start(worker, key);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,16 +6,15 @@ void lfrfid_scene_emulate_on_enter(void* context) {
|
||||
|
||||
FuriString* display_text = furi_string_alloc_set("\e#Emulating\e#\n");
|
||||
|
||||
if(furi_string_empty(app->file_name)) {
|
||||
furi_string_cat(display_text, "Unsaved\n");
|
||||
furi_string_cat(display_text, protocol_dict_get_name(app->dict, app->protocol_id));
|
||||
} else {
|
||||
furi_string_cat(display_text, app->file_name);
|
||||
}
|
||||
furi_string_cat_printf(
|
||||
display_text,
|
||||
"[%s]\n%s",
|
||||
protocol_dict_get_name(app->dict, app->protocol_id),
|
||||
furi_string_empty(app->file_name) ? "Unsaved Tag" : furi_string_get_cstr(app->file_name));
|
||||
|
||||
widget_add_icon_element(widget, 0, 0, &I_NFC_dolphin_emulation_51x64);
|
||||
widget_add_text_box_element(
|
||||
widget, 55, 16, 67, 48, AlignCenter, AlignTop, furi_string_get_cstr(display_text), true);
|
||||
widget, 51, 6, 79, 50, AlignCenter, AlignTop, furi_string_get_cstr(display_text), false);
|
||||
|
||||
furi_string_free(display_text);
|
||||
|
||||
|
||||
@@ -25,12 +25,18 @@ void lfrfid_scene_write_on_enter(void* context) {
|
||||
popup_set_header(popup, "Writing", 94, 16, AlignCenter, AlignTop);
|
||||
|
||||
if(!furi_string_empty(app->file_name)) {
|
||||
popup_set_text(popup, furi_string_get_cstr(app->file_name), 94, 29, AlignCenter, AlignTop);
|
||||
snprintf(
|
||||
app->text_store,
|
||||
LFRFID_TEXT_STORE_SIZE,
|
||||
"[%s]\n%s",
|
||||
protocol_dict_get_name(app->dict, app->protocol_id),
|
||||
furi_string_get_cstr(app->file_name));
|
||||
popup_set_text(popup, app->text_store, 94, 29, AlignCenter, AlignTop);
|
||||
} else {
|
||||
snprintf(
|
||||
app->text_store,
|
||||
LFRFID_TEXT_STORE_SIZE,
|
||||
"Unsaved\n%s",
|
||||
"[%s]\nUnsaved Tag",
|
||||
protocol_dict_get_name(app->dict, app->protocol_id));
|
||||
popup_set_text(popup, app->text_store, 94, 29, AlignCenter, AlignTop);
|
||||
}
|
||||
|
||||
@@ -22,8 +22,14 @@ void lfrfid_scene_write_and_set_pass_on_enter(void* context) {
|
||||
LfRfid* app = context;
|
||||
Popup* popup = app->popup;
|
||||
|
||||
popup_set_header(popup, "Writing\nwith password", 89, 30, AlignCenter, AlignTop);
|
||||
popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
|
||||
popup_set_header(popup, "Writing\nwith\npassword", 94, 8, AlignCenter, AlignTop);
|
||||
popup_set_icon(popup, 0, 8, &I_NFC_manual_60x50);
|
||||
snprintf(
|
||||
app->text_store,
|
||||
LFRFID_TEXT_STORE_SIZE,
|
||||
"[%s]",
|
||||
protocol_dict_get_name(app->dict, app->protocol_id));
|
||||
popup_set_text(popup, app->text_store, 94, 45, AlignCenter, AlignTop);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup);
|
||||
|
||||
|
||||
@@ -505,8 +505,8 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
card_use_before_date_s.year);
|
||||
|
||||
if(data_block.valid_from_date == 0 || data_block.valid_to_date == 0) {
|
||||
furi_string_cat(result, "\e#No ticket\n");
|
||||
return true;
|
||||
furi_string_cat(result, "\e#No ticket");
|
||||
return false;
|
||||
}
|
||||
//remaining_trips
|
||||
furi_string_cat_printf(result, "Trips: %d\n", data_block.total_trips);
|
||||
@@ -625,7 +625,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
data_block.valid_from_date + data_block.valid_for_days, &card_valid_to_date_s, 1992);
|
||||
furi_string_cat_printf(
|
||||
result,
|
||||
"Valid to: %02d.%02d.%04d\n",
|
||||
"Valid to: %02d.%02d.%04d",
|
||||
card_valid_to_date_s.day,
|
||||
card_valid_to_date_s.month,
|
||||
card_valid_to_date_s.year);
|
||||
@@ -663,7 +663,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
2016);
|
||||
furi_string_cat_printf(
|
||||
result,
|
||||
"Valid to: %02d.%02d.%04d\n",
|
||||
"Valid to: %02d.%02d.%04d",
|
||||
card_valid_to_date_s.day,
|
||||
card_valid_to_date_s.month,
|
||||
card_valid_to_date_s.year);
|
||||
@@ -676,7 +676,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
2016);
|
||||
furi_string_cat_printf(
|
||||
result,
|
||||
"Trip from: %02d.%02d.%04d %02d:%02d\n",
|
||||
"\nTrip from: %02d.%02d.%04d %02d:%02d",
|
||||
card_start_trip_minutes_s.day,
|
||||
card_start_trip_minutes_s.month,
|
||||
card_start_trip_minutes_s.year,
|
||||
@@ -693,7 +693,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
2016);
|
||||
furi_string_cat_printf(
|
||||
result,
|
||||
"Trip switch: %02d.%02d.%04d %02d:%02d\n",
|
||||
"\nTrip switch: %02d.%02d.%04d %02d:%02d",
|
||||
card_start_switch_trip_minutes_s.day,
|
||||
card_start_switch_trip_minutes_s.month,
|
||||
card_start_switch_trip_minutes_s.year,
|
||||
@@ -703,10 +703,10 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
//transport
|
||||
FuriString* transport = furi_string_alloc();
|
||||
parse_transport_type(&data_block, transport);
|
||||
furi_string_cat_printf(result, "Transport: %s\n", furi_string_get_cstr(transport));
|
||||
furi_string_cat_printf(result, "\nTransport: %s", furi_string_get_cstr(transport));
|
||||
//validator
|
||||
if(data_block.validator) {
|
||||
furi_string_cat_printf(result, "Validator: %05d\n", data_block.validator);
|
||||
furi_string_cat_printf(result, "\nValidator: %05d", data_block.validator);
|
||||
}
|
||||
furi_string_free(transport);
|
||||
break;
|
||||
@@ -740,9 +740,9 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
card_valid_to_date_s.month,
|
||||
card_valid_to_date_s.year);
|
||||
//remaining_trips
|
||||
furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips);
|
||||
furi_string_cat_printf(result, "Trips left: %d", data_block.remaining_trips);
|
||||
//trip_from
|
||||
if(data_block.start_trip_date) {
|
||||
if(data_block.start_trip_date) { // TODO: (-nofl) unused
|
||||
DateTime card_start_trip_minutes_s = {0};
|
||||
from_minutes_to_datetime(
|
||||
data_block.start_trip_date * 24 * 60 + data_block.start_trip_time,
|
||||
@@ -751,7 +751,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
}
|
||||
//validator
|
||||
if(data_block.validator) {
|
||||
furi_string_cat_printf(result, "Validator: %05d", data_block.validator);
|
||||
furi_string_cat_printf(result, "\nValidator: %05d", data_block.validator);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -785,12 +785,12 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
data_block.valid_from_date + data_block.valid_for_days, &card_valid_to_date_s, 1992);
|
||||
furi_string_cat_printf(
|
||||
result,
|
||||
"Valid to: %02d.%02d.%04d\n",
|
||||
"Valid to: %02d.%02d.%04d",
|
||||
card_valid_to_date_s.day,
|
||||
card_valid_to_date_s.month,
|
||||
card_valid_to_date_s.year);
|
||||
//trip_from
|
||||
if(data_block.start_trip_date) {
|
||||
if(data_block.start_trip_date) { // TODO: (-nofl) unused
|
||||
DateTime card_start_trip_minutes_s = {0};
|
||||
from_minutes_to_datetime(
|
||||
data_block.start_trip_date * 24 * 60 + data_block.start_trip_time,
|
||||
@@ -798,7 +798,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
1992);
|
||||
}
|
||||
//trip_switch
|
||||
if(data_block.passage_5_minutes) {
|
||||
if(data_block.passage_5_minutes) { // TODO: (-nofl) unused
|
||||
DateTime card_start_switch_trip_minutes_s = {0};
|
||||
from_minutes_to_datetime(
|
||||
data_block.start_trip_date * 24 * 60 + data_block.start_trip_time +
|
||||
@@ -808,7 +808,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
}
|
||||
//validator
|
||||
if(data_block.validator) {
|
||||
furi_string_cat_printf(result, "Validator: %05d", data_block.validator);
|
||||
furi_string_cat_printf(result, "\nValidator: %05d", data_block.validator);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -870,10 +870,10 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
furi_string_cat(transport, "");
|
||||
break;
|
||||
}
|
||||
furi_string_cat_printf(result, "Transport: %s\n", furi_string_get_cstr(transport));
|
||||
furi_string_cat_printf(result, "Transport: %s", furi_string_get_cstr(transport));
|
||||
//validator
|
||||
if(data_block.validator) {
|
||||
furi_string_cat_printf(result, "Validator: %05d", data_block.validator);
|
||||
furi_string_cat_printf(result, "\nValidator: %05d", data_block.validator);
|
||||
}
|
||||
furi_string_free(transport);
|
||||
break;
|
||||
@@ -899,7 +899,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992);
|
||||
furi_string_cat_printf(
|
||||
result,
|
||||
"Valid from: %02d.%02d.%04d\n",
|
||||
"Valid from: %02d.%02d.%04d",
|
||||
card_valid_from_date_s.day,
|
||||
card_valid_from_date_s.month,
|
||||
card_valid_from_date_s.year);
|
||||
@@ -912,7 +912,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
1992);
|
||||
furi_string_cat_printf(
|
||||
result,
|
||||
"Valid to: %02d.%02d.%04d\n",
|
||||
"\nValid to: %02d.%02d.%04d",
|
||||
card_valid_to_date_s.day,
|
||||
card_valid_to_date_s.month,
|
||||
card_valid_to_date_s.year);
|
||||
@@ -924,7 +924,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
1992);
|
||||
furi_string_cat_printf(
|
||||
result,
|
||||
"Valid to: %02d.%02d.%04d\n",
|
||||
"\nValid to: %02d.%02d.%04d",
|
||||
card_valid_to_date_s.day,
|
||||
card_valid_to_date_s.month,
|
||||
card_valid_to_date_s.year);
|
||||
@@ -939,7 +939,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
1992); //-time
|
||||
furi_string_cat_printf(
|
||||
result,
|
||||
"Trip from: %02d.%02d.%04d %02d:%02d\n",
|
||||
"\nTrip from: %02d.%02d.%04d %02d:%02d",
|
||||
card_start_trip_minutes_s.day,
|
||||
card_start_trip_minutes_s.month,
|
||||
card_start_trip_minutes_s.year,
|
||||
@@ -956,7 +956,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
1992);
|
||||
furi_string_cat_printf(
|
||||
result,
|
||||
"Trip switch: %02d.%02d.%04d %02d:%02d\n",
|
||||
"\nTrip switch: %02d.%02d.%04d %02d:%02d",
|
||||
card_start_switch_trip_minutes_s.day,
|
||||
card_start_switch_trip_minutes_s.month,
|
||||
card_start_switch_trip_minutes_s.year,
|
||||
@@ -965,7 +965,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
}
|
||||
//transport
|
||||
FuriString* transport = furi_string_alloc();
|
||||
switch(data_block.transport_type) {
|
||||
switch(data_block.transport_type) { // TODO: (-nofl) unused
|
||||
case 1:
|
||||
furi_string_cat(transport, "Metro");
|
||||
break;
|
||||
@@ -981,7 +981,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
}
|
||||
//validator
|
||||
if(data_block.validator) {
|
||||
furi_string_cat_printf(result, "Validator: %05d", data_block.validator);
|
||||
furi_string_cat_printf(result, "\nValidator: %05d", data_block.validator);
|
||||
}
|
||||
furi_string_free(transport);
|
||||
break;
|
||||
@@ -1094,14 +1094,14 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
data_block.valid_from_date * 24 * 60 + data_block.valid_for_minutes -
|
||||
data_block.start_trip_neg_minutes,
|
||||
&card_start_trip_minutes_s,
|
||||
2016);
|
||||
2016); // TODO: (-nofl) unused
|
||||
//transport
|
||||
FuriString* transport = furi_string_alloc();
|
||||
parse_transport_type(&data_block, transport);
|
||||
furi_string_cat_printf(result, "Transport: %s\n", furi_string_get_cstr(transport));
|
||||
furi_string_cat_printf(result, "Transport: %s", furi_string_get_cstr(transport));
|
||||
// validator
|
||||
if(data_block.validator) {
|
||||
furi_string_cat_printf(result, "Validator: %05d", data_block.validator);
|
||||
furi_string_cat_printf(result, "\nValidator: %05d", data_block.validator);
|
||||
}
|
||||
furi_string_free(transport);
|
||||
break;
|
||||
@@ -1121,7 +1121,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
card_use_before_date_s.month,
|
||||
card_use_before_date_s.year);
|
||||
//remaining_funds
|
||||
furi_string_cat_printf(result, "Balance: %ld rub\n", data_block.remaining_funds / 100);
|
||||
furi_string_cat_printf(result, "Balance: %ld rub", data_block.remaining_funds / 100);
|
||||
//start_trip_minutes
|
||||
if(data_block.start_trip_minutes) {
|
||||
DateTime card_start_trip_minutes_s = {0};
|
||||
@@ -1129,7 +1129,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
data_block.start_trip_minutes, &card_start_trip_minutes_s, 2019);
|
||||
furi_string_cat_printf(
|
||||
result,
|
||||
"Trip from: %02d.%02d.%04d %02d:%02d\n",
|
||||
"\nTrip from: %02d.%02d.%04d %02d:%02d",
|
||||
card_start_trip_minutes_s.day,
|
||||
card_start_trip_minutes_s.month,
|
||||
card_start_trip_minutes_s.year,
|
||||
@@ -1145,7 +1145,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
2019);
|
||||
furi_string_cat_printf(
|
||||
result,
|
||||
"(M) from: %02d.%02d.%04d %02d:%02d\n",
|
||||
"\n(M) from: %02d.%02d.%04d %02d:%02d",
|
||||
card_start_m_trip_minutes_s.day,
|
||||
card_start_m_trip_minutes_s.month,
|
||||
card_start_m_trip_minutes_s.year,
|
||||
@@ -1160,7 +1160,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
2019);
|
||||
furi_string_cat_printf(
|
||||
result,
|
||||
"Trip edit: %02d.%02d.%04d %02d:%02d\n",
|
||||
"\nTrip edit: %02d.%02d.%04d %02d:%02d",
|
||||
card_start_change_trip_minutes_s.day,
|
||||
card_start_change_trip_minutes_s.month,
|
||||
card_start_change_trip_minutes_s.year,
|
||||
@@ -1170,7 +1170,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
//transport
|
||||
//validator
|
||||
if(data_block.validator) {
|
||||
furi_string_cat_printf(result, "Validator: %05d", data_block.validator);
|
||||
furi_string_cat_printf(result, "\nValidator: %05d", data_block.validator);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1207,7 +1207,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
2019);
|
||||
furi_string_cat_printf(
|
||||
result,
|
||||
"Valid to: %02d.%02d.%04d\n",
|
||||
"Valid to: %02d.%02d.%04d",
|
||||
card_use_to_date_s.day,
|
||||
card_use_to_date_s.month,
|
||||
card_use_to_date_s.year);
|
||||
@@ -1221,7 +1221,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
2019); //-time
|
||||
furi_string_cat_printf(
|
||||
result,
|
||||
"Trip from: %02d.%02d.%04d %02d:%02d\n",
|
||||
"\nTrip from: %02d.%02d.%04d %02d:%02d",
|
||||
card_start_trip_minutes_s.day,
|
||||
card_start_trip_minutes_s.month,
|
||||
card_start_trip_minutes_s.year,
|
||||
@@ -1238,7 +1238,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
2019);
|
||||
furi_string_cat_printf(
|
||||
result,
|
||||
"(M) from: %02d.%02d.%04d %02d:%02d\n",
|
||||
"\n(M) from: %02d.%02d.%04d %02d:%02d",
|
||||
card_start_trip_m_minutes_s.day,
|
||||
card_start_trip_m_minutes_s.month,
|
||||
card_start_trip_m_minutes_s.year,
|
||||
@@ -1248,7 +1248,7 @@ bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString*
|
||||
//transport
|
||||
//validator
|
||||
if(data_block.validator) {
|
||||
furi_string_cat_printf(result, "Validator: %05d", data_block.validator);
|
||||
furi_string_cat_printf(result, "\nValidator: %05d", data_block.validator);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ static void nfc_scene_more_info_on_enter_mf_desfire(NfcApp* instance) {
|
||||
static NfcCommand nfc_scene_read_poller_callback_mf_desfire(NfcGenericEvent event, void* context) {
|
||||
furi_assert(event.protocol == NfcProtocolMfDesfire);
|
||||
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
NfcApp* instance = context;
|
||||
const MfDesfirePollerEvent* mf_desfire_event = event.event_data;
|
||||
|
||||
@@ -41,10 +43,12 @@ static NfcCommand nfc_scene_read_poller_callback_mf_desfire(NfcGenericEvent even
|
||||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolMfDesfire, nfc_poller_get_data(instance->poller));
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
|
||||
return NfcCommandStop;
|
||||
command = NfcCommandStop;
|
||||
} else if(mf_desfire_event->type == MfDesfirePollerEventTypeReadFailed) {
|
||||
command = NfcCommandReset;
|
||||
}
|
||||
|
||||
return NfcCommandContinue;
|
||||
return command;
|
||||
}
|
||||
|
||||
static void nfc_scene_read_on_enter_mf_desfire(NfcApp* instance) {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "../iso14443_4a/iso14443_4a_render.h"
|
||||
|
||||
#define MF_DESFIRE_RENDER_MAX_RECORD_SIZE (256U)
|
||||
|
||||
void nfc_render_mf_desfire_info(
|
||||
const MfDesfireData* data,
|
||||
NfcProtocolFormatType format_type,
|
||||
@@ -11,24 +13,29 @@ void nfc_render_mf_desfire_info(
|
||||
const uint32_t bytes_total = 1UL << (data->version.sw_storage >> 1);
|
||||
const uint32_t bytes_free = data->free_memory.is_present ? data->free_memory.bytes_free : 0;
|
||||
|
||||
if(data->master_key_settings.is_free_directory_list) {
|
||||
const uint32_t app_count = simple_array_get_count(data->applications);
|
||||
uint32_t file_count = 0;
|
||||
|
||||
for(uint32_t i = 0; i < app_count; ++i) {
|
||||
const MfDesfireApplication* app = simple_array_cget(data->applications, i);
|
||||
if(app->key_settings.is_free_directory_list) {
|
||||
file_count += simple_array_get_count(app->file_ids);
|
||||
}
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "\n%lu Application%s", app_count, app_count != 1 ? "s" : "");
|
||||
furi_string_cat_printf(str, ", %lu File%s", file_count, file_count != 1 ? "s" : "");
|
||||
} else {
|
||||
furi_string_cat_printf(str, "\nAuth required to read apps!");
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "\n%lu", bytes_total);
|
||||
|
||||
if(data->version.sw_storage & 1) {
|
||||
furi_string_push_back(str, '+');
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, " bytes, %lu bytes free\n", bytes_free);
|
||||
|
||||
const uint32_t app_count = simple_array_get_count(data->applications);
|
||||
uint32_t file_count = 0;
|
||||
|
||||
for(uint32_t i = 0; i < app_count; ++i) {
|
||||
const MfDesfireApplication* app = simple_array_cget(data->applications, i);
|
||||
file_count += simple_array_get_count(app->file_ids);
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "%lu Application%s", app_count, app_count != 1 ? "s" : "");
|
||||
furi_string_cat_printf(str, ", %lu File%s", file_count, file_count != 1 ? "s" : "");
|
||||
furi_string_cat_printf(str, " bytes, %lu bytes free", bytes_free);
|
||||
|
||||
if(format_type != NfcProtocolFormatTypeFull) return;
|
||||
|
||||
@@ -101,17 +108,29 @@ void nfc_render_mf_desfire_free_memory(const MfDesfireFreeMemory* data, FuriStri
|
||||
}
|
||||
|
||||
void nfc_render_mf_desfire_key_settings(const MfDesfireKeySettings* data, FuriString* str) {
|
||||
furi_string_cat_printf(str, "changeKeyID %d\n", data->change_key_id);
|
||||
furi_string_cat_printf(str, "configChangeable %d\n", data->is_config_changeable);
|
||||
furi_string_cat_printf(str, "freeCreateDelete %d\n", data->is_free_create_delete);
|
||||
furi_string_cat_printf(str, "freeDirectoryList %d\n", data->is_free_directory_list);
|
||||
furi_string_cat_printf(str, "masterChangeable %d\n", data->is_master_key_changeable);
|
||||
if(data->is_free_directory_list) {
|
||||
furi_string_cat_printf(str, "changeKeyID %d\n", data->change_key_id);
|
||||
furi_string_cat_printf(str, "configChangeable %d\n", data->is_config_changeable);
|
||||
furi_string_cat_printf(str, "freeCreateDelete %d\n", data->is_free_create_delete);
|
||||
furi_string_cat_printf(str, "freeDirectoryList %d\n", data->is_free_directory_list);
|
||||
furi_string_cat_printf(str, "masterChangeable %d\n", data->is_master_key_changeable);
|
||||
} else {
|
||||
furi_string_cat_printf(str, "changeKeyID ??\n");
|
||||
furi_string_cat_printf(str, "configChangeable ??\n");
|
||||
furi_string_cat_printf(str, "freeCreateDelete ??\n");
|
||||
furi_string_cat_printf(str, "freeDirectoryList 0\n");
|
||||
furi_string_cat_printf(str, "masterChangeable ??\n");
|
||||
}
|
||||
|
||||
if(data->flags) {
|
||||
furi_string_cat_printf(str, "flags %d\n", data->flags);
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "maxKeys %d\n", data->max_keys);
|
||||
if(data->is_free_directory_list) {
|
||||
furi_string_cat_printf(str, "maxKeys %d\n", data->max_keys);
|
||||
} else {
|
||||
furi_string_cat_printf(str, "maxKeys ??\n");
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_render_mf_desfire_key_version(
|
||||
@@ -123,14 +142,16 @@ void nfc_render_mf_desfire_key_version(
|
||||
|
||||
void nfc_render_mf_desfire_application_id(const MfDesfireApplicationId* data, FuriString* str) {
|
||||
const uint8_t* app_id = data->data;
|
||||
furi_string_cat_printf(str, "Application %02x%02x%02x\n", app_id[0], app_id[1], app_id[2]);
|
||||
furi_string_cat_printf(str, "Application %02x%02x%02x\n", app_id[2], app_id[1], app_id[0]);
|
||||
}
|
||||
|
||||
void nfc_render_mf_desfire_application(const MfDesfireApplication* data, FuriString* str) {
|
||||
nfc_render_mf_desfire_key_settings(&data->key_settings, str);
|
||||
|
||||
for(uint32_t i = 0; i < simple_array_get_count(data->key_versions); ++i) {
|
||||
nfc_render_mf_desfire_key_version(simple_array_cget(data->key_versions, i), i, str);
|
||||
if(data->key_settings.is_free_directory_list) {
|
||||
for(uint32_t i = 0; i < simple_array_get_count(data->key_versions); ++i) {
|
||||
nfc_render_mf_desfire_key_version(simple_array_cget(data->key_versions, i), i, str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,19 +200,20 @@ void nfc_render_mf_desfire_file_settings_data(
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "%s %s\n", type, comm);
|
||||
furi_string_cat_printf(
|
||||
str,
|
||||
"r %d w %d rw %d c %d\n",
|
||||
settings->access_rights >> 12 & 0xF,
|
||||
settings->access_rights >> 8 & 0xF,
|
||||
settings->access_rights >> 4 & 0xF,
|
||||
settings->access_rights & 0xF);
|
||||
|
||||
for(size_t i = 0; i < settings->access_rights_len; i++) {
|
||||
furi_string_cat_printf(
|
||||
str,
|
||||
"r %d w %d rw %d c %d\n",
|
||||
settings->access_rights[i] >> 12 & 0xF,
|
||||
settings->access_rights[i] >> 8 & 0xF,
|
||||
settings->access_rights[i] >> 4 & 0xF,
|
||||
settings->access_rights[i] & 0xF);
|
||||
}
|
||||
|
||||
uint32_t record_count = 1;
|
||||
uint32_t record_size = 0;
|
||||
|
||||
const uint32_t total_size = simple_array_get_count(data->data);
|
||||
|
||||
switch(settings->type) {
|
||||
case MfDesfireFileTypeStandard:
|
||||
case MfDesfireFileTypeBackup:
|
||||
@@ -217,21 +239,32 @@ void nfc_render_mf_desfire_file_settings_data(
|
||||
break;
|
||||
}
|
||||
|
||||
bool is_auth_required = true;
|
||||
for(size_t i = 0; i < settings->access_rights_len; i++) {
|
||||
uint8_t read_rights = (settings->access_rights[i] >> 12) & 0x0f;
|
||||
uint8_t read_write_rights = (settings->access_rights[i] >> 4) & 0x0f;
|
||||
if((read_rights == 0x0e) || (read_write_rights == 0x0e)) {
|
||||
is_auth_required = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(is_auth_required) {
|
||||
furi_string_cat_printf(str, "Auth required to read file data\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(simple_array_get_count(data->data) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(uint32_t rec = 0; rec < record_count; rec++) {
|
||||
const uint32_t size_offset = rec * record_size;
|
||||
const uint32_t size_remaining = total_size > size_offset ? total_size - size_offset : 0;
|
||||
// Limit record size
|
||||
bool trim_data = record_size > MF_DESFIRE_RENDER_MAX_RECORD_SIZE;
|
||||
if(trim_data) {
|
||||
record_size = MF_DESFIRE_RENDER_MAX_RECORD_SIZE;
|
||||
}
|
||||
|
||||
if(size_remaining < record_size) {
|
||||
furi_string_cat_printf(
|
||||
str, "record %lu (partial %lu of %lu)\n", rec, size_remaining, record_size);
|
||||
record_size = size_remaining;
|
||||
} else {
|
||||
furi_string_cat_printf(str, "record %lu\n", rec);
|
||||
}
|
||||
for(uint32_t rec = 0; rec < record_count; rec++) {
|
||||
furi_string_cat_printf(str, "record %lu\n", rec);
|
||||
|
||||
for(uint32_t ch = 0; ch < record_size; ch += 4) {
|
||||
furi_string_cat_printf(str, "%03lx|", ch);
|
||||
@@ -260,6 +293,9 @@ void nfc_render_mf_desfire_file_settings_data(
|
||||
|
||||
furi_string_push_back(str, '\n');
|
||||
}
|
||||
if(trim_data) {
|
||||
furi_string_cat_str(str, "...");
|
||||
}
|
||||
|
||||
furi_string_push_back(str, '\n');
|
||||
}
|
||||
|
||||
@@ -59,6 +59,29 @@ static const MfClassicKeyPair plantain_4k_keys[] = {
|
||||
{.a = 0xb27addfb64b0, .b = 0x152fd0c420a7}, {.a = 0x7259fa0197c6, .b = 0x5583698df085},
|
||||
};
|
||||
|
||||
static const MfClassicKeyPair plantain_4k_keys_legacy[] = {
|
||||
{.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff},
|
||||
{.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff},
|
||||
{.a = 0xe56ac127dd45, .b = 0x19fc84a3784b}, {.a = 0x77dabc9825e1, .b = 0x9764fec3154a},
|
||||
{.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff},
|
||||
{.a = 0x26973ea74321, .b = 0xd27058c6e2c7}, {.a = 0xeb0a8ff88ade, .b = 0x578a9ada41e3},
|
||||
{.a = 0xea0fd73cb149, .b = 0x29c35fa068fb}, {.a = 0xc76bf71a2509, .b = 0x9ba241db3f56},
|
||||
{.a = 0xacffffffffff, .b = 0x71f3a315ad26}, {.a = 0xffffffffffff, .b = 0xffffffffffff},
|
||||
{.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff},
|
||||
{.a = 0x72f96bdd3714, .b = 0x462225cd34cf}, {.a = 0x044ce1872bc3, .b = 0x8c90c70cff4a},
|
||||
{.a = 0xbc2d1791dec1, .b = 0xca96a487de0b}, {.a = 0x8791b2ccb5c4, .b = 0xc956c3b80da3},
|
||||
{.a = 0x8e26e45e7d65, .b = 0x8e65b3af7d22}, {.a = 0x0f318130ed18, .b = 0x0c420a20e056},
|
||||
{.a = 0x045ceca15535, .b = 0x31bec3d9e510}, {.a = 0x9d993c5d4ef4, .b = 0x86120e488abf},
|
||||
{.a = 0xc65d4eaa645b, .b = 0xb69d40d1a439}, {.a = 0x46d78e850a7e, .b = 0xa470f8130991},
|
||||
{.a = 0x42e9b54e51ab, .b = 0x0231b86df52e}, {.a = 0x0f01ceff2742, .b = 0x6fec74559ca7},
|
||||
{.a = 0xb81f2b0c2f66, .b = 0xa7e2d95f0003}, {.a = 0x9ea3387a63c1, .b = 0x437e59f57561},
|
||||
{.a = 0x0eb23cc8110b, .b = 0x04dc35277635}, {.a = 0xbc4580b7f20b, .b = 0xd0a4131fb290},
|
||||
{.a = 0x7a396f0d633d, .b = 0xad2bdc097023}, {.a = 0xa3faa6daff67, .b = 0x7600e889adf9},
|
||||
{.a = 0xfd8705e721b0, .b = 0x296fc317a513}, {.a = 0x22052b480d11, .b = 0xe19504c39461},
|
||||
{.a = 0xa7141147d430, .b = 0xff16014fefc7}, {.a = 0x8a8d88151a00, .b = 0x038b5f9b5a2a},
|
||||
{.a = 0xb27addfb64b0, .b = 0x152fd0c420a7}, {.a = 0x7259fa0197c6, .b = 0x5583698df085},
|
||||
};
|
||||
|
||||
static bool plantain_get_card_config(PlantainCardConfig* config, MfClassicType type) {
|
||||
bool success = true;
|
||||
|
||||
@@ -125,6 +148,21 @@ static bool plantain_read(Nfc* nfc, NfcDevice* device) {
|
||||
PlantainCardConfig cfg = {};
|
||||
if(!plantain_get_card_config(&cfg, data->type)) break;
|
||||
|
||||
const uint8_t legacy_check_sec_num = 26;
|
||||
const uint8_t legacy_check_block_num =
|
||||
mf_classic_get_first_block_num_of_sector(legacy_check_sec_num);
|
||||
|
||||
MfClassicKey key = {0};
|
||||
bit_lib_num_to_bytes_be(
|
||||
plantain_4k_keys_legacy[legacy_check_sec_num].a, COUNT_OF(key.data), key.data);
|
||||
|
||||
error = mf_classic_poller_sync_auth(
|
||||
nfc, legacy_check_block_num, &key, MfClassicKeyTypeA, NULL);
|
||||
if(error == MfClassicErrorNone) {
|
||||
FURI_LOG_D(TAG, "Legacy keys detected");
|
||||
cfg.keys = plantain_4k_keys_legacy;
|
||||
}
|
||||
|
||||
MfClassicDeviceKeys keys = {};
|
||||
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
|
||||
bit_lib_num_to_bytes_be(cfg.keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data);
|
||||
|
||||
@@ -83,6 +83,20 @@ static const MfClassicKeyPair troika_4k_keys[] = {
|
||||
{.a = 0xBB52F8CCE07F, .b = 0x6B6119752C70}, //40
|
||||
};
|
||||
|
||||
static void troika_render_section_header(
|
||||
FuriString* str,
|
||||
const char* name,
|
||||
uint8_t prefix_separator_cnt,
|
||||
uint8_t suffix_separator_cnt) {
|
||||
for(uint8_t i = 0; i < prefix_separator_cnt; i++) {
|
||||
furi_string_cat_printf(str, ":");
|
||||
}
|
||||
furi_string_cat_printf(str, "[ %s ]", name);
|
||||
for(uint8_t i = 0; i < suffix_separator_cnt; i++) {
|
||||
furi_string_cat_printf(str, ":");
|
||||
}
|
||||
}
|
||||
|
||||
static bool troika_get_card_config(TroikaCardConfig* config, MfClassicType type) {
|
||||
bool success = true;
|
||||
|
||||
@@ -204,18 +218,19 @@ static bool troika_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||
bool result3 = mosgortrans_parse_transport_block(&data->block[16], tat_result);
|
||||
|
||||
furi_string_cat_printf(parsed_data, "\e#Troyka card\n");
|
||||
if(result1) {
|
||||
furi_string_cat_printf(
|
||||
parsed_data, "\e#Metro\n%s\n", furi_string_get_cstr(metro_result));
|
||||
if(result1 && !furi_string_empty(metro_result)) {
|
||||
troika_render_section_header(parsed_data, "Metro", 22, 21);
|
||||
furi_string_cat_printf(parsed_data, "%s\n", furi_string_get_cstr(metro_result));
|
||||
}
|
||||
|
||||
if(result2) {
|
||||
furi_string_cat_printf(
|
||||
parsed_data, "\e#Ediniy\n%s\n", furi_string_get_cstr(ground_result));
|
||||
if(result2 && !furi_string_empty(ground_result)) {
|
||||
troika_render_section_header(parsed_data, "Ediny", 22, 22);
|
||||
furi_string_cat_printf(parsed_data, "%s\n", furi_string_get_cstr(ground_result));
|
||||
}
|
||||
|
||||
if(result3) {
|
||||
furi_string_cat_printf(parsed_data, "\e#TAT\n%s\n", furi_string_get_cstr(tat_result));
|
||||
if(result3 && !furi_string_empty(tat_result)) {
|
||||
troika_render_section_header(parsed_data, "TAT", 24, 23);
|
||||
furi_string_cat_printf(parsed_data, "%s\n", furi_string_get_cstr(tat_result));
|
||||
}
|
||||
|
||||
furi_string_free(tat_result);
|
||||
|
||||
@@ -35,7 +35,7 @@ void nfc_scene_mf_desfire_more_info_on_enter(void* context) {
|
||||
for(uint32_t i = 0; i < simple_array_get_count(data->application_ids); ++i) {
|
||||
const MfDesfireApplicationId* app_id = simple_array_cget(data->application_ids, i);
|
||||
furi_string_printf(
|
||||
label, "App %02x%02x%02x", app_id->data[0], app_id->data[1], app_id->data[2]);
|
||||
label, "App %02x%02x%02x", app_id->data[2], app_id->data[1], app_id->data[0]);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
furi_string_get_cstr(label),
|
||||
|
||||
@@ -299,7 +299,7 @@ Desktop* desktop_alloc(void) {
|
||||
|
||||
desktop->lock_menu = desktop_lock_menu_alloc();
|
||||
desktop->debug_view = desktop_debug_alloc();
|
||||
desktop->hw_mismatch_popup = popup_alloc();
|
||||
desktop->popup = popup_alloc();
|
||||
desktop->locked_view = desktop_view_locked_alloc();
|
||||
desktop->pin_input_view = desktop_view_pin_input_alloc();
|
||||
desktop->pin_timeout_view = desktop_view_pin_timeout_alloc();
|
||||
@@ -335,9 +335,7 @@ Desktop* desktop_alloc(void) {
|
||||
view_dispatcher_add_view(
|
||||
desktop->view_dispatcher, DesktopViewIdDebug, desktop_debug_get_view(desktop->debug_view));
|
||||
view_dispatcher_add_view(
|
||||
desktop->view_dispatcher,
|
||||
DesktopViewIdHwMismatch,
|
||||
popup_get_view(desktop->hw_mismatch_popup));
|
||||
desktop->view_dispatcher, DesktopViewIdPopup, popup_get_view(desktop->popup));
|
||||
view_dispatcher_add_view(
|
||||
desktop->view_dispatcher,
|
||||
DesktopViewIdPinTimeout,
|
||||
@@ -477,6 +475,17 @@ int32_t desktop_srv(void* p) {
|
||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneFault);
|
||||
}
|
||||
|
||||
uint8_t keys_total, keys_valid;
|
||||
if(!furi_hal_crypto_enclave_verify(&keys_total, &keys_valid)) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Secure Enclave verification failed: total %hhu, valid %hhu",
|
||||
keys_total,
|
||||
keys_valid);
|
||||
|
||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneSecureEnclave);
|
||||
}
|
||||
|
||||
// Special case: autostart application is already running
|
||||
if(loader_is_locked(desktop->loader) &&
|
||||
animation_manager_is_animation_loaded(desktop->animation_manager)) {
|
||||
|
||||
@@ -28,7 +28,7 @@ typedef enum {
|
||||
DesktopViewIdLockMenu,
|
||||
DesktopViewIdLocked,
|
||||
DesktopViewIdDebug,
|
||||
DesktopViewIdHwMismatch,
|
||||
DesktopViewIdPopup,
|
||||
DesktopViewIdPinInput,
|
||||
DesktopViewIdPinTimeout,
|
||||
DesktopViewIdSlideshow,
|
||||
@@ -43,7 +43,7 @@ struct Desktop {
|
||||
ViewDispatcher* view_dispatcher;
|
||||
SceneManager* scene_manager;
|
||||
|
||||
Popup* hw_mismatch_popup;
|
||||
Popup* popup;
|
||||
DesktopLockMenuView* lock_menu;
|
||||
DesktopDebugView* debug_view;
|
||||
DesktopViewLocked* locked_view;
|
||||
|
||||
@@ -7,3 +7,4 @@ ADD_SCENE(desktop, locked, Locked)
|
||||
ADD_SCENE(desktop, pin_input, PinInput)
|
||||
ADD_SCENE(desktop, pin_timeout, PinTimeout)
|
||||
ADD_SCENE(desktop, slideshow, Slideshow)
|
||||
ADD_SCENE(desktop, secure_enclave, SecureEnclave)
|
||||
@@ -12,20 +12,21 @@ void desktop_scene_fault_callback(void* context) {
|
||||
void desktop_scene_fault_on_enter(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
|
||||
Popup* popup = desktop->hw_mismatch_popup;
|
||||
Popup* popup = desktop->popup;
|
||||
popup_set_context(popup, desktop);
|
||||
popup_set_header(
|
||||
popup,
|
||||
"Flipper crashed\n and was rebooted",
|
||||
60,
|
||||
64,
|
||||
14 + STATUS_BAR_Y_SHIFT,
|
||||
AlignCenter,
|
||||
AlignCenter);
|
||||
|
||||
char* message = (char*)furi_hal_rtc_get_fault_data();
|
||||
popup_set_text(popup, message, 60, 37 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter);
|
||||
popup_set_text(popup, message, 64, 37 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter);
|
||||
popup_set_callback(popup, desktop_scene_fault_callback);
|
||||
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdHwMismatch);
|
||||
|
||||
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdPopup);
|
||||
}
|
||||
|
||||
bool desktop_scene_fault_on_event(void* context, SceneManagerEvent event) {
|
||||
@@ -47,6 +48,11 @@ bool desktop_scene_fault_on_event(void* context, SceneManagerEvent event) {
|
||||
}
|
||||
|
||||
void desktop_scene_fault_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
furi_assert(desktop);
|
||||
|
||||
Popup* popup = desktop->popup;
|
||||
popup_reset(popup);
|
||||
|
||||
furi_hal_rtc_set_fault_data(0);
|
||||
}
|
||||
|
||||
@@ -4,17 +4,15 @@
|
||||
#include "desktop_scene.h"
|
||||
#include "../desktop_i.h"
|
||||
|
||||
#define HW_MISMATCH_BACK_EVENT (0UL)
|
||||
|
||||
void desktop_scene_hw_mismatch_callback(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, HW_MISMATCH_BACK_EVENT);
|
||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopHwMismatchExit);
|
||||
}
|
||||
|
||||
void desktop_scene_hw_mismatch_on_enter(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
furi_assert(desktop);
|
||||
Popup* popup = desktop->hw_mismatch_popup;
|
||||
Popup* popup = desktop->popup;
|
||||
|
||||
char* text_buffer = malloc(256);
|
||||
scene_manager_set_scene_state(
|
||||
@@ -28,10 +26,10 @@ void desktop_scene_hw_mismatch_on_enter(void* context) {
|
||||
version_get_target(NULL));
|
||||
popup_set_context(popup, desktop);
|
||||
popup_set_header(
|
||||
popup, "!!!! HW Mismatch !!!!", 60, 14 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter);
|
||||
popup_set_text(popup, text_buffer, 60, 37 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter);
|
||||
popup, "!!!! HW Mismatch !!!!", 64, 12 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, text_buffer, 64, 33 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter);
|
||||
popup_set_callback(popup, desktop_scene_hw_mismatch_callback);
|
||||
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdHwMismatch);
|
||||
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdPopup);
|
||||
}
|
||||
|
||||
bool desktop_scene_hw_mismatch_on_event(void* context, SceneManagerEvent event) {
|
||||
@@ -40,11 +38,10 @@ bool desktop_scene_hw_mismatch_on_event(void* context, SceneManagerEvent event)
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
switch(event.event) {
|
||||
case HW_MISMATCH_BACK_EVENT:
|
||||
case DesktopHwMismatchExit:
|
||||
scene_manager_previous_scene(desktop->scene_manager);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -55,11 +52,10 @@ bool desktop_scene_hw_mismatch_on_event(void* context, SceneManagerEvent event)
|
||||
void desktop_scene_hw_mismatch_on_exit(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
furi_assert(desktop);
|
||||
Popup* popup = desktop->hw_mismatch_popup;
|
||||
popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
|
||||
popup_set_callback(popup, NULL);
|
||||
popup_set_context(popup, NULL);
|
||||
|
||||
Popup* popup = desktop->popup;
|
||||
popup_reset(popup);
|
||||
|
||||
char* text_buffer =
|
||||
(char*)scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneHwMismatch);
|
||||
free(text_buffer);
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#include <gui/scene_manager.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include "desktop_scene.h"
|
||||
#include "../desktop_i.h"
|
||||
|
||||
void desktop_scene_secure_enclave_callback(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopEnclaveExit);
|
||||
}
|
||||
|
||||
void desktop_scene_secure_enclave_on_enter(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
furi_assert(desktop);
|
||||
|
||||
Popup* popup = desktop->popup;
|
||||
popup_set_context(popup, desktop);
|
||||
popup_set_header(
|
||||
popup, "No Factory Keys Found", 64, 12 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignBottom);
|
||||
popup_set_text(
|
||||
popup,
|
||||
"Secure Enclave is damaged.\n"
|
||||
"Some apps will not work.",
|
||||
64,
|
||||
33 + STATUS_BAR_Y_SHIFT,
|
||||
AlignCenter,
|
||||
AlignCenter);
|
||||
popup_set_callback(popup, desktop_scene_secure_enclave_callback);
|
||||
|
||||
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdPopup);
|
||||
}
|
||||
|
||||
bool desktop_scene_secure_enclave_on_event(void* context, SceneManagerEvent event) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
switch(event.event) {
|
||||
case DesktopEnclaveExit:
|
||||
scene_manager_previous_scene(desktop->scene_manager);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void desktop_scene_secure_enclave_on_exit(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
furi_assert(desktop);
|
||||
|
||||
Popup* popup = desktop->popup;
|
||||
popup_reset(popup);
|
||||
}
|
||||
@@ -51,6 +51,10 @@ typedef enum {
|
||||
DesktopSlideshowCompleted,
|
||||
DesktopSlideshowPoweroff,
|
||||
|
||||
DesktopHwMismatchExit,
|
||||
|
||||
DesktopEnclaveExit,
|
||||
|
||||
// Global events
|
||||
DesktopGlobalBeforeAppStarted,
|
||||
DesktopGlobalAfterAppFinished,
|
||||
|
||||
@@ -44,25 +44,19 @@ static void menu_draw_callback(Canvas* canvas, void* _model) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
shift_position = (0 + position + items_count - 1) % items_count;
|
||||
item = MenuItemArray_get(model->items, shift_position);
|
||||
if(item->icon) {
|
||||
canvas_draw_icon_animation(canvas, 4, 3, item->icon);
|
||||
}
|
||||
canvas_draw_icon_animation(canvas, 4, 3, item->icon);
|
||||
canvas_draw_str(canvas, 22, 14, item->label);
|
||||
// Second line main
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
shift_position = (1 + position + items_count - 1) % items_count;
|
||||
item = MenuItemArray_get(model->items, shift_position);
|
||||
if(item->icon) {
|
||||
canvas_draw_icon_animation(canvas, 4, 25, item->icon);
|
||||
}
|
||||
canvas_draw_icon_animation(canvas, 4, 25, item->icon);
|
||||
canvas_draw_str(canvas, 22, 36, item->label);
|
||||
// Third line
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
shift_position = (2 + position + items_count - 1) % items_count;
|
||||
item = MenuItemArray_get(model->items, shift_position);
|
||||
if(item->icon) {
|
||||
canvas_draw_icon_animation(canvas, 4, 47, item->icon);
|
||||
}
|
||||
canvas_draw_icon_animation(canvas, 4, 47, item->icon);
|
||||
canvas_draw_str(canvas, 22, 58, item->label);
|
||||
// Frame and scrollbar
|
||||
elements_frame(canvas, 0, 21, 128 - 5, 21);
|
||||
@@ -107,8 +101,8 @@ static void menu_enter(void* context) {
|
||||
menu->view,
|
||||
MenuModel * model,
|
||||
{
|
||||
MenuItem* item = MenuItemArray_get(model->items, model->position);
|
||||
if(item && item->icon) {
|
||||
if(MenuItemArray_size(model->items)) {
|
||||
MenuItem* item = MenuItemArray_get(model->items, model->position);
|
||||
icon_animation_start(item->icon);
|
||||
}
|
||||
},
|
||||
@@ -121,8 +115,8 @@ static void menu_exit(void* context) {
|
||||
menu->view,
|
||||
MenuModel * model,
|
||||
{
|
||||
MenuItem* item = MenuItemArray_get(model->items, model->position);
|
||||
if(item && item->icon) {
|
||||
if(MenuItemArray_size(model->items)) {
|
||||
MenuItem* item = MenuItemArray_get(model->items, model->position);
|
||||
icon_animation_stop(item->icon);
|
||||
}
|
||||
},
|
||||
@@ -230,19 +224,17 @@ static void menu_process_up(Menu* menu) {
|
||||
menu->view,
|
||||
MenuModel * model,
|
||||
{
|
||||
MenuItem* item = MenuItemArray_get(model->items, model->position);
|
||||
if(item && item->icon) {
|
||||
if(MenuItemArray_size(model->items)) {
|
||||
MenuItem* item = MenuItemArray_get(model->items, model->position);
|
||||
icon_animation_stop(item->icon);
|
||||
}
|
||||
|
||||
if(model->position > 0) {
|
||||
model->position--;
|
||||
} else {
|
||||
model->position = MenuItemArray_size(model->items) - 1;
|
||||
}
|
||||
if(model->position > 0) {
|
||||
model->position--;
|
||||
} else {
|
||||
model->position = MenuItemArray_size(model->items) - 1;
|
||||
}
|
||||
|
||||
item = MenuItemArray_get(model->items, model->position);
|
||||
if(item && item->icon) {
|
||||
item = MenuItemArray_get(model->items, model->position);
|
||||
icon_animation_start(item->icon);
|
||||
}
|
||||
},
|
||||
@@ -254,19 +246,17 @@ static void menu_process_down(Menu* menu) {
|
||||
menu->view,
|
||||
MenuModel * model,
|
||||
{
|
||||
MenuItem* item = MenuItemArray_get(model->items, model->position);
|
||||
if(item && item->icon) {
|
||||
if(MenuItemArray_size(model->items)) {
|
||||
MenuItem* item = MenuItemArray_get(model->items, model->position);
|
||||
icon_animation_stop(item->icon);
|
||||
}
|
||||
|
||||
if(model->position < MenuItemArray_size(model->items) - 1) {
|
||||
model->position++;
|
||||
} else {
|
||||
model->position = 0;
|
||||
}
|
||||
if(model->position < MenuItemArray_size(model->items) - 1) {
|
||||
model->position++;
|
||||
} else {
|
||||
model->position = 0;
|
||||
}
|
||||
|
||||
item = MenuItemArray_get(model->items, model->position);
|
||||
if(item && item->icon) {
|
||||
item = MenuItemArray_get(model->items, model->position);
|
||||
icon_animation_start(item->icon);
|
||||
}
|
||||
},
|
||||
@@ -279,7 +269,7 @@ static void menu_process_ok(Menu* menu) {
|
||||
menu->view,
|
||||
MenuModel * model,
|
||||
{
|
||||
if(model->position < MenuItemArray_size(model->items)) {
|
||||
if(MenuItemArray_size(model->items)) {
|
||||
item = MenuItemArray_get(model->items, model->position);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -117,3 +117,11 @@ App(
|
||||
requires=["js_app"],
|
||||
sources=["modules/js_widget.c"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="js_vgm",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="js_vgm_ep",
|
||||
requires=["js_app"],
|
||||
sources=["modules/js_vgm/*.c", "modules/js_vgm/ICM42688P/*.c"],
|
||||
)
|
||||
|
||||
297
applications/system/js_app/modules/js_vgm/ICM42688P/ICM42688P.c
Normal file
297
applications/system/js_app/modules/js_vgm/ICM42688P/ICM42688P.c
Normal file
@@ -0,0 +1,297 @@
|
||||
#include "ICM42688P_regs.h"
|
||||
#include "ICM42688P.h"
|
||||
|
||||
#define TAG "ICM42688P"
|
||||
|
||||
#define ICM42688P_TIMEOUT 100
|
||||
|
||||
struct ICM42688P {
|
||||
FuriHalSpiBusHandle* spi_bus;
|
||||
const GpioPin* irq_pin;
|
||||
float accel_scale;
|
||||
float gyro_scale;
|
||||
};
|
||||
|
||||
static const struct AccelFullScale {
|
||||
float value;
|
||||
uint8_t reg_mask;
|
||||
} accel_fs_modes[] = {
|
||||
[AccelFullScale16G] = {16.f, ICM42688_AFS_16G},
|
||||
[AccelFullScale8G] = {8.f, ICM42688_AFS_8G},
|
||||
[AccelFullScale4G] = {4.f, ICM42688_AFS_4G},
|
||||
[AccelFullScale2G] = {2.f, ICM42688_AFS_2G},
|
||||
};
|
||||
|
||||
static const struct GyroFullScale {
|
||||
float value;
|
||||
uint8_t reg_mask;
|
||||
} gyro_fs_modes[] = {
|
||||
[GyroFullScale2000DPS] = {2000.f, ICM42688_GFS_2000DPS},
|
||||
[GyroFullScale1000DPS] = {1000.f, ICM42688_GFS_1000DPS},
|
||||
[GyroFullScale500DPS] = {500.f, ICM42688_GFS_500DPS},
|
||||
[GyroFullScale250DPS] = {250.f, ICM42688_GFS_250DPS},
|
||||
[GyroFullScale125DPS] = {125.f, ICM42688_GFS_125DPS},
|
||||
[GyroFullScale62_5DPS] = {62.5f, ICM42688_GFS_62_5DPS},
|
||||
[GyroFullScale31_25DPS] = {31.25f, ICM42688_GFS_31_25DPS},
|
||||
[GyroFullScale15_625DPS] = {15.625f, ICM42688_GFS_15_625DPS},
|
||||
};
|
||||
|
||||
static bool icm42688p_write_reg(FuriHalSpiBusHandle* spi_bus, uint8_t addr, uint8_t value) {
|
||||
bool res = false;
|
||||
furi_hal_spi_acquire(spi_bus);
|
||||
do {
|
||||
uint8_t cmd_data[2] = {addr & 0x7F, value};
|
||||
if(!furi_hal_spi_bus_tx(spi_bus, cmd_data, 2, ICM42688P_TIMEOUT)) break;
|
||||
res = true;
|
||||
} while(0);
|
||||
furi_hal_spi_release(spi_bus);
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool icm42688p_read_reg(FuriHalSpiBusHandle* spi_bus, uint8_t addr, uint8_t* value) {
|
||||
bool res = false;
|
||||
furi_hal_spi_acquire(spi_bus);
|
||||
do {
|
||||
uint8_t cmd_byte = addr | (1 << 7);
|
||||
if(!furi_hal_spi_bus_tx(spi_bus, &cmd_byte, 1, ICM42688P_TIMEOUT)) break;
|
||||
if(!furi_hal_spi_bus_rx(spi_bus, value, 1, ICM42688P_TIMEOUT)) break;
|
||||
res = true;
|
||||
} while(0);
|
||||
furi_hal_spi_release(spi_bus);
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool
|
||||
icm42688p_read_mem(FuriHalSpiBusHandle* spi_bus, uint8_t addr, uint8_t* data, uint8_t len) {
|
||||
bool res = false;
|
||||
furi_hal_spi_acquire(spi_bus);
|
||||
do {
|
||||
uint8_t cmd_byte = addr | (1 << 7);
|
||||
if(!furi_hal_spi_bus_tx(spi_bus, &cmd_byte, 1, ICM42688P_TIMEOUT)) break;
|
||||
if(!furi_hal_spi_bus_rx(spi_bus, data, len, ICM42688P_TIMEOUT)) break;
|
||||
res = true;
|
||||
} while(0);
|
||||
furi_hal_spi_release(spi_bus);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool icm42688p_accel_config(
|
||||
ICM42688P* icm42688p,
|
||||
ICM42688PAccelFullScale full_scale,
|
||||
ICM42688PDataRate rate) {
|
||||
icm42688p->accel_scale = accel_fs_modes[full_scale].value;
|
||||
uint8_t reg_value = accel_fs_modes[full_scale].reg_mask | rate;
|
||||
return icm42688p_write_reg(icm42688p->spi_bus, ICM42688_ACCEL_CONFIG0, reg_value);
|
||||
}
|
||||
|
||||
float icm42688p_accel_get_full_scale(ICM42688P* icm42688p) {
|
||||
return icm42688p->accel_scale;
|
||||
}
|
||||
|
||||
bool icm42688p_gyro_config(
|
||||
ICM42688P* icm42688p,
|
||||
ICM42688PGyroFullScale full_scale,
|
||||
ICM42688PDataRate rate) {
|
||||
icm42688p->gyro_scale = gyro_fs_modes[full_scale].value;
|
||||
uint8_t reg_value = gyro_fs_modes[full_scale].reg_mask | rate;
|
||||
return icm42688p_write_reg(icm42688p->spi_bus, ICM42688_GYRO_CONFIG0, reg_value);
|
||||
}
|
||||
|
||||
float icm42688p_gyro_get_full_scale(ICM42688P* icm42688p) {
|
||||
return icm42688p->gyro_scale;
|
||||
}
|
||||
|
||||
bool icm42688p_read_accel_raw(ICM42688P* icm42688p, ICM42688PRawData* data) {
|
||||
bool ret = icm42688p_read_mem(
|
||||
icm42688p->spi_bus, ICM42688_ACCEL_DATA_X1, (uint8_t*)data, sizeof(ICM42688PRawData));
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool icm42688p_read_gyro_raw(ICM42688P* icm42688p, ICM42688PRawData* data) {
|
||||
bool ret = icm42688p_read_mem(
|
||||
icm42688p->spi_bus, ICM42688_GYRO_DATA_X1, (uint8_t*)data, sizeof(ICM42688PRawData));
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool icm42688p_write_gyro_offset(ICM42688P* icm42688p, ICM42688PScaledData* scaled_data) {
|
||||
if((fabsf(scaled_data->x) > 64.f) || (fabsf(scaled_data->y) > 64.f) ||
|
||||
(fabsf(scaled_data->z) > 64.f)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t offset_x = (uint16_t)(-(int16_t)(scaled_data->x * 32.f) * 16) >> 4;
|
||||
uint16_t offset_y = (uint16_t)(-(int16_t)(scaled_data->y * 32.f) * 16) >> 4;
|
||||
uint16_t offset_z = (uint16_t)(-(int16_t)(scaled_data->z * 32.f) * 16) >> 4;
|
||||
|
||||
uint8_t offset_regs[9];
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_REG_BANK_SEL, 4);
|
||||
icm42688p_read_mem(icm42688p->spi_bus, ICM42688_OFFSET_USER0, offset_regs, 5);
|
||||
|
||||
offset_regs[0] = offset_x & 0xFF;
|
||||
offset_regs[1] = (offset_x & 0xF00) >> 8;
|
||||
offset_regs[1] |= (offset_y & 0xF00) >> 4;
|
||||
offset_regs[2] = offset_y & 0xFF;
|
||||
offset_regs[3] = offset_z & 0xFF;
|
||||
offset_regs[4] &= 0xF0;
|
||||
offset_regs[4] |= (offset_z & 0x0F00) >> 8;
|
||||
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_OFFSET_USER0, offset_regs[0]);
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_OFFSET_USER1, offset_regs[1]);
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_OFFSET_USER2, offset_regs[2]);
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_OFFSET_USER3, offset_regs[3]);
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_OFFSET_USER4, offset_regs[4]);
|
||||
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_REG_BANK_SEL, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void icm42688p_apply_scale(ICM42688PRawData* raw_data, float full_scale, ICM42688PScaledData* data) {
|
||||
data->x = ((float)(raw_data->x)) / 32768.f * full_scale;
|
||||
data->y = ((float)(raw_data->y)) / 32768.f * full_scale;
|
||||
data->z = ((float)(raw_data->z)) / 32768.f * full_scale;
|
||||
}
|
||||
|
||||
void icm42688p_apply_scale_fifo(
|
||||
ICM42688P* icm42688p,
|
||||
ICM42688PFifoPacket* fifo_data,
|
||||
ICM42688PScaledData* accel_data,
|
||||
ICM42688PScaledData* gyro_data) {
|
||||
float full_scale = icm42688p->accel_scale;
|
||||
accel_data->x = ((float)(fifo_data->a_x)) / 32768.f * full_scale;
|
||||
accel_data->y = ((float)(fifo_data->a_y)) / 32768.f * full_scale;
|
||||
accel_data->z = ((float)(fifo_data->a_z)) / 32768.f * full_scale;
|
||||
|
||||
full_scale = icm42688p->gyro_scale;
|
||||
gyro_data->x = ((float)(fifo_data->g_x)) / 32768.f * full_scale;
|
||||
gyro_data->y = ((float)(fifo_data->g_y)) / 32768.f * full_scale;
|
||||
gyro_data->z = ((float)(fifo_data->g_z)) / 32768.f * full_scale;
|
||||
}
|
||||
|
||||
float icm42688p_read_temp(ICM42688P* icm42688p) {
|
||||
uint8_t reg_val[2];
|
||||
|
||||
icm42688p_read_mem(icm42688p->spi_bus, ICM42688_TEMP_DATA1, reg_val, 2);
|
||||
int16_t temp_int = (reg_val[0] << 8) | reg_val[1];
|
||||
return ((float)temp_int / 132.48f) + 25.f;
|
||||
}
|
||||
|
||||
void icm42688_fifo_enable(
|
||||
ICM42688P* icm42688p,
|
||||
ICM42688PIrqCallback irq_callback,
|
||||
void* irq_context) {
|
||||
// FIFO mode: stream
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_FIFO_CONFIG, (1 << 6));
|
||||
// Little-endian data, FIFO count in records
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INTF_CONFIG0, (1 << 7) | (1 << 6));
|
||||
// FIFO partial read, FIFO packet: gyro + accel TODO: 20bit
|
||||
icm42688p_write_reg(
|
||||
icm42688p->spi_bus, ICM42688_FIFO_CONFIG1, (1 << 6) | (1 << 5) | (1 << 1) | (1 << 0));
|
||||
// FIFO irq watermark
|
||||
uint16_t fifo_watermark = 1;
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_FIFO_CONFIG2, fifo_watermark & 0xFF);
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_FIFO_CONFIG3, fifo_watermark >> 8);
|
||||
|
||||
// IRQ1: push-pull, active high
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_CONFIG, (1 << 1) | (1 << 0));
|
||||
// Clear IRQ on status read
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_CONFIG0, 0);
|
||||
// IRQ pulse duration
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_CONFIG1, (1 << 6) | (1 << 5));
|
||||
|
||||
uint8_t reg_data = 0;
|
||||
icm42688p_read_reg(icm42688p->spi_bus, ICM42688_INT_STATUS, ®_data);
|
||||
|
||||
furi_hal_gpio_init(icm42688p->irq_pin, GpioModeInterruptRise, GpioPullDown, GpioSpeedVeryHigh);
|
||||
furi_hal_gpio_remove_int_callback(icm42688p->irq_pin);
|
||||
furi_hal_gpio_add_int_callback(icm42688p->irq_pin, irq_callback, irq_context);
|
||||
|
||||
// IRQ1 source: FIFO threshold
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE0, (1 << 2));
|
||||
}
|
||||
|
||||
void icm42688_fifo_disable(ICM42688P* icm42688p) {
|
||||
furi_hal_gpio_remove_int_callback(icm42688p->irq_pin);
|
||||
furi_hal_gpio_init(icm42688p->irq_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE0, 0);
|
||||
|
||||
// FIFO mode: bypass
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_FIFO_CONFIG, 0);
|
||||
}
|
||||
|
||||
uint16_t icm42688_fifo_get_count(ICM42688P* icm42688p) {
|
||||
uint16_t reg_val = 0;
|
||||
icm42688p_read_mem(icm42688p->spi_bus, ICM42688_FIFO_COUNTH, (uint8_t*)®_val, 2);
|
||||
return reg_val;
|
||||
}
|
||||
|
||||
bool icm42688_fifo_read(ICM42688P* icm42688p, ICM42688PFifoPacket* data) {
|
||||
icm42688p_read_mem(
|
||||
icm42688p->spi_bus, ICM42688_FIFO_DATA, (uint8_t*)data, sizeof(ICM42688PFifoPacket));
|
||||
return (data->header) & (1 << 7);
|
||||
}
|
||||
|
||||
ICM42688P* icm42688p_alloc(FuriHalSpiBusHandle* spi_bus, const GpioPin* irq_pin) {
|
||||
ICM42688P* icm42688p = malloc(sizeof(ICM42688P));
|
||||
icm42688p->spi_bus = spi_bus;
|
||||
icm42688p->irq_pin = irq_pin;
|
||||
return icm42688p;
|
||||
}
|
||||
|
||||
void icm42688p_free(ICM42688P* icm42688p) {
|
||||
free(icm42688p);
|
||||
}
|
||||
|
||||
bool icm42688p_init(ICM42688P* icm42688p) {
|
||||
furi_hal_spi_bus_handle_init(icm42688p->spi_bus);
|
||||
|
||||
// Software reset
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_REG_BANK_SEL, 0); // Set reg bank to 0
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_DEVICE_CONFIG, 0x01); // SPI Mode 0, SW reset
|
||||
furi_delay_ms(1);
|
||||
|
||||
uint8_t reg_value = 0;
|
||||
bool read_ok = icm42688p_read_reg(icm42688p->spi_bus, ICM42688_WHO_AM_I, ®_value);
|
||||
if(!read_ok) {
|
||||
FURI_LOG_E(TAG, "Chip ID read failed");
|
||||
return false;
|
||||
} else if(reg_value != ICM42688_WHOAMI) {
|
||||
FURI_LOG_E(
|
||||
TAG, "Sensor returned wrong ID 0x%02X, expected 0x%02X", reg_value, ICM42688_WHOAMI);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disable all interrupts
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE0, 0);
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE1, 0);
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE3, 0);
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE4, 0);
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_REG_BANK_SEL, 4);
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE6, 0);
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE7, 0);
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_REG_BANK_SEL, 0);
|
||||
|
||||
// Data format: little endian
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INTF_CONFIG0, 0);
|
||||
|
||||
// Enable all sensors
|
||||
icm42688p_write_reg(
|
||||
icm42688p->spi_bus,
|
||||
ICM42688_PWR_MGMT0,
|
||||
ICM42688_PWR_TEMP_ON | ICM42688_PWR_GYRO_MODE_LN | ICM42688_PWR_ACCEL_MODE_LN);
|
||||
furi_delay_ms(45);
|
||||
|
||||
icm42688p_accel_config(icm42688p, AccelFullScale16G, DataRate1kHz);
|
||||
icm42688p_gyro_config(icm42688p, GyroFullScale2000DPS, DataRate1kHz);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool icm42688p_deinit(ICM42688P* icm42688p) {
|
||||
// Software reset
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_REG_BANK_SEL, 0); // Set reg bank to 0
|
||||
icm42688p_write_reg(icm42688p->spi_bus, ICM42688_DEVICE_CONFIG, 0x01); // SPI Mode 0, SW reset
|
||||
|
||||
furi_hal_spi_bus_handle_deinit(icm42688p->spi_bus);
|
||||
return true;
|
||||
}
|
||||
127
applications/system/js_app/modules/js_vgm/ICM42688P/ICM42688P.h
Normal file
127
applications/system/js_app/modules/js_vgm/ICM42688P/ICM42688P.h
Normal file
@@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
DataRate32kHz = 0x01,
|
||||
DataRate16kHz = 0x02,
|
||||
DataRate8kHz = 0x03,
|
||||
DataRate4kHz = 0x04,
|
||||
DataRate2kHz = 0x05,
|
||||
DataRate1kHz = 0x06,
|
||||
DataRate200Hz = 0x07,
|
||||
DataRate100Hz = 0x08,
|
||||
DataRate50Hz = 0x09,
|
||||
DataRate25Hz = 0x0A,
|
||||
DataRate12_5Hz = 0x0B,
|
||||
DataRate6_25Hz = 0x0C, // Accelerometer only
|
||||
DataRate3_125Hz = 0x0D, // Accelerometer only
|
||||
DataRate1_5625Hz = 0x0E, // Accelerometer only
|
||||
DataRate500Hz = 0x0F,
|
||||
} ICM42688PDataRate;
|
||||
|
||||
typedef enum {
|
||||
AccelFullScale16G = 0,
|
||||
AccelFullScale8G,
|
||||
AccelFullScale4G,
|
||||
AccelFullScale2G,
|
||||
AccelFullScaleTotal,
|
||||
} ICM42688PAccelFullScale;
|
||||
|
||||
typedef enum {
|
||||
GyroFullScale2000DPS = 0,
|
||||
GyroFullScale1000DPS,
|
||||
GyroFullScale500DPS,
|
||||
GyroFullScale250DPS,
|
||||
GyroFullScale125DPS,
|
||||
GyroFullScale62_5DPS,
|
||||
GyroFullScale31_25DPS,
|
||||
GyroFullScale15_625DPS,
|
||||
GyroFullScaleTotal,
|
||||
} ICM42688PGyroFullScale;
|
||||
|
||||
typedef struct {
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
int16_t z;
|
||||
} __attribute__((packed)) ICM42688PRawData;
|
||||
|
||||
typedef struct {
|
||||
uint8_t header;
|
||||
int16_t a_x;
|
||||
int16_t a_y;
|
||||
int16_t a_z;
|
||||
int16_t g_x;
|
||||
int16_t g_y;
|
||||
int16_t g_z;
|
||||
uint8_t temp;
|
||||
uint16_t ts;
|
||||
} __attribute__((packed)) ICM42688PFifoPacket;
|
||||
|
||||
typedef struct {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
} ICM42688PScaledData;
|
||||
|
||||
typedef struct ICM42688P ICM42688P;
|
||||
|
||||
typedef void (*ICM42688PIrqCallback)(void* ctx);
|
||||
|
||||
ICM42688P* icm42688p_alloc(FuriHalSpiBusHandle* spi_bus, const GpioPin* irq_pin);
|
||||
|
||||
bool icm42688p_init(ICM42688P* icm42688p);
|
||||
|
||||
bool icm42688p_deinit(ICM42688P* icm42688p);
|
||||
|
||||
void icm42688p_free(ICM42688P* icm42688p);
|
||||
|
||||
bool icm42688p_accel_config(
|
||||
ICM42688P* icm42688p,
|
||||
ICM42688PAccelFullScale full_scale,
|
||||
ICM42688PDataRate rate);
|
||||
|
||||
float icm42688p_accel_get_full_scale(ICM42688P* icm42688p);
|
||||
|
||||
bool icm42688p_gyro_config(
|
||||
ICM42688P* icm42688p,
|
||||
ICM42688PGyroFullScale full_scale,
|
||||
ICM42688PDataRate rate);
|
||||
|
||||
float icm42688p_gyro_get_full_scale(ICM42688P* icm42688p);
|
||||
|
||||
bool icm42688p_read_accel_raw(ICM42688P* icm42688p, ICM42688PRawData* data);
|
||||
|
||||
bool icm42688p_read_gyro_raw(ICM42688P* icm42688p, ICM42688PRawData* data);
|
||||
|
||||
bool icm42688p_write_gyro_offset(ICM42688P* icm42688p, ICM42688PScaledData* scaled_data);
|
||||
|
||||
void icm42688p_apply_scale(ICM42688PRawData* raw_data, float full_scale, ICM42688PScaledData* data);
|
||||
|
||||
void icm42688p_apply_scale_fifo(
|
||||
ICM42688P* icm42688p,
|
||||
ICM42688PFifoPacket* fifo_data,
|
||||
ICM42688PScaledData* accel_data,
|
||||
ICM42688PScaledData* gyro_data);
|
||||
|
||||
float icm42688p_read_temp(ICM42688P* icm42688p);
|
||||
|
||||
void icm42688_fifo_enable(
|
||||
ICM42688P* icm42688p,
|
||||
ICM42688PIrqCallback irq_callback,
|
||||
void* irq_context);
|
||||
|
||||
void icm42688_fifo_disable(ICM42688P* icm42688p);
|
||||
|
||||
uint16_t icm42688_fifo_get_count(ICM42688P* icm42688p);
|
||||
|
||||
bool icm42688_fifo_read(ICM42688P* icm42688p, ICM42688PFifoPacket* data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,176 @@
|
||||
#pragma once
|
||||
|
||||
#define ICM42688_WHOAMI 0x47
|
||||
|
||||
// Bank 0
|
||||
#define ICM42688_DEVICE_CONFIG 0x11
|
||||
#define ICM42688_DRIVE_CONFIG 0x13
|
||||
#define ICM42688_INT_CONFIG 0x14
|
||||
#define ICM42688_FIFO_CONFIG 0x16
|
||||
#define ICM42688_TEMP_DATA1 0x1D
|
||||
#define ICM42688_TEMP_DATA0 0x1E
|
||||
#define ICM42688_ACCEL_DATA_X1 0x1F
|
||||
#define ICM42688_ACCEL_DATA_X0 0x20
|
||||
#define ICM42688_ACCEL_DATA_Y1 0x21
|
||||
#define ICM42688_ACCEL_DATA_Y0 0x22
|
||||
#define ICM42688_ACCEL_DATA_Z1 0x23
|
||||
#define ICM42688_ACCEL_DATA_Z0 0x24
|
||||
#define ICM42688_GYRO_DATA_X1 0x25
|
||||
#define ICM42688_GYRO_DATA_X0 0x26
|
||||
#define ICM42688_GYRO_DATA_Y1 0x27
|
||||
#define ICM42688_GYRO_DATA_Y0 0x28
|
||||
#define ICM42688_GYRO_DATA_Z1 0x29
|
||||
#define ICM42688_GYRO_DATA_Z0 0x2A
|
||||
#define ICM42688_TMST_FSYNCH 0x2B
|
||||
#define ICM42688_TMST_FSYNCL 0x2C
|
||||
#define ICM42688_INT_STATUS 0x2D
|
||||
#define ICM42688_FIFO_COUNTH 0x2E
|
||||
#define ICM42688_FIFO_COUNTL 0x2F
|
||||
#define ICM42688_FIFO_DATA 0x30
|
||||
#define ICM42688_APEX_DATA0 0x31
|
||||
#define ICM42688_APEX_DATA1 0x32
|
||||
#define ICM42688_APEX_DATA2 0x33
|
||||
#define ICM42688_APEX_DATA3 0x34
|
||||
#define ICM42688_APEX_DATA4 0x35
|
||||
#define ICM42688_APEX_DATA5 0x36
|
||||
#define ICM42688_INT_STATUS2 0x37
|
||||
#define ICM42688_INT_STATUS3 0x38
|
||||
#define ICM42688_SIGNAL_PATH_RESET 0x4B
|
||||
#define ICM42688_INTF_CONFIG0 0x4C
|
||||
#define ICM42688_INTF_CONFIG1 0x4D
|
||||
#define ICM42688_PWR_MGMT0 0x4E
|
||||
#define ICM42688_GYRO_CONFIG0 0x4F
|
||||
#define ICM42688_ACCEL_CONFIG0 0x50
|
||||
#define ICM42688_GYRO_CONFIG1 0x51
|
||||
#define ICM42688_GYRO_ACCEL_CONFIG0 0x52
|
||||
#define ICM42688_ACCEL_CONFIG1 0x53
|
||||
#define ICM42688_TMST_CONFIG 0x54
|
||||
#define ICM42688_APEX_CONFIG0 0x56
|
||||
#define ICM42688_SMD_CONFIG 0x57
|
||||
#define ICM42688_FIFO_CONFIG1 0x5F
|
||||
#define ICM42688_FIFO_CONFIG2 0x60
|
||||
#define ICM42688_FIFO_CONFIG3 0x61
|
||||
#define ICM42688_FSYNC_CONFIG 0x62
|
||||
#define ICM42688_INT_CONFIG0 0x63
|
||||
#define ICM42688_INT_CONFIG1 0x64
|
||||
#define ICM42688_INT_SOURCE0 0x65
|
||||
#define ICM42688_INT_SOURCE1 0x66
|
||||
#define ICM42688_INT_SOURCE3 0x68
|
||||
#define ICM42688_INT_SOURCE4 0x69
|
||||
#define ICM42688_FIFO_LOST_PKT0 0x6C
|
||||
#define ICM42688_FIFO_LOST_PKT1 0x6D
|
||||
#define ICM42688_SELF_TEST_CONFIG 0x70
|
||||
#define ICM42688_WHO_AM_I 0x75
|
||||
#define ICM42688_REG_BANK_SEL 0x76
|
||||
|
||||
// Bank 1
|
||||
#define ICM42688_SENSOR_CONFIG0 0x03
|
||||
#define ICM42688_GYRO_CONFIG_STATIC2 0x0B
|
||||
#define ICM42688_GYRO_CONFIG_STATIC3 0x0C
|
||||
#define ICM42688_GYRO_CONFIG_STATIC4 0x0D
|
||||
#define ICM42688_GYRO_CONFIG_STATIC5 0x0E
|
||||
#define ICM42688_GYRO_CONFIG_STATIC6 0x0F
|
||||
#define ICM42688_GYRO_CONFIG_STATIC7 0x10
|
||||
#define ICM42688_GYRO_CONFIG_STATIC8 0x11
|
||||
#define ICM42688_GYRO_CONFIG_STATIC9 0x12
|
||||
#define ICM42688_GYRO_CONFIG_STATIC10 0x13
|
||||
#define ICM42688_XG_ST_DATA 0x5F
|
||||
#define ICM42688_YG_ST_DATA 0x60
|
||||
#define ICM42688_ZG_ST_DATA 0x61
|
||||
#define ICM42688_TMSTVAL0 0x62
|
||||
#define ICM42688_TMSTVAL1 0x63
|
||||
#define ICM42688_TMSTVAL2 0x64
|
||||
#define ICM42688_INTF_CONFIG4 0x7A
|
||||
#define ICM42688_INTF_CONFIG5 0x7B
|
||||
#define ICM42688_INTF_CONFIG6 0x7C
|
||||
|
||||
// Bank 2
|
||||
#define ICM42688_ACCEL_CONFIG_STATIC2 0x03
|
||||
#define ICM42688_ACCEL_CONFIG_STATIC3 0x04
|
||||
#define ICM42688_ACCEL_CONFIG_STATIC4 0x05
|
||||
#define ICM42688_XA_ST_DATA 0x3B
|
||||
#define ICM42688_YA_ST_DATA 0x3C
|
||||
#define ICM42688_ZA_ST_DATA 0x3D
|
||||
|
||||
// Bank 4
|
||||
#define ICM42688_APEX_CONFIG1 0x40
|
||||
#define ICM42688_APEX_CONFIG2 0x41
|
||||
#define ICM42688_APEX_CONFIG3 0x42
|
||||
#define ICM42688_APEX_CONFIG4 0x43
|
||||
#define ICM42688_APEX_CONFIG5 0x44
|
||||
#define ICM42688_APEX_CONFIG6 0x45
|
||||
#define ICM42688_APEX_CONFIG7 0x46
|
||||
#define ICM42688_APEX_CONFIG8 0x47
|
||||
#define ICM42688_APEX_CONFIG9 0x48
|
||||
#define ICM42688_ACCEL_WOM_X_THR 0x4A
|
||||
#define ICM42688_ACCEL_WOM_Y_THR 0x4B
|
||||
#define ICM42688_ACCEL_WOM_Z_THR 0x4C
|
||||
#define ICM42688_INT_SOURCE6 0x4D
|
||||
#define ICM42688_INT_SOURCE7 0x4E
|
||||
#define ICM42688_INT_SOURCE8 0x4F
|
||||
#define ICM42688_INT_SOURCE9 0x50
|
||||
#define ICM42688_INT_SOURCE10 0x51
|
||||
#define ICM42688_OFFSET_USER0 0x77
|
||||
#define ICM42688_OFFSET_USER1 0x78
|
||||
#define ICM42688_OFFSET_USER2 0x79
|
||||
#define ICM42688_OFFSET_USER3 0x7A
|
||||
#define ICM42688_OFFSET_USER4 0x7B
|
||||
#define ICM42688_OFFSET_USER5 0x7C
|
||||
#define ICM42688_OFFSET_USER6 0x7D
|
||||
#define ICM42688_OFFSET_USER7 0x7E
|
||||
#define ICM42688_OFFSET_USER8 0x7F
|
||||
|
||||
// PWR_MGMT0
|
||||
#define ICM42688_PWR_TEMP_ON (0 << 5)
|
||||
#define ICM42688_PWR_TEMP_OFF (1 << 5)
|
||||
#define ICM42688_PWR_IDLE (1 << 4)
|
||||
#define ICM42688_PWR_GYRO_MODE_OFF (0 << 2)
|
||||
#define ICM42688_PWR_GYRO_MODE_LN (3 << 2)
|
||||
#define ICM42688_PWR_ACCEL_MODE_OFF (0 << 0)
|
||||
#define ICM42688_PWR_ACCEL_MODE_LP (2 << 0)
|
||||
#define ICM42688_PWR_ACCEL_MODE_LN (3 << 0)
|
||||
|
||||
// GYRO_CONFIG0
|
||||
#define ICM42688_GFS_2000DPS (0x00 << 5)
|
||||
#define ICM42688_GFS_1000DPS (0x01 << 5)
|
||||
#define ICM42688_GFS_500DPS (0x02 << 5)
|
||||
#define ICM42688_GFS_250DPS (0x03 << 5)
|
||||
#define ICM42688_GFS_125DPS (0x04 << 5)
|
||||
#define ICM42688_GFS_62_5DPS (0x05 << 5)
|
||||
#define ICM42688_GFS_31_25DPS (0x06 << 5)
|
||||
#define ICM42688_GFS_15_625DPS (0x07 << 5)
|
||||
|
||||
#define ICM42688_GODR_32kHz 0x01
|
||||
#define ICM42688_GODR_16kHz 0x02
|
||||
#define ICM42688_GODR_8kHz 0x03
|
||||
#define ICM42688_GODR_4kHz 0x04
|
||||
#define ICM42688_GODR_2kHz 0x05
|
||||
#define ICM42688_GODR_1kHz 0x06
|
||||
#define ICM42688_GODR_200Hz 0x07
|
||||
#define ICM42688_GODR_100Hz 0x08
|
||||
#define ICM42688_GODR_50Hz 0x09
|
||||
#define ICM42688_GODR_25Hz 0x0A
|
||||
#define ICM42688_GODR_12_5Hz 0x0B
|
||||
#define ICM42688_GODR_500Hz 0x0F
|
||||
|
||||
// ACCEL_CONFIG0
|
||||
#define ICM42688_AFS_16G (0x00 << 5)
|
||||
#define ICM42688_AFS_8G (0x01 << 5)
|
||||
#define ICM42688_AFS_4G (0x02 << 5)
|
||||
#define ICM42688_AFS_2G (0x03 << 5)
|
||||
|
||||
#define ICM42688_AODR_32kHz 0x01
|
||||
#define ICM42688_AODR_16kHz 0x02
|
||||
#define ICM42688_AODR_8kHz 0x03
|
||||
#define ICM42688_AODR_4kHz 0x04
|
||||
#define ICM42688_AODR_2kHz 0x05
|
||||
#define ICM42688_AODR_1kHz 0x06
|
||||
#define ICM42688_AODR_200Hz 0x07
|
||||
#define ICM42688_AODR_100Hz 0x08
|
||||
#define ICM42688_AODR_50Hz 0x09
|
||||
#define ICM42688_AODR_25Hz 0x0A
|
||||
#define ICM42688_AODR_12_5Hz 0x0B
|
||||
#define ICM42688_AODR_6_25Hz 0x0C
|
||||
#define ICM42688_AODR_3_125Hz 0x0D
|
||||
#define ICM42688_AODR_1_5625Hz 0x0E
|
||||
#define ICM42688_AODR_500Hz 0x0F
|
||||
3
applications/system/js_app/modules/js_vgm/README.md
Normal file
3
applications/system/js_app/modules/js_vgm/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
The files imu.c, imu.h, and ICM42688P are from https://github.com/flipperdevices/flipperzero-game-engine.git
|
||||
|
||||
Please see that file for the license.
|
||||
328
applications/system/js_app/modules/js_vgm/imu.c
Normal file
328
applications/system/js_app/modules/js_vgm/imu.c
Normal file
@@ -0,0 +1,328 @@
|
||||
#include <furi.h>
|
||||
#include "imu.h"
|
||||
#include "ICM42688P/ICM42688P.h"
|
||||
|
||||
#define TAG "IMU"
|
||||
|
||||
#define ACCEL_GYRO_RATE DataRate100Hz
|
||||
|
||||
#define FILTER_SAMPLE_FREQ 100.f
|
||||
#define FILTER_BETA 0.08f
|
||||
|
||||
#define SAMPLE_RATE_DIV 5
|
||||
|
||||
#define SENSITIVITY_K 30.f
|
||||
#define EXP_RATE 1.1f
|
||||
|
||||
#define IMU_CALI_AVG 64
|
||||
|
||||
typedef enum {
|
||||
ImuStop = (1 << 0),
|
||||
ImuNewData = (1 << 1),
|
||||
} ImuThreadFlags;
|
||||
|
||||
#define FLAGS_ALL (ImuStop | ImuNewData)
|
||||
|
||||
typedef struct {
|
||||
float q0;
|
||||
float q1;
|
||||
float q2;
|
||||
float q3;
|
||||
float roll;
|
||||
float pitch;
|
||||
float yaw;
|
||||
} ImuProcessedData;
|
||||
|
||||
typedef struct {
|
||||
FuriThread* thread;
|
||||
ICM42688P* icm42688p;
|
||||
ImuProcessedData processed_data;
|
||||
bool lefty;
|
||||
} ImuThread;
|
||||
|
||||
static void imu_madgwick_filter(
|
||||
ImuProcessedData* out,
|
||||
ICM42688PScaledData* accel,
|
||||
ICM42688PScaledData* gyro);
|
||||
|
||||
static void imu_irq_callback(void* context) {
|
||||
furi_assert(context);
|
||||
ImuThread* imu = context;
|
||||
furi_thread_flags_set(furi_thread_get_id(imu->thread), ImuNewData);
|
||||
}
|
||||
|
||||
static void imu_process_data(ImuThread* imu, ICM42688PFifoPacket* in_data) {
|
||||
ICM42688PScaledData accel_data;
|
||||
ICM42688PScaledData gyro_data;
|
||||
|
||||
// Get accel and gyro data in g and degrees/s
|
||||
icm42688p_apply_scale_fifo(imu->icm42688p, in_data, &accel_data, &gyro_data);
|
||||
|
||||
// Gyro: degrees/s to rads/s
|
||||
gyro_data.x = gyro_data.x / 180.f * M_PI;
|
||||
gyro_data.y = gyro_data.y / 180.f * M_PI;
|
||||
gyro_data.z = gyro_data.z / 180.f * M_PI;
|
||||
|
||||
// Sensor Fusion algorithm
|
||||
ImuProcessedData* out = &imu->processed_data;
|
||||
imu_madgwick_filter(out, &accel_data, &gyro_data);
|
||||
|
||||
// Quaternion to euler angles
|
||||
float roll = atan2f(
|
||||
out->q0 * out->q1 + out->q2 * out->q3, 0.5f - out->q1 * out->q1 - out->q2 * out->q2);
|
||||
float pitch = asinf(-2.0f * (out->q1 * out->q3 - out->q0 * out->q2));
|
||||
float yaw = atan2f(
|
||||
out->q1 * out->q2 + out->q0 * out->q3, 0.5f - out->q2 * out->q2 - out->q3 * out->q3);
|
||||
|
||||
// Euler angles: rads to degrees
|
||||
out->roll = roll / M_PI * 180.f;
|
||||
out->pitch = pitch / M_PI * 180.f;
|
||||
out->yaw = yaw / M_PI * 180.f;
|
||||
}
|
||||
|
||||
static void calibrate_gyro(ImuThread* imu) {
|
||||
ICM42688PRawData data;
|
||||
ICM42688PScaledData offset_scaled = {.x = 0.f, .y = 0.f, .z = 0.f};
|
||||
|
||||
icm42688p_write_gyro_offset(imu->icm42688p, &offset_scaled);
|
||||
furi_delay_ms(10);
|
||||
|
||||
int32_t avg_x = 0;
|
||||
int32_t avg_y = 0;
|
||||
int32_t avg_z = 0;
|
||||
|
||||
for(uint8_t i = 0; i < IMU_CALI_AVG; i++) {
|
||||
icm42688p_read_gyro_raw(imu->icm42688p, &data);
|
||||
avg_x += data.x;
|
||||
avg_y += data.y;
|
||||
avg_z += data.z;
|
||||
furi_delay_ms(2);
|
||||
}
|
||||
|
||||
data.x = avg_x / IMU_CALI_AVG;
|
||||
data.y = avg_y / IMU_CALI_AVG;
|
||||
data.z = avg_z / IMU_CALI_AVG;
|
||||
|
||||
icm42688p_apply_scale(&data, icm42688p_gyro_get_full_scale(imu->icm42688p), &offset_scaled);
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Offsets: x %f, y %f, z %f",
|
||||
(double)offset_scaled.x,
|
||||
(double)offset_scaled.y,
|
||||
(double)offset_scaled.z);
|
||||
icm42688p_write_gyro_offset(imu->icm42688p, &offset_scaled);
|
||||
}
|
||||
|
||||
// static float imu_angle_diff(float a, float b) {
|
||||
// float diff = a - b;
|
||||
// if(diff > 180.f)
|
||||
// diff -= 360.f;
|
||||
// else if(diff < -180.f)
|
||||
// diff += 360.f;
|
||||
|
||||
// return diff;
|
||||
// }
|
||||
|
||||
static int32_t imu_thread(void* context) {
|
||||
furi_assert(context);
|
||||
ImuThread* imu = context;
|
||||
|
||||
// float yaw_last = 0.f;
|
||||
// float pitch_last = 0.f;
|
||||
// float diff_x = 0.f;
|
||||
// float diff_y = 0.f;
|
||||
|
||||
calibrate_gyro(imu);
|
||||
|
||||
icm42688p_accel_config(imu->icm42688p, AccelFullScale16G, ACCEL_GYRO_RATE);
|
||||
icm42688p_gyro_config(imu->icm42688p, GyroFullScale2000DPS, ACCEL_GYRO_RATE);
|
||||
|
||||
imu->processed_data.q0 = 1.f;
|
||||
imu->processed_data.q1 = 0.f;
|
||||
imu->processed_data.q2 = 0.f;
|
||||
imu->processed_data.q3 = 0.f;
|
||||
icm42688_fifo_enable(imu->icm42688p, imu_irq_callback, imu);
|
||||
|
||||
while(1) {
|
||||
uint32_t events = furi_thread_flags_wait(FLAGS_ALL, FuriFlagWaitAny, FuriWaitForever);
|
||||
|
||||
if(events & ImuStop) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(events & ImuNewData) {
|
||||
uint16_t data_pending = icm42688_fifo_get_count(imu->icm42688p);
|
||||
ICM42688PFifoPacket data;
|
||||
while(data_pending--) {
|
||||
icm42688_fifo_read(imu->icm42688p, &data);
|
||||
imu_process_data(imu, &data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
icm42688_fifo_disable(imu->icm42688p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ImuThread* imu_start(ICM42688P* icm42688p) {
|
||||
ImuThread* imu = malloc(sizeof(ImuThread));
|
||||
imu->icm42688p = icm42688p;
|
||||
imu->thread = furi_thread_alloc_ex("ImuThread", 4096, imu_thread, imu);
|
||||
imu->lefty = furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient);
|
||||
furi_thread_start(imu->thread);
|
||||
|
||||
return imu;
|
||||
}
|
||||
|
||||
void imu_stop(ImuThread* imu) {
|
||||
furi_assert(imu);
|
||||
|
||||
furi_thread_flags_set(furi_thread_get_id(imu->thread), ImuStop);
|
||||
|
||||
furi_thread_join(imu->thread);
|
||||
furi_thread_free(imu->thread);
|
||||
|
||||
free(imu);
|
||||
}
|
||||
|
||||
static float imu_inv_sqrt(float number) {
|
||||
union {
|
||||
float f;
|
||||
uint32_t i;
|
||||
} conv = {.f = number};
|
||||
conv.i = 0x5F3759Df - (conv.i >> 1);
|
||||
conv.f *= 1.5f - (number * 0.5f * conv.f * conv.f);
|
||||
return conv.f;
|
||||
}
|
||||
|
||||
/* Simple madgwik filter, based on: https://github.com/arduino-libraries/MadgwickAHRS/ */
|
||||
|
||||
static void imu_madgwick_filter(
|
||||
ImuProcessedData* out,
|
||||
ICM42688PScaledData* accel,
|
||||
ICM42688PScaledData* gyro) {
|
||||
float recipNorm;
|
||||
float s0, s1, s2, s3;
|
||||
float qDot1, qDot2, qDot3, qDot4;
|
||||
float _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2, _8q1, _8q2, q0q0, q1q1, q2q2, q3q3;
|
||||
|
||||
// Rate of change of quaternion from gyroscope
|
||||
qDot1 = 0.5f * (-out->q1 * gyro->x - out->q2 * gyro->y - out->q3 * gyro->z);
|
||||
qDot2 = 0.5f * (out->q0 * gyro->x + out->q2 * gyro->z - out->q3 * gyro->y);
|
||||
qDot3 = 0.5f * (out->q0 * gyro->y - out->q1 * gyro->z + out->q3 * gyro->x);
|
||||
qDot4 = 0.5f * (out->q0 * gyro->z + out->q1 * gyro->y - out->q2 * gyro->x);
|
||||
|
||||
// Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation)
|
||||
if(!((accel->x == 0.0f) && (accel->y == 0.0f) && (accel->z == 0.0f))) {
|
||||
// Normalise accelerometer measurement
|
||||
recipNorm = imu_inv_sqrt(accel->x * accel->x + accel->y * accel->y + accel->z * accel->z);
|
||||
accel->x *= recipNorm;
|
||||
accel->y *= recipNorm;
|
||||
accel->z *= recipNorm;
|
||||
|
||||
// Auxiliary variables to avoid repeated arithmetic
|
||||
_2q0 = 2.0f * out->q0;
|
||||
_2q1 = 2.0f * out->q1;
|
||||
_2q2 = 2.0f * out->q2;
|
||||
_2q3 = 2.0f * out->q3;
|
||||
_4q0 = 4.0f * out->q0;
|
||||
_4q1 = 4.0f * out->q1;
|
||||
_4q2 = 4.0f * out->q2;
|
||||
_8q1 = 8.0f * out->q1;
|
||||
_8q2 = 8.0f * out->q2;
|
||||
q0q0 = out->q0 * out->q0;
|
||||
q1q1 = out->q1 * out->q1;
|
||||
q2q2 = out->q2 * out->q2;
|
||||
q3q3 = out->q3 * out->q3;
|
||||
|
||||
// Gradient decent algorithm corrective step
|
||||
s0 = _4q0 * q2q2 + _2q2 * accel->x + _4q0 * q1q1 - _2q1 * accel->y;
|
||||
s1 = _4q1 * q3q3 - _2q3 * accel->x + 4.0f * q0q0 * out->q1 - _2q0 * accel->y - _4q1 +
|
||||
_8q1 * q1q1 + _8q1 * q2q2 + _4q1 * accel->z;
|
||||
s2 = 4.0f * q0q0 * out->q2 + _2q0 * accel->x + _4q2 * q3q3 - _2q3 * accel->y - _4q2 +
|
||||
_8q2 * q1q1 + _8q2 * q2q2 + _4q2 * accel->z;
|
||||
s3 = 4.0f * q1q1 * out->q3 - _2q1 * accel->x + 4.0f * q2q2 * out->q3 - _2q2 * accel->y;
|
||||
recipNorm =
|
||||
imu_inv_sqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude
|
||||
s0 *= recipNorm;
|
||||
s1 *= recipNorm;
|
||||
s2 *= recipNorm;
|
||||
s3 *= recipNorm;
|
||||
|
||||
// Apply feedback step
|
||||
qDot1 -= FILTER_BETA * s0;
|
||||
qDot2 -= FILTER_BETA * s1;
|
||||
qDot3 -= FILTER_BETA * s2;
|
||||
qDot4 -= FILTER_BETA * s3;
|
||||
}
|
||||
|
||||
// Integrate rate of change of quaternion to yield quaternion
|
||||
out->q0 += qDot1 * (1.0f / FILTER_SAMPLE_FREQ);
|
||||
out->q1 += qDot2 * (1.0f / FILTER_SAMPLE_FREQ);
|
||||
out->q2 += qDot3 * (1.0f / FILTER_SAMPLE_FREQ);
|
||||
out->q3 += qDot4 * (1.0f / FILTER_SAMPLE_FREQ);
|
||||
|
||||
// Normalise quaternion
|
||||
recipNorm = imu_inv_sqrt(
|
||||
out->q0 * out->q0 + out->q1 * out->q1 + out->q2 * out->q2 + out->q3 * out->q3);
|
||||
out->q0 *= recipNorm;
|
||||
out->q1 *= recipNorm;
|
||||
out->q2 *= recipNorm;
|
||||
out->q3 *= recipNorm;
|
||||
}
|
||||
|
||||
/* IMU API */
|
||||
|
||||
struct Imu {
|
||||
FuriHalSpiBusHandle* icm42688p_device;
|
||||
ICM42688P* icm42688p;
|
||||
ImuThread* thread;
|
||||
bool present;
|
||||
};
|
||||
|
||||
Imu* imu_alloc(void) {
|
||||
Imu* imu = malloc(sizeof(Imu));
|
||||
imu->icm42688p_device = malloc(sizeof(FuriHalSpiBusHandle));
|
||||
memcpy(imu->icm42688p_device, &furi_hal_spi_bus_handle_external, sizeof(FuriHalSpiBusHandle));
|
||||
imu->icm42688p_device->cs = &gpio_ext_pc3;
|
||||
|
||||
imu->icm42688p = icm42688p_alloc(imu->icm42688p_device, &gpio_ext_pb2);
|
||||
imu->present = icm42688p_init(imu->icm42688p);
|
||||
|
||||
if(imu->present) {
|
||||
imu->thread = imu_start(imu->icm42688p);
|
||||
}
|
||||
|
||||
return imu;
|
||||
}
|
||||
|
||||
void imu_free(Imu* imu) {
|
||||
if(imu->present) {
|
||||
imu_stop(imu->thread);
|
||||
}
|
||||
icm42688p_deinit(imu->icm42688p);
|
||||
icm42688p_free(imu->icm42688p);
|
||||
free(imu->icm42688p_device);
|
||||
free(imu);
|
||||
}
|
||||
|
||||
bool imu_present(Imu* imu) {
|
||||
return imu->present;
|
||||
}
|
||||
|
||||
float imu_pitch_get(Imu* imu) {
|
||||
// we pretend that reading a float is an atomic operation
|
||||
return imu->thread->lefty ? -imu->thread->processed_data.pitch :
|
||||
imu->thread->processed_data.pitch;
|
||||
}
|
||||
|
||||
float imu_roll_get(Imu* imu) {
|
||||
// we pretend that reading a float is an atomic operation
|
||||
return imu->thread->processed_data.roll;
|
||||
}
|
||||
|
||||
float imu_yaw_get(Imu* imu) {
|
||||
// we pretend that reading a float is an atomic operation
|
||||
return imu->thread->lefty ? -imu->thread->processed_data.yaw : imu->thread->processed_data.yaw;
|
||||
}
|
||||
15
applications/system/js_app/modules/js_vgm/imu.h
Normal file
15
applications/system/js_app/modules/js_vgm/imu.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
typedef struct Imu Imu;
|
||||
|
||||
Imu* imu_alloc(void);
|
||||
|
||||
void imu_free(Imu* imu);
|
||||
|
||||
bool imu_present(Imu* imu);
|
||||
|
||||
float imu_pitch_get(Imu* imu);
|
||||
|
||||
float imu_roll_get(Imu* imu);
|
||||
|
||||
float imu_yaw_get(Imu* imu);
|
||||
159
applications/system/js_app/modules/js_vgm/js_vgm.c
Normal file
159
applications/system/js_app/modules/js_vgm/js_vgm.c
Normal file
@@ -0,0 +1,159 @@
|
||||
#include "../../js_modules.h"
|
||||
#include <furi.h>
|
||||
#include <toolbox/path.h>
|
||||
#include "imu.h"
|
||||
|
||||
#define TAG "JsVgm"
|
||||
|
||||
typedef struct {
|
||||
Imu* imu;
|
||||
bool present;
|
||||
} JsVgmInst;
|
||||
|
||||
static void js_vgm_get_pitch(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsVgmInst* vgm = mjs_get_ptr(mjs, obj_inst);
|
||||
furi_assert(vgm);
|
||||
|
||||
if(vgm->present) {
|
||||
mjs_return(mjs, mjs_mk_number(mjs, imu_pitch_get(vgm->imu)));
|
||||
} else {
|
||||
FURI_LOG_T(TAG, "VGM not present.");
|
||||
mjs_return(mjs, mjs_mk_undefined());
|
||||
}
|
||||
}
|
||||
|
||||
static void js_vgm_get_roll(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsVgmInst* vgm = mjs_get_ptr(mjs, obj_inst);
|
||||
furi_assert(vgm);
|
||||
|
||||
if(vgm->present) {
|
||||
mjs_return(mjs, mjs_mk_number(mjs, imu_roll_get(vgm->imu)));
|
||||
} else {
|
||||
FURI_LOG_T(TAG, "VGM not present.");
|
||||
mjs_return(mjs, mjs_mk_undefined());
|
||||
}
|
||||
}
|
||||
|
||||
static void js_vgm_get_yaw(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsVgmInst* vgm = mjs_get_ptr(mjs, obj_inst);
|
||||
furi_assert(vgm);
|
||||
|
||||
if(vgm->present) {
|
||||
mjs_return(mjs, mjs_mk_number(mjs, imu_yaw_get(vgm->imu)));
|
||||
} else {
|
||||
FURI_LOG_T(TAG, "VGM not present.");
|
||||
mjs_return(mjs, mjs_mk_undefined());
|
||||
}
|
||||
}
|
||||
|
||||
static float distance(float current, float start, float angle) {
|
||||
// make 0 to 359.999...
|
||||
current = (current < 0) ? current + 360 : current;
|
||||
start = (start < 0) ? start + 360 : start;
|
||||
|
||||
// get max and min
|
||||
bool max_is_current = current > start;
|
||||
float max = (max_is_current) ? current : start;
|
||||
float min = (max_is_current) ? start : current;
|
||||
|
||||
// get diff, check if it's greater than 180, and adjust
|
||||
float diff = max - min;
|
||||
bool diff_gt_180 = diff > 180;
|
||||
if(diff_gt_180) {
|
||||
diff = 360 - diff;
|
||||
}
|
||||
|
||||
// if diff is less than angle return 0
|
||||
if(diff < angle) {
|
||||
FURI_LOG_T(TAG, "diff: %f, angle: %f", (double)diff, (double)angle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// return diff with sign
|
||||
return (max_is_current ^ diff_gt_180) ? -diff : diff;
|
||||
}
|
||||
|
||||
static void js_vgm_delta_yaw(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsVgmInst* vgm = mjs_get_ptr(mjs, obj_inst);
|
||||
furi_assert(vgm);
|
||||
size_t num_args = mjs_nargs(mjs);
|
||||
if(num_args < 1 || num_args > 2) {
|
||||
mjs_prepend_errorf(
|
||||
mjs,
|
||||
MJS_BAD_ARGS_ERROR,
|
||||
"Invalid args. Pass (angle [, timeout]). Got %d args.",
|
||||
num_args);
|
||||
mjs_return(mjs, mjs_mk_undefined());
|
||||
return;
|
||||
}
|
||||
|
||||
if(!vgm->present) {
|
||||
FURI_LOG_T(TAG, "VGM not present.");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
double angle = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
if(isnan(angle)) {
|
||||
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "Invalid arg (angle).");
|
||||
mjs_return(mjs, mjs_mk_undefined());
|
||||
return;
|
||||
}
|
||||
uint32_t ms = (num_args == 2) ? mjs_get_int(mjs, mjs_arg(mjs, 1)) : 3000;
|
||||
uint32_t timeout = furi_get_tick() + ms;
|
||||
float initial_yaw = imu_yaw_get(vgm->imu);
|
||||
do {
|
||||
float current_yaw = imu_yaw_get(vgm->imu);
|
||||
double diff = distance(current_yaw, initial_yaw, angle);
|
||||
if(diff != 0) {
|
||||
mjs_return(mjs, mjs_mk_number(mjs, diff));
|
||||
return;
|
||||
}
|
||||
furi_delay_ms(100);
|
||||
} while(furi_get_tick() < timeout);
|
||||
|
||||
mjs_return(mjs, mjs_mk_number(mjs, 0));
|
||||
}
|
||||
|
||||
static void* js_vgm_create(struct mjs* mjs, mjs_val_t* object) {
|
||||
JsVgmInst* vgm = malloc(sizeof(JsVgmInst));
|
||||
vgm->imu = imu_alloc();
|
||||
vgm->present = imu_present(vgm->imu);
|
||||
if(!vgm->present) FURI_LOG_W(TAG, "VGM not present.");
|
||||
mjs_val_t vgm_obj = mjs_mk_object(mjs);
|
||||
mjs_set(mjs, vgm_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, vgm));
|
||||
mjs_set(mjs, vgm_obj, "getPitch", ~0, MJS_MK_FN(js_vgm_get_pitch));
|
||||
mjs_set(mjs, vgm_obj, "getRoll", ~0, MJS_MK_FN(js_vgm_get_roll));
|
||||
mjs_set(mjs, vgm_obj, "getYaw", ~0, MJS_MK_FN(js_vgm_get_yaw));
|
||||
mjs_set(mjs, vgm_obj, "deltaYaw", ~0, MJS_MK_FN(js_vgm_delta_yaw));
|
||||
*object = vgm_obj;
|
||||
return vgm;
|
||||
}
|
||||
|
||||
static void js_vgm_destroy(void* inst) {
|
||||
JsVgmInst* vgm = inst;
|
||||
furi_assert(vgm);
|
||||
imu_free(vgm->imu);
|
||||
vgm->imu = NULL;
|
||||
free(vgm);
|
||||
}
|
||||
|
||||
static const JsModuleDescriptor js_vgm_desc = {
|
||||
name: "vgm",
|
||||
create: js_vgm_create,
|
||||
destroy: js_vgm_destroy,
|
||||
};
|
||||
|
||||
static const FlipperAppPluginDescriptor plugin_descriptor = {
|
||||
.appid = PLUGIN_APP_ID,
|
||||
.ep_api_version = PLUGIN_API_VERSION,
|
||||
.entry_point = &js_vgm_desc,
|
||||
};
|
||||
|
||||
const FlipperAppPluginDescriptor* js_vgm_ep(void) {
|
||||
return &plugin_descriptor;
|
||||
}
|
||||
@@ -851,9 +851,8 @@ static void widget_remove_view(void* context) {
|
||||
ComponentArray_it(it, model->component);
|
||||
while(!ComponentArray_end_p(it)) {
|
||||
WidgetComponent* component = *ComponentArray_ref(it);
|
||||
if(component->free) {
|
||||
if(component && component->free) {
|
||||
component->free(component);
|
||||
component->free = NULL;
|
||||
}
|
||||
ComponentArray_next(it);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user