app.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. #include <furi.h>
  2. #include <furi_hal.h>
  3. #include <gui/gui.h>
  4. #include <gui/view.h>
  5. #include <gui/view_dispatcher.h>
  6. #include <gui/modules/submenu.h>
  7. #include <gui/modules/widget.h>
  8. #include <gui/modules/variable_item_list.h>
  9. #include "led_driver.h"
  10. #define TAG "WS2812B-Tester"
  11. // Our application menu has 2 items.
  12. typedef enum {
  13. LedTesterSubmenuIndexLeds,
  14. LedTesterSubmenuIndexAbout,
  15. } LedTesterSubmenuIndex;
  16. // Custom events that describe why the view dispatcher is calling our custom event callback.
  17. typedef enum {
  18. LedTesterEventInitialized,
  19. LedTesterEventDeinit,
  20. LedTesterEventTimer,
  21. LedTesterEventConfigChange,
  22. LedTesterEventClicked,
  23. } LedTesterEventId;
  24. // Each view is a screen we show the user.
  25. typedef enum {
  26. LedTesterViewSubmenu, // The menu when the app starts
  27. LedTesterViewLeds, // The LED screen
  28. LedTesterViewAbout, // The about screen with directions, link to social channel, etc.
  29. } LedTesterView;
  30. // Our model is the data we use to control the LEDs.
  31. typedef struct {
  32. uint8_t led_pin_index; // The index of the pin we are using to control the LEDs
  33. uint32_t led_count; // The number of LEDs
  34. uint8_t led_pattern_index; // The pattern index
  35. int8_t led_divisor; // The speed divisor (1-4, -1 = stopped)
  36. uint8_t led_max_brightness; // The maximum brightness (0-100)
  37. uint8_t timer_counter; // The current timer counter (used for auto-scrolling)
  38. bool enable_5v; // Enable 5V output
  39. } LedTesterModel;
  40. // The application object.
  41. typedef struct {
  42. ViewDispatcher* view_dispatcher; // Switches between our views
  43. Submenu* submenu; // The application menu
  44. VariableItemList* variable_item_list; // The WS2812B settings
  45. Widget* widget_about; // The about screen
  46. FuriTimer* timer; // Timer for automatic updating the LEDs
  47. LedTesterModel* model; // The model
  48. LedDriver* led_driver; // The LED driver
  49. } LedTesterApp;
  50. // Hack so that we can access the application object from a variable_item_list on_enter/exit callback.
  51. static LedTesterApp* global_app = NULL;
  52. /**
  53. * @brief Callback for exiting the application.
  54. * @details This function is called when user press back button. We return VIEW_NONE to
  55. * indicate that we want to exit the application.
  56. * @param _context The context - unused
  57. * @return next view id
  58. */
  59. static uint32_t led_tester_navigation_exit_callback(void* _context) {
  60. UNUSED(_context);
  61. return VIEW_NONE;
  62. }
  63. /**
  64. * @brief Callback for the application's menu.
  65. * @details This function is called when user press back button. We return LedTesterViewSubmenu to
  66. * indicate that we want to return to the application menu.
  67. * @param _context The context - unused
  68. * @return next view id
  69. */
  70. static uint32_t led_tester_navigation_submenu_callback(void* _context) {
  71. UNUSED(_context);
  72. return LedTesterViewSubmenu;
  73. }
  74. /**
  75. * @brief Handle submenu item selection.
  76. * @details This function is called when user selects an item from the submenu.
  77. * @param context The context - LedTesterApp object.
  78. * @param index The LedTesterSubmenuIndex item that was clicked.
  79. */
  80. static void led_tester_submenu_callback(void* context, uint32_t index) {
  81. LedTesterApp* app = (LedTesterApp*)context;
  82. switch(index) {
  83. case LedTesterSubmenuIndexLeds:
  84. view_dispatcher_switch_to_view(app->view_dispatcher, LedTesterViewLeds);
  85. break;
  86. case LedTesterSubmenuIndexAbout:
  87. view_dispatcher_switch_to_view(app->view_dispatcher, LedTesterViewAbout);
  88. break;
  89. default:
  90. break;
  91. }
  92. }
  93. /**
  94. * @brief Callback for timer elapsed.
  95. * @details This function is called when the timer is elapsed.
  96. * @param context The context - LedTesterApp object.
  97. */
  98. static void led_tester_timer_callback(void* context) {
  99. LedTesterApp* app = (LedTesterApp*)context;
  100. view_dispatcher_send_custom_event(app->view_dispatcher, LedTesterEventTimer);
  101. }
  102. /**
  103. * @brief Callback for when the LED configuration settings are updated.
  104. * @details This function is called when the LED configuration settings are changed by the user.
  105. * @param context The context - LedTesterApp object.
  106. */
  107. static void led_tester_settings_updated(LedTesterApp* app) {
  108. view_dispatcher_send_custom_event(app->view_dispatcher, LedTesterEventConfigChange);
  109. }
  110. /**
  111. * @brief Callback for when the LED configuration settings are clicked.
  112. * @details This function is called when the user presses OK button while in the LED configuration settings.
  113. * @param context The context - LedTesterApp object.
  114. */
  115. static void led_tester_item_clicked(void* context, uint32_t index) {
  116. UNUSED(index);
  117. LedTesterApp* app = (LedTesterApp*)context;
  118. view_dispatcher_send_custom_event(app->view_dispatcher, LedTesterEventClicked);
  119. }
  120. /**
  121. * @brief Enable 5V power.
  122. * @details This function enables the 5V power output on pin 1.
  123. */
  124. static void led_tester_5v_power_on() {
  125. uint8_t attempts = 5;
  126. while(--attempts > 0) {
  127. if(furi_hal_power_enable_otg()) break;
  128. }
  129. }
  130. /**
  131. * @brief Disable 5V power.
  132. * @details This function disables the 5V power output on pin 1.
  133. */
  134. static void led_tester_5v_power_off() {
  135. if(furi_hal_power_is_otg_enabled()) {
  136. furi_hal_power_disable_otg();
  137. }
  138. }
  139. // Settings for configuring which GPIO pin to use for controlling the WS2812B LEDs.
  140. static const char* setting_led_pin_config_label = "LED Pin";
  141. static const GpioPin* setting_led_pin_values[] =
  142. {&gpio_ext_pa7, &gpio_ext_pa6, &gpio_ext_pa4, &gpio_ext_pb3, &gpio_ext_pb2, &gpio_ext_pc3};
  143. static char* setting_led_pin_names[] = {"A7", "A6", "A4", "B3", "B2", "C3"};
  144. static void led_tester_setting_led_pin_change(VariableItem* item) {
  145. LedTesterApp* app = variable_item_get_context(item);
  146. LedTesterModel* model = app->model;
  147. uint8_t index = variable_item_get_current_value_index(item);
  148. variable_item_set_current_value_text(item, setting_led_pin_names[index]);
  149. model->led_pin_index = index;
  150. }
  151. // Settings for configuring how many LEDs to enable.
  152. static const char* setting_led_count_config_label = "LED Count";
  153. static uint16_t setting_led_count_values[] = {1, 2, 3, 4, 5, 6, 7, 8, 16, 32, 64, 128, 256, 512};
  154. static char* setting_led_count_names[] =
  155. {"1", "2", "3", "4", "5", "6", "7", "8", "16", "32", "64", "128", "256", "512"};
  156. static uint8_t setting_led_count_default_index = 3; // 4 LEDs
  157. static void led_tester_setting_led_count_change(VariableItem* item) {
  158. LedTesterApp* app = variable_item_get_context(item);
  159. LedTesterModel* model = app->model;
  160. uint8_t index = variable_item_get_current_value_index(item);
  161. variable_item_set_current_value_text(item, setting_led_count_names[index]);
  162. model->led_count = setting_led_count_values[index];
  163. led_tester_settings_updated(app);
  164. }
  165. // Settings for configuring which LED pattern to use.
  166. static const char* setting_led_pattern_config_label = "LED Pattern";
  167. static char* setting_led_pattern_names[] =
  168. {"Red", "Green", "Blue", "White", "RGBW", "GBWR", "BWRG", "WRGB"};
  169. static void led_tester_setting_led_pattern_change(VariableItem* item) {
  170. LedTesterApp* app = variable_item_get_context(item);
  171. LedTesterModel* model = app->model;
  172. uint8_t index = variable_item_get_current_value_index(item);
  173. variable_item_set_current_value_text(item, setting_led_pattern_names[index]);
  174. model->led_pattern_index = index;
  175. led_tester_settings_updated(app);
  176. }
  177. // Settings for configuring which LED speed to use.
  178. static const char* setting_led_speed_config_label = "LED Speed";
  179. static int16_t setting_led_speed_values[] = {-1, 3, 2, 1};
  180. static char* setting_led_speed_names[] = {"Stopped", "Slow", "Medium", "Fast"};
  181. static void led_tester_setting_led_speed_change(VariableItem* item) {
  182. LedTesterApp* app = variable_item_get_context(item);
  183. LedTesterModel* model = app->model;
  184. uint8_t index = variable_item_get_current_value_index(item);
  185. variable_item_set_current_value_text(item, setting_led_speed_names[index]);
  186. model->led_divisor = setting_led_speed_values[index];
  187. led_tester_settings_updated(app);
  188. }
  189. // Settings for configuring the LED brightness.
  190. static const char* setting_led_brightness_config_label = "LED Brightness";
  191. static uint16_t setting_led_brightness_values[] = {1, 2, 3, 5, 10, 20, 25, 50, 75, 100};
  192. static char* setting_led_brightness_names[] =
  193. {"1%", "2%", "3%", "5%", "10%", "20%", "25%", "50%", "75%", "100%"};
  194. static void led_tester_setting_led_brightness_change(VariableItem* item) {
  195. LedTesterApp* app = variable_item_get_context(item);
  196. LedTesterModel* model = app->model;
  197. uint8_t index = variable_item_get_current_value_index(item);
  198. variable_item_set_current_value_text(item, setting_led_brightness_names[index]);
  199. model->led_max_brightness = setting_led_brightness_values[index];
  200. led_tester_settings_updated(app);
  201. }
  202. // Settings for configuring the 5V output.
  203. static const char* setting_5v_config_label = "Enable +5V pin";
  204. static bool setting_5v_values[] = {true, false};
  205. static char* setting_5v_names[] = {"Yes", "No"};
  206. static void led_tester_setting_5v_change(VariableItem* item) {
  207. LedTesterApp* app = variable_item_get_context(item);
  208. LedTesterModel* model = app->model;
  209. uint8_t index = variable_item_get_current_value_index(item);
  210. variable_item_set_current_value_text(item, setting_5v_names[index]);
  211. model->enable_5v = setting_5v_values[index];
  212. if(app->model->enable_5v) {
  213. led_tester_5v_power_on();
  214. } else {
  215. led_tester_5v_power_off();
  216. }
  217. }
  218. /**
  219. * @brief Callback for custom events.
  220. * @details This function is called when a custom event is sent to the view dispatcher.
  221. * @param context The context - LedTesterApp object.
  222. * @param event The event id - LedTesterEventId value.
  223. */
  224. static bool led_tester_custom_event_callback(void* context, uint32_t event) {
  225. LedTesterApp* app = (LedTesterApp*)context;
  226. const GpioPin* pin = setting_led_pin_values[app->model->led_pin_index];
  227. if(!app->led_driver) {
  228. FURI_LOG_E(TAG, "led_driver is NULL. Custom event %lu ignored.", event);
  229. return false;
  230. }
  231. if(event == LedTesterEventTimer) {
  232. app->model->timer_counter++;
  233. }
  234. uint8_t offset = (app->model->led_divisor == -1) ?
  235. 0 :
  236. (app->model->timer_counter / app->model->led_divisor) & 0x3;
  237. uint32_t rgb[4] = {0};
  238. switch(app->model->led_pattern_index) {
  239. case 0: // RED
  240. for(size_t i = 0; i < COUNT_OF(rgb); i++) {
  241. rgb[i] = 0xFF0000;
  242. }
  243. break;
  244. case 1: // GREEN
  245. for(size_t i = 0; i < COUNT_OF(rgb); i++) {
  246. rgb[i] = 0x00FF00;
  247. }
  248. break;
  249. case 2: // BLUE
  250. for(size_t i = 0; i < COUNT_OF(rgb); i++) {
  251. rgb[i] = 0x0000FF;
  252. }
  253. break;
  254. case 3: // WHITE
  255. for(size_t i = 0; i < COUNT_OF(rgb); i++) {
  256. rgb[i] = 0xFFFFFF;
  257. }
  258. break;
  259. case 4: // RGBW
  260. rgb[0] = 0xFF0000;
  261. rgb[1] = 0x00FF00;
  262. rgb[2] = 0x0000FF;
  263. rgb[3] = 0xFFFFFF;
  264. break;
  265. case 5: // GBWR
  266. rgb[3] = 0xFF0000;
  267. rgb[0] = 0x00FF00;
  268. rgb[1] = 0x0000FF;
  269. rgb[2] = 0xFFFFFF;
  270. break;
  271. case 6: // BWRG
  272. rgb[2] = 0xFF0000;
  273. rgb[3] = 0x00FF00;
  274. rgb[0] = 0x0000FF;
  275. rgb[1] = 0xFFFFFF;
  276. break;
  277. case 7: // WRGB
  278. rgb[0] = 0xFFFFFF;
  279. rgb[1] = 0xFF0000;
  280. rgb[2] = 0x00FF00;
  281. rgb[3] = 0x0000FF;
  282. break;
  283. default:
  284. break;
  285. }
  286. // Rotate the pattern
  287. for(size_t i = 0; i < offset; i++) {
  288. uint32_t tmp = rgb[0];
  289. for(size_t j = 0; j < COUNT_OF(rgb) - 1; j++) {
  290. rgb[j] = rgb[j + 1];
  291. }
  292. rgb[COUNT_OF(rgb) - 1] = tmp;
  293. }
  294. // If deinit, turn off the LEDs
  295. if(event == LedTesterEventDeinit) {
  296. for(size_t i = 0; i < COUNT_OF(rgb); i++) {
  297. rgb[i] = 0;
  298. }
  299. }
  300. // Scale the brightness
  301. for(size_t i = 0; i < COUNT_OF(rgb); i++) {
  302. rgb[i] = (((rgb[i] >> 16) & 0xFF) * app->model->led_max_brightness / 100) << 16 |
  303. (((rgb[i] >> 8) & 0xFF) * app->model->led_max_brightness / 100) << 8 |
  304. ((rgb[i] & 0xFF) * app->model->led_max_brightness / 100);
  305. // rgb[i] = (rgb[i] & 0x0F0F0F); // For Debugging, just use lower 4-bits.
  306. }
  307. led_driver_set_pin(app->led_driver, pin);
  308. // Set the LEDs to the pattern
  309. for(size_t i = 0; i < app->model->led_count; i++) {
  310. led_driver_set_led(app->led_driver, i, rgb[i % COUNT_OF(rgb)]);
  311. }
  312. // Turn off any remaining LEDs
  313. for(size_t i = app->model->led_count; i < MAX_LED_COUNT; i++) {
  314. led_driver_set_led(app->led_driver, i, 0);
  315. }
  316. led_driver_transmit(app->led_driver, true);
  317. if(event == LedTesterEventDeinit) {
  318. led_driver_free(app->led_driver);
  319. app->led_driver = NULL;
  320. }
  321. return true;
  322. }
  323. /**
  324. * @brief Callback for entering the LED configuration settings.
  325. * @details This function is called when the user enters the LED configuration settings. We
  326. * start the periodic timer to update the LEDs & we signal initialized so the LEDs
  327. * turn on to their default state.
  328. * @param _context The context - unused
  329. */
  330. void led_tester_enter_leds_callback(void* _context) {
  331. // _context is variable_item_list, but it doesn't expose the item, so we can't get the context.
  332. UNUSED(_context);
  333. // Hack, we use a global to access the app object.
  334. LedTesterApp* app = (LedTesterApp*)global_app;
  335. app->timer = furi_timer_alloc(led_tester_timer_callback, FuriTimerTypePeriodic, app);
  336. furi_timer_start(app->timer, 250);
  337. view_dispatcher_send_custom_event(app->view_dispatcher, LedTesterEventInitialized);
  338. app->led_driver =
  339. led_driver_alloc(MAX_LED_COUNT, setting_led_pin_values[app->model->led_pin_index]);
  340. }
  341. /**
  342. * @brief Callback for exiting the LED configuration settings.
  343. * @details This function is called when the user exits the LED configuration settings. We
  344. * stop the periodic timer to update the LEDs.
  345. * @param _context The context - unused
  346. */
  347. void led_tester_exit_leds_callback(void* _context) {
  348. // _context is variable_item_list, but it doesn't expose the item, so we can't get the context.
  349. UNUSED(_context);
  350. // Hack, we use a global to access the app object.
  351. LedTesterApp* app = (LedTesterApp*)global_app;
  352. furi_timer_stop(app->timer);
  353. furi_timer_free(app->timer);
  354. app->timer = NULL;
  355. view_dispatcher_send_custom_event(app->view_dispatcher, LedTesterEventDeinit);
  356. }
  357. /**
  358. * @brief Allocate the led tester application.
  359. * @details This function allocates the led tester application resources.
  360. * @return LedTesterApp object.
  361. */
  362. static LedTesterApp* led_tester_app_alloc() {
  363. LedTesterApp* app = (LedTesterApp*)malloc(sizeof(LedTesterApp));
  364. global_app = app;
  365. app->model = (LedTesterModel*)malloc(sizeof(LedTesterModel));
  366. Gui* gui = furi_record_open(RECORD_GUI);
  367. app->view_dispatcher = view_dispatcher_alloc();
  368. view_dispatcher_enable_queue(app->view_dispatcher);
  369. view_dispatcher_attach_to_gui(app->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
  370. view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
  371. app->submenu = submenu_alloc();
  372. submenu_add_item(
  373. app->submenu, "Test WS2812B", LedTesterSubmenuIndexLeds, led_tester_submenu_callback, app);
  374. submenu_add_item(
  375. app->submenu, "About", LedTesterSubmenuIndexAbout, led_tester_submenu_callback, app);
  376. view_set_previous_callback(
  377. submenu_get_view(app->submenu), led_tester_navigation_exit_callback);
  378. view_dispatcher_add_view(
  379. app->view_dispatcher, LedTesterViewSubmenu, submenu_get_view(app->submenu));
  380. view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
  381. view_dispatcher_set_custom_event_callback(
  382. app->view_dispatcher, led_tester_custom_event_callback);
  383. view_dispatcher_switch_to_view(app->view_dispatcher, LedTesterViewSubmenu);
  384. app->variable_item_list = variable_item_list_alloc();
  385. variable_item_list_set_enter_callback(app->variable_item_list, led_tester_item_clicked, app);
  386. view_set_enter_callback(
  387. variable_item_list_get_view(app->variable_item_list), led_tester_enter_leds_callback);
  388. view_set_exit_callback(
  389. variable_item_list_get_view(app->variable_item_list), led_tester_exit_leds_callback);
  390. variable_item_list_reset(app->variable_item_list);
  391. // Pin
  392. VariableItem* item = variable_item_list_add(
  393. app->variable_item_list,
  394. setting_led_pin_config_label,
  395. COUNT_OF(setting_led_pin_names),
  396. led_tester_setting_led_pin_change,
  397. app);
  398. uint8_t setting_led_pin_index = 0;
  399. variable_item_set_current_value_index(item, setting_led_pin_index);
  400. variable_item_set_current_value_text(item, setting_led_pin_names[setting_led_pin_index]);
  401. app->model->led_pin_index = setting_led_pin_index;
  402. // Count
  403. item = variable_item_list_add(
  404. app->variable_item_list,
  405. setting_led_count_config_label,
  406. COUNT_OF(setting_led_count_names),
  407. led_tester_setting_led_count_change,
  408. app);
  409. uint8_t setting_led_count_index = setting_led_count_default_index;
  410. variable_item_set_current_value_index(item, setting_led_count_index);
  411. variable_item_set_current_value_text(item, setting_led_count_names[setting_led_count_index]);
  412. app->model->led_count = setting_led_count_values[setting_led_count_index];
  413. // Pattern
  414. item = variable_item_list_add(
  415. app->variable_item_list,
  416. setting_led_pattern_config_label,
  417. COUNT_OF(setting_led_pattern_names),
  418. led_tester_setting_led_pattern_change,
  419. app);
  420. uint8_t setting_led_pattern_index = 0;
  421. variable_item_set_current_value_index(item, setting_led_pattern_index);
  422. variable_item_set_current_value_text(
  423. item, setting_led_pattern_names[setting_led_pattern_index]);
  424. app->model->led_pattern_index = setting_led_pattern_index;
  425. // Speed
  426. item = variable_item_list_add(
  427. app->variable_item_list,
  428. setting_led_speed_config_label,
  429. COUNT_OF(setting_led_speed_names),
  430. led_tester_setting_led_speed_change,
  431. app);
  432. uint8_t setting_led_speed_index = 0;
  433. variable_item_set_current_value_index(item, setting_led_speed_index);
  434. variable_item_set_current_value_text(item, setting_led_speed_names[setting_led_speed_index]);
  435. app->model->led_divisor = setting_led_speed_values[setting_led_speed_index];
  436. // Brightness
  437. item = variable_item_list_add(
  438. app->variable_item_list,
  439. setting_led_brightness_config_label,
  440. COUNT_OF(setting_led_brightness_names),
  441. led_tester_setting_led_brightness_change,
  442. app);
  443. uint8_t setting_led_brightness_index = 3;
  444. variable_item_set_current_value_index(item, setting_led_brightness_index);
  445. variable_item_set_current_value_text(
  446. item, setting_led_brightness_names[setting_led_brightness_index]);
  447. app->model->led_max_brightness = setting_led_brightness_values[setting_led_brightness_index];
  448. // 5-volt pin
  449. item = variable_item_list_add(
  450. app->variable_item_list,
  451. setting_5v_config_label,
  452. COUNT_OF(setting_5v_names),
  453. led_tester_setting_5v_change,
  454. app);
  455. uint8_t setting_5v_index = 0;
  456. variable_item_set_current_value_index(item, setting_5v_index);
  457. variable_item_set_current_value_text(item, setting_5v_names[setting_5v_index]);
  458. app->model->enable_5v = setting_5v_values[setting_5v_index];
  459. if(app->model->enable_5v) {
  460. led_tester_5v_power_on();
  461. }
  462. view_set_previous_callback(
  463. variable_item_list_get_view(app->variable_item_list),
  464. led_tester_navigation_submenu_callback);
  465. view_dispatcher_add_view(
  466. app->view_dispatcher,
  467. LedTesterViewLeds,
  468. variable_item_list_get_view(app->variable_item_list));
  469. app->widget_about = widget_alloc();
  470. widget_add_text_scroll_element(
  471. app->widget_about,
  472. 0,
  473. 0,
  474. 128,
  475. 64,
  476. "This is a WS2812B LED tester\nVersion 1.7\nConnect WS2812B LED data\nwire to GPIO pin on Flipper.\n\nThe 3V3 pin has a 1200mA\nmax current (~4 watts). The\n5V pin has a 1000mA max\ncurrent (5 watts).\n\nauthors: @codeallnight and\nZ3BRO!\n\nhttps://discord.com/invite/NsjCvqwPAd\nhttps://youtube.com/@MrDerekJamison\n\n");
  477. view_set_previous_callback(
  478. widget_get_view(app->widget_about), led_tester_navigation_submenu_callback);
  479. view_dispatcher_add_view(
  480. app->view_dispatcher, LedTesterViewAbout, widget_get_view(app->widget_about));
  481. return app;
  482. }
  483. /**
  484. * @brief Free the led tester application.
  485. * @details This function frees the led tester application resources.
  486. * @param app The led tester application object.
  487. */
  488. static void led_tester_app_free(LedTesterApp* app) {
  489. view_dispatcher_remove_view(app->view_dispatcher, LedTesterViewAbout);
  490. widget_free(app->widget_about);
  491. view_dispatcher_remove_view(app->view_dispatcher, LedTesterViewLeds);
  492. variable_item_list_free(app->variable_item_list);
  493. view_dispatcher_remove_view(app->view_dispatcher, LedTesterViewSubmenu);
  494. submenu_free(app->submenu);
  495. view_dispatcher_free(app->view_dispatcher);
  496. furi_record_close(RECORD_GUI);
  497. free(app);
  498. }
  499. /**
  500. * @brief Main function for led tester application.
  501. * @details This function is the entry point for the led tester application. It should be defined in
  502. * application.fam as the entry_point setting.
  503. * @param _p Input parameter - unused
  504. * @return 0 - Success
  505. */
  506. int32_t ws2812b_led_tester_app(void* _p) {
  507. UNUSED(_p);
  508. LedTesterApp* app = led_tester_app_alloc();
  509. view_dispatcher_run(app->view_dispatcher);
  510. led_tester_app_free(app);
  511. return 0;
  512. }