| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565 |
- #include <furi.h>
- #include <furi_hal.h>
- #include <storage/storage.h>
- #include <toolbox/stream/stream.h>
- #include <toolbox/stream/file_stream.h>
- #include <gui/gui.h>
- #include <input/input.h>
- /* Magic happens here -- this file is generated by fbt.
- * Just set fap_icon_assets in application.fam and #include {APPID}_icons.h */
- #include "gpio_controller_icons.h"
- #include "app_defines.h"
- static void draw_main_view(Canvas* canvas, void* ctx);
- static void draw_config_menu_view(Canvas* canvas, void* ctx);
- static DrawView draw_view_funcs[] = {
- draw_main_view,
- draw_config_menu_view
- };
- static void handle_main_input(InputEvent* event, void* ctx);
- static void handle_config_menu_input(InputEvent* event, void* ctx);
- static HandleInput input_handlers[] = {
- handle_main_input,
- handle_config_menu_input
- };
- static ViewerState vstate;
- static int wiggle[] = {-1,1,-1,1};
- static uint32_t wiggle_frame_count = 4;
- static ViewElement elements[] = {
- {PIN_5V, PIN_3V, true, false, true, true, -1, 0, 0, "5V" , (Icon*)&I_5v_pin, NULL},
- {PIN_A7, PIN_SWC, true, false, true, true, -1, 14, 0, "PA7", (Icon*)&I_a7_pin, NULL},
- {PIN_A6, NONE, true, false, true, true, -1, 28, 0, "PA6", (Icon*)&I_a6_pin, NULL},
- {PIN_A4, PIN_SIO, true, false, true, true, -1, 42, 0, "PA4", (Icon*)&I_a4_pin, NULL},
- {PIN_B3, PIN_TX, true, false, true, true, -1, 56, 0, "PB3", (Icon*)&I_b3_pin, NULL},
- {PIN_B2, PIN_RX, true, false, true, true, -1, 70, 0, "PB2", (Icon*)&I_b2_pin, NULL},
- {PIN_C3, PIN_C1, true, false, true, true, -1, 84, 0, "PC3", (Icon*)&I_c3_pin, NULL},
- {GEARIC, PIN_1W, true, true, true, false, -1, 112, 0, "Settings", (Icon*)&I_gear_unhighlighted, (Icon*)&I_gear_highlighted},
- {PIN_3V, PIN_5V, true, false, false, true, -1, 0, 48, "3.3V", (Icon*)&I_3v_pin, NULL},
- {PIN_SWC, PIN_A7, true, false, false, true, -1, 14, 48, "Serial Wire Clock", (Icon*)&I_swc_pin, NULL},
- {PIN_SIO, PIN_A4, true, false, false, true, -1, 42, 48, "Serial IO", (Icon*)&I_sio_pin, NULL},
- {PIN_TX, PIN_B3, true, false, false, true, -1, 56, 48, "UART - Transmit", (Icon*)&I_tx_pin, NULL},
- {PIN_RX, PIN_B2, true, false, false, true, -1, 70, 48, "UART - Receive", (Icon*)&I_rx_pin, NULL},
- {PIN_C1, PIN_C3, true, false, false, true, -1, 84, 48, "PC1", (Icon*)&I_c1_pin, NULL},
- {PIN_C0, NONE, true, false, false, true, -1, 98, 48, "PC0", (Icon*)&I_c0_pin, NULL},
- {PIN_1W, GEARIC, true, true, false, true, -1, 112, 48, "1-Wire", (Icon*)&I_1w_pin, NULL},
- {PIN_GND_08, NONE, false, false, true, false, -1, 98, -1, "GND (Ground)", (Icon*)&I_gnd_pin, NULL},
- {PIN_GND_11, NONE, false, false, false, false, -1, 28, 48, "GND (Ground)", (Icon*)&I_gnd_pin, NULL},
- {PIN_GND_18, NONE, false, false, false, false, -1, 126, 48, "GND (Ground)", (Icon*)&I_gnd_pin, NULL},
- };
- static GPIOPin gpio_pin_config[GPIO_PIN_COUNT];
- static int element_count = NONE; // The NONE enum will a value equal to the number of elements defined in enum_view_element
- size_t strnlen(const char *str, size_t maxlen) {
- size_t len = 0;
- while (len < maxlen && str[len] != '\0') {
- len++;
- }
- return len;
- }
- static void init_state()
- {
- vstate.selected = PIN_A7;
- vstate.wiggle_frame=-1;
- vstate.view = MAIN_VIEW;
- }
- static void init_gpio()
- {
- int count = 0;
- for(size_t i = 0; i < gpio_pins_count; i++) {
- if(!gpio_pins[i].debug) {
- for(int j = 0; j < element_count; j++) {
- if( strcmp(elements[j].name,gpio_pins[i].name) == 0 )
- {
- gpio_pin_config[count].element_idx = j;
- gpio_pin_config[count].pin = gpio_pins[i].pin;
- gpio_pin_config[count].mode = GpioModeOutputPushPull;
- gpio_pin_config[count].pull = GpioPullNo;
- gpio_pin_config[count].speed = GpioSpeedVeryHigh;
- gpio_pin_config[count].value = 0;
- gpio_pin_config[count].name = gpio_pins[i].name;
- gpio_pin_config[count].unset = true;
- gpio_pin_config[count].found = true;
- gpio_pin_config[count].input = false;
- gpio_pin_config[count].user.mode = GPIO_MODE_UNSET;
- gpio_pin_config[count].user.value = GPIO_VALUE_FALSE;
- gpio_pin_config[count].user.gp_idx_input = -1;
- gpio_pin_config[count].user.changed = false;
- elements[j].gp_idx = i;
- elements[j].editable = true;
- count++;
- }
- }
- }
- }
- vstate.result = 0;
- }
- static void update_gpio()
- {
- // read from gpio pins
- for(int i = 0; i < GPIO_PIN_COUNT; i++) {
- GPIOPin* gpc = &gpio_pin_config[i];
- if( !gpc->unset )
- {
- if( gpc->mode == GpioModeInput ) {
- gpc->value = furi_hal_gpio_read(gpc->pin) ? 1 : 0;
- }
- }
- }
- }
- #define TOGGLECOLOR(state, canvas, setting, selected_col, deselected_col) \
- canvas_set_color(canvas, (state == setting) ? selected_col : deselected_col)
- const char* gpio_user_mode_strs[] = {"INPUT","INPUT_PULLUP","OUTPUT","UNSET"};
- const char* gpio_user_value_strs[] = {"TRUE","FALSE","INPUT"};
- static void draw_config_menu_view(Canvas* canvas, void* ctx)
- {
- UNUSED(ctx);
- int gp_idx = elements[vstate.selected].gp_idx;
- GPIOPin* gpc = &gpio_pin_config[gp_idx];
- UNUSED(gpc);
- canvas_set_font(canvas, FontSecondary);
- canvas_set_color(canvas, ColorBlack);
- canvas_draw_rframe(canvas, 1, 1, 126, 62, 0);
-
- TOGGLECOLOR(vstate.config_menu_selected, canvas, CONFIG_MENU_MODE, ColorBlack, ColorWhite);
- canvas_draw_box(canvas, 2, 2, 124, 15);
- TOGGLECOLOR(vstate.config_menu_selected, canvas, CONFIG_MENU_MODE, ColorWhite, ColorBlack);
- canvas_draw_str(canvas, 6, 12, "Mode");
- if( gpc->user.mode > 0 ) canvas_draw_str(canvas, 34, 12, "<");
- canvas_draw_str(canvas, 45, 12, gpio_user_mode_strs[gpc->user.mode]);
- if( gpc->user.mode < GPIO_MODE_UNSET ) canvas_draw_str(canvas, 120, 12, ">");
- if( gpc->user.mode == GPIO_MODE_OUTPUT )
- {
- TOGGLECOLOR(vstate.config_menu_selected, canvas, CONFIG_MENU_VALUE, ColorBlack, ColorWhite);
- canvas_draw_box(canvas, 2, 16, 124, 15);
- TOGGLECOLOR(vstate.config_menu_selected, canvas, CONFIG_MENU_VALUE, ColorWhite, ColorBlack);
- canvas_draw_str(canvas, 6, 12 + 16, "Value");
- if( gpc->user.value > 0 ) canvas_draw_str(canvas, 34, 12 + 16, "<");
- canvas_draw_str(canvas, 45, 12 + 16, gpio_user_value_strs[gpc->user.value]);
- if( gpc->user.value < GPIO_VALUE_INPUT ) canvas_draw_str(canvas, 120, 12 + 16, ">");
- }
- }
- // TODO: Determine the lowest frame delta we can get away with.
- // TODO: Redraw only what changes.
- // - clear previous (drawn) selected pin
- // - clear newly selected pin
- static void draw_main_view(Canvas* canvas, void* ctx)
- {
- UNUSED(ctx);
- canvas_clear(canvas);
- size_t current_frame_time = furi_get_tick();
- size_t delta_cycles = (current_frame_time > vstate.prev_frame_time ? current_frame_time - vstate.prev_frame_time : 0);
- size_t delta_time_ms = delta_cycles * 1000 / furi_kernel_get_tick_frequency();
- // delay until desired delta time and recalculate
- if( delta_time_ms < FRAME_TIME )
- {
- furi_delay_ms(FRAME_TIME-delta_time_ms);
- current_frame_time = furi_get_tick();
- delta_cycles = (current_frame_time > vstate.prev_frame_time ? current_frame_time - vstate.prev_frame_time : 0);
- delta_time_ms = delta_cycles * 1000 / furi_kernel_get_tick_frequency();
- }
-
- vstate.elapsed_time += delta_time_ms;
- vstate.prev_frame_time = current_frame_time;
- canvas_set_font(canvas, FontSecondary);
- char hex_string[3];
- // draw values
- for(int i = 0; i < GPIO_PIN_COUNT; i++) {
- if( !gpio_pin_config[i].unset )
- {
- ViewElement e = elements[gpio_pin_config[i].element_idx];
- // draw wire
- if(e.top_row)
- {
- canvas_draw_line(canvas, e.x_pos + 6, e.y_pos + 16, e.x_pos + 6, e.y_pos + 16 + 8);
- }
- else
- {
- canvas_draw_line(canvas, e.x_pos + 6, e.y_pos, e.x_pos + 6, e.y_pos - 8);
- }
-
- if(gpio_pin_config[i].mode == GpioModeAnalog)
- {
- snprintf(hex_string, sizeof(hex_string), "%02X", (int)gpio_pin_config[i].value);
- if(e.top_row)
- {
- canvas_draw_icon(canvas, e.x_pos - 1, e.y_pos + 20, &I_analog_box);
- canvas_draw_str(canvas, e.x_pos + 1, e.y_pos + 22 + 7, hex_string);
- }
- else
- {
- canvas_draw_icon(canvas, e.x_pos - 1, e.y_pos - 15, &I_analog_box);
- canvas_draw_str(canvas, e.x_pos + 1, e.y_pos - 6, hex_string);
- }
- }
- else
- {
- const Icon* icon = (int)gpio_pin_config[i].value ? &I_digi_one : &I_digi_zero;
- if(e.top_row)
- {
- canvas_draw_icon(canvas, e.x_pos + 2, e.y_pos + 20, icon);
- }
- else
- {
- canvas_draw_icon(canvas, e.x_pos + 2, e.y_pos - 13, icon);
- }
-
- }
- }
- }
- for(int i = 0; i < element_count; i++)
- {
- ViewElement e = elements[i];
- int x = e.x_pos;
- int y = e.y_pos + (e.top_row && e.pull_out ? -3 : 0);
- Icon* icon = e.icon;
- if( vstate.selected == i )
- {
- if( e.pull_out )
- {
- y += e.top_row ? 3 : -3;
- }
- if( e.selected_icon != NULL )
- {
- icon = e.selected_icon;
- }
- if(vstate.wiggle_frame >= 0)
- {
- x += wiggle[vstate.wiggle_frame];
- if(vstate.elapsed_time >= ANIMATE_FRAME_TIME_MS)
- {
- vstate.wiggle_frame++;
- if ((unsigned int)(vstate.wiggle_frame) >= wiggle_frame_count)
- {
- vstate.wiggle_frame = -1;
- }
- vstate.elapsed_time = 0;
- }
- }
- }
- canvas_draw_icon(canvas, x, y, icon);
- }
- // draw arrows
- for(int i = 0; i < GPIO_PIN_COUNT; i++) {
- if( !gpio_pin_config[i].unset )
- {
- ViewElement e = elements[gpio_pin_config[i].element_idx];
- bool selected = vstate.selected == gpio_pin_config[i].element_idx;
- // draw arrow
- if(e.top_row)
- {
- int offset = selected ? 3 : 0;
- const Icon* arrow_icon = gpio_pin_config[i].input ? &I_arrow_up : &I_arrow_down;
- canvas_draw_icon(canvas, e.x_pos + 3, e.y_pos + 8 + offset, arrow_icon);
- }
- else
- {
- int offset = selected ? 0 : 3;
- const Icon* arrow_icon = gpio_pin_config[i].input ? &I_arrow_down : &I_arrow_up;
- canvas_draw_icon(canvas, e.x_pos + 3, e.y_pos + -1 + offset, arrow_icon);
- }
- }
- }
-
- canvas_set_font(canvas, FontSecondary);
- canvas_draw_str(canvas, 0, 42, elements[vstate.selected].name);
- }
- static void handle_main_input(InputEvent* event, void* ctx) {
- if( vstate.wiggle_frame < 0 )
- {
- furi_assert(ctx);
- FuriMessageQueue* event_queue = ctx;
- // place in queue to handle backing out of app
- furi_message_queue_put(event_queue, event, FuriWaitForever);
- if( (event->type == InputTypePress || event->type == InputTypeRelease) && event->key == InputKeyOk )
- {
- if( event->type == InputTypePress && elements[vstate.selected].gp_idx < 0 )
- {
- vstate.wiggle_frame = 0;
- vstate.elapsed_time = 0;
- }
- else if( elements[vstate.selected].gp_idx >= 0 && (event->type == InputTypePress || event->type == InputTypeRelease) )
- {
- int gp_idx = elements[vstate.selected].gp_idx;
- gpio_pin_config[gp_idx].user.prev_mode = gpio_pin_config[gp_idx].user.mode;
- vstate.view = CONFIG_MENU_VIEW;
- vstate.config_menu_selected = CONFIG_MENU_MODE;
- }
- }
- else if(event->type == InputTypePress || event->type == InputTypeRepeat) {
- switch(event->key) {
- case InputKeyLeft:
- vstate.selected--;
- if(vstate.selected == GEARIC) vstate.selected = PIN_1W;
- else if(vstate.selected < 0) vstate.selected = GEARIC;
- break;
- case InputKeyRight:
- if(vstate.selected <= GEARIC)
- {
- vstate.selected++;
- vstate.selected = vstate.selected > GEARIC ? PIN_5V : vstate.selected;
- }
- else
- {
- vstate.selected++;
- vstate.selected = vstate.selected > PIN_1W ? PIN_3V : vstate.selected;
- }
- break;
- case InputKeyUp:
- case InputKeyDown:
- if (elements[vstate.selected].opposite != NONE) vstate.selected = elements[vstate.selected].opposite;
- break;
- default:
- break;
- }
- }
- }
- }
- static void set_GPIO_pin_via_user(int gp_idx)
- {
- GPIOPin* gpc = &gpio_pin_config[gp_idx];
- if(gpc->user.changed)
- {
- // update attributes
- switch(gpc->user.mode)
- {
- case GPIO_MODE_INPUT:
- gpc->mode = GpioModeInput;
- gpc->pull = GpioPullNo;
- gpc->input = true;
- break;
- case GPIO_MODE_INPUT_PULLUP:
- gpc->mode = GpioModeInput;
- gpc->pull = GpioPullUp;
- gpc->input = true;
- break;
- case GPIO_MODE_OUTPUT:
- gpc->mode = GpioModeOutputPushPull;
- gpc->pull = GpioPullNo;
- gpc->input = false;
- break;
- default:
- break;
- }
- switch(gpc->user.value)
- {
- case GPIO_VALUE_TRUE:
- gpc->value = (double)1.0;
- break;
- case GPIO_VALUE_FALSE:
- case GPIO_VALUE_INPUT:
- case GPIO_VALUE_NONE:
- gpc->value = (double)0.0;
- break;
- default:
- break;
- }
- furi_hal_gpio_write(gpc->pin, gpc->value != (double)0.0 ? true : false);
- if( gpc->user.mode != gpc->user.prev_mode) {
- furi_hal_gpio_init(gpc->pin, gpc->mode, gpc->pull, gpc->speed);
- gpc->unset = false;
- }
-
- gpc->user.changed = false;
- }
- }
- static void handle_config_menu_input(InputEvent* event, void* ctx) {
- UNUSED(ctx);
- int gp_idx = elements[vstate.selected].gp_idx;
- GPIOPin* gpc = &gpio_pin_config[gp_idx];
- if(event->type == InputTypePress || event->type == InputTypeRepeat) {
- switch(event->key) {
- case InputKeyLeft:
- switch(vstate.config_menu_selected)
- {
- case CONFIG_MENU_MODE:
- if(gpc->user.mode > 0) {
- gpc->user.mode--;
- gpc->user.changed = true;
- }
- break;
- case CONFIG_MENU_VALUE:
- if(gpc->user.value > 0) {
- gpc->user.value--;
- gpc->user.changed = true;
- }
- break;
- case CONFIG_MENU_INPUT:
- break;
- default:
- break;
- }
- break;
- case InputKeyRight:
- switch(vstate.config_menu_selected)
- {
- case CONFIG_MENU_MODE:
- if(gpc->user.mode < GPIO_MODE_UNSET) {
- gpc->user.mode++;
- gpc->user.changed = true;
- }
- break;
- case CONFIG_MENU_VALUE:
- if(gpc->user.value < GPIO_VALUE_FALSE) {
- gpc->user.value++;
- gpc->user.changed = true;
- }
- break;
- case CONFIG_MENU_INPUT:
- break;
- default:
- break;
- }
- break;
- case InputKeyUp:
- if(gpc->user.mode == GPIO_MODE_OUTPUT )
- {
- if( vstate.config_menu_selected == 0 ) vstate.config_menu_selected = CONFIG_MENU_VALUE;
- else vstate.config_menu_selected--;
- }
- break;
- case InputKeyDown:
- if(gpc->user.mode == GPIO_MODE_OUTPUT )
- {
- if( vstate.config_menu_selected == CONFIG_MENU_VALUE ) vstate.config_menu_selected = 0;
- else vstate.config_menu_selected++;
- }
- break;
- case InputKeyBack:
-
- // Set new pin configuration
- set_GPIO_pin_via_user(gp_idx);
- vstate.view = MAIN_VIEW;
- break;
- default:
- break;
- }
- }
- }
- static void app_draw_callback(Canvas* canvas, void* ctx) {
- draw_view_funcs[vstate.view](canvas,ctx);
- }
- static void app_input_callback(InputEvent* input_event, void* ctx) {
- input_handlers[vstate.view](input_event,ctx);
- }
- int32_t gpio_controller_main(void* p) {
- UNUSED(p);
- 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, view_port);
- 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);
- InputEvent event;
- init_state();
- init_gpio();
- vstate.prev_frame_time = furi_get_tick();
- vstate.elapsed_time = 0;
- bool running = true;
- while(running) {
- if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {
- if(event.type == InputTypePress || event.type == InputTypeRepeat) {
- switch(event.key) {
- case InputKeyBack:
- running = false;
- break;
- default:
- break;
- }
- }
- }
- update_gpio();
- view_port_update(view_port);
- }
- 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);
- return 0;
- }
|