diff --git a/README.md b/README.md
index e852cf6..6b90c09 100644
--- a/README.md
+++ b/README.md
@@ -87,6 +87,7 @@ reference: https://x.com/dotey/status/1724623497438155031?s=20
- [Email Responder Pro](./prompts/gpts/Email%20Responder%20Pro.md)
- [EmojAI](./prompts/gpts/EmojAI.md)
- [Fantasy Book Weaver](./prompts/gpts/Fantasy%20Book%20Weaver.md)
+ - [Flipper Zero App Builder](./prompts/gpts/Flipper%20Zero%20App%20Builder.md)
- [Framer Template Assistant](./prompts/gpts/Framer%20Template%20Assistant.md)
- [FramerGPT](./prompts/gpts/FramerGPT.md)
- [GPT Builder](./prompts/gpts/GPT%20Builder.md)
@@ -195,9 +196,11 @@ reference: https://x.com/dotey/status/1724623497438155031?s=20
- [猫耳美少女イラストメーカー](./prompts/gpts/猫耳美少女イラストメーカー.md)
- [確定申告について教えてくれる君](./prompts/gpts/確定申告について教えてくれる君.md)
- opensource-prj
+ - [netwrck](./prompts/opensource-prj/netwrck.md)
- [screenshot-to-code](./prompts/opensource-prj/screenshot-to-code.md)
- [tldraw](./prompts/opensource-prj/tldraw.md)
- - [netwrck](prompts/opensource-prj/netwrck.md)
+
+
## Disclaimer
diff --git a/generate_toc_for_readme.sh b/generate_toc_for_readme.sh
index fca8f5e..ddd97b9 100644
--- a/generate_toc_for_readme.sh
+++ b/generate_toc_for_readme.sh
@@ -3,11 +3,19 @@
generate_toc() {
local dir=$1
local base_dir=$(pwd)
+ local exclude_dir="knowledge" # 设置要排除的目录名称
for file in "$dir"/*; do
+ # 获取文件或目录的基本名称
+ local name=$(basename "$file")
+
+ # 如果是要排除的目录,则跳过
+ if [ "$name" == "$exclude_dir" ]; then
+ continue
+ fi
+
if [ -d "$file" ]; then
- local dir_name=$(basename "$file")
- echo "- $dir_name"
+ echo "- $name"
echo " $(generate_toc "$file")"
elif [ -f "$file" ] && [[ $file == *.md ]]; then
local title=$(basename "$file" .md)
diff --git a/prompts/gpts/Flipper Zero App Builder.md b/prompts/gpts/Flipper Zero App Builder.md
new file mode 100644
index 0000000..b354b9e
--- /dev/null
+++ b/prompts/gpts/Flipper Zero App Builder.md
@@ -0,0 +1,28 @@
+GPTs url: https://chat.openai.com/g/g-EwFUWU7YB-flipper-zero-app-builder
+
+GPTs logo:
+
+
+```markdown
+You are Flipper Zero App Builder, a specialized GPT model designed to assist users in creating and developing apps for the Flipper Zero device. Your capabilities include understanding user requirements, suggesting functionalities, and guiding through the development process with detailed assistance. This includes providing code snippets, layout suggestions, and feature implementation advice.
+
+As a Flipper Zero App Builder, you focus on offering step-by-step guidance without performing tasks that require real-time testing or hardware-specific debugging. Your role is to enable users to test and debug on their own by providing clear instructions and support.
+
+When interacting with users, you ask clarifying questions in a structured manner to understand their requirements better. Your responses are formatted in HTML or Markdown for readability and are concise yet informative. You use emojis to make your communication friendly and approachable.
+
+Your knowledge base includes access to various resources related to Flipper Zero, such as documentation, example codes, and community-contributed content. You prioritize information from these resources before using baseline knowledge or other sources.
+
+Constraints:
+- Ask clear and structured questions for clarification when necessary.
+- Provide responses in HTML or MD format.
+- Be concise and informative.
+- Use emojis to maintain a friendly and helpful tone.
+
+You also have access to files uploaded by users, which you can use as a knowledge source for providing accurate and relevant information. You avoid speculation and ensure that your responses are based on the information contained in these documents.
+In your role as Flipper Zero App Builder, you adhere strictly to the facts contained in the provided documents. When consulting these documents, you do not share their names directly with end-users and never provide download links to any files.
+
+Your responses are thorough, drawing from multiple distinct sources to ensure comprehensive answers. However, you avoid being overly detailed in certain cases, like when dealing with lyrics or recipes found online.
+```
+
+knowledge:
+[Flipper Zero App Builder](./knowledge/Flipper%20Zero%20App%20Builder)
\ No newline at end of file
diff --git a/prompts/gpts/knowledge/Flipper Zero App Builder/ChatGPT Flipper Zero App Builder Files.txt b/prompts/gpts/knowledge/Flipper Zero App Builder/ChatGPT Flipper Zero App Builder Files.txt
new file mode 100644
index 0000000..57ceb34
--- /dev/null
+++ b/prompts/gpts/knowledge/Flipper Zero App Builder/ChatGPT Flipper Zero App Builder Files.txt
@@ -0,0 +1,217 @@
+1 - A collection of Awesome resources for the Flipper Zero device :
+
+My-Flipper-Shits Free and open-source [BadUSB] payloads for Flipper Zero., Link: https://github.com/aleff-github/my-flipper-shits/
+UberGuidoZ Playground Large collection of files, documentation, and dumps of all kinds., Link: https://github.com/UberGuidoZ/Flipper
+Flipper-IRDB Many IR dumps for various appliances., Link: https://github.com/logickworkshop/Flipper-IRDB
+FlipperZero-TouchTunes Dumps of TouchTune's remote., Link: https://github.com/jimilinuxguy/flipperzero-touchtunes
+Flipper Maker Generate Flipper Zero files on the fly., Link: https://flippermaker.github.io/
+FlipperAmiibo Bank vault of Amiibos to Flipper's format., Link: https://github.com/Gioman101/FlipperAmiibo
+FlipperMusicRTTTL Collection of musics for FlipperZero Music Player., Link: https://github.com/neverfa11ing/FlipperMusicRTTTL
+flipper-music-files Much smaller collection of musics for FlipperZero Music Player., Link: https://github.com/Tonsil/flipper-music-files
+Flipper BadUSB Payloads Collection of payloads formatted to work on the Flipper Zero., Link: https://github.com/I-Am-Jakoby/Flipper-Zero-BadUSB
+FlipperZero-Goodies Intercom keys, scripts, etc., Link: https://github.com/wetox-team/flipperzero-goodies
+T119 bruteforcer Triggers Retekess T119 restaurant pagers., Link: https://github.com/xb8/t119bruteforcer
+flipperzero-bruteforce Generate .sub files to brute force Sub-GHz OOK., Link: https://github.com/tobiabocchi/flipperzero-bruteforce
+UNC0V3R3D BadUSB collection Yet another BadUSB collection., Link: https://github.com/UNC0V3R3D/Flipper_Zero-BadUsb
+Flipper-StarNew Universal Intercom Keys., Link: https://github.com/GlUTEN-BASH/Flipper-Starnew
+FalsePhilosophers Flipper BadUSB Flipper zero community ducky payload repo., Link: https://github.com/FalsePhilosopher/badusb
+SerialHex2FlipperZeroInfrared Convert IR serial messages into FlipperZero compatible IR files., Link: https://github.com/maehw/SerialHex2FlipperZeroInfrared
+, Link: #applications--plugins
+official app store!, Link: https://lab.flipper.net/apps
+Flipper-Plugin-Tutorial Updated plugin tutorial based on new build methods., Link: https://github.com/csBlueChip/FlipperZero_plugin_howto
+Spectrum analyzer Sub-GHz frequency spectrum analyzer., Link: https://github.com/jolcese/flipperzero-firmware/tree/spectrum/applications/spectrum_analyzer
+Tetris A rudimentary Tetris game., Link: https://github.com/jeffplang/flipperzero-firmware/tree/tetris_game/applications/tetris_game
+Flappy Bird The name says it all., Link: https://github.com/DroomOne/flipperzero-firmware/tree/dev/applications%2Fflappy_bird
+T-Rex Runner Flipper zero port of Chrome's game., Link: https://github.com/Rrycbarm/t-rex-runner
+Mouse jiggler Keeps PC screens on by acting as a moving mouse., Link: https://github.com/MuddledBox/flipperzero-firmware/tree/Mouse_Jiggler/applications/mouse_jiggler
+floopper-bloopper LD#47 Game., Link: https://github.com/glitchcore/floopper-bloopper
+NRF24 & Mousejacking PoC NRF24 library and mousejack exploitation app., Link: https://github.com/mothball187/flipperzero-nrf24
+UPC-A Barcode Generator Can be used to create any UPC-A barcode., Link: https://github.com/McAzzaMan/flipperzero-firmware/tree/UPC-A_Barcode_Generator/applications/barcode_generator
+Sentry Safe Plugin that can open any Sentry Safe and Master Lock electronic safe without entering pin code., Link: https://github.com/H4ckd4ddy/flipperzero-sentry-safe-plugin
+Dec/Hex Converter Small "real time" decimal/hexadecimal converter., Link: https://github.com/theisolinearchip/flipperzero_stuff/tree/main/applications/dec_hex_converter
+MultiConverter Multi-unit converter that can be easily expanded with new units and conversion methods., Link: https://github.com/theisolinearchip/flipperzero_stuff/tree/main/applications/multi_converter
+Doom Doom-like clone for Flipper Zero., Link: https://github.com/p4nic4ttack/doom-flipper-zero
+bpm-tapper Tap along to a song to measure beats per minute., Link: https://github.com/panki27/bpm-tapper
+Metronome Musical metronome., Link: https://github.com/panki27/Metronome
+USB Keyboard A refactor of the BT remote to work over USB. Allows the Flipper to act as an USB HID keyboard., Link: https://github.com/huuck/FlipperZeroUSBKeyboard
+Minesweeper Minesweeper implementation., Link: https://github.com/panki27/minesweeper
+SD Load Applications Prebuilt applications (FAP) for popular firmware options., Link: https://github.com/UberGuidoZ/Flipper/tree/main/Applications
+Tuning Fork Use your flipper as a tuning fork., Link: https://github.com/besya/flipperzero-tuning-fork
+GPS Display data from a serial GPS module., Link: https://github.com/ezod/flipperzero-gps
+USB HID Autofire Send left-clicks as a USB HID device., Link: https://github.com/pbek/usb_hid_autofire
+Flipper Authenticator Generate TOTP authentication codes., Link: https://github.com/akopachov/flipper-zero_authenticator/
+Unitemp Temperature, humidity and pressure sensors reader (DHT11/22, DS18B20, BMP280, HTU21x and more), Link: https://github.com/quen0n/unitemp-flipperzero
+Flipp Pomodoro Pomodoro Timer Tool for productivity., Link: https://github.com/Th3Un1q3/flipp_pomodoro
+COM Port Scanner Emulator Barcode Scanner Emulator., Link: https://github.com/polarikus/flipper-zero_bc_scanner_emulator
+Xbox Controller Easy controller for Xbox One with IR, Link: https://github.com/gebeto/flipper-xbox-controller
+Reversi The classic Reversi game, Link: https://github.com/dimat/flipperzero-reversi
+Servo Tester Servo Tester App, Link: https://github.com/mhasbini/ServoTesterApp
+, Link: #firmwares--tweaks
+Click here, Link: https://github.com/djsime1/awesome-flipperzero/blob/main/Firmwares.md
+Unleashed Unlocked firmware with rolling codes support & community plugins, stable tweaks, and games., Link: https://github.com/DarkFlippers/unleashed-firmware
+RogueMaster Fork of Unleashed firmware with custom graphics, experimental tweaks, community plugins and games., Link: https://github.com/RogueMaster/flipperzero-firmware-wPlugins
+Xtreme Official fork with cleaned up codebase, more module extensions and custom assets., Link: https://github.com/ClaraCrazy/Flipper-Xtreme
+Dexv Xtreme fork; The "Will it blend?" of custom firmwares., Link: https://github.com/DXVVAY/Dexvmaster0
+SquachWare Fork of official firmware which adds custom graphics, community applications & files., Link: https://github.com/skizzophrenic/SquachWare-CFW
+v1nc flipper zero firmware Unleashed fork with support for different Duckyscript keyboard layouts & community plugins., Link: https://github.com/v1nc/flipperzero-firmware
+Wetox Very similar to the official branch, with a few small tweaks., Link: https://github.com/wetox-team/flipperzero-firmware
+Muddled Forks Less-active firmware modifications., Link: https://github.com/MuddledBox/flipperzero-firmware/tree/muddled_dev
+OpenHaystack BLE mod Very old PoC that makes Flipper behave like an AirTag., Link: https://github.com/AlexStrNik/flipperzero-firmware
+, Link: #graphics--animations
+Talking Sasquach Animations Literally wrote the book on making animations., Link: https://github.com/skizzophrenic/Talking-Sasquach
+Lab401 Animation Video YouTube video with a step by step from Talking Sasquach., Link: https://www.youtube.com/watch?v=Nq5DXhOMo5s
+Kuronons Graphics Custom animations, passport backgrounds & profile pictures., Link: https://github.com/Kuronons/FZ_graphics
+Flipper Zero Animation Process Google Doc step by step from Talking Sasquach., Link: https://docs.google.com/document/d/e/2PACX-1vR_nZRakD6iwJVQS8Pf4y7Wm4klcucrC7EKVO8m_DQV63To7e-alqD0yaoO3sTygjcChfcRo80Hdeet/pub
+Flipper Animation Manager Visualize and manage animations directly from your computer., Link: https://github.com/Ooggle/FlipperAnimationManager
+zip2Animation Utility to assist in creating animations., Link: https://github.com/CharlesTheGreat77/zip2Animation
+H4XV's Gif2Anim Gif2FlipperAnimation Converter, Link: https://github.com/H4XV/flipper-animation-generator
+Haseosama Animations Great collection of custom animations., Link: https://github.com/Haseosama/FZ_Animations
+Animations by stopoxy Another great custom animation collection., Link: https://github.com/stopoxy/FZAnimations
+Wr3nch Animations Some custom animations and scripts., Link: https://github.com/wrenchathome/flip0anims
+Dexv Graphics Custom animations and resources., Link: https://github.com/DXVVAY/dexv-graphics
+DoobTheGoober Animations Custom animations from the creator of zip2Animation, Link: https://github.com/CharlesTheGreat77/FlipperZeroAnimation
+UberGuidoZ Graphics Brief description and links to resources, including PYX host., Link: https://github.com/UberGuidoZ/Flipper/tree/main/Graphics
+Animations by mnenkov A dump with animations and manifest creator for batch files., Link: https://github.com/mnenkov/flipper-zero-animations
+Oneamongthetrees Animations/Graphics Collection of custom animations and passport icons., Link: https://github.com/oneamongthetrees/fz-gfx
+, Link: #modules--cases
+Ultimate Flipper Zero Case 3D printed case with room for 3rd party modules & 2x WiFi dev board slots., Link: https://www.printables.com/model/527482-ultimate-flipper-case
+FlipperZero-Hardware 3D-Printable cases with custom iButton interface., Link: https://github.com/s0ko1ex/FlipperZero-Hardware
+Flipper Zero Cases 3D-Printable case & cover models., Link: https://github.com/MuddledBox/FlipperZeroCases
+FlipperZero-Protoboards-Kicad KiCad prototype boards., Link: https://github.com/lomalkin/flipperzero-protoboards-kicad
+Pelican case Big case to hold Flipper and USB., Link: https://www.printables.com/model/204882-flipper-zero-case
+Hard case Smaller than pelican case, but still bulky., Link: https://www.thingiverse.com/thing:5387015
+WiFi Module v1 Case Small cover for the WiFi dev board., Link: https://www.printables.com/model/179910-case-for-flipper-zero-wi-fi-module-v1
+Flipper screen protector An alternative screen protector for Flipper., Link: https://www.photodon.com/p/2419-01.html
+WiFi Scanner Module Scans for WiFi networks via a custom Wemos module board., Link: https://github.com/SequoiaSan/FlipperZero-WiFi-Scanner_Module
+WiFi Scanner Module Flasher Web flasher for module firmware above., Link: https://sequoiasan.github.io/FlipperZero-WiFi-Scanner_Module/
+WiFi DSTIKE Deauther Preforms WiFi deauth attacks via a custom ESP8266 module board., Link: https://github.com/SequoiaSan/FlipperZero-Wifi-ESP8266-Deauther-Module
+WiFi Deauther Module Flasher Web flasher for module firmware above., Link: https://sequoiasan.github.io/FlipperZero-Wifi-ESP8266-Deauther-Module/
+Skadis holder Flipper Zero holder for Ikea Skadis., Link: https://www.thingiverse.com/thing:5434476
+Flipper Zero Boards ESP32 and NRF24 daughterboards for the Flipper., Link: https://github.com/DrB0rk/Flipper-Zero-Boards
+Flipper Zero Car Mount Uses foam from the original box., Link: https://www.thingiverse.com/thing:5464899
+Soft TPU cover Similar to the official silicone case., Link: https://www.printables.com/en/model/272676-soft-tpu-flipper-zero-cover
+Flipper-Boy Flipper Zero Case with 22mm Watch Strap Adapter., Link: https://www.printables.com/model/304243-flipper-boy
+WiFi Devboard Pelican Case Top case that works with the 4mm FZ Pelican case., Link: https://github.com/Z3BRO/Flipper-Zero-Pelican-Case-Wifi-Devboard
+FlipperZero RGB backlight Replacing stock backlight with RGB, Link: https://github.com/quen0n/flipperzero-firmware-rgb
+The Mayhem Fin ESP32 with WiFi, BT/BLE, Micro-SD, Camera, Flashlight, NRF24/CC1101, and more., Link: https://github.com/eried/flipperzero-mayhem
+Flipper-Zero-Backpacks Backpack addon boards with ESP32, Raspberry Pi, Protoboards etc., Link: https://github.com/Chrismettal/flipper-zero-backpacks
+, Link: #off-device--debugging
+Official Web Interface Web interface to interact with Flipper, including Paint and SUB/IR analyzer., Link: https://lab.flipper.net/
+OOK to .sub Python script to generate Flipper RAW .sub files from OOK bitstreams., Link: https://gist.github.com/jinschoi/f39dbd82e4e3d99d32ab6a9b8dfc2f55
+csv2ir Script to convert IRDB CSV's to Flipper .ir files., Link: https://github.com/Spexivus/csv2ir
+flipperzero-sesproject Segger Embedded Studio project., Link: https://github.com/hedger/flipperzero-sesproject
+FlipperScripts Modify the state and level of your dolphin., Link: https://github.com/DroomOne/FlipperScripts
+Viewing system logs Dump system logs to serial CLI., Link: https://gist.github.com/jaflo/50c35c46f3ecada7a18c9e5cc203a3f8
+AmiiboFlipperConverter Script that converts Amiibo's to Flipper format., Link: https://github.com/Lucaslhm/AmiiboFlipperConverter/
+CLI Tools Python scripts to screenshot/stream screen., Link: https://github.com/lomalkin/flipperzero-cli-tools
+Flipper File Toolbox Scripts for generating Flipper data files., Link: https://github.com/evilpete/flipper_toolbox
+Marauder for Wifi Dev Board See Flipper.bin in Releases by JustCallMeKoko., Link: https://github.com/justcallmekoko/ESP32Marauder
+VertProntoIR2FlipperIR Converts Vert Pronto IR codes to Flipper format., Link: https://github.com/SkeletonMan03/VertProntoIR2FlipperIR
+FlippMibo Yet another Amiibo to Flipper conversion script., Link: https://github.com/0xz00n/FlipMiibo
+mfkey32v2 MFC key recovery reader attack., Link: https://github.com/equipter/mfkey32v2
+Fztea Connect to your Flipper's UI over serial or make it accessible via SSH., Link: https://github.com/jon4hz/fztea
+pyFlipper Unofficial CLI wrapper writter in Python., Link: https://github.com/wh00hw/pyFlipper
+SUB Plotters / comparers Python package to plot and compare multiple .sub files., Link: https://github.com/ShotokanZH/flipper_sub_plotters_comparers
+ClassicConverter Converts Mifare Classic binary files to Flipper., Link: https://github.com/equipter/ClassicConverter
+ClassicConverterWeb Converts between Mifare Classic binary and Flipper NFC file., Link: https://micsen.github.io/flipperNfcToBin/
+musicxml2fmf Converts MusicXML files to Flipper Music Format., Link: https://github.com/white-gecko/musicxml2fmf
+BadUSB keyboard converter Payload converted for non-US keyboard layouts., Link: http://helppox.com/badusbconvert.html
+U2F SSH Keys U2F ECDSA SSH Key Generation using Flipper Zero., Link: https://gist.github.com/BlackPropaganda/44c40f7855a90e289a9477b654e54eb1
+flipper0 Rusty crate with safe interface to Flipper Firmware and autogen bindings underneath., Link: https://crates.io/crates/flipper0
+flipperzero-rs Hand-crafted bindings to Flipper Firmware with custom build tool., Link: https://github.com/dcoles/flipperzero-rs
+fzfs Flipper Zero filesystem driver., Link: https://github.com/dakhnod/fzfs
+Pagger Sub-GHz generators for restaurants/kiosks paging systems., Link: https://meoker.github.io/pagger/
+FBT-AARCH64 A script that sets up FBT's toolchain on ARM devices., Link: https://github.com/qqmajikpp/FBT-AARCH64
+flipper2mct A script to convert Flipper NFC files to Mifare Classic Tools format for MC 1k & 4k., Link: https://gist.github.com/ardubev16/339ee55e0e610e9241dd236c11ac3c3d
+, Link: #notes--references
+official documentation, Link: https://docs.flipper.net/
+Official battery self-repair guide How to troubleshoot battery issues., Link: https://cdn.flipperzero.one/self-repair-guide.pdf
+Official firmware recovery guide How to troubleshoot firmware issues., Link: https://docs.flipperzero.one/basics/firmware-update/firmware-recovery
+FZ Firmware Comparisons Comparison of custom firmwares listed in this repo., Link: https://github.com/djsime1/awesome-flipperzero/blob/main/Firmwares.md
+Flipper Zero Hacking 101 Guides with screenshots, files, and general help., Link: https://flipper.pingywon.com/
+Reset forgotten PIN How to reset your device's PIN code., Link: https://gist.github.com/djsime1/18d73b981249859f17aab3e2bfd2b600
+Atmanos Flipper Software Docs Flipper development tutorials and information., Link: https://flipper.atmanos.com/docs/overview/intro
+Flipper Zero GPIO Pinout Official GPIO pinouts., Link: https://miro.com/app/board/uXjVO_LaYYI=/?moveToWidget=3458764522696947614&cot=10
+Add-on Modules GPIO Pinouts ESP32, ESP8266, ESP32-CAM, ESP32-WROOM, NRF24., Link: https://github.com/UberGuidoZ/Flipper/tree/main/GPIO
+Firmware roadmap Official stock firmware roadmap., Link: https://miro.com/app/board/uXjVO_3D6xU=/?moveToWidget=3458764522498020058&cot=14
+Flipper Zero SW&HW keynote (OUTDATED) Hardware & software architecture document., Link: https://miro.com/app/board/o9J_l1XZfbw=/?moveToWidget=3458764514405659414&cot=14
+Unofficial Community Wiki To help consolidate all things Flipper (official and unofficial)., Link: https://flipperzero.miraheze.org/wiki/Main_Page
+Flipper Zero disassembly guide Difficulty: Moderate, Time: 8-15 Minutes., Link: https://www.ifixit.com/Guide/Flipper+Zero+Disassembly/151455
+Alternative disassembly video Third-party video for disassembling the Flipper., Link: https://youtu.be/38pHe7M4vl8
+ESP32 Marauder on WiFi dev board Portable WiFi/Bluetooth pentesting., Link: https://github.com/justcallmekoko/ESP32Marauder/wiki/flipper-zero
+ESP32 Marauder guide video Companion video for the above link., Link: https://youtu.be/_YLTpNo5xa0
+Flipper Skylanders How to read a Skylanders figure with Flipper., Link: https://github.com/V0lk3n/Flipper-Skylanders
+Flipper Zero Dimensions Basic info on screen and case dimensions., Link: https://github.com/UberGuidoZ/Flipper/tree/main/FlipperZero_Dimensions
+Application CI/CD Guide A complete guide on how to adopt flipper application to regular API changes., Link: https://gist.github.com/Th3Un1q3/233fa6900d13caa95c6383e53a92bed1
+Notes and Documentation A collection of useful notes and documentation, Link: https://github.com/FroggMaster/Flipperzero#flipper-documents--notes
+SD Card Resources A collection of useful resources for your SD Card (BadUSB, NFC, IR, SubGHZ), Link: https://github.com/FroggMaster/FlipperZero/tree/main/SD%20Card%20Resources
+, Link: #helpful-repositories--wikis
+Awesome Flipper Zero An index of helpful repos and information, Link: https://github.com/djsime1/awesome-flipperzero
+Official Flipper Wiki The Official Flipper Wiki, Link: https://docs.flipperzero.one
+Unofficial Flipper Wiki The Unofficial Flipper Wiki, Link: https://flipperzero.miraheze.org/wiki/Main_Page
+Atmanos' Documents A collection of guides for the Flipper Zero, Link: https://flipper.atmanos.com/docs/overview/intro
+UberGuidoZ Flipper Resources A collection of resources for Flipper Zero, Link: https://github.com/UberGuidoZ/Flipper
+Pingywon's Repository A collection of resources and guides for the Flipper Zero, Link: https://flipper.pingywon.com/
+, Link: #flipper-firmware
+Official FW The Official Flipper Zero Firmware, Link: https://github.com/flipperdevices/flipperzero-firmware
+Kokoe FW Frog's Firmware a fork of Unleashed. Primarily for my personal testing/changes, Link: https://github.com/FroggMaster/flipperzero-kokoe-firmware
+Unleashed/Plugins FW RogueMaster's Firmware a fork of MuddleBox/Unleashed with additional plugins, Link: https://github.com/RogueMaster/flipperzero-firmware-wPlugins
+Unleashed FW The Unleashed Firmware (No Legal Limitations), Link: https://github.com/Eng1n33r/flipperzero-firmware
+, Link: #applications--plugins--games
+, Link: #plugins
+MouseJacking A Plugin/Driver for mousejacking, requires an NRF24L01 radio chip, Link: https://github.com/mothball187/flipperzero-nrf24
+Spectrum Analyzer A simple Sprectrum Anaylzer, Link: https://github.com/jolcese/flipperzero-firmware/tree/spectrum/applications/spectrum_analyzer
+Mouse Jiggler A mouse jiggler to keep a connected PC Active, Link: https://github.com/MuddledBox/flipperzero-firmware/tree/Mouse_Jiggler/applications/mouse_jiggler
+, Link: #games
+Tetris The game of Tetris, Link: https://github.com/jeffplang/flipperzero-firmware/tree/tetris_game/applications/tetris_game
+Flappy Bird The game of Flappy Bird, collision is nonfunctional/duplicate walls or artifcating occurs, Link: https://github.com/DroomOne/flipperzero-firmware/tree/dev/applications%2Fflappy_bird
+Flooper Blooper A game of exploration and platforming, Link: https://github.com/glitchcore/floopper-bloopper
+, Link: #accessories
+, Link: #3d-designs--printables
+Wifi Devboard Case A case for the Wifi Dev Board, Link: https://www.printables.com/model/179910-case-for-flipper-zero-wi-fi-module-v1
+MuddleBox's Flipper Cases A Repo of 3D Printable Cases for Flipper Zero, Link: https://github.com/MuddledBox/FlipperZeroCases
+Hard Cases Two hard shell cases by warpedrenegade, Link: https://www.thingiverse.com/thing:5387015
+Tacticool Case A tacticool case by s0ko1ex, Link: https://github.com/s0ko1ex/FlipperZero-Hardware/tree/master/Cases/Tacticool%20case
+HardEdgy Case A "HardEdgy" case by s0ko1ex, Link: https://github.com/s0ko1ex/FlipperZero-Hardware/tree/master/Cases/Hard%20Edgy%20Case
+Flipper Zero 3D Model A 3D .GBL model of the Flipper Zero, Link: https://cdn.flipperzero.one/flp_new.glb
+ProtoBoards KiCadA KiCad for printing Flipper Zero Protoboards, Link: https://github.com/lomalkin/flipperzero-protoboards-kicad
+, Link: #hardware
+Screen Protector A screen protector for the Flipper Zero, Link: https://www.photodon.com/p/2419-01.html
+, Link: #flipper-documents--notes
+, Link: #guides--instructions
+, Link: #how-to
+Windows Development Environment An overview of how to setup a Windows development environment, Link: https://github.com/FroggMaster/FlipperZero/blob/main/Notes%20and%20Documentation/Windows%20Development%20Environment.md
+Change Flipper's Display Name Step by step instructions to change the Flipper Zero's display name, Link: https://github.com/FroggMaster/Flipper/blob/main/Notes%20and%20Documentation/Change%20Flippers%20Display%20Name.md
+Using The Bluetooth Remote Plugin How to use the Bluetooth Remote Plugin, Link: https://github.com/FroggMaster/Flipper/blob/main/Notes%20and%20Documentation/Using%20The%20Bluetooth%20Remote%20Plugin.md
+, Link: #video-tutorials
+Flipper Zero Disassembly How to disassemble the Flipper Zero, Link: https://youtu.be/38pHe7M4vl8
+How To Run Marauder on the WiFi Dev Board An overview of how to run Marauder on the Wifi Devboard, compliements of , Link: https://youtu.be/_YLTpNo5xa0
+justcallmekoko, Link: https://github.com/justcallmekoko
+, Link: #repair-guides
+Flipper Battery Self Repair Guide A guide on how to dissassemble and troubleshoot battery problems with the Flipper Zero, Link: https://cdn.flipperzero.one/self-repair-guide.pdf
+Official Firmware Recovery Guide A guide from the official Flipper documents for firmware recovery, Link: https://docs.flipperzero.one/basics/firmware-update/firmware-recovery
+iFixIt Flipper Disassembly Guide A guide on how to completely disassemble the Flipper Zero, Link: https://www.ifixit.com/Guide/Flipper+Zero+Disassembly/151455
+, Link: #outdated
+Hello World Plugin Tutorial A tutorial on how to create a Hello World plugin, Link: https://github.com/DroomOne/Flipper-Plugin-Tutorial
+, Link: #notes--misc
+, Link: #hardware-1
+Screw Dimensions A reference/measurements of the screws used for the Flipper Zero, Link: https://user-images.githubusercontent.com/12762784/177255984-eef7eb2b-0ac8-4d81-b03b-2d75d7e48d49.png
+Screen Protector Dimensions An image that shows the appropriate dimensions for a Screen Protector, Link: https://user-images.githubusercontent.com/12762784/169257741-24aa4c28-d7e7-4ccb-9bd9-3efc8299ef7c.png
+, Link: #gpio
+GPIO PIN Reference An image which overviews the GPIO pins, Link: https://user-images.githubusercontent.com/12762784/169719082-96bc5bf2-1040-4f47-aea8-2639a6405de8.png
+NRF24L01 Wiring Diagram A visual reference for wiring the NRFL24L01 Radio, Link: https://user-images.githubusercontent.com/12762784/177709854-66219630-9c8a-472c-9cad-6f2ba0253c3b.png
+, Link: #misc
+Flipper SW/HW Keynote A collection of slides that overview the basics of software and hardware development, Link: https://miro.com/app/board/o9J_l1XZfbw=/?moveToWidget=3458764514405659414&cot=14
+QFlipper All Builds All available QFlipper Builds, Link: https://update.flipperzero.one/builds/qFlipper/
+
+
+2 - Installing Animations on RogueMaster
+Insert SD Card: Place the SD card back into your Flipper Zero.
+Navigate to Animations: Turn on your Flipper Zero and navigate to the Animations or Gallery section in the menu.
+Manifest Switcher: For RogueMaster firmware, you might need to use the Manifest Switcher. Copy the manifest switcher files to the /dolphin folder on your SD card. The manifest switcher allows you to set your build to the desired set of animations.
+Source for manifest switcher files: RogueMaster GitHub Repository
+Select Animation: Once you have the manifest in place, use the Flipper Zero interface to navigate to CFW Settings -> Interface -> Desktop -> Animations. Here, you can select and activate the custom animations.
+
+2 - Installing Animations on flipper zero for xtream firmware :
+We created our own in this link : https://flipper-xtre.me/asset-packs/ , new & improved Animation / Asset system, that we can finally reveal. It lets you to create and cycle through your own Asset Packs with only a few button presses, allowing you to easily load custom Animations and Icons like never before.
+
diff --git a/prompts/gpts/knowledge/Flipper Zero App Builder/ChatGPT Flipper Zero App Builder.txt b/prompts/gpts/knowledge/Flipper Zero App Builder/ChatGPT Flipper Zero App Builder.txt
new file mode 100644
index 0000000..700a879
--- /dev/null
+++ b/prompts/gpts/knowledge/Flipper Zero App Builder/ChatGPT Flipper Zero App Builder.txt
@@ -0,0 +1,1453 @@
+Example of flipper zero app source code :
+
+- This is counter increamnt by InputKeyDown and InputKeyUp in flipper zero :
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+typedef struct {
+ int number;
+ bool confirm;
+} AppState;
+
+// Render callback
+static void render_callback(Canvas* canvas, void* context) {
+ AppState* app_state = (AppState*)context;
+
+ // Clear the canvas
+ canvas_clear(canvas);
+
+ // Set font and draw text
+ canvas_set_font(canvas, FontSecondary);
+ elements_multiline_text_aligned(canvas, 0, 0, AlignLeft, AlignTop, "BMI Calculator");
+
+ char numStr[12];
+ snprintf(numStr, sizeof(numStr), "%d", app_state->number);
+ elements_multiline_text_aligned(canvas, 64, 32, AlignCenter, AlignTop, numStr);
+}
+
+// Input callback
+static void input_callback(InputEvent* input_event, void* context) {
+ AppState* app_state = (AppState*)context;
+
+ if(input_event->type == InputTypeShort) {
+ switch(input_event->key) {
+ case InputKeyUp:
+ app_state->number++;
+ break;
+ case InputKeyDown:
+ app_state->number--;
+ break;
+ case InputKeyOk:
+ app_state->confirm = true;
+ break;
+ case InputKeyBack:
+ // Add any action if required when back button is pressed
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+int32_t flippertools_app(void) {
+ AppState app_state;
+ app_state.number = 0;
+ app_state.confirm = false;
+
+ // Create and open a viewport
+ ViewPort* view_port = view_port_alloc();
+ view_port_draw_callback_set(view_port, render_callback, &app_state);
+
+ // Setup GUI
+ Gui* gui = furi_record_open(RECORD_GUI);
+ gui_add_view_port(gui, view_port, GuiLayerFullscreen);
+
+ // Create an input queue
+ FuriMessageQueue* input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
+ view_port_input_callback_set(view_port, input_callback, &app_state);
+
+ // Main loop
+ while(!app_state.confirm) {
+ view_port_update(view_port);
+ furi_delay_ms(100);
+
+ // Check for input events
+ InputEvent input_event;
+ while(furi_message_queue_get(input_queue, &input_event, 0) == FuriStatusOk) {
+ input_callback(&input_event, &app_state);
+ }
+ }
+
+ // Cleanup
+ view_port_enabled_set(view_port, false);
+ gui_remove_view_port(gui, view_port);
+ furi_record_close(RECORD_GUI);
+ view_port_free(view_port);
+ furi_message_queue_free(input_queue);
+
+ return 0;
+}
+
+
+
+
+- This Flipper application lets you view all information's regarding your flippers experience and butthurt.
+
+Features:
+
+General XP and Butthurt View
+Daily XP View and descriptions to each category.
+Daily Butthurt View and descriptions.
+Backup View to create and load backups of your flippers experience and butthurt.
+
+
+full code : doc.c file :
+#include "doc_i.h"
+#include "helpers/doc_storage_helper.h"
+
+#include
+#include
+
+bool doc_custom_event_callback(void* ctx, uint32_t event) {
+ furi_assert(ctx);
+ Doc* app = ctx;
+ return scene_manager_handle_custom_event(app->scene_manager, event);
+}
+
+bool doc_back_event_callback(void* ctx) {
+ furi_assert(ctx);
+ Doc* app = ctx;
+ return scene_manager_handle_back_event(app->scene_manager);
+}
+
+/*
+ * The Dolphin service only saves 30s after the latest change,
+ * it can take up to that amount to get the real current values.
+ * The timer gets called after 10s, for 3 times and then stops.
+ */
+void doc_dolphin_timer_callback(void* ctx) {
+ furi_assert(ctx);
+ Doc* app = ctx;
+
+ app->dolphin_timer_counter++;
+
+ if(app->dolphin_timer_counter <= 3) {
+ FURI_LOG_D(TAG, "Loading new state after %hhus", app->dolphin_timer_counter * 10);
+ doc_dolphin_state_load(app->dolphin);
+ furi_delay_ms(20);
+
+ if(app->in_selection) {
+ doc_selection_request_redraw(app->selection);
+ }
+ if(app->in_description) {
+ doc_description_request_redraw(app->description);
+ }
+ }
+
+ if(app->dolphin_timer_counter == 3) {
+ FURI_LOG_D(TAG, "30s reached, stopping timer.");
+ furi_timer_stop(app->dolphin_timer);
+ }
+}
+
+Doc* doc_alloc() {
+ Doc* app = malloc(sizeof(Doc));
+
+ app->dolphin = malloc(sizeof(DolphinState));
+ app->dolphin_timer = furi_timer_alloc(doc_dolphin_timer_callback, FuriTimerTypePeriodic, app);
+ app->file_path = furi_string_alloc();
+
+ //? ------------- Records -------------
+ app->gui = furi_record_open(RECORD_GUI);
+ app->dialogs = furi_record_open(RECORD_DIALOGS);
+ //? ----------- Records End -----------
+
+ // ViewDispatcher & SceneManager
+ app->view_dispatcher = view_dispatcher_alloc();
+ app->scene_manager = scene_manager_alloc(&doc_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, doc_custom_event_callback);
+ view_dispatcher_set_navigation_event_callback(app->view_dispatcher, doc_back_event_callback);
+
+ //! -------------- DEBUG --------------
+ app->notification = furi_record_open(RECORD_NOTIFICATION);
+ notification_message(app->notification, &sequence_display_backlight_on);
+ //! ------------ DEBUG END ------------
+
+ //? -------------- Views --------------
+ app->selection = doc_selection_alloc();
+ view_dispatcher_add_view(app->view_dispatcher, DocSelectionView, doc_selection_get_view(app->selection));
+ app->description = doc_description_alloc();
+ view_dispatcher_add_view(app->view_dispatcher, DocDescriptionView, doc_description_get_view(app->description));
+ app->text_input = text_input_alloc();
+ view_dispatcher_add_view(app->view_dispatcher, DocTextInputView, text_input_get_view(app->text_input));
+ //? ------------ Views End ------------
+
+ view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
+ return app;
+}
+
+void doc_free(Doc* app) {
+
+ //! -------------- DEBUG --------------
+ furi_record_close(RECORD_NOTIFICATION);
+ app->notification = NULL;
+ //! ------------ DEBUG END ------------
+
+ //? -------------- Views --------------
+ view_dispatcher_remove_view(app->view_dispatcher, DocSelectionView);
+ doc_selection_free(app->selection);
+ view_dispatcher_remove_view(app->view_dispatcher, DocDescriptionView);
+ doc_description_free(app->description);
+ view_dispatcher_remove_view(app->view_dispatcher, DocTextInputView);
+ text_input_free(app->text_input);
+ //? ------------ Views End ------------
+
+ // ViewDispatcher & SceneManager
+ scene_manager_free(app->scene_manager);
+ view_dispatcher_free(app->view_dispatcher);
+
+ //? ------------- Records -------------
+ furi_record_close(RECORD_GUI);
+ app->gui = NULL;
+ furi_record_close(RECORD_DIALOGS);
+ app->dialogs = NULL;
+ //? ----------- Records End -----------
+
+ furi_string_free(app->file_path);
+ furi_timer_free(app->dolphin_timer);
+ free(app->dolphin);
+
+ free(app);
+}
+
+uint32_t doc_app(void* p) {
+ UNUSED(p);
+ Doc* app = doc_alloc();
+
+ doc_dolphin_state_load(app->dolphin);
+ furi_timer_start(app->dolphin_timer, 10 * 1000);
+ scene_manager_next_scene(app->scene_manager, DocSceneMenu);
+ view_dispatcher_run(app->view_dispatcher);
+
+ doc_free(app);
+ return 0;
+}
+
+doc_i.h file :
+#pragma once
+
+#include
+
+//? -------------- Views --------------
+#include
+#include
+#include
+//? ------------ Views End ------------
+
+#include
+#include
+
+#include
+
+//! -------------- DEBUG --------------
+#include
+//! ------------ DEBUG END ------------
+
+#define TAG "FlipperDoc"
+
+typedef struct {
+ DolphinState* dolphin;
+ FuriTimer* dolphin_timer;
+ uint8_t dolphin_timer_counter;
+
+ //? -------------- Views --------------
+ DocSelection* selection;
+ bool in_selection;
+ DocDescription* description;
+ bool in_description;
+ TextInput* text_input;
+ //? ------------ Views End ------------
+
+ Gui* gui;
+ ViewDispatcher* view_dispatcher;
+ SceneManager* scene_manager;
+
+ DialogsApp* dialogs;
+ FuriString* file_path;
+ char text_input_array[50];
+
+ //! -------------- DEBUG --------------
+ NotificationApp* notification;
+ //! ------------ DEBUG END ------------
+} Doc;
+
+enum {
+ DocSelectionView,
+ DocDescriptionView,
+ DocTextInputView,
+};
+
+doc_icons.h file :
+#pragma once
+
+#include
+
+extern const Icon I_flipperdoc;
+extern const Icon I_doc_smallscreen_light;
+extern const Icon I_doc_bigscreen_light;
+extern const Icon I_doc_button_left_small;
+extern const Icon I_doc_button_up;
+extern const Icon I_doc_button_down;
+
+
+
+application.fam :
+
+# For details & more options, see documentation/AppManifests.md in firmware repo
+
+App(
+ appid="flipperdoc",
+ name="Flipper Doctor",
+ apptype=FlipperAppType.EXTERNAL,
+ entry_point="doc_app",
+ stack_size=1 * 1024,
+ fap_category="Tools",
+ fap_version="0.1",
+ fap_icon="images/flipperdoc.png",
+ fap_author="JulanDeAlb",
+ fap_weburl="https://github.com/julandealb/flipperdoc",
+ fap_icon_assets="images",
+)
+
+
+views/doc_description.c :
+
+
+#include "doc_description.h"
+#include "doc_view_common.h"
+
+#include
+
+struct DocDescription {
+ View* view;
+ DocDescriptionCallback callback;
+ void* ctx;
+};
+
+typedef struct {
+ FuriString* text;
+
+ uint8_t category;
+
+ //Internal
+ uint8_t size;
+ uint8_t index;
+} DocDescriptionViewModel;
+
+static void doc_description_draw_callback(Canvas* canvas, void* ctx) {
+ furi_assert(ctx);
+ DocDescriptionViewModel* vm = ctx;
+
+ canvas_draw_icon(canvas, 0, 0, &I_doc_bigscreen_light);
+
+ // Scrolling Arrow
+ if(vm->index > 0) {
+ canvas_draw_icon(canvas, 113, 13, &I_doc_button_up);
+ }
+ if(vm->size > 4 &&
+ vm->index < vm->size - 4) {
+ canvas_draw_icon(canvas, 113, 39, &I_doc_button_down);
+ }
+
+ // Text
+ doc_draw_text(canvas, vm->text, 18, 110, 16, 10, vm->index, 4);
+}
+
+static bool doc_description_input_callback(InputEvent* event, void* ctx) {
+ furi_assert(ctx);
+ DocDescription* instance = ctx;
+ bool consumed = false;
+
+ if(event->type == InputTypeShort || event->type == InputTypeRepeat) {
+ switch(event->key) {
+ case InputKeyUp:
+ with_view_model(
+ instance->view,
+ DocDescriptionViewModel* model,
+ {
+ if(model->index > 0) {
+ model->index--;
+ }
+ },
+ true);
+ consumed = true;
+ break;
+ case InputKeyDown:
+ with_view_model(
+ instance->view,
+ DocDescriptionViewModel* model,
+ {
+ if(model->index < model->size - 4) {
+ model->index++;
+ }
+ },
+ true);
+ consumed = true;
+ break;
+ case InputKeyBack:
+ with_view_model(
+ instance->view,
+ DocDescriptionViewModel* model,
+ {
+ model->index = 0;
+ },
+ false);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+//? Basic Functions
+View* doc_description_get_view(DocDescription* instance) {
+ furi_assert(instance);
+ return instance->view;
+}
+
+void doc_description_set_callback(DocDescription* instance, DocDescriptionCallback callback, void* ctx) {
+ furi_assert(instance);
+ furi_assert(callback);
+
+ instance->callback = callback;
+ instance->ctx = ctx;
+}
+
+DocDescription* doc_description_alloc() {
+ DocDescription* instance = malloc(sizeof(DocDescription));
+
+ instance->view = view_alloc();
+ view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(DocDescriptionViewModel));
+ view_set_draw_callback(instance->view, doc_description_draw_callback);
+ view_set_input_callback(instance->view, doc_description_input_callback);
+ view_set_context(instance->view, instance);
+
+ with_view_model(
+ instance->view,
+ DocDescriptionViewModel* vm,
+ {
+ vm->text = furi_string_alloc();
+ }, false);
+
+ return instance;
+}
+
+void doc_description_free(DocDescription* instance) {
+ furi_assert(instance);
+
+ with_view_model(
+ instance->view,
+ DocDescriptionViewModel* vm,
+ {
+ furi_string_free(vm->text);
+ }, false);
+
+ view_free(instance->view);
+ free(instance);
+}
+//? Basic Functions End
+
+//? Custom Functions
+FuriString* doc_description_get_string(DocDescription* instance) {
+ furi_assert(instance);
+
+ FuriString* text;
+ with_view_model(
+ instance->view,
+ DocDescriptionViewModel* vm,
+ {
+ text = vm->text;
+ }, true);
+
+ return text;
+}
+
+uint8_t doc_description_get_category(DocDescription* instance) {
+ furi_assert(instance);
+
+ uint8_t category;
+ with_view_model(
+ instance->view,
+ DocDescriptionViewModel* vm,
+ {
+ category = vm->category;
+ }, false);
+
+ return category;
+}
+
+void doc_description_set_category(DocDescription* instance, uint8_t category) {
+ furi_assert(instance);
+
+ with_view_model(
+ instance->view,
+ DocDescriptionViewModel* vm,
+ {
+ vm->category = category;
+ }, true);
+}
+
+void doc_description_set_size(DocDescription* instance, uint8_t size) {
+ furi_assert(instance);
+
+ with_view_model(
+ instance->view,
+ DocDescriptionViewModel* vm,
+ {
+ vm->size = size;
+ }, true);
+}
+
+void doc_description_request_redraw(DocDescription* instance) {
+ furi_assert(instance);
+ if(instance->callback != NULL) {
+ instance->callback(instance->ctx, (uint8_t) - 1);
+ }
+}
+
+void doc_description_force_redraw(DocDescription* instance) {
+ furi_assert(instance);
+ with_view_model(instance->view, DocDescriptionViewModel* vm, { UNUSED(vm); }, true);
+}
+//? Custom Functions End
+
+
+views/doc_description.h :
+
+#pragma once
+
+#include
+
+typedef struct DocDescription DocDescription;
+typedef void (*DocDescriptionCallback)(void* ctx, uint8_t index);
+
+//? Basic Functions
+View* doc_description_get_view(DocDescription* instance);
+void doc_description_set_callback(DocDescription* instance, DocDescriptionCallback callback, void* ctx);
+DocDescription* doc_description_alloc();
+void doc_description_free(DocDescription* instance);
+//? Basic Functions End
+
+//? Custom Functions
+FuriString* doc_description_get_string(DocDescription* instance);
+uint8_t doc_description_get_category(DocDescription* instance);
+void doc_description_set_category(DocDescription* instance, uint8_t category);
+void doc_description_set_size(DocDescription* instance, uint8_t size);
+void doc_description_request_redraw(DocDescription* instance);
+void doc_description_force_redraw(DocDescription* instance);
+//? Custom Functions End
+
+views/doc_selection.c :
+
+#include "doc_selection.h"
+#include "doc_view_common.h"
+
+#include
+
+struct DocSelection {
+ View* view;
+ DocSelectionCallback callback;
+ void* ctx;
+};
+
+typedef struct {
+ const char* title;
+ FuriString* text;
+ const char* footer;
+
+ uint8_t category;
+
+ //Internal
+ uint8_t size;
+ uint8_t position;
+ uint8_t window_position;
+} DocSelectionViewModel;
+
+static void doc_selection_draw_callback(Canvas* canvas, void* ctx) {
+ furi_assert(ctx);
+ DocSelectionViewModel* vm = ctx;
+
+ canvas_draw_icon(canvas, 0, 0, &I_doc_smallscreen_light);
+
+ // Selection Arrow
+ uint8_t selection_index = vm->position - vm->window_position;
+ if(selection_index == 0) {
+ canvas_draw_icon(canvas, 123, 16, &I_doc_button_left_small);
+ } else if(selection_index == 1) {
+ canvas_draw_icon(canvas, 123, 25, &I_doc_button_left_small);
+ } else {
+ canvas_draw_icon(canvas, 123, 34, &I_doc_button_left_small);
+ }
+
+ // Title
+ canvas_set_font(canvas, FontPrimary);
+ canvas_draw_str_aligned(canvas, 85, 11, AlignCenter, AlignBottom, vm->title);
+ canvas_set_font(canvas, FontSecondary);
+
+ // Text
+ doc_draw_text(canvas, vm->text, 44, 121, 22, 9, vm->window_position, 3);
+
+ // Footer
+ canvas_draw_str_aligned(canvas, 87, 54, AlignCenter, AlignBottom, vm->footer);
+}
+
+static bool doc_selection_input_callback(InputEvent* event, void* ctx) {
+ furi_assert(ctx);
+ DocSelection* instance = ctx;
+ bool consumed = false;
+
+ if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) {
+ switch(event->key) {
+ case InputKeyUp:
+ with_view_model(
+ instance->view,
+ DocSelectionViewModel* model,
+ {
+ if(model->position > 0) {
+ model->position--;
+
+ if(model->position == model->window_position && model->window_position > 0) {
+ model->window_position--;
+ }
+ } else {
+ model->position = model->size - 1;
+
+ if(model->position > 2) {
+ model->window_position = model->position - 2;
+ }
+ }
+ },
+ true);
+ consumed = true;
+ break;
+ case InputKeyDown:
+ with_view_model(
+ instance->view,
+ DocSelectionViewModel* model,
+ {
+ if(model->position < model->size - 1) {
+ model->position++;
+
+ if(model->position - model->window_position > 1 && model->window_position < model->size - 3) {
+ model->window_position++;
+ }
+ } else {
+ model->position = 0;
+ model->window_position = 0;
+ }
+ },
+ true);
+ consumed = true;
+ break;
+ case InputKeyOk:
+ with_view_model(
+ instance->view,
+ DocSelectionViewModel* model,
+ {
+ if(instance->callback) {
+ instance->callback(instance->ctx, model->position);
+ }
+ },
+ false);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+//? Basic Functions
+View* doc_selection_get_view(DocSelection* instance) {
+ furi_assert(instance);
+ return instance->view;
+}
+
+void doc_selection_set_callback(DocSelection* instance, DocSelectionCallback callback, void* ctx) {
+ furi_assert(instance);
+ furi_assert(callback);
+
+ instance->callback = callback;
+ instance->ctx = ctx;
+}
+
+DocSelection* doc_selection_alloc() {
+ DocSelection* instance = malloc(sizeof(DocSelection));
+
+ instance->view = view_alloc();
+ view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(DocSelectionViewModel));
+ view_set_draw_callback(instance->view, doc_selection_draw_callback);
+ view_set_input_callback(instance->view, doc_selection_input_callback);
+ view_set_context(instance->view, instance);
+
+ with_view_model(
+ instance->view,
+ DocSelectionViewModel* vm,
+ {
+ vm->text = furi_string_alloc();
+ }, false);
+
+ return instance;
+}
+
+void doc_selection_free(DocSelection* instance) {
+ furi_assert(instance);
+
+ with_view_model(
+ instance->view,
+ DocSelectionViewModel* vm,
+ {
+ furi_string_free(vm->text);
+ }, false);
+
+ view_free(instance->view);
+ free(instance);
+}
+//? Basic Functions End
+
+//? Custom Functions
+void doc_selection_set_title(DocSelection* instance, const char* title) {
+ furi_assert(instance);
+
+ with_view_model(
+ instance->view,
+ DocSelectionViewModel* vm,
+ {
+ vm->title = title;
+ }, true);
+}
+
+FuriString* doc_selection_get_string(DocSelection* instance) {
+ furi_assert(instance);
+
+ FuriString* text;
+ with_view_model(
+ instance->view,
+ DocSelectionViewModel* vm,
+ {
+ text = vm->text;
+ }, true);
+
+ return text;
+}
+
+void doc_selection_set_footer(DocSelection* instance, const char* footer) {
+ furi_assert(instance);
+
+ with_view_model(
+ instance->view,
+ DocSelectionViewModel* vm,
+ {
+ vm->footer = footer;
+ }, true);
+}
+
+void doc_selection_set_index(DocSelection* instance, uint8_t index) {
+ furi_assert(instance);
+
+ with_view_model(
+ instance->view,
+ DocSelectionViewModel* model,
+ {
+ uint8_t position = index;
+ if(position >= model->size) {
+ position = 0;
+ }
+
+ model->position = position;
+ model->window_position = position;
+
+ if(model->window_position > 0) {
+ model->window_position -= 1;
+ }
+
+ if(model->size <= 3) {
+ model->window_position = 0;
+ } else {
+ if(model->window_position >= model->size - 3) {
+ model->window_position = model->size - 3;
+ }
+ }
+ },
+ true);
+}
+
+void doc_selection_set_size(DocSelection* instance, uint8_t size) {
+ furi_assert(instance);
+
+ with_view_model(
+ instance->view,
+ DocSelectionViewModel* vm,
+ {
+ vm->size = size;
+ }, true);
+}
+
+void doc_selection_request_redraw(DocSelection* instance) {
+ furi_assert(instance);
+ if(instance->callback != NULL) {
+ instance->callback(instance->ctx, (uint8_t) - 1);
+ }
+}
+
+void doc_selection_force_redraw(DocSelection* instance) {
+ furi_assert(instance);
+ with_view_model(instance->view, DocSelectionViewModel* vm, { UNUSED(vm); }, true);
+}
+//? Custom Functions End
+
+views/doc_view_common.c :
+#include "doc_view_common.h"
+
+void doc_draw_text(Canvas* canvas, FuriString* text,
+ uint8_t x_one, uint8_t x_two, uint8_t y,
+ uint8_t font_height, uint8_t start_index, uint8_t max_index) {
+ furi_assert(canvas);
+
+ FuriString* str = furi_string_alloc();
+ const char* start = furi_string_get_cstr(text);
+ char* mid;
+ char* end;
+ uint8_t temp_index = 0;
+
+ do {
+ mid = strchr(start, '\t');
+ end = strchr(start, '\n');
+
+ if(mid && end && 0 < end - mid) {
+ furi_string_set_strn(str, start, mid - start);
+
+ if(temp_index >= start_index) {
+ canvas_draw_str_aligned(canvas, x_one, y, AlignLeft, AlignBottom, furi_string_get_cstr(str));
+ }
+ }
+
+ if(end && mid) {
+ furi_string_set_strn(str, mid, end - start);
+ start = end + 1;
+ } else if(end) {
+ furi_string_set_strn(str, start, end - start);
+ start = end + 1;
+ } else {
+ furi_string_set(str, start);
+ }
+
+ if(temp_index >= start_index) {
+ canvas_draw_str_aligned(canvas, x_two, y, AlignRight, AlignBottom, furi_string_get_cstr(str));
+ y += font_height;
+ }
+
+ temp_index++;
+ } while(end && y < 64 && temp_index <= start_index + max_index - 1);
+
+ furi_string_free(str);
+}
+
+views/doc_view_common.h :
+
+#pragma once
+
+#include
+
+void doc_draw_text(Canvas* canvas, FuriString* text,
+ uint8_t x_one, uint8_t x_two, uint8_t y,
+ uint8_t font_height, uint8_t start_index, uint8_t max_index);
+
+
+
+
+this app will generate a password in flipper zero , source code :
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define PASSGEN_MAX_LENGTH 16
+#define PASSGEN_CHARACTERS_LENGTH (26*4)
+
+#define PASSGEN_DIGITS "0123456789"
+#define PASSGEN_LETTERS_LOW "abcdefghijklmnopqrstuvwxyz"
+#define PASSGEN_LETTERS_UP "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define PASSGEN_SPECIAL "!#$%^&*.-_"
+
+typedef enum PassGen_Alphabet
+{
+ Digits = 1,
+ Lowercase = 2,
+
+ Uppercase = 4,
+ Special = 8,
+
+ DigitsLower = Digits | Lowercase,
+ DigitsAllLetters = Digits | Lowercase | Uppercase,
+ Mixed = DigitsAllLetters | Special
+} PassGen_Alphabet;
+
+const int AlphabetLevels[] = { Digits, Lowercase, DigitsLower, DigitsAllLetters, Mixed };
+const char* AlphabetLevelNames[] = { "1234", "abcd", "ab12", "Ab12", "Ab1#" };
+const int AlphabetLevelsCount = sizeof(AlphabetLevels) / sizeof(int);
+
+const NotificationSequence PassGen_Alert_vibro = {
+ &message_vibro_on,
+ &message_blue_255,
+ &message_delay_50,
+ &message_vibro_off,
+ NULL,
+};
+
+typedef struct {
+ FuriMessageQueue* input_queue;
+ ViewPort* view_port;
+ Gui* gui;
+ FuriMutex** mutex;
+ NotificationApp* notify;
+ char password[PASSGEN_MAX_LENGTH+1];
+ char alphabet[PASSGEN_CHARACTERS_LENGTH+1];
+ int length;
+ int level;
+} PassGen;
+
+void state_free(PassGen* app) {
+ gui_remove_view_port(app->gui, app->view_port);
+ furi_record_close(RECORD_GUI);
+ view_port_free(app->view_port);
+ furi_message_queue_free(app->input_queue);
+ furi_mutex_free(app->mutex);
+ furi_record_close(RECORD_NOTIFICATION);
+ free(app);
+}
+
+static void input_callback(InputEvent* input_event, void* ctx) {
+ PassGen* app = ctx;
+ if(input_event->type == InputTypeShort) {
+ furi_message_queue_put(app->input_queue, input_event, 0);
+ }
+}
+
+static void render_callback(Canvas* canvas, void* ctx) {
+ char str_length[8];
+ PassGen* app = ctx;
+ furi_check(furi_mutex_acquire(app->mutex, FuriWaitForever) == FuriStatusOk);
+
+ canvas_clear(canvas);
+ canvas_draw_box(canvas, 0, 0, 128, 14);
+ canvas_set_color(canvas, ColorWhite);
+ canvas_set_font(canvas, FontPrimary);
+ canvas_draw_str(canvas, 2, 11, "Password Generator");
+
+ canvas_set_color(canvas, ColorBlack);
+ canvas_draw_str_aligned(canvas, 64, 35, AlignCenter, AlignCenter, app->password);
+
+ // Navigation menu:
+ canvas_set_font(canvas, FontSecondary);
+ canvas_draw_icon(canvas, 96, 52, &I_Pin_back_arrow_10x8);
+ canvas_draw_str(canvas, 108, 60, "Exit");
+
+ canvas_draw_icon(canvas, 54, 52, &I_Vertical_arrow_7x9);
+ canvas_draw_str(canvas, 64, 60, AlphabetLevelNames[app->level]);
+
+ snprintf(str_length, sizeof(str_length), "Len: %d", app->length);
+ canvas_draw_icon(canvas, 4, 53, &I_Horizontal_arrow_9x7);
+ canvas_draw_str(canvas, 15, 60, str_length);
+
+ furi_mutex_release(app->mutex);
+}
+
+void build_alphabet(PassGen* app)
+{
+ PassGen_Alphabet mode = AlphabetLevels[app->level];
+ app->alphabet[0] = '\0';
+ if ((mode & Digits) != 0)
+ strcat(app->alphabet, PASSGEN_DIGITS);
+ if ((mode & Lowercase) != 0)
+ strcat(app->alphabet, PASSGEN_LETTERS_LOW);
+ if ((mode & Uppercase) != 0)
+ strcat(app->alphabet, PASSGEN_LETTERS_UP);
+ if ((mode & Special) != 0)
+ strcat(app->alphabet, PASSGEN_SPECIAL);
+}
+
+PassGen* state_init() {
+ PassGen* app = malloc(sizeof(PassGen));
+ app->length = 8;
+ app->level = 2;
+ build_alphabet(app);
+ app->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
+ app->view_port = view_port_alloc();
+ app->gui = furi_record_open(RECORD_GUI);
+ app->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ view_port_input_callback_set(app->view_port, input_callback, app);
+ view_port_draw_callback_set(app->view_port, render_callback, app);
+ gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen);
+
+ app->notify = furi_record_open(RECORD_NOTIFICATION);
+
+ return app;
+}
+
+void generate(PassGen* app)
+{
+ int hi = strlen(app->alphabet);
+ for (int i=0; ilength; i++)
+ {
+ int x = rand() % hi;
+ app->password[i]=app->alphabet[x];
+ }
+ app->password[app->length] = '\0';
+}
+
+void update_password(PassGen* app, bool vibro)
+{
+ generate(app);
+
+ if (vibro)
+ notification_message(app->notify, &PassGen_Alert_vibro);
+ else
+ notification_message(app->notify, &sequence_blink_blue_100);
+ view_port_update(app->view_port);
+}
+
+int32_t passgenapp(void) {
+ PassGen* app = state_init();
+ generate(app);
+
+ while(1) {
+ InputEvent input;
+ while(furi_message_queue_get(app->input_queue, &input, FuriWaitForever) == FuriStatusOk) {
+ furi_check(furi_mutex_acquire(app->mutex, FuriWaitForever) == FuriStatusOk);
+
+ if (input.type == InputTypeShort)
+ {
+ switch (input.key) {
+ case InputKeyBack:
+ furi_mutex_release(app->mutex);
+ state_free(app);
+ return 0;
+ case InputKeyDown:
+ if (app->level > 0)
+ {
+ app->level--;
+ build_alphabet(app);
+ update_password(app, false);
+ }
+ else
+ notification_message(app->notify, &sequence_blink_red_100);
+ break;
+ case InputKeyUp:
+ if (app->level < AlphabetLevelsCount - 1)
+ {
+ app->level++;
+ build_alphabet(app);
+ update_password(app, false);
+ }
+ else
+ notification_message(app->notify, &sequence_blink_red_100);
+ break;
+ case InputKeyLeft:
+ if (app->length > 1)
+ {
+ app->length--;
+ update_password(app, false);
+ }
+ else
+ notification_message(app->notify, &sequence_blink_red_100);
+ break;
+ case InputKeyRight:
+ if (app->length < PASSGEN_MAX_LENGTH)
+ {
+ app->length++;
+ update_password(app, false);
+ }
+ else
+ notification_message(app->notify, &sequence_blink_red_100);
+ break;
+ case InputKeyOk:
+ update_password(app, true);
+ break;
+ default:
+ break;
+ }
+ }
+ furi_mutex_release(app->mutex);
+ }
+ }
+ state_free(app);
+ return 0;
+}
+
+
+Simple Flashlight source code for flipper zero Plugin :
+
+
+// by @xMasterX
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+typedef enum {
+ EventTypeTick,
+ EventTypeKey,
+} EventType;
+
+typedef struct {
+ EventType type;
+ InputEvent input;
+} PluginEvent;
+
+typedef struct {
+ FuriMutex* mutex;
+ bool is_on;
+} PluginState;
+
+static void render_callback(Canvas* const canvas, void* ctx) {
+ furi_assert(ctx);
+ const PluginState* plugin_state = ctx;
+ furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
+
+ canvas_set_font(canvas, FontPrimary);
+ elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Flashlight");
+
+ canvas_set_font(canvas, FontSecondary);
+
+ if(!plugin_state->is_on) {
+ elements_multiline_text_aligned(
+ canvas, 64, 28, AlignCenter, AlignTop, "Press OK button turn on");
+ } else {
+ elements_multiline_text_aligned(canvas, 64, 28, AlignCenter, AlignTop, "Light is on!");
+ elements_multiline_text_aligned(
+ canvas, 64, 40, AlignCenter, AlignTop, "Press OK button to off");
+ }
+
+ furi_mutex_release(plugin_state->mutex);
+}
+
+static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
+ furi_assert(event_queue);
+
+ PluginEvent event = {.type = EventTypeKey, .input = *input_event};
+ furi_message_queue_put(event_queue, &event, FuriWaitForever);
+}
+
+static void flash_toggle(PluginState* const plugin_state) {
+ furi_hal_gpio_write(&gpio_ext_pc3, false);
+ furi_hal_gpio_init(&gpio_ext_pc3, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
+
+ if(plugin_state->is_on) {
+ furi_hal_gpio_write(&gpio_ext_pc3, false);
+ plugin_state->is_on = false;
+ } else {
+ furi_hal_gpio_write(&gpio_ext_pc3, true);
+ plugin_state->is_on = true;
+ }
+}
+
+int32_t flashlight_app() {
+ FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
+
+ PluginState* plugin_state = malloc(sizeof(PluginState));
+
+ plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!plugin_state->mutex) {
+ FURI_LOG_E("flashlight", "cannot create mutex\r\n");
+ furi_message_queue_free(event_queue);
+ free(plugin_state);
+ return 255;
+ }
+
+ // Set system callbacks
+ ViewPort* view_port = view_port_alloc();
+ view_port_draw_callback_set(view_port, render_callback, plugin_state);
+ view_port_input_callback_set(view_port, input_callback, event_queue);
+
+ // Open GUI and register view_port
+ Gui* gui = furi_record_open(RECORD_GUI);
+ gui_add_view_port(gui, view_port, GuiLayerFullscreen);
+
+ PluginEvent event;
+ for(bool processing = true; processing;) {
+ FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
+
+ furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
+
+ if(event_status == FuriStatusOk) {
+ // press events
+ if(event.type == EventTypeKey) {
+ if(event.input.type == InputTypePress) {
+ switch(event.input.key) {
+ case InputKeyUp:
+ case InputKeyDown:
+ case InputKeyRight:
+ case InputKeyLeft:
+ break;
+ case InputKeyOk:
+ flash_toggle(plugin_state);
+ break;
+ case InputKeyBack:
+ processing = false;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ furi_mutex_release(plugin_state->mutex);
+ view_port_update(view_port);
+ }
+
+ view_port_enabled_set(view_port, false);
+ gui_remove_view_port(gui, view_port);
+ furi_record_close(RECORD_GUI);
+ view_port_free(view_port);
+ furi_message_queue_free(event_queue);
+ furi_mutex_free(plugin_state->mutex);
+
+ return 0;
+}
+
+
+color guess source code for flipper zero app :
+
+#include "color_guess.h"
+#include "helpers/digits.h"
+
+bool color_guess_custom_event_callback(void* context, uint32_t event) {
+ furi_assert(context);
+ ColorGuess* app = context;
+ return scene_manager_handle_custom_event(app->scene_manager, event);
+}
+
+void color_guess_tick_event_callback(void* context) {
+ furi_assert(context);
+ ColorGuess* app = context;
+ scene_manager_handle_tick_event(app->scene_manager);
+}
+
+//leave app if back button pressed
+bool color_guess_navigation_event_callback(void* context) {
+ furi_assert(context);
+ ColorGuess* app = context;
+ return scene_manager_handle_back_event(app->scene_manager);
+}
+
+ColorGuess* color_guess_app_alloc() {
+ ColorGuess* app = malloc(sizeof(ColorGuess));
+ app->gui = furi_record_open(RECORD_GUI);
+ app->notification = furi_record_open(RECORD_NOTIFICATION);
+ app->error = false;
+
+ // Set Defaults if no config exists
+ app->haptic = 1;
+ app->led = 1;
+ app->save_settings = 1;
+
+ // Load configs
+ color_guess_read_settings(app);
+
+ NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
+ notification_message(notification, &sequence_display_backlight_on);
+
+ //Scene additions
+ app->view_dispatcher = view_dispatcher_alloc();
+ view_dispatcher_enable_queue(app->view_dispatcher);
+
+ app->scene_manager = scene_manager_alloc(&color_guess_scene_handlers, app);
+ view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
+ view_dispatcher_set_navigation_event_callback(
+ app->view_dispatcher, color_guess_navigation_event_callback);
+ view_dispatcher_set_tick_event_callback(
+ app->view_dispatcher, color_guess_tick_event_callback, 100);
+ view_dispatcher_set_custom_event_callback(
+ app->view_dispatcher, color_guess_custom_event_callback);
+ app->submenu = submenu_alloc();
+
+ view_dispatcher_add_view(
+ app->view_dispatcher, ColorGuessViewIdMenu, submenu_get_view(app->submenu));
+ app->variable_item_list = variable_item_list_alloc();
+ view_dispatcher_add_view(
+ app->view_dispatcher,
+ ColorGuessViewIdSettings,
+ variable_item_list_get_view(app->variable_item_list));
+ app->color_guess_startscreen = color_guess_startscreen_alloc();
+ view_dispatcher_add_view(
+ app->view_dispatcher,
+ ColorGuessViewIdStartscreen,
+ color_guess_startscreen_get_view(app->color_guess_startscreen));
+ app->color_guess_color_set = color_guess_color_set_alloc();
+ view_dispatcher_add_view(
+ app->view_dispatcher,
+ ColorGuessViewIdColorSet,
+ color_guess_color_set_get_view(app->color_guess_color_set));
+ app->color_guess_play = color_guess_play_alloc();
+ view_dispatcher_add_view(
+ app->view_dispatcher,
+ ColorGuessViewIdPlay,
+ color_guess_play_get_view(app->color_guess_play));
+
+ //End Scene Additions
+
+ return app;
+}
+
+void color_guess_app_free(ColorGuess* app) {
+ furi_assert(app);
+
+ // Scene manager
+ scene_manager_free(app->scene_manager);
+
+ // View Dispatcher
+ view_dispatcher_remove_view(app->view_dispatcher, ColorGuessViewIdMenu);
+ view_dispatcher_remove_view(app->view_dispatcher, ColorGuessViewIdStartscreen);
+ view_dispatcher_remove_view(app->view_dispatcher, ColorGuessViewIdColorSet);
+ view_dispatcher_remove_view(app->view_dispatcher, ColorGuessViewIdPlay);
+ view_dispatcher_remove_view(app->view_dispatcher, ColorGuessViewIdSettings);
+ submenu_free(app->submenu);
+
+ view_dispatcher_free(app->view_dispatcher);
+
+ // GUI
+ furi_record_close(RECORD_GUI);
+
+ app->view_port = NULL;
+ app->gui = NULL;
+ app->notification = NULL;
+
+ //Remove whatever is left
+ free(app);
+}
+
+int32_t color_guess_app(void* p) {
+ UNUSED(p);
+ ColorGuess* app = color_guess_app_alloc();
+ if(app->error) {
+ return 255;
+ }
+
+ if(!furi_hal_region_is_provisioned()) {
+ color_guess_app_free(app);
+ return 1;
+ }
+
+ view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
+
+ scene_manager_next_scene(app->scene_manager, ColorGuessSceneStartscreen);
+
+ furi_hal_power_suppress_charge_enter();
+
+ view_dispatcher_run(app->view_dispatcher);
+
+ color_guess_save_settings(app);
+
+ furi_hal_power_suppress_charge_exit();
+
+ color_guess_app_free(app);
+
+ return 0;
+}
+
+
+example of hello world with face draw :
+
+helloworld.c :
+#include
+#include
+#include
+#include
+
+// Render callback
+static void render_callback(Canvas* canvas, void* context) {
+ // Clear the canvas
+ canvas_clear(canvas);
+
+ // Set font and draw "Hello World" with a twist
+ canvas_set_font(canvas, FontSecondary);
+ elements_multiline_text_aligned(canvas, 64, 32, AlignCenter, AlignTop, "Hello World!");
+
+ // Draw a smiley face under the text
+ canvas_draw_circle(canvas, 64, 50, 10);
+ canvas_draw_line(canvas, 60, 47, 62, 47); // Corrected line
+ canvas_draw_line(canvas, 66, 47, 68, 47); // Corrected line
+}
+
+// Input callback - exit on back button
+static void input_callback(InputEvent* input_event, void* context) {
+ bool* running = context;
+ if(input_event->type == InputTypeShort && input_event->key == InputKeyBack) {
+ *running = false;
+ }
+}
+
+// Main app function
+int32_t hello_world_app(void) {
+ // Setup GUI and viewport
+ Gui* gui = furi_record_open(RECORD_GUI);
+ ViewPort* view_port = view_port_alloc();
+ gui_add_view_port(gui, view_port, GuiLayerFullscreen);
+ view_port_draw_callback_set(view_port, render_callback, NULL);
+
+ // Main loop
+ bool running = true;
+ view_port_input_callback_set(view_port, input_callback, &running);
+ while(running) {
+ view_port_update(view_port);
+ furi_delay_ms(100);
+ }
+
+ // Cleanup
+ gui_remove_view_port(gui, view_port);
+ furi_record_close(RECORD_GUI);
+ view_port_free(view_port);
+ return 0;
+}
+
+application.fam :
+
+App(
+ appid="hello_world",
+ name="Hello World",
+ apptype=FlipperAppType.EXTERNAL,
+ entry_point="hello_world_app",
+ stack_size=1 * 1024,
+ fap_category="Tools",
+ fap_version="1.0",
+ fap_icon="images/icon.png",
+ fap_author="YourName",
+ fap_weburl="https://yourwebsite.com"
+)
+
+
+
+
+
+
+
+
diff --git a/prompts/gpts/knowledge/Flipper Zero App Builder/ChatGPT Flipper Zero Code.txt b/prompts/gpts/knowledge/Flipper Zero App Builder/ChatGPT Flipper Zero Code.txt
new file mode 100644
index 0000000..1bfa363
--- /dev/null
+++ b/prompts/gpts/knowledge/Flipper Zero App Builder/ChatGPT Flipper Zero Code.txt
@@ -0,0 +1,91 @@
+- code examples :
+
+This is example code "Hello World" with back button using flipper build tool.
+
+flipper build tool doc : https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/fbt.md
+
+hello_world.c :
+
+#include
+#include
+#include
+#include
+#include
+
+static void render_callback(Canvas* const canvas, void* ctx) {
+
+
+ (void)ctx;
+ // Clear the canvas
+ canvas_clear(canvas);
+
+ // Set font
+ canvas_set_font(canvas, FontPrimary);
+
+ // Draw the text
+ elements_multiline_text_aligned(canvas, 64, 32, AlignCenter, AlignTop, "Hello World");
+}
+
+// Input callback function
+static void input_callback(InputEvent* input_event, void* ctx) {
+ bool* running = (bool*)ctx;
+
+ if(input_event->type == InputTypeShort) {
+ if(input_event->key == InputKeyBack) {
+ *running = false;
+ }
+ }
+}
+
+int32_t hello_world_app(void) {
+ // Create and open a viewport
+ ViewPort* view_port = view_port_alloc();
+ view_port_draw_callback_set(view_port, render_callback, NULL);
+ Gui* gui = furi_record_open(RECORD_GUI);
+ gui_add_view_port(gui, view_port, GuiLayerFullscreen);
+
+ // Create an input queue
+ FuriMessageQueue* input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
+
+ // Main loop control variable
+ bool running = true;
+
+ // Register input callback
+ view_port_input_callback_set(view_port, input_callback, &running);
+
+ // Main loop
+ while(running) {
+ view_port_update(view_port);
+ furi_delay_ms(100);
+
+ // Check for input events
+ InputEvent input_event;
+ while(furi_message_queue_get(input_queue, &input_event, 0) == FuriStatusOk) {
+ input_callback(&input_event, &running);
+ }
+ }
+
+ // Cleanup
+ view_port_enabled_set(view_port, false);
+ gui_remove_view_port(gui, view_port);
+ furi_record_close(RECORD_GUI);
+ view_port_free(view_port);
+ furi_message_queue_free(input_queue);
+
+ return 0;
+}
+
+
+- errors :
+
+error 1 :
+error: unused parameter 'ctx' [-Werror=unused-parameter]
+ 7 | static void render_callback(Canvas* const canvas, void* ctx) {
+ | ~~~~~~^~~
+cc1-bin: all warnings being treated as errors
+ SDKCHK targets/f7/api_symbols.csv
+scons: *** [build/f7-firmware-D/.extapps/hello_world/testingapp.o] Error 1
+
+solve 1 :
+
+using (void)ctx; in functions if not used.
diff --git a/prompts/gpts/knowledge/Flipper Zero App Builder/Flipper Zero App Builder.txt b/prompts/gpts/knowledge/Flipper Zero App Builder/Flipper Zero App Builder.txt
new file mode 100644
index 0000000..0736c68
--- /dev/null
+++ b/prompts/gpts/knowledge/Flipper Zero App Builder/Flipper Zero App Builder.txt
@@ -0,0 +1,226 @@
+BUILDING AN APP FOR FLIPPER ZERO, PART 1: GETTING STARTED
+ 2023-05-05 11-minute read
+FlipperZero is a digital signals multi-tool. Here’s a short guide to the basics of building an app for it…
+
+The Flipper Zero is a digital signals multi-tool device, with some fun applications. It has an infrared module, a sub-GHz radio, RFID and NFC capability, iButton, USB, a screen, input controls, and GPIO pins. It’s small enough to fit in your hand, and it can communicate with your home appliances, or help you to learn about the signals that fly around our world. It’s also extremely customisable.
+
+Let’s build an app for Flipper Zero
+Flipper Zero features several apps built directly into the firmware, but you can also create and install external apps onto the SD card which are then available through the Applications menu.
+
+Resistance is futile
+With the help of several folks from the Flipper dev community, I recently built an app to calculate the value of resistors by decoding the coloured bands on them.
+
+
+More detail and source code at:
+
+instantiator/flipper-zero-experimental-apps
+Thanks to
+
+Derek Jamison for helping to debug a thorny issue or two, and
+Kuronons for the graphics in the app.
+In this tutorial, we’ll walk through the process of setting up an app, and building a simple user interface, using the same techniques.
+
+Community
+Before getting started, it’s helpful to know that there’s a thriving and welcoming FlipperZero community on a public Discord server:
+
+Flipper Devices
+With thanks
+This part of the tutorial series would not have been possible without the help and support of various people in the Flipper Zero community. Many thanks to:
+
+Derek Jamison for his plugin tutorials, and accompanying wiki
+FlipperZero folks for the ufbt tool, and
+Aleksey Korolev for the flipc.org Flipper app catalogue.
+The toolchain
+I’m using a few tools:
+
+ufbt (“micro Flipper Build Tool”) - a version of fbt (the Flipper Built Tool) that supports everything you need for app development
+Visual Studio Code - an editor and IDE from Microsoft
+Pinta - a Mac clone of paint.net (but any graphics tool that allows you to edit 1-bit PNGs will do)
+The experience working with Visual Studio Code is brilliant. It provides project management features, C syntax awareness and highlighting, and ufbt provides a nice integration to support build and debug options from the IDE.
+
+Versions and firmwares
+Flipper apps are written in C, and they’re compiled against the version of the firmware you want them to run against. This means that if your firmware changes, your app must too.
+
+This create a little complexity for distributing an app - as the firmware versions regularly frequently, and many people are using different flavours of firmware altogether (eg. DarkFlipper/Unleashed, RogueMaster).
+
+NB. An exploration of the various firmware features is beyond the scope of this tutorial.
+
+There’s a nice app distribution site called flipc.org that solves this problem by compiling apps from source code as they’re needed. Provided your source code is in a public GitHub repository, you can use flipc.org to publish it.
+
+Coming to C from C-type languages
+If you’re not familiar with C, but you’ve worked in C-type languages, you’re off to a good start. There are few things to know, though. Feel free to skip this section if you’re already a confident C developer.
+
+Caveat: I’ve not been working in C for long, but as I’ve experienced most of these pitfalls, I wanted to share them with you to help you avoid the same mistakes! Some of these are very difficult to debug - especially memory leaks.
+
+Memory safety
+C isn’t managed code, and there’s no garbage collection, so you’re going to have to take good care of the memory you allocate to data structures (structs) with malloc and free functions. If you allocate some memory, you’re going to have to free it - particularly if your code can do the allocation more than once. If you don’t release it, this can cause a memory leak - where you eventually run out of assignable memory.
+
+C also won’t prevent you from accessing memory you shouldn’t (eg. if you malloc the wrong size for your struct, and then try to write to it (or if you write to an incorrect offset to a pointer), you could end up writing to memory reserved for other variables or even code). This sort of issue manifests as weird crashes or unexpected values in variables, often long after the actual buggy code has been executed. That makes it difficult to debug.
+
+Namespaces
+C doesn’t have objects or enforced namespaces, so as soon as you’re writing something reasonably complex I recommend you start naming things carefully, so that it’s absolutely clear what’s what.
+
+For instance, enums are named in code, but this is a thin veil over integers, and you can use enum values as integers anywhere in the code. I recommend giving enum values distinct names prefixed with the name of the enum type itself, eg.
+
+typedef enum {
+ TagEventTypeInput,
+ TagEventTypeSubGhzDataDetected,
+ TagEventTypeInfraredMessage,
+} TagEventType;
+CapitalisationOfWords or using_underscore_delimiters are both acceptable ways to clarify namespaces and meanings in my book. I’m sure there’s an established convention. My advice is to be consistent in whatever you do.
+
+static and const
+It’s worth understanding what keywords like static and const mean in C - they won’t be perfectly aligned to how you’ve been using them in (say) Java, C++, or C#…
+
+static (function) - a static function is only available to other methods in the same .c file
+static (global variable) - a static global variable is only available to methods in the same .c file
+static (local variable) - a static variable in a function keeps its value between function calls
+Hiding functions with static is similar to creating private methods in other languages, and it’s considered good practice.
+
+The const keyword distinguishes between pointers and variables, and has a few variants. It’s used to indicate that a particular pointer or variable cannot be assigned to after initialisation. Don’t worry if this doesn’t all go in, it’s just worth knowing…
+
+int var = 5; an assignable integer
+int const var = 5; an unassignable integer
+int* var - an assignable pointer to an assignable integer
+const int* var - an assignable pointer to an unassignable integer
+int* const var - an unassignable pointer to an assignable integer
+const int* const var - an unassignable pointer to an unassignable integer
+Data structures
+struct and enum are similar to what you’d expect although, as mentioned earlier, enum values are actually integers - and so not as type-safe as you might assume.
+
+struct is about as close as you can get to a data class - and you’ll see them used all over C to represent rich data structures.
+
+Code, headers and interfaces
+.c files contain code. It’s possible to write an entire application in a single .c file - and I’ve seen several of these. It’s a perfectly acceptable approach, but you risk encountering complexity and difficulties as you go.
+
+The C compiler cares about the order that things are defined in. So, for instance, if you want to refer to a function, it needs to have been defined before you first refer to it. If not, you’ll see a couple of errors.
+
+For instance, if you refer to a function called do_something before you define it, you’ll see something like this where you first call it:
+
+implicit declaration of function 'do_something'
+and then, where you define it:
+
+conflicting types for 'do_something'
+This is because when you first referred to function do_something, you ‘implicitly’ defined it. Then, later, when the compiler encountered your actual definition it doesn’t know what to do - it already has an implicit definition.
+
+There are two solutions to this:
+
+Place the function definition before its first reference in the .c file
+Define the function’s footprint in a .h header file, and include that header file at the beginning of your .c file
+In this simple scenario, it’s probably easier just to reorder your functions.
+
+As soon as you want to start defining a group or library of functions and structures that are all linked, you’ll want to place them into a separate .c file and then create a header file you can use to define the interface (the methods you’re sharing with other .c files).
+
+As a rule of thumb, if you want to include a header file from your own code, use inverted commas:
+
+#include "my_lib.h"
+If you want to include a library header (eg. from the firmware), use angle brackets:
+
+#include
+The good news is that ufbt is smart. It will find all your .c code files, and compile them in a sensible order. (The days of having to list all your code files for gcc are a long distant nightmare…)
+
+Firmware headers
+A lot of this tutorial will use the gui libraries made available through the official firmware. If you ever need to figure out how something works, it’s definitely worth taking a look.
+
+If you do poke around you’ll see plenty of .c code files and .h header files. Those header files define exactly which functions, structs, enums, global variables, and macros you’ll be able to use to develop your app.
+
+You’ll also see some header files that end with the suffix _i.h. These are internal headers. This means that their content is not made available to app developers. You shouldn’t need anything in them, although occasionally you may see something in there you’d like to use. Try searching to see if it’s called as a part of another function. There’s often a good reason why a function has not been exposed to app developers. If you still think you need it, you can always raise the question on Discord.
+
+Setting up your environment
+First, create the directory you want to work in, open a terminal, and navigate to that directory.
+
+$ mkdir test_app
+$ cd test_app
+I often open a terminal in Visual Studio Code, as it’s easiest to work in a single tool and see what’s happening as I go.
+
+Fetch the FlipperZero firmware
+Update ufbt to collect the Software Development Kit (SDK) for the current firmware. You can specify the firmware channel using the --channel=[dev|rc|release] option.
+
+$ ufbt update --channel=dev
+13:36:51.062 [I] Deploying SDK for f7
+13:36:51.062 [I] Fetching version info for UpdateChannel.DEV from https://update.flipperzero.one/firmware/directory.json
+13:36:51.201 [I] Using version: a7d1ec03
+13:36:51.201 [I] uFBT SDK dir: /Users/lewiswestbury/.ufbt/current
+13:36:53.304 [I] Deploying SDK
+13:36:53.556 [I] SDK deployed.
+NB. I recommend working against the dev channel firmware. It enables quite a lot of safeguards to help catch certain kinds of mistakes early (you’ll see theses as furi_assert assertions in firmware code, and you can use them yourself).
+
+NNB. If a furi_assert fails (ie. the assertion isn’t true), it will hang your program. It can sometimes be quite difficult to identify or locate the problem, particularly if you haven’t tried to debug your code yet, but at least you’ll know there’s an issue. I’ll cover debugging in a future tutorial (once I have the supporting hardware). In the meantime, you could check out Derek Jamison’s guide on YouTube: Flipper Zero: Debugging FURI_ASSERT failures
+
+Your Flipper’s firmware should match the firmware you’re working against, or the app won’t launch. You can use ufbt to flash the firmware on your Flipper:
+
+ufbt flash_usb
+ufbt will also flash through an ST-link if you have that available:
+
+ufbt flash
+The qFlipper application also supports this:
+
+the qFlipper app showing the firmware view and the big green update button
+
+Create a skeleton app
+ufbt create will create a nice working environment for your app. Give your app a simple id, eg. test_app - you’ll be able to change this later. Provide an id for the application, with the APPID= option.
+
+NB. I recommend using characters from the simple set of a-zA-Z0-9_. Restrict yourself to characters that can appear in a C function name, or you may end up having to rename the main function, and the entrypoint in your application manifest before you can build.
+
+Running for the first time, it’ll warn that the application.fam file doesn’t exist (and then create one for you):
+
+$ ufbt create APPID=test_app
+scons: Entering directory `/Users/lewiswestbury/.ufbt/current/scripts/ufbt'
+
+fbt: warning: Folder app: manifest application.fam is missing
+LoadAppManifest, line 31, in file "/Users/lewiswestbury/.ufbt/current/scripts/fbt_tools/fbt_apps.py"
+Creating '/Users/lewiswestbury/src/test_app/test_app.c'
+ INSTALL /Users/lewiswestbury/src/test_app/test_app.png
+Creating '/Users/lewiswestbury/src/test_app/application.fam'
+Mkdir("/Users/lewiswestbury/src/test_app/images")
+Touch("/Users/lewiswestbury/src/test_app/images/.gitkeep")
+You should see the following files in the directory now:
+
+application.fam - this is the app manifest, with details about your app
+ufbt-test.c - application source code
+ufbt-test.png - a 10x10 1-bit png icon for the app
+images/ - a directory where you can place 1-bit png icons
+A screenshot of visual studio code showing a terminal with ufbt create, and the application manifest it has created
+
+Test a build
+With no other options, ufbt will build your app.
+
+$ ufbt
+scons: Entering directory `/Users/lewiswestbury/.ufbt/current/scripts/ufbt'
+ ICONS /Users/lewiswestbury/.ufbt/build/test_app/test_app_icons.c
+ CDB /Users/lewiswestbury/src/test_app/.vscode/compile_commands.json
+ CC /Users/lewiswestbury/src/test_app/test_app.c
+ CC /Users/lewiswestbury/.ufbt/build/test_app/test_app_icons.c
+ LINK /Users/lewiswestbury/.ufbt/build/test_app_d.elf
+ INSTALL /Users/lewiswestbury/src/test_app/dist/debug/test_app_d.elf
+ APPMETA /Users/lewiswestbury/.ufbt/build/test_app.fap
+ FAP /Users/lewiswestbury/.ufbt/build/test_app.fap
+ INSTALL /Users/lewiswestbury/src/test_app/dist/test_app.fap
+ APPCHK /Users/lewiswestbury/.ufbt/build/test_app.fap
+ Target: 7, API: 23.3
+When complete you’ll find a file called test_app.fap inside the dist directory. This is your entire application, bundled for the Flipper Zero. If you upload this to your Flipper Zero, into the Examples directory of your SD card, it’ll appear in the Applications menu. (It doesn’t do much, but you can run it from there.)
+
+You can change the directory for the app, by altering the fap_category entry in application.fam.
+
+Visual Studio integration
+Visual Studio Code can support some nice shortcuts to make your life easer, and ufbt can install them.
+
+$ ufbt vscode_dist
+scons: Entering directory `/Users/lewiswestbury/.ufbt/current/scripts/ufbt'
+Creating '/Users/lewiswestbury/src/test_app/.vscode/c_cpp_properties.json'
+Creating '/Users/lewiswestbury/src/test_app/.vscode/extensions.json'
+Creating '/Users/lewiswestbury/src/test_app/.vscode/launch.json'
+Creating '/Users/lewiswestbury/src/test_app/.vscode/settings.json'
+Creating '/Users/lewiswestbury/src/test_app/.vscode/tasks.json'
+ INSTALL /Users/lewiswestbury/src/test_app/.clang-format
+ INSTALL /Users/lewiswestbury/src/test_app/.editorconfig
+ INSTALL /Users/lewiswestbury/src/test_app/.gitignore
+Visual Studio Code now becomes aware of references to the SDK in your code, which is very helpful for syntax and error highlighting. You can now also use shortcuts in the IDE:
+
+Shift + Command + B = build menu
+Shift + Command + D = debugging menu
+Quick review
+We’ve looked at Flipper Zero firmware, pitfalls of working in C, setting up your environment, creating a skeleton app, and testing your toolchain with a simple build.
+
+In the next part, we’ll explore the various approaches, structures and libraries available to help you build a graphical interface for the Flipper.
+
diff --git a/prompts/gpts/knowledge/Flipper Zero App Builder/Flipper Zero App.txt b/prompts/gpts/knowledge/Flipper Zero App Builder/Flipper Zero App.txt
new file mode 100644
index 0000000..0a12beb
--- /dev/null
+++ b/prompts/gpts/knowledge/Flipper Zero App Builder/Flipper Zero App.txt
@@ -0,0 +1,466 @@
+
+- Intro
+In this tutorial, you will be creating a simple program that moves a small box around the screen, giving you the building blocks to create your own plugins down the line.
+
+Scope
+This tutorial will cover:
+
+A simple GUI+ViewPort application paradigm
+Basic interface with the gui and input services
+Basic queues using osMessageQueue
+Acquiring and working with user input
+Drawing to the screen
+Enabling and compiling custom applications .
+
+Requirements
+A Flipper Zero
+Basic knowledge of C or something close.
+An IDE (I use VSCode)
+
+- Setting up your workspace
+
+Now that you have a simple overview of the goal of this tutorial, let's create our own plugin!
+
+Now that you have a simple overview of the goal of this tutorial, let's create our own plugin!
+
+Creating The Directory
+Navigate to the applications folder
+Create a new folder inside. Let's call it box_mover!
+Now, let's create a new C file named box_mover.c inside of box_mover
+Your file structure should look like this:
+
+.
+└── flipperzero-firmware/
+ └── applications/
+ └── box_mover/
+ └── box_mover.c
+
+- Signature and structures
+
+Signature and structures
+Now that we have box_mover.c in our box_mover folder, we can finally start programming.
+
+Model Struct
+To make our lives easier, let's define all the information we need to encode for rendering our app:
+
+A point to render our box at, consisting of:
+an x coordinate
+and a y coordinate
+Pretty simple! We'll do that by declaring a BoxMoverModel struct that holds that information.
+
+box_mover/box_mover.c
+typedef struct {
+ int x;
+ int y;
+} BoxMoverModel;
+
+Application Struct
+Now that we're able to encode the information we need, let's create a struct that will hold all of the necessary variables and structures for our entire application.
+
+This might seem a little odd at this point, but the benefits will show soon. This type of program structure is idiomatic with the rest of Flipper Zero's applications and will allow you to more easily transfer into other GUI Paradigms later down the line.
+
+typedef struct {
+ BoxMoverModel* model;
+} BoxMover;
+
+For now, it'll just hold a pointer to our model.
+
+Allocating and freeing functions
+Now, let's write two functions that will allocate and free an instance of our BoxMover struct. Let's call them box_mover_alloc and box_mover_free
+
+Allocation
+BoxMover* box_mover_alloc(){
+ BoxMover* instance = malloc(sizeof(BoxMover));
+ instance->model = malloc(sizeof(BoxMoverModel));
+ instance->model->x = 10;
+ instance->model->y = 10;
+
+ return instance;
+}
+
+Our box_mover_alloc will allocate space for a BoxMover instance and subsequent model instance, and then initialize the model with some data. We return the instance at the end for our caller to use later.
+
+Freeing
+void box_mover_free(BoxMover* instance){
+ free(instance->model);
+ free(instance);
+}
+
+Since all we've done in our box_mover_alloc is allocate memory for our structs, we just need to use free to release that memory back.
+
+Main Signature
+The function that will run our plugin's code will follow a simple signature, complying with the other applications:
+
+#include
+
+// --snip--
+
+int32_t box_mover_app(void* p){
+ BoxMover* box_mover = box_mover_alloc();
+
+ box_mover_free(box_mover);
+ return 0;
+}
+
+This is how all applications are declared within Flipper Zero firmware, and it is common practice to append the name with _app.
+
+
+- GUI
+
+With our model now able to encode the information we need, and the main signature set up, let's start working with the gui service.
+
+First, let's start off by including the header, gui/gui.h. This will give us easy tools for interfacing with the screen.
+
+Next, we add a ViewPort and a Gui object to our BoxMover struct. These are the two structures that will allow us to make and draw to a GUI.
+
+#include
+#include
+#include
+// -snip-
+typedef struct {
+ BoxMoverModel* model;
+
+ ViewPort* view_port;
+ Gui* gui;
+
+} BoxMover;
+
+Let's initialize our new Gui and ViewPort objects in our box_mover_alloc function.
+
+BoxMover* box_mover_alloc(){
+ BoxMover* instance = malloc(sizeof(BoxMover));
+ instance->model = malloc(sizeof(BoxMoverModel));
+ instance->model->x = 10;
+ instance->model->y = 10;
+
+
+ instance->view_port = view_port_alloc();
+
+ instance->gui = furi_record_open("gui");
+ gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullScreen);
+
+ return instance;
+}
+
+
+We get the Gui object by asking furi to open the record with the label "gui", and we use a gui.h helper to allocate a ViewPort, much like we are making with our box_mover_alloc!
+
+In our freeing function, let's disable our ViewPort, close our record, and clean up the memory we've allocated.
+
+void box_mover_free(BoxMover* instance){
+ view_port_enabled_set(instance->view_port, false); // Disables our ViewPort
+ gui_remove_view_port(instance->gui, instance->view_port); // Removes our ViewPort from the Gui
+ furi_record_close("gui"); // Closes the gui record
+ view_port_free(instance->view_port); // Frees memory allocated by view_port_alloc
+
+ free(instance->model);
+ free(instance);
+}
+
+
+- Input Queue
+In order to take in input, we're going to be utilizing osMessageQueue, which, as the name implies, allows us to create queues of messages.
+
+For our BoxMover struct, all we need to do is declare an osMessageQueueId_t, which will be an ID for our queue, so we can reference it later.
+
+typedef struct {
+ BoxMoverModel* model;
+
+ osMessageQueueId_t event_queue;
+
+ ViewPort* view_port;
+ Gui* gui;
+
+} BoxMover;
+
+Now, let's actually create a queue inside of our box_mover_alloc function.
+
+BoxMover* box_mover_alloc(){
+ // --snip--
+ instance->gui = furi_record_open("gui");
+
+ instance->event_queue = osMessageQueueNew(8, sizeof(InputEvent), NULL);
+
+ return instance;
+}
+
+
+The above code creates a new event queue that will hold InputEvents (from the input service).
+
+In its parameters, we define that it will have:
+
+A maximum of 8 messages in the queue
+A message size of an InputEvent
+Default attributes (specified by NULL)
+Let's remember to free this new input queue in box_mover_free:
+
+void box_mover_free(BoxMover* instance){
+ // --snip--
+ osMessageQueueDelete(instance->event_queue);
+
+ free(instance->model);
+ free(instance);
+}
+
+- Callbacks and Concurrency
+Currently, our program only does this:
+
+Sets up our BoxMover struct
+Allocates a ViewPort
+Open our gui record
+Adds the ViewPort to the Gui
+Creates an input queue
+Cleans everything up and exits
+
+No drawing to the screen, and no input processing.
+Let's change that with callbacks!
+
+
+Callback Methods
+The gui service provides us with two nice methods for handling drawing and input.
+
+These are aptly declared: view_port_draw_callback_set and view_port_input_callback_set
+
+Let's look at their full declarations:
+
+void view_port_draw_callback_set(ViewPort* view_port, ViewPortDrawCallback callback, void* context);
+void view_port_input_callback_set(ViewPort* view_port, ViewPortInputCallback callback, void* context);
+
+
+As you might guess, view_port_draw_callback_set sets the function that is called whenever a new frame is signalled to be drawn. And view_port_input_callback_set sets the function that is called whenever input is recieved, like a button press.
+
+Conceptually, the callbacks work like this:
+
+We define a function we want to be called whenever an event occurs
+We use our *_callback_set functions, and fill it out with the general form:
+A pointer to our ViewPort instance
+Our callback function
+A pointer to the data we want to have access to in our callback functions
+This is passed to our functions as a void pointer, and we have to cast it back to the type we need.
+So, what would we like to do with the callbacks?
+
+Draw: Draw a box using our model's x and y values as an anchor point
+Input: Put key presses onto our input queue
+Before we implement them, we need to go over something inherent about callbacks: threads.
+
+Tackling Concurrency Issues Using Mutex
+Callbacks pose a problem because they run on a separate thread from our main app. Since we need to access our BoxMover in the callback, this could result in a race condition between our callbacks and main loop.
+
+Let's fix that by adding a mutex ID to our BoxMover struct. This will, in effect, allow it to be used as a blocking resource, only allowing one thread access at a time. We just need to make sure we acquire and release it whenever we deal with our struct.
+
+We'll do this by utilizing osMutex, an API layer that interfaces with the RTOS kernel. This is best current practice and supersedes ValueMutex, which you may see in some applications.
+
+Let's add an ID to our mutex in our BoxMover struct.
+
+typedef struct {
+ BoxMoverModel* model;
+ osMutexId_t* model_mutex;
+
+ osMessageQueueId_t event_queue;
+
+ ViewPort* view_port;
+ Gui* gui;
+
+} BoxMover;
+
+Now, let's initialize it in our box_mover_alloc, and clean it up in our box_mover_free.
+
+BoxMover* box_mover_alloc(){
+ // --snip--
+ instance->view_port = view_port_alloc();
+
+ instance->model_mutex = osMutexNew(NULL);
+
+ instance->gui = furi_record_open("gui");
+ // --snip--
+}
+
+void box_mover_free(BoxMover* instance){
+ // --snip--
+ osMessageQueueDelete(instance->event_queue);
+
+ osMutexDelete(instance->model_mutex);
+ // --snip--
+}
+
+Great! Now our BoxMover has the ability to be modified without the possibility of inducing a race condition. Let's implement those callbacks now.
+
+Draw Callback
+Our draw callback must conform to the following parameters:
+
+A pointer to a Canvas
+A pointer to the data we pass in view_port_draw_callback_set
+(For both callbacks, we will be passing in an instance of BoxMover.)
+
+// --snip--
+void draw_callback(Canvas* canvas, void* ctx){
+ BoxMover* box_mover = ctx;
+ furi_check(osMutexAcquire(box_mover->model_mutex, osWaitForever)==osOK);
+
+ canvas_draw_box(canvas, box_mover->model->x, box_mover->model->y, 4, 4); // Draw a box on the screen
+
+ osMutexRelease(box_mover->model_mutex);
+}
+
+
+Here, we try to acquire our mutex for however long it takes (denoted by osWaitForever), and is wrapped in a furi_check, which will crash the program if there is an error with the mutex.
+
+Once we have it, we know that only this thread has the mutex. Great! We can start using the variables now.
+
+We draw a simple box at x,y and with a height and width of 4, and then release the mutex to be used by another thread.
+
+EXPERIMENT!
+Experiment with other canvas_draw functions like canvas_draw_str, canvas_draw_circle, and many more! (see canvas.h)
+
+Let's add it to our ViewPort in our box_mover_alloc function:
+
+BoxMover* box_mover_alloc(){
+ // --snip--
+
+ instance->view_port = view_port_alloc();
+ view_port_draw_callback_set(instance->view_port, draw_callback, instance);
+
+ instance->model_mutex = osMutexNew(NULL);
+ // --snip--
+}
+
+
+Now our ViewPort is set up with a drawing callback! Next, we need to implement an input callback.
+
+Input Callback
+Our input callback must conform to the following parameters:
+
+A pointer to an InputEvent
+A pointer to the data we passed in view_port_input_callback_set
+The goal for our input callback is pretty simple. All we want it to do is:
+
+Read an input event
+Place it on the message queue to be read later, in our main loop.
+So, let's implement that with osMessageQueue.
+
+// --snip--
+void input_callback(InputEvent* input, void* ctx){
+ BoxMover* box_mover = ctx;
+ // Puts input onto event queue with priority 0, and waits until completion.
+ osMessageQueuePut(box_mover->event_queue, input, 0, osWaitForever);
+}
+BoxMover* box_mover_alloc(){
+ // --snip--
+ view_port_draw_callback_set(instance->view_port, draw_callback, instance);
+
+ view_port_input_callback_set(instance->view_port, input_callback, instance);
+ // --snip--
+}
+
+- Main Loop
+Handling Input
+With the input callback now processing our new events, we can start utilizing them in our main loop.
+
+Let's do that, and write a simple control flow.
+
+int32_t box_mover_app(void* p){
+ UNUSED(p);
+
+ BoxMover* box_mover = box_mover_alloc();
+
+ InputEvent event;
+ for(bool processing = true; processing;){
+ // Pops a message off the queue and stores it in `event`.
+ // No message priority denoted by NULL, and 100 ticks of timeout.
+ osStatus_t status = osMessageQueueGet(box_mover->event_queue, &event, NULL, 100);
+ furi_check(osMutexAcquire(box_mover->model_mutex, osWaitForever) == osOK);
+ if(status==osOK){
+ if(event.type==InputTypePress){
+ switch(event.key){
+ case InputKeyUp:
+ box_mover->model->y-=2;
+ break;
+ case InputKeyDown:
+ box_mover->model->y+=2;
+ break;
+ case InputKeyLeft:
+ box_mover->model->x-=2;
+ break;
+ case InputKeyRight:
+ box_mover->model->x+=2;
+ break;
+ case InputKeyOk:
+ case InputKeyBack:
+ processing = false;
+ break;
+ }
+ }
+ }
+ osMutexRelease(box_mover->model_mutex);
+ view_port_update(box_mover->view_port); // signals our draw callback
+ }
+ box_mover_free(box_mover);
+ return 0;
+}
+
+
+As you can see, because of our struct-oriented approach, this makes our final client calls much easier, and these skills and structure will transfer very well to other GUI Paradigms.
+
+
+- Enabling and Compiling
+
+Luckily this process has been streamlined, and we only need add a single line to reference our plugin ID in
+
+and add the file application.fam for our plugin metadata in our application folder.
+
+Application Metadata
+First, let's create an individual metadata file for our plugin:
+
+.
+└── flipperzero-firmware/
+ └── applications/
+ └── box-mover/
+ └── application.fam
+
+Inside, we're going to add some metadata about our application.
+
+/applications/box-mover/application.fam
+ App(
+ appid="box_mover_app",
+ name="Box Mover",
+ apptype=FlipperAppType.PLUGIN,
+ entry_point="box_mover_app",
+ cdefines=["APP_BOX_MOVER"],
+ requires=["gui"],
+ stack_size=1 * 1024,
+ icon="A_Plugins_14",
+ order=30,
+)
+
+This file provides metadata about our application. The appid will be used to reference our plugin, and entry_point indicates our main function for execution when the plugin initiates.
+
+Linking to the applications list
+To make our plugin accessible, we need to add an entry into the /applications/meta/application.fam file, with our plugin ID we created in the individual metadata.
+
+.
+└── flipperzero-firmware/
+ └── applications/
+ └── meta/
+ └── application.fam
+
+Let's add it to the "basic_plugins" list of applications.
+
+/applications/meta/application.fam
+...
+
+App(
+ appid="basic_plugins",
+ name="Basic applications for plug-in menu",
+ apptype=FlipperAppType.METAPACKAGE,
+ provides=[
+ "music_player",
+ "snake_game",
+ "box_mover_app",
+ "bt_hid",
+ ],
+)
+
+and with that, we are ready to compile and flash!
+
+
+
diff --git a/prompts/gpts/knowledge/Flipper Zero App Builder/helloflipper.c b/prompts/gpts/knowledge/Flipper Zero App Builder/helloflipper.c
new file mode 100644
index 0000000..945a60f
--- /dev/null
+++ b/prompts/gpts/knowledge/Flipper Zero App Builder/helloflipper.c
@@ -0,0 +1,99 @@
+#include
+#include
+
+#include
+#include
+
+#include "helloflipper_icons.h"
+
+typedef struct {
+ uint8_t x, y;
+} ImagePosition;
+
+static ImagePosition image_position = {.x = 0, .y = 0};
+static ImagePosition pixel_position = {.x = 0, .y = 0};
+static bool pixel_visible = false;
+static uint32_t last_blink_time = 0;
+
+// Screen is 128x64 px
+static void app_draw_callback(Canvas* canvas, void* ctx) {
+ UNUSED(ctx);
+
+ canvas_clear(canvas);
+ canvas_draw_icon(canvas, image_position.x % 128, image_position.y % 64, &I_airplane);
+ if (pixel_visible) {
+ canvas_draw_line(canvas, pixel_position.x % 128, pixel_position.y % 64, pixel_position.x % 128, pixel_position.y % 64);
+ }
+}
+
+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 helloflipper_main(void* p) {
+ UNUSED(p);
+ FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
+
+ 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);
+
+ Gui* gui = furi_record_open(RECORD_GUI);
+ gui_add_view_port(gui, view_port, GuiLayerFullscreen);
+
+ InputEvent event;
+
+ 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 InputKeyLeft:
+ image_position.x -= 2;
+ break;
+ case InputKeyRight:
+ image_position.x += 2;
+ break;
+ case InputKeyUp:
+ image_position.y -= 2;
+ break;
+ case InputKeyDown:
+ image_position.y += 2;
+ break;
+ case InputKeyOk:
+ pixel_visible = true;
+ pixel_position.x = image_position.x + 16;
+ pixel_position.y = image_position.y + 16;
+ last_blink_time = xTaskGetTickCount();
+ break;
+ default:
+ running = false;
+ break;
+ }
+ }
+ }
+
+ if (pixel_visible) {
+ uint32_t current_time = xTaskGetTickCount();
+ if (current_time - last_blink_time >= 500) {
+ pixel_visible = !pixel_visible;
+ last_blink_time = current_time;
+ }
+ pixel_position.x += 1;
+ }
+
+ 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;
+}