Sfoglia il codice sorgente

[FL-1884] GPIO application (#732)

* gpio: rename gpio application
* bq25896: add reading OTG config
* furi-hal-power: add is_otg_enabled API
* gpio: introduce new GPIO app, add OTG enable / disable
* variable-item-list: add enter callback
* gpio: add output test view and scene
* gpio app: fix GpioItemTester -> GpioItemTest

Co-authored-by: あく <alleteam@gmail.com>
gornekich 4 anni fa
parent
commit
638f276308

+ 3 - 3
applications/applications.c

@@ -20,7 +20,7 @@ extern int32_t accessor_app(void* p);
 extern int32_t archive_app(void* p);
 extern int32_t archive_app(void* p);
 extern int32_t blink_test_app(void* p);
 extern int32_t blink_test_app(void* p);
 extern int32_t flipper_test_app(void* p);
 extern int32_t flipper_test_app(void* p);
-extern int32_t gpio_test_app(void* p);
+extern int32_t gpio_app(void* p);
 extern int32_t ibutton_app(void* p);
 extern int32_t ibutton_app(void* p);
 extern int32_t irda_app(void* p);
 extern int32_t irda_app(void* p);
 extern int32_t irda_monitor_app(void* p);
 extern int32_t irda_monitor_app(void* p);
@@ -132,8 +132,8 @@ const FlipperApplication FLIPPER_APPS[] = {
     {.app = ibutton_app, .name = "iButton", .stack_size = 2048, .icon = &A_iButton_14},
     {.app = ibutton_app, .name = "iButton", .stack_size = 2048, .icon = &A_iButton_14},
 #endif
 #endif
 
 
-#ifdef APP_GPIO_TEST
-    {.app = gpio_test_app, .name = "GPIO", .stack_size = 1024, .icon = &A_GPIO_14},
+#ifdef APP_GPIO
+    {.app = gpio_app, .name = "GPIO", .stack_size = 1024, .icon = &A_GPIO_14},
 #endif
 #endif
 
 
 };
 };

+ 4 - 4
applications/applications.mk

@@ -24,7 +24,7 @@ SRV_STORAGE	= 1
 # Apps
 # Apps
 SRV_DESKTOP	= 1
 SRV_DESKTOP	= 1
 APP_ARCHIVE	= 1
 APP_ARCHIVE	= 1
-APP_GPIO_TEST = 1
+APP_GPIO = 1
 APP_IBUTTON	= 1
 APP_IBUTTON	= 1
 APP_IRDA	= 1
 APP_IRDA	= 1
 APP_LF_RFID	= 1
 APP_LF_RFID	= 1
@@ -135,9 +135,9 @@ SRV_GUI		= 1
 endif
 endif
 
 
 
 
-APP_GPIO_TEST ?= 0
-ifeq ($(APP_GPIO_TEST), 1)
-CFLAGS		+= -DAPP_GPIO_TEST
+APP_GPIO ?= 0
+ifeq ($(APP_GPIO), 1)
+CFLAGS		+= -DAPP_GPIO
 SRV_GUI		= 1
 SRV_GUI		= 1
 endif
 endif
 
 

+ 0 - 148
applications/gpio-tester/gpio-tester.c

@@ -1,148 +0,0 @@
-#include <furi.h>
-#include <furi-hal.h>
-
-#include <gui/gui.h>
-#include <notification/notification-messages.h>
-
-typedef struct {
-    const char* name;
-    const GpioPin* pin;
-} GpioItem;
-
-static const GpioItem GPIO_PINS[] = {
-    {"1.2: PA7", &gpio_ext_pa7},
-    {"1.3: PA6", &gpio_ext_pa6},
-    {"1.4: PA4", &gpio_ext_pa4},
-    {"1.5: PB3", &gpio_ext_pb3},
-    {"1.6: PB2", &gpio_ext_pb2},
-    {"1.7: PC3", &gpio_ext_pc3},
-    {"2.7: PC1", &gpio_ext_pc1},
-    {"2.8: PC0", &gpio_ext_pc0},
-    {"*.*: ALL", NULL},
-};
-
-static const size_t GPIO_PINS_COUNT = sizeof(GPIO_PINS) / sizeof(GPIO_PINS[0]);
-
-typedef struct {
-    osMessageQueueId_t input_queue;
-    uint8_t gpio_index;
-    ViewPort* view_port;
-    Gui* gui;
-    NotificationApp* notification;
-} GpioTest;
-
-static void gpio_test_render_callback(Canvas* canvas, void* ctx) {
-    GpioTest* gpio_test = ctx;
-
-    canvas_clear(canvas);
-    canvas_set_color(canvas, ColorBlack);
-    canvas_set_font(canvas, FontPrimary);
-    canvas_draw_str(canvas, 2, 10, "GPIO Control");
-    canvas_set_font(canvas, FontSecondary);
-    canvas_draw_str(canvas, 2, 25, GPIO_PINS[gpio_test->gpio_index].name);
-}
-
-static void gpio_test_input_callback(InputEvent* input_event, void* ctx) {
-    GpioTest* gpio_test = ctx;
-
-    osMessageQueuePut(gpio_test->input_queue, input_event, 0, 0);
-}
-
-static void gpio_test_configure_pins(GpioMode mode) {
-    for(size_t i = 0; i < GPIO_PINS_COUNT; i++) {
-        if(!GPIO_PINS[i].pin) continue;
-        hal_gpio_write(GPIO_PINS[i].pin, false);
-        hal_gpio_init(GPIO_PINS[i].pin, mode, GpioPullNo, GpioSpeedVeryHigh);
-    }
-}
-
-static void gpio_test_set_pin(uint8_t index, bool level) {
-    if(GPIO_PINS[index].pin) {
-        hal_gpio_write(GPIO_PINS[index].pin, level);
-    } else {
-        for(size_t i = 0; i < GPIO_PINS_COUNT; i++) {
-            if(!GPIO_PINS[i].pin) continue;
-            hal_gpio_write(GPIO_PINS[i].pin, level);
-        }
-    }
-}
-
-GpioTest* gpio_test_alloc() {
-    GpioTest* instance = furi_alloc(sizeof(GpioTest));
-
-    gpio_test_configure_pins(GpioModeOutputPushPull);
-
-    instance->input_queue = osMessageQueueNew(8, sizeof(InputEvent), NULL);
-    furi_check(instance->input_queue);
-
-    instance->view_port = view_port_alloc();
-    view_port_draw_callback_set(instance->view_port, gpio_test_render_callback, instance);
-    view_port_input_callback_set(instance->view_port, gpio_test_input_callback, instance);
-
-    instance->gui = furi_record_open("gui");
-    gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullscreen);
-
-    instance->notification = furi_record_open("notification");
-
-    return instance;
-}
-
-void gpio_test_free(GpioTest* instance) {
-    furi_assert(instance);
-
-    furi_record_close("notification");
-
-    view_port_enabled_set(instance->view_port, false);
-    gui_remove_view_port(instance->gui, instance->view_port);
-    furi_record_close("gui");
-
-    view_port_free(instance->view_port);
-
-    osMessageQueueDelete(instance->input_queue);
-
-    gpio_test_configure_pins(GpioModeAnalog);
-
-    free(instance);
-}
-
-int32_t gpio_test_app(void* p) {
-    GpioTest* gpio_test = gpio_test_alloc();
-
-    InputEvent event;
-    while(osMessageQueueGet(gpio_test->input_queue, &event, NULL, osWaitForever) == osOK) {
-        if(event.type == InputTypeShort) {
-            if(event.key == InputKeyBack) {
-                notification_message(gpio_test->notification, &sequence_reset_green);
-                break;
-            }
-
-            if(event.key == InputKeyRight) {
-                if(gpio_test->gpio_index < (GPIO_PINS_COUNT - 1)) {
-                    gpio_test->gpio_index++;
-                }
-            }
-
-            if(event.key == InputKeyLeft) {
-                if(gpio_test->gpio_index > 0) {
-                    gpio_test->gpio_index--;
-                }
-            }
-        } else {
-            if(event.key == InputKeyOk) {
-                if(event.type == InputTypePress) {
-                    gpio_test_set_pin(gpio_test->gpio_index, true);
-                    notification_message(gpio_test->notification, &sequence_set_green_255);
-                } else if(event.type == InputTypeRelease) {
-                    gpio_test_set_pin(gpio_test->gpio_index, false);
-                    notification_message(gpio_test->notification, &sequence_reset_green);
-                }
-            }
-        }
-
-        view_port_update(gpio_test->view_port);
-    }
-
-    gpio_test_free(gpio_test);
-
-    return 0;
-}

+ 76 - 0
applications/gpio/gpio_app.c

@@ -0,0 +1,76 @@
+#include "gpio_app_i.h"
+
+#include <furi.h>
+#include <furi-hal.h>
+
+static bool gpio_app_custom_event_callback(void* context, uint32_t event) {
+    furi_assert(context);
+    GpioApp* app = context;
+    return scene_manager_handle_custom_event(app->scene_manager, event);
+}
+
+static bool gpio_app_back_event_callback(void* context) {
+    furi_assert(context);
+    GpioApp* app = context;
+    return scene_manager_handle_back_event(app->scene_manager);
+}
+
+GpioApp* gpio_app_alloc() {
+    GpioApp* app = furi_alloc(sizeof(GpioApp));
+
+    app->gui = furi_record_open("gui");
+    app->notifications = furi_record_open("notification");
+
+    app->view_dispatcher = view_dispatcher_alloc();
+    app->scene_manager = scene_manager_alloc(&gpio_scene_handlers, app);
+    view_dispatcher_enable_queue(app->view_dispatcher);
+    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
+
+    view_dispatcher_set_custom_event_callback(
+        app->view_dispatcher, gpio_app_custom_event_callback);
+    view_dispatcher_set_navigation_event_callback(
+        app->view_dispatcher, gpio_app_back_event_callback);
+
+    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
+
+    app->var_item_list = variable_item_list_alloc();
+    view_dispatcher_add_view(
+        app->view_dispatcher,
+        GpioAppViewVarItemList,
+        variable_item_list_get_view(app->var_item_list));
+    app->gpio_test = gpio_test_alloc();
+    view_dispatcher_add_view(
+        app->view_dispatcher, GpioAppViewGpioTest, gpio_test_get_view(app->gpio_test));
+
+    scene_manager_next_scene(app->scene_manager, GpioSceneStart);
+
+    return app;
+}
+
+void gpio_app_free(GpioApp* app) {
+    furi_assert(app);
+
+    // Views
+    view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewVarItemList);
+    variable_item_list_free(app->var_item_list);
+    view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewGpioTest);
+    gpio_test_free(app->gpio_test);
+    // View dispatcher
+    view_dispatcher_free(app->view_dispatcher);
+    scene_manager_free(app->scene_manager);
+    // Close records
+    furi_record_close("gui");
+    furi_record_close("notification");
+
+    free(app);
+}
+
+int32_t gpio_app(void* p) {
+    GpioApp* gpio_app = gpio_app_alloc();
+
+    view_dispatcher_run(gpio_app->view_dispatcher);
+
+    gpio_app_free(gpio_app);
+
+    return 0;
+}

+ 11 - 0
applications/gpio/gpio_app.h

@@ -0,0 +1,11 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct GpioApp GpioApp;
+
+#ifdef __cplusplus
+}
+#endif

+ 28 - 0
applications/gpio/gpio_app_i.h

@@ -0,0 +1,28 @@
+#pragma once
+
+#include "gpio_app.h"
+#include "gpio_item.h"
+#include "scenes/gpio_scene.h"
+
+#include <gui/gui.h>
+#include <gui/view_dispatcher.h>
+#include <gui/scene_manager.h>
+#include <notification/notification-messages.h>
+
+#include <gui/modules/variable-item-list.h>
+#include "views/gpio_test.h"
+
+struct GpioApp {
+    Gui* gui;
+    ViewDispatcher* view_dispatcher;
+    SceneManager* scene_manager;
+    NotificationApp* notifications;
+
+    VariableItemList* var_item_list;
+    GpioTest* gpio_test;
+};
+
+typedef enum {
+    GpioAppViewVarItemList,
+    GpioAppViewGpioTest,
+} GpioAppView;

+ 51 - 0
applications/gpio/gpio_item.c

@@ -0,0 +1,51 @@
+#include "gpio_item.h"
+
+#include <furi-hal-resources.h>
+
+typedef struct {
+    const char* name;
+    const GpioPin* pin;
+} GpioItem;
+
+static const GpioItem gpio_item[GPIO_ITEM_COUNT] = {
+    {"1.2: PA7", &gpio_ext_pa7},
+    {"1.3: PA6", &gpio_ext_pa6},
+    {"1.4: PA4", &gpio_ext_pa4},
+    {"1.5: PB3", &gpio_ext_pb3},
+    {"1.6: PB2", &gpio_ext_pb2},
+    {"1.7: PC3", &gpio_ext_pc3},
+    {"2.7: PC1", &gpio_ext_pc1},
+    {"2.8: PC0", &gpio_ext_pc0},
+};
+
+void gpio_item_configure_pin(uint8_t index, GpioMode mode) {
+    furi_assert(index < GPIO_ITEM_COUNT);
+    hal_gpio_write(gpio_item[index].pin, false);
+    hal_gpio_init(gpio_item[index].pin, mode, GpioPullNo, GpioSpeedVeryHigh);
+}
+
+void gpio_item_configure_all_pins(GpioMode mode) {
+    for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) {
+        gpio_item_configure_pin(i, mode);
+    }
+}
+
+void gpio_item_set_pin(uint8_t index, bool level) {
+    furi_assert(index < GPIO_ITEM_COUNT);
+    hal_gpio_write(gpio_item[index].pin, level);
+}
+
+void gpio_item_set_all_pins(bool level) {
+    for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) {
+        gpio_item_set_pin(i, level);
+    }
+}
+
+const char* gpio_item_get_pin_name(uint8_t index) {
+    furi_assert(index < GPIO_ITEM_COUNT + 1);
+    if(index == GPIO_ITEM_COUNT) {
+        return "ALL";
+    } else {
+        return gpio_item[index].name;
+    }
+}

+ 15 - 0
applications/gpio/gpio_item.h

@@ -0,0 +1,15 @@
+#pragma once
+
+#include <furi-hal-gpio.h>
+
+#define GPIO_ITEM_COUNT 8
+
+void gpio_item_configure_pin(uint8_t index, GpioMode mode);
+
+void gpio_item_configure_all_pins(GpioMode mode);
+
+void gpio_item_set_pin(uint8_t index, bool level);
+
+void gpio_item_set_all_pins(bool level);
+
+const char* gpio_item_get_pin_name(uint8_t index);

+ 30 - 0
applications/gpio/scenes/gpio_scene.c

@@ -0,0 +1,30 @@
+#include "gpio_scene.h"
+
+// Generate scene on_enter handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
+void (*const gpio_scene_on_enter_handlers[])(void*) = {
+#include "gpio_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Generate scene on_event handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
+bool (*const gpio_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
+#include "gpio_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Generate scene on_exit handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
+void (*const gpio_scene_on_exit_handlers[])(void* context) = {
+#include "gpio_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Initialize scene handlers configuration structure
+const SceneManagerHandlers gpio_scene_handlers = {
+    .on_enter_handlers = gpio_scene_on_enter_handlers,
+    .on_event_handlers = gpio_scene_on_event_handlers,
+    .on_exit_handlers = gpio_scene_on_exit_handlers,
+    .scene_num = GpioSceneNum,
+};

+ 29 - 0
applications/gpio/scenes/gpio_scene.h

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

+ 2 - 0
applications/gpio/scenes/gpio_scene_config.h

@@ -0,0 +1,2 @@
+ADD_SCENE(gpio, start, Start)
+ADD_SCENE(gpio, test, Test)

+ 92 - 0
applications/gpio/scenes/gpio_scene_start.c

@@ -0,0 +1,92 @@
+#include "../gpio_app_i.h"
+#include "furi-hal-power.h"
+
+#define GPIO_SCENE_START_CUSTOM_EVENT_OTG_OFF (0UL)
+#define GPIO_SCENE_START_CUSTOM_EVENT_OTG_ON (1UL)
+#define GPIO_SCENE_START_CUSTOM_EVENT_TEST (2UL)
+
+enum GpioItem {
+    GpioItemOtg,
+    GpioItemTest,
+};
+
+enum GpioOtg {
+    GpioOtgOff,
+    GpioOtgOn,
+    GpioOtgSettingsNum,
+};
+
+const char* const gpio_otg_text[GpioOtgSettingsNum] = {
+    "Off",
+    "On",
+};
+
+static void gpio_scene_start_var_list_enter_callback(void* context, uint32_t index) {
+    furi_assert(context);
+    GpioApp* app = context;
+    if(index == GpioItemTest) {
+        view_dispatcher_send_custom_event(
+            app->view_dispatcher, GPIO_SCENE_START_CUSTOM_EVENT_TEST);
+    }
+}
+
+static void gpio_scene_start_var_list_change_callback(VariableItem* item) {
+    GpioApp* app = variable_item_get_context(item);
+    uint8_t index = variable_item_get_current_value_index(item);
+
+    variable_item_set_current_value_text(item, gpio_otg_text[index]);
+    if(index == GpioOtgOff) {
+        view_dispatcher_send_custom_event(
+            app->view_dispatcher, GPIO_SCENE_START_CUSTOM_EVENT_OTG_OFF);
+    } else if(index == GpioOtgOn) {
+        view_dispatcher_send_custom_event(
+            app->view_dispatcher, GPIO_SCENE_START_CUSTOM_EVENT_OTG_ON);
+    }
+}
+
+void gpio_scene_start_on_enter(void* context) {
+    GpioApp* app = context;
+    VariableItemList* var_item_list = app->var_item_list;
+
+    VariableItem* item;
+    variable_item_list_set_enter_callback(
+        var_item_list, gpio_scene_start_var_list_enter_callback, app);
+    item = variable_item_list_add(
+        var_item_list,
+        "5V on GPIO",
+        GpioOtgSettingsNum,
+        gpio_scene_start_var_list_change_callback,
+        app);
+    if(furi_hal_power_is_otg_enabled()) {
+        variable_item_set_current_value_index(item, GpioOtgOn);
+        variable_item_set_current_value_text(item, gpio_otg_text[GpioOtgOn]);
+    } else {
+        variable_item_set_current_value_index(item, GpioOtgOff);
+        variable_item_set_current_value_text(item, gpio_otg_text[GpioOtgOff]);
+    }
+    variable_item_list_add(var_item_list, "GPIO tester", 0, NULL, NULL);
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewVarItemList);
+}
+
+bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) {
+    GpioApp* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == GPIO_SCENE_START_CUSTOM_EVENT_OTG_ON) {
+            furi_hal_power_enable_otg();
+        } else if(event.event == GPIO_SCENE_START_CUSTOM_EVENT_OTG_OFF) {
+            furi_hal_power_disable_otg();
+        } else if(event.event == GPIO_SCENE_START_CUSTOM_EVENT_TEST) {
+            scene_manager_next_scene(app->scene_manager, GpioSceneTest);
+        }
+        consumed = true;
+    }
+    return consumed;
+}
+
+void gpio_scene_start_on_exit(void* context) {
+    GpioApp* app = context;
+    variable_item_list_clean(app->var_item_list);
+}

+ 27 - 0
applications/gpio/scenes/gpio_scene_test.c

@@ -0,0 +1,27 @@
+#include "../gpio_app_i.h"
+
+void gpio_scene_test_ok_callback(InputType type, void* context) {
+    furi_assert(context);
+    GpioApp* app = context;
+
+    if(type == InputTypePress) {
+        notification_message(app->notifications, &sequence_set_green_255);
+    } else if(type == InputTypeRelease) {
+        notification_message(app->notifications, &sequence_reset_green);
+    }
+}
+
+void gpio_scene_test_on_enter(void* context) {
+    GpioApp* app = context;
+    gpio_item_configure_all_pins(GpioModeOutputPushPull);
+    gpio_test_set_ok_callback(app->gpio_test, gpio_scene_test_ok_callback, app);
+    view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewGpioTest);
+}
+
+bool gpio_scene_test_on_event(void* context, SceneManagerEvent event) {
+    return false;
+}
+
+void gpio_scene_test_on_exit(void* context) {
+    gpio_item_configure_all_pins(GpioModeAnalog);
+}

+ 130 - 0
applications/gpio/views/gpio_test.c

@@ -0,0 +1,130 @@
+#include "gpio_test.h"
+#include "../gpio_item.h"
+
+#include <gui/elements.h>
+
+struct GpioTest {
+    View* view;
+    GpioTestOkCallback callback;
+    void* context;
+};
+
+typedef struct {
+    uint8_t pin_idx;
+} GpioTestModel;
+
+static bool gpio_test_process_left(GpioTest* gpio_test);
+static bool gpio_test_process_right(GpioTest* gpio_test);
+static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event);
+
+static void gpio_test_draw_callback(Canvas* canvas, void* _model) {
+    GpioTestModel* model = _model;
+    canvas_set_font(canvas, FontPrimary);
+    elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Gpio Output mode test");
+    canvas_set_font(canvas, FontSecondary);
+    elements_multiline_text_aligned(
+        canvas, 64, 16, AlignCenter, AlignTop, "Press < or > to change pin");
+    elements_multiline_text_aligned(
+        canvas, 64, 32, AlignCenter, AlignTop, gpio_item_get_pin_name(model->pin_idx));
+}
+
+static bool gpio_test_input_callback(InputEvent* event, void* context) {
+    furi_assert(context);
+    GpioTest* gpio_test = context;
+    bool consumed = false;
+
+    if(event->type == InputTypeShort) {
+        if(event->key == InputKeyRight) {
+            consumed = gpio_test_process_right(gpio_test);
+        } else if(event->key == InputKeyLeft) {
+            consumed = gpio_test_process_left(gpio_test);
+        }
+    } else if(event->key == InputKeyOk) {
+        consumed = gpio_test_process_ok(gpio_test, event);
+    }
+
+    return consumed;
+}
+
+static bool gpio_test_process_left(GpioTest* gpio_test) {
+    with_view_model(
+        gpio_test->view, (GpioTestModel * model) {
+            if(model->pin_idx) {
+                model->pin_idx--;
+            }
+            return true;
+        });
+    return true;
+}
+
+static bool gpio_test_process_right(GpioTest* gpio_test) {
+    with_view_model(
+        gpio_test->view, (GpioTestModel * model) {
+            if(model->pin_idx < GPIO_ITEM_COUNT) {
+                model->pin_idx++;
+            }
+            return true;
+        });
+    return true;
+}
+
+static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event) {
+    bool consumed = false;
+
+    with_view_model(
+        gpio_test->view, (GpioTestModel * model) {
+            if(event->type == InputTypePress) {
+                if(model->pin_idx < GPIO_ITEM_COUNT) {
+                    gpio_item_set_pin(model->pin_idx, true);
+                } else {
+                    gpio_item_set_all_pins(true);
+                }
+                consumed = true;
+            } else if(event->type == InputTypeRelease) {
+                if(model->pin_idx < GPIO_ITEM_COUNT) {
+                    gpio_item_set_pin(model->pin_idx, false);
+                } else {
+                    gpio_item_set_all_pins(false);
+                }
+                consumed = true;
+            }
+            gpio_test->callback(event->type, gpio_test->context);
+            return true;
+        });
+
+    return consumed;
+}
+
+GpioTest* gpio_test_alloc() {
+    GpioTest* gpio_test = furi_alloc(sizeof(GpioTest));
+
+    gpio_test->view = view_alloc();
+    view_allocate_model(gpio_test->view, ViewModelTypeLocking, sizeof(GpioTestModel));
+    view_set_context(gpio_test->view, gpio_test);
+    view_set_draw_callback(gpio_test->view, gpio_test_draw_callback);
+    view_set_input_callback(gpio_test->view, gpio_test_input_callback);
+
+    return gpio_test;
+}
+
+void gpio_test_free(GpioTest* gpio_test) {
+    furi_assert(gpio_test);
+    view_free(gpio_test->view);
+    free(gpio_test);
+}
+
+View* gpio_test_get_view(GpioTest* gpio_test) {
+    furi_assert(gpio_test);
+    return gpio_test->view;
+}
+
+void gpio_test_set_ok_callback(GpioTest* gpio_test, GpioTestOkCallback callback, void* context) {
+    furi_assert(gpio_test);
+    furi_assert(callback);
+    with_view_model(
+        gpio_test->view, (GpioTestModel * model) {
+            gpio_test->callback = callback;
+            gpio_test->context = context;
+            return false;
+        });
+}

+ 14 - 0
applications/gpio/views/gpio_test.h

@@ -0,0 +1,14 @@
+#pragma once
+
+#include <gui/view.h>
+
+typedef struct GpioTest GpioTest;
+typedef void (*GpioTestOkCallback)(InputType type, void* context);
+
+GpioTest* gpio_test_alloc();
+
+void gpio_test_free(GpioTest* gpio_test);
+
+View* gpio_test_get_view(GpioTest* gpio_test);
+
+void gpio_test_set_ok_callback(GpioTest* gpio_test, GpioTestOkCallback callback, void* context);

+ 29 - 0
applications/gui/modules/variable-item-list.c

@@ -18,6 +18,8 @@ ARRAY_DEF(VariableItemArray, VariableItem, M_POD_OPLIST);
 
 
 struct VariableItemList {
 struct VariableItemList {
     View* view;
     View* view;
+    VariableItemListEnterCallback callback;
+    void* context;
 };
 };
 
 
 typedef struct {
 typedef struct {
@@ -30,6 +32,7 @@ static void variable_item_list_process_up(VariableItemList* variable_item_list);
 static void variable_item_list_process_down(VariableItemList* variable_item_list);
 static void variable_item_list_process_down(VariableItemList* variable_item_list);
 static void variable_item_list_process_left(VariableItemList* variable_item_list);
 static void variable_item_list_process_left(VariableItemList* variable_item_list);
 static void variable_item_list_process_right(VariableItemList* variable_item_list);
 static void variable_item_list_process_right(VariableItemList* variable_item_list);
+static void variable_item_list_process_ok(VariableItemList* variable_item_list);
 
 
 static void variable_item_list_draw_callback(Canvas* canvas, void* _model) {
 static void variable_item_list_draw_callback(Canvas* canvas, void* _model) {
     VariableItemListModel* model = _model;
     VariableItemListModel* model = _model;
@@ -104,6 +107,9 @@ static bool variable_item_list_input_callback(InputEvent* event, void* context)
             consumed = true;
             consumed = true;
             variable_item_list_process_right(variable_item_list);
             variable_item_list_process_right(variable_item_list);
             break;
             break;
+        case InputKeyOk:
+            variable_item_list_process_ok(variable_item_list);
+            break;
         default:
         default:
             break;
             break;
         }
         }
@@ -198,6 +204,16 @@ void variable_item_list_process_right(VariableItemList* variable_item_list) {
         });
         });
 }
 }
 
 
+void variable_item_list_process_ok(VariableItemList* variable_item_list) {
+    with_view_model(
+        variable_item_list->view, (VariableItemListModel * model) {
+            if(variable_item_list->callback) {
+                variable_item_list->callback(variable_item_list->context, model->position);
+            }
+            return false;
+        });
+}
+
 VariableItemList* variable_item_list_alloc() {
 VariableItemList* variable_item_list_alloc() {
     VariableItemList* variable_item_list = furi_alloc(sizeof(VariableItemList));
     VariableItemList* variable_item_list = furi_alloc(sizeof(VariableItemList));
     variable_item_list->view = view_alloc();
     variable_item_list->view = view_alloc();
@@ -280,6 +296,19 @@ VariableItem* variable_item_list_add(
     return item;
     return item;
 }
 }
 
 
+void variable_item_list_set_enter_callback(
+    VariableItemList* variable_item_list,
+    VariableItemListEnterCallback callback,
+    void* context) {
+    furi_assert(callback);
+    with_view_model(
+        variable_item_list->view, (VariableItemListModel * model) {
+            variable_item_list->callback = callback;
+            variable_item_list->context = context;
+            return false;
+        });
+}
+
 void variable_item_set_current_value_index(VariableItem* item, uint8_t current_value_index) {
 void variable_item_set_current_value_index(VariableItem* item, uint8_t current_value_index) {
     item->current_value_index = current_value_index;
     item->current_value_index = current_value_index;
 }
 }

+ 14 - 0
applications/gui/modules/variable-item-list.h

@@ -8,6 +8,7 @@ extern "C" {
 typedef struct VariableItemList VariableItemList;
 typedef struct VariableItemList VariableItemList;
 typedef struct VariableItem VariableItem;
 typedef struct VariableItem VariableItem;
 typedef void (*VariableItemChangeCallback)(VariableItem* item);
 typedef void (*VariableItemChangeCallback)(VariableItem* item);
+typedef void (*VariableItemListEnterCallback)(void* context, uint32_t index);
 
 
 /** Allocate and initialize VariableItemList
 /** Allocate and initialize VariableItemList
  * @return VariableItemList* 
  * @return VariableItemList* 
@@ -19,6 +20,9 @@ VariableItemList* variable_item_list_alloc();
  */
  */
 void variable_item_list_free(VariableItemList* variable_item_list);
 void variable_item_list_free(VariableItemList* variable_item_list);
 
 
+/** Clear all elements from list
+ * @param variable_item_list VariableItemList instance
+ */
 void variable_item_list_clean(VariableItemList* variable_item_list);
 void variable_item_list_clean(VariableItemList* variable_item_list);
 
 
 View* variable_item_list_get_view(VariableItemList* variable_item_list);
 View* variable_item_list_get_view(VariableItemList* variable_item_list);
@@ -38,6 +42,16 @@ VariableItem* variable_item_list_add(
     VariableItemChangeCallback change_callback,
     VariableItemChangeCallback change_callback,
     void* context);
     void* context);
 
 
+/** Set enter callback
+ * @param variable_item_list VariableItemList instance
+ * @param calback VariableItemListEnterCallback instance
+ * @param context pointer to context
+ */
+void variable_item_list_set_enter_callback(
+    VariableItemList* variable_item_list,
+    VariableItemListEnterCallback callback,
+    void* context);
+
 /** Set item current selected index
 /** Set item current selected index
  * @param item VariableItem* instance
  * @param item VariableItem* instance
  * @param current_value_index 
  * @param current_value_index 

+ 4 - 0
firmware/targets/f6/furi-hal/furi-hal-power.c

@@ -180,6 +180,10 @@ void furi_hal_power_disable_otg() {
     bq25896_disable_otg();
     bq25896_disable_otg();
 }
 }
 
 
+bool furi_hal_power_is_otg_enabled() {
+    return bq25896_is_otg_enabled();
+}
+
 uint32_t furi_hal_power_get_battery_remaining_capacity() {
 uint32_t furi_hal_power_get_battery_remaining_capacity() {
     return bq27220_get_remaining_capacity();
     return bq27220_get_remaining_capacity();
 }
 }

+ 4 - 0
firmware/targets/f7/furi-hal/furi-hal-power.c

@@ -180,6 +180,10 @@ void furi_hal_power_disable_otg() {
     bq25896_disable_otg();
     bq25896_disable_otg();
 }
 }
 
 
+bool furi_hal_power_is_otg_enabled() {
+    return bq25896_is_otg_enabled();
+}
+
 uint32_t furi_hal_power_get_battery_remaining_capacity() {
 uint32_t furi_hal_power_get_battery_remaining_capacity() {
     return bq27220_get_remaining_capacity();
     return bq27220_get_remaining_capacity();
 }
 }

+ 3 - 0
firmware/targets/furi-hal-include/furi-hal-power.h

@@ -69,6 +69,9 @@ void furi_hal_power_enable_otg();
 /** OTG disable */
 /** OTG disable */
 void furi_hal_power_disable_otg();
 void furi_hal_power_disable_otg();
 
 
+/** Get OTG status */
+bool furi_hal_power_is_otg_enabled();
+
 /** Get remaining battery battery capacity in mAh */
 /** Get remaining battery battery capacity in mAh */
 uint32_t furi_hal_power_get_battery_remaining_capacity();
 uint32_t furi_hal_power_get_battery_remaining_capacity();
 
 

+ 5 - 0
lib/drivers/bq25896.c

@@ -101,6 +101,11 @@ void bq25896_disable_otg() {
     bq25896_write_reg(0x03, (uint8_t*)&bq25896_regs.r03);
     bq25896_write_reg(0x03, (uint8_t*)&bq25896_regs.r03);
 }
 }
 
 
+bool bq25896_is_otg_enabled() {
+    bq25896_read_reg(0x03, (uint8_t*)&bq25896_regs.r03);
+    return bq25896_regs.r03.OTG_CONFIG;
+}
+
 uint16_t bq25896_get_vbus_voltage() {
 uint16_t bq25896_get_vbus_voltage() {
     bq25896_read_reg(0x11, (uint8_t*)&bq25896_regs.r11);
     bq25896_read_reg(0x11, (uint8_t*)&bq25896_regs.r11);
     if(bq25896_regs.r11.VBUS_GD) {
     if(bq25896_regs.r11.VBUS_GD) {

+ 3 - 0
lib/drivers/bq25896.h

@@ -18,6 +18,9 @@ void bq25896_enable_otg();
 /** Disable otg */
 /** Disable otg */
 void bq25896_disable_otg();
 void bq25896_disable_otg();
 
 
+/** Is otg enabled */
+bool bq25896_is_otg_enabled();
+
 /** Get VBUS Voltage in mV */
 /** Get VBUS Voltage in mV */
 uint16_t bq25896_get_vbus_voltage();
 uint16_t bq25896_get_vbus_voltage();