From f92a94a78afcabb63aa8c6ec8ce825c96a6a0e96 Mon Sep 17 00:00:00 2001 From: Spooks <62370103+Spooks4576@users.noreply.github.com> Date: Tue, 5 Mar 2024 20:17:00 -0700 Subject: [PATCH 01/22] JS: Added Submenu support --- applications/system/js_app/application.fam | 8 + .../js_app/examples/apps/Scripts/submenu.js | 11 ++ .../system/js_app/modules/js_submenu.c | 146 ++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 applications/system/js_app/examples/apps/Scripts/submenu.js create mode 100644 applications/system/js_app/modules/js_submenu.c diff --git a/applications/system/js_app/application.fam b/applications/system/js_app/application.fam index 16963317ef..588ad03fae 100644 --- a/applications/system/js_app/application.fam +++ b/applications/system/js_app/application.fam @@ -62,3 +62,11 @@ App( requires=["js_app"], sources=["modules/js_usbdisk/*.c"], ) + +App( + appid="js_submenu", + apptype=FlipperAppType.PLUGIN, + entry_point="js_submenu_ep", + requires=["js_app"], + sources=["modules/js_submenu.c"], +) diff --git a/applications/system/js_app/examples/apps/Scripts/submenu.js b/applications/system/js_app/examples/apps/Scripts/submenu.js new file mode 100644 index 0000000000..4c0cd79d03 --- /dev/null +++ b/applications/system/js_app/examples/apps/Scripts/submenu.js @@ -0,0 +1,11 @@ +let submenu = require("submenu"); + +submenu.addItem("Item 1", 0); +submenu.addItem("Item 2", 1); +submenu.addItem("Item 3", 2); + +submenu.setHeader("Select an option:"); + +let result = submenu.show(); + +print("Result: ", result); diff --git a/applications/system/js_app/modules/js_submenu.c b/applications/system/js_app/modules/js_submenu.c new file mode 100644 index 0000000000..08c62f404f --- /dev/null +++ b/applications/system/js_app/modules/js_submenu.c @@ -0,0 +1,146 @@ +#include +#include +#include +#include "../js_modules.h" + +typedef struct { + Submenu* submenu; + ViewDispatcher* view_dispatcher; + uint32_t result; +} JsSubmenuInst; + +static JsSubmenuInst* get_this_ctx(struct mjs* mjs) { + mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); + JsSubmenuInst* storage = mjs_get_ptr(mjs, obj_inst); + furi_assert(storage); + return storage; +} + +static void ret_bad_args(struct mjs* mjs, const char* error) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "%s", error); + mjs_return(mjs, MJS_UNDEFINED); +} + +static bool check_arg_count(struct mjs* mjs, size_t count) { + size_t num_args = mjs_nargs(mjs); + if(num_args != count) { + ret_bad_args(mjs, "Wrong argument count"); + return false; + } + return true; +} + +static bool get_str_arg(struct mjs* mjs, size_t index, const char** value) { + mjs_val_t str_obj = mjs_arg(mjs, index); + if(!mjs_is_string(str_obj)) { + ret_bad_args(mjs, "Argument must be a string"); + return false; + } + size_t str_len = 0; + *value = mjs_get_string(mjs, &str_obj, &str_len); + if((str_len == 0) || (*value == NULL)) { + ret_bad_args(mjs, "Bad string argument"); + return false; + } + return true; +} + +static int32_t get_int_arg(struct mjs* mjs, size_t index, int32_t* value) { + mjs_val_t int_obj = mjs_arg(mjs, index); + if(!mjs_is_number(int_obj)) { + ret_bad_args(mjs, "Argument must be a number"); + return false; + } + *value = mjs_get_int32(mjs, int_obj); + return true; +} + +static void submenu_callback(void* context, uint32_t id) { + UNUSED(id); + JsSubmenuInst* submenu = context; + submenu->result = id; + view_dispatcher_stop(submenu->view_dispatcher); +} + +static void js_submenu_add_item(struct mjs* mjs) { + JsSubmenuInst* submenu = get_this_ctx(mjs); + if(!check_arg_count(mjs, 2)) return; + + const char* label; + if(!get_str_arg(mjs, 0, &label)) return; + + int32_t id; + if(!get_int_arg(mjs, 1, &id)) return; + + submenu_add_item(submenu->submenu, label, id, submenu_callback, submenu); + + mjs_return(mjs, MJS_UNDEFINED); +} + +static void js_submenu_set_header(struct mjs* mjs) { + JsSubmenuInst* submenu = get_this_ctx(mjs); + if(!check_arg_count(mjs, 1)) return; + + const char* header; + if(!get_str_arg(mjs, 0, &header)) return; + + submenu_set_header(submenu->submenu, header); + + mjs_return(mjs, MJS_UNDEFINED); +} + +static void js_submenu_show(struct mjs* mjs) { + JsSubmenuInst* submenu = get_this_ctx(mjs); + if(!check_arg_count(mjs, 0)) return; + submenu->result = 0; + + view_dispatcher_attach_to_gui( + submenu->view_dispatcher, furi_record_open(RECORD_GUI), ViewDispatcherTypeFullscreen); + furi_record_close(RECORD_GUI); + + view_dispatcher_switch_to_view(submenu->view_dispatcher, 0); + + view_dispatcher_run(submenu->view_dispatcher); + + submenu_reset(submenu->submenu); + + mjs_return(mjs, mjs_mk_number(mjs, submenu->result)); +} + +static void* js_submenu_create(struct mjs* mjs, mjs_val_t* object) { + JsSubmenuInst* submenu = malloc(sizeof(JsSubmenuInst)); + mjs_val_t submenu_obj = mjs_mk_object(mjs); + mjs_set(mjs, submenu_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, submenu)); + mjs_set(mjs, submenu_obj, "addItem", ~0, MJS_MK_FN(js_submenu_add_item)); + mjs_set(mjs, submenu_obj, "setHeader", ~0, MJS_MK_FN(js_submenu_set_header)); + mjs_set(mjs, submenu_obj, "show", ~0, MJS_MK_FN(js_submenu_show)); + submenu->submenu = submenu_alloc(); + submenu->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(submenu->view_dispatcher); + view_dispatcher_add_view(submenu->view_dispatcher, 0, submenu_get_view(submenu->submenu)); + *object = submenu_obj; + return submenu; +} + +static void js_submenu_destroy(void* inst) { + JsSubmenuInst* submenu = inst; + submenu_free(submenu->submenu); + view_dispatcher_free(submenu->view_dispatcher); + free(submenu); +} + +static const JsModuleDescriptor js_submenu_desc = { + "submenu", + js_submenu_create, + js_submenu_destroy, +}; + +static const FlipperAppPluginDescriptor submenu_plugin_descriptor = { + .appid = PLUGIN_APP_ID, + .ep_api_version = PLUGIN_API_VERSION, + .entry_point = &js_submenu_desc, +}; + +const FlipperAppPluginDescriptor* js_submenu_ep(void) { + return &submenu_plugin_descriptor; +} \ No newline at end of file From ecab4d53d2bd0b9e58503184c4985e94c90cdf20 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Wed, 6 Mar 2024 08:25:21 +0200 Subject: [PATCH 02/22] [FL-870] Auto-generated firmware documentation take two (#2944) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add doxygen and doxygen-awesome css, cleanup docs files * Ignore more libraries and remove leftover local variables * Create an actual intro page * .md files linting * Add doxygen action * Fix Doxygen path * Fix doxyfile path * Try to upload * Change docs branch * Add submudules checkout * Disable doxygen on PR * Mention the firmware docs in the readme * More dev docs mentions in the readme * Fix runner group, add tags * Test dev in PR * Disable running on PR * Fix a typo in the doxyfile * Try upload to S3 * Fix local path * Fix S3 ACL * Add delete flag, unifying dev and tags * Update ignored directories * More ignored directories * Even more ignored directories * Fix submodule * Change S3 uploader * Change S3 uploader version * Fix aws sync flags * Fix ACL * Disable ACL * Improve ignores, add WiFi devboard docs * TEMP: generate dev docs * TEMP: generate 0.89.0 docs * Disabling PR trigger * Enable submodules and test build * Enable test build * Disable test build * Change docs directory structure * Fix accidentally committed submodule * Fix submodules * Update links to the developer documentation * Markdown linting * Update workflow, enable test build * Fix doxygen dir path * Update Doxyfile-awesome.cfg * Change paths * Fix upload docs path * Disable pull_request debug trigger * Disable tags building * Remove autolinks and namespaces * Establish basic documentation structure * Add missing changes * Improve stylesheet, move some files * Improve examples * Improve the main page * Improve application dev docs * Improve system programming docs * Improve development tools docs * Improve other docs * Improve application examples * Fix formatting * Fix PVS-studio warnings * Improve visuals * Fix doxygen syntax warnings * Fix broken links * Update doxygen action Co-authored-by: DrunkBatya Co-authored-by: あく Co-authored-by: Georgii Surkov Co-authored-by: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> --- .github/workflows/docs.yml | 56 ++++ .gitmodules | 5 +- .vscode/ReadMe.md | 2 +- ReadMe.md | 6 +- .../drivers/subghz/cc1101_ext/cc1101_ext.h | 4 +- .../examples/example_apps_assets/README.md | 6 +- .../example_apps_assets/example_apps_assets.c | 4 + .../examples/example_apps_data/README.md | 6 +- .../example_apps_data/example_apps_data.c | 4 + .../example_ble_beacon/ble_beacon_app.h | 4 + .../example_custom_font/example_custom_font.c | 4 + .../examples/example_images/ReadMe.md | 19 +- .../examples/example_images/example_images.c | 4 + .../example_plugins/example_plugins.c | 6 +- .../example_plugins/example_plugins_multi.c | 6 +- .../examples/example_plugins/plugin1.c | 7 +- .../examples/example_plugins/plugin2.c | 7 +- .../example_plugins/plugin_interface.h | 8 +- .../example_plugins_advanced/app_api.h | 9 +- .../example_plugins_advanced/plugin1.c | 5 +- .../example_plugins_advanced/plugin2.c | 5 +- .../plugin_interface.h | 8 +- .../examples/example_thermo/README.md | 10 +- .../examples/example_thermo/example_thermo.c | 5 +- applications/services/gui/canvas.h | 1 - applications/services/gui/canvas_i.h | 2 +- applications/services/gui/elements.h | 3 +- applications/services/gui/modules/dialog_ex.h | 1 - applications/services/gui/scene_manager.h | 1 - applications/services/gui/view.h | 20 +- applications/services/gui/view_stack.h | 4 +- applications/services/storage/storage.h | 6 +- .../services/storage/storage_internal_api.c | 9 +- assets/ReadMe.md | 16 +- assets/dolphin/ReadMe.md | 2 +- documentation/.gitignore | 3 +- documentation/AppManifests.md | 32 +-- documentation/AppsOnSDCard.md | 28 +- documentation/ExpansionModules.md | 2 +- documentation/FuriCheck.md | 2 +- documentation/FuriHalBus.md | 2 +- documentation/FuriHalDebuging.md | 2 +- documentation/HardwareTargets.md | 4 +- documentation/KeyCombo.md | 2 +- documentation/LFRFIDRaw.md | 2 +- documentation/OTA.md | 30 ++- documentation/UnitTests.md | 14 +- documentation/UniversalRemotes.md | 8 +- .../Firmware update on Developer Board.md | 246 ++++++++++++++++++ .../Get started with the Dev Board.md | 175 +++++++++++++ .../Reading logs via the Dev Board.md | 152 +++++++++++ documentation/doxygen/Doxyfile-awesome.cfg | 11 + .../{Doxyfile => doxygen/Doxyfile.cfg} | 90 +++++-- documentation/doxygen/applications.dox | 12 + documentation/doxygen/dev_board.dox | 10 + documentation/doxygen/dev_tools.dox | 9 + documentation/doxygen/doxygen-awesome-css | 1 + documentation/doxygen/examples.dox | 10 + documentation/doxygen/expansion_modules.dox | 8 + documentation/doxygen/favicon.ico | Bin 0 -> 2734 bytes documentation/doxygen/file_formats.dox | 13 + documentation/doxygen/header.html | 84 ++++++ documentation/doxygen/index.dox | 25 ++ documentation/doxygen/logo.png | Bin 0 -> 873 bytes documentation/doxygen/misc.dox | 9 + documentation/doxygen/system.dox | 13 + documentation/fbt.md | 10 +- .../file_formats/BadUsbScriptFormat.md | 26 +- .../file_formats/InfraredFileFormats.md | 27 +- .../file_formats/LfRfidFileFormat.md | 2 +- documentation/file_formats/NfcFileFormats.md | 2 +- .../file_formats/SubGhzFileFormats.md | 30 +-- .../file_formats/iButtonFileFormat.md | 2 +- furi/core/check.h | 10 +- furi/core/kernel.h | 2 +- furi/core/log.h | 9 +- furi/core/memmgr_heap.h | 6 +- furi/core/message_queue.h | 2 - furi/core/string.h | 2 +- furi/core/thread.h | 4 +- lib/flipper_application/elf/elf_file.h | 5 +- lib/nfc/nfc.h | 2 +- lib/nfc/nfc_scanner.h | 6 +- targets/f7/furi_hal/furi_hal_rtc.h | 2 +- targets/furi_hal_include/furi_hal_bt.h | 9 +- targets/furi_hal_include/furi_hal_i2c.h | 4 +- targets/furi_hal_include/furi_hal_infrared.h | 8 +- targets/furi_hal_include/furi_hal_nfc.h | 18 +- targets/furi_hal_include/furi_hal_power.h | 10 +- 89 files changed, 1208 insertions(+), 254 deletions(-) create mode 100644 .github/workflows/docs.yml create mode 100644 documentation/devboard/Firmware update on Developer Board.md create mode 100644 documentation/devboard/Get started with the Dev Board.md create mode 100644 documentation/devboard/Reading logs via the Dev Board.md create mode 100644 documentation/doxygen/Doxyfile-awesome.cfg rename documentation/{Doxyfile => doxygen/Doxyfile.cfg} (98%) create mode 100644 documentation/doxygen/applications.dox create mode 100644 documentation/doxygen/dev_board.dox create mode 100644 documentation/doxygen/dev_tools.dox create mode 160000 documentation/doxygen/doxygen-awesome-css create mode 100644 documentation/doxygen/examples.dox create mode 100644 documentation/doxygen/expansion_modules.dox create mode 100644 documentation/doxygen/favicon.ico create mode 100644 documentation/doxygen/file_formats.dox create mode 100644 documentation/doxygen/header.html create mode 100644 documentation/doxygen/index.dox create mode 100644 documentation/doxygen/logo.png create mode 100644 documentation/doxygen/misc.dox create mode 100644 documentation/doxygen/system.dox diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000000..e6e53ee016 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,56 @@ +name: 'Generate documentation with Doxygen' + +on: + push: + branches: + - dev + +env: + TARGETS: f7 + DEFAULT_TARGET: f7 + +jobs: + doxygen: + if: ${{ !github.event.pull_request.head.repo.fork }} + runs-on: ubuntu-latest + steps: + - name: 'Wipe workspace' + run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; + + - name: 'Checkout code' + uses: actions/checkout@v4 + with: + submodules: true + fetch-depth: 1 + ref: ${{ github.event.pull_request.head.sha }} + + - name: 'Get commit details' + id: names + run: | + if [[ ${{ github.event_name }} == 'pull_request' ]]; then + TYPE="pull" + elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then + TYPE="tag" + else + TYPE="other" + fi + python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" + + - name: 'Generate documentation' + uses: mattnotmitt/doxygen-action@v1.9.8 + with: + working-directory: 'documentation/' + doxyfile-path: './doxygen/Doxyfile-awesome.cfg' + + - name: 'Upload documentation' + uses: jakejarvis/s3-sync-action@v0.5.1 + env: + AWS_S3_BUCKET: "${{ secrets.FW_DOCS_AWS_BUCKET }}" + AWS_ACCESS_KEY_ID: "${{ secrets.FW_DOCS_AWS_ACCESS_KEY }}" + AWS_SECRET_ACCESS_KEY: "${{ secrets.FW_DOCS_AWS_SECRET_KEY }}" + AWS_REGION: "${{ secrets.FW_DOCS_AWS_REGION }}" + SOURCE_DIR: "./documentation/doxygen/build/html" + DEST_DIR: "${{steps.names.outputs.branch_name}}" + with: + args: "--delete" + diff --git a/.gitmodules b/.gitmodules index 52cf4a207b..c4c68a6a77 100644 --- a/.gitmodules +++ b/.gitmodules @@ -37,4 +37,7 @@ url = https://github.com/STMicroelectronics/stm32wbxx_hal_driver [submodule "lib/stm32wb_copro"] path = lib/stm32wb_copro - url = https://github.com/flipperdevices/stm32wb_copro.git + url = https://github.com/flipperdevices/stm32wb_copro.git +[submodule "documentation/doxygen/doxygen-awesome-css"] + path = documentation/doxygen/doxygen-awesome-css + url = https://github.com/jothepro/doxygen-awesome-css.git diff --git a/.vscode/ReadMe.md b/.vscode/ReadMe.md index 5aed0435cb..c7fc69f78b 100644 --- a/.vscode/ReadMe.md +++ b/.vscode/ReadMe.md @@ -1,4 +1,4 @@ -# Visual Studio Code workspace for Flipper Zero +# Visual Studio Code workspace for Flipper Zero {#vscode} ## Setup diff --git a/ReadMe.md b/ReadMe.md index 387ac2de78..34776ebd15 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -11,6 +11,7 @@ - [Flipper Zero Official Website](https://flipperzero.one). A simple way to explain to your friends what Flipper Zero can do. - [Flipper Zero Firmware Update](https://update.flipperzero.one). Improvements for your dolphin: latest firmware releases, upgrade tools for PC and mobile devices. - [User Documentation](https://docs.flipperzero.one). Learn more about your dolphin: specs, usage guides, and anything you want to ask. +- [Developer Documentation](https://developer.flipper.net/flipperzero/doxygen). Dive into the Flipper Zero Firmware source code: build system, firmware structure, and more. # Contributing @@ -18,7 +19,7 @@ Our main goal is to build a healthy and sustainable community around Flipper, so ## I need help -The best place to search for answers is our [User Documentation](https://docs.flipperzero.one). If you can't find the answer there, check our [Discord Server](https://flipp.dev/discord) or our [Forum](https://forum.flipperzero.one/). +The best place to search for answers is our [User Documentation](https://docs.flipperzero.one). If you can't find the answer there, check our [Discord Server](https://flipp.dev/discord) or our [Forum](https://forum.flipperzero.one/). If you want to contribute to the firmware development, or modify it for your own needs, you can also check our [Developer Documentation](https://developer.flipper.net/flipperzero/doxygen). ## I want to report an issue @@ -95,7 +96,8 @@ Make sure your Flipper is on, and your firmware is functioning. Connect your Fli - [Hardware combos and Un-bricking](/documentation/KeyCombo.md) - recovering your Flipper from the most nasty situations - [Flipper File Formats](/documentation/file_formats) - everything about how Flipper stores your data and how you can work with it - [Universal Remotes](/documentation/UniversalRemotes.md) - contributing your infrared remote to the universal remote database -- And much more in the [documentation](/documentation) folder +- [Firmware Roadmap](/documentation/RoadMap.md) +- And much more in the [Developer Documentation](https://developer.flipper.net/flipperzero/doxygen) # Project structure diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext.h b/applications/drivers/subghz/cc1101_ext/cc1101_ext.h index d972fcb661..dffb8a46d9 100644 --- a/applications/drivers/subghz/cc1101_ext/cc1101_ext.h +++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext.h @@ -1,6 +1,6 @@ /** - * @file furi_hal_subghz.h - * SubGhz HAL API + * @file cc1101_ext.h + * @brief External CC1101 transceiver access API. */ #pragma once diff --git a/applications/examples/example_apps_assets/README.md b/applications/examples/example_apps_assets/README.md index 024c0877be..bf7e63e428 100644 --- a/applications/examples/example_apps_assets/README.md +++ b/applications/examples/example_apps_assets/README.md @@ -1,7 +1,11 @@ -# Apps Assets folder Example +# Apps Assets folder Example {#example_app_assets} This example shows how to use the Apps Assets folder to store data that is not part of the application itself, but is required for its operation, and that data is provided with the application. +## Source code + +Source code for this example can be found [here](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/examples/example_apps_assets). + ## What is the Apps Assets Folder? The **Apps Assets** folder is a folder where external applications unpack their assets. diff --git a/applications/examples/example_apps_assets/example_apps_assets.c b/applications/examples/example_apps_assets/example_apps_assets.c index 2c2cc8a874..dae81a8dac 100644 --- a/applications/examples/example_apps_assets/example_apps_assets.c +++ b/applications/examples/example_apps_assets/example_apps_assets.c @@ -1,3 +1,7 @@ +/** + * @file example_apps_assets.c + * @brief Application assets example. + */ #include #include #include diff --git a/applications/examples/example_apps_data/README.md b/applications/examples/example_apps_data/README.md index 0e51daf18c..fb76175470 100644 --- a/applications/examples/example_apps_data/README.md +++ b/applications/examples/example_apps_data/README.md @@ -1,7 +1,11 @@ -# Apps Data folder Example +# Apps Data folder Example {#example_app_data} This example demonstrates how to utilize the Apps Data folder to store data that is not part of the app itself, such as user data, configuration files, and so forth. +## Source code + +Source code for this example can be found [here](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/examples/example_apps_data). + ## What is the Apps Data Folder? The **Apps Data** folder is a folder used to store data for external apps that are not part of the main firmware. diff --git a/applications/examples/example_apps_data/example_apps_data.c b/applications/examples/example_apps_data/example_apps_data.c index 7a297b01cf..f40d526c9a 100644 --- a/applications/examples/example_apps_data/example_apps_data.c +++ b/applications/examples/example_apps_data/example_apps_data.c @@ -1,3 +1,7 @@ +/** + * @file example_apps_data.c + * @brief Application data example. + */ #include #include diff --git a/applications/examples/example_ble_beacon/ble_beacon_app.h b/applications/examples/example_ble_beacon/ble_beacon_app.h index 563bd5beda..61c8c56d1e 100644 --- a/applications/examples/example_ble_beacon/ble_beacon_app.h +++ b/applications/examples/example_ble_beacon/ble_beacon_app.h @@ -1,3 +1,7 @@ +/** + * @file ble_beacon_app.h + * @brief BLE beacon example. + */ #pragma once #include "extra_beacon.h" diff --git a/applications/examples/example_custom_font/example_custom_font.c b/applications/examples/example_custom_font/example_custom_font.c index 15eeb5f02a..2fec419041 100644 --- a/applications/examples/example_custom_font/example_custom_font.c +++ b/applications/examples/example_custom_font/example_custom_font.c @@ -1,3 +1,7 @@ +/** + * @file example_custom_font.c + * @brief Custom font example. + */ #include #include diff --git a/applications/examples/example_images/ReadMe.md b/applications/examples/example_images/ReadMe.md index d884a0a975..bf57950086 100644 --- a/applications/examples/example_images/ReadMe.md +++ b/applications/examples/example_images/ReadMe.md @@ -1,11 +1,21 @@ -# Application icons +# Application icons {#example_app_images} + +## Source code + +Source code for this example can be found [here](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/examples/example_images). + +## General principle + To use icons, do the following: -* add a line to the application manifest: `fap_icon_assets="folder"`, where `folder` points to the folder where your icons are located -* add `#include "application_id_icons.h"` to the application code, where `application_id` is the appid from the manifest -* every icon in the folder will be available as a `I_icon_name` variable, where `icon_name` is the name of the icon file without the extension + +* Add a line to the application manifest: `fap_icon_assets="folder"`, where `folder` points to the folder where your icons are located +* Add `#include "application_id_icons.h"` to the application code, where `application_id` is the appid from the manifest +* Every icon in the folder will be available as a `I_icon_name` variable, where `icon_name` is the name of the icon file without the extension ## Example + We have an application with the following manifest: + ``` App( appid="example_images", @@ -17,6 +27,7 @@ App( So the icons are in the `images` folder and will be available in the generated `example_images_icons.h` file. The example code is located in `example_images_main.c` and contains the following line: + ``` #include "example_images_icons.h" ``` diff --git a/applications/examples/example_images/example_images.c b/applications/examples/example_images/example_images.c index b00818cd66..c43a30b698 100644 --- a/applications/examples/example_images/example_images.c +++ b/applications/examples/example_images/example_images.c @@ -1,3 +1,7 @@ +/** + * @file example_images.c + * @brief Custom images example. + */ #include #include diff --git a/applications/examples/example_plugins/example_plugins.c b/applications/examples/example_plugins/example_plugins.c index 7e71e0d2eb..4f2150884c 100644 --- a/applications/examples/example_plugins/example_plugins.c +++ b/applications/examples/example_plugins/example_plugins.c @@ -1,5 +1,7 @@ -/* - * An example of a plugin host application. +/** + * @file example_plugins.c + * @brief Plugin host application example. + * * Loads a single plugin and calls its methods. */ diff --git a/applications/examples/example_plugins/example_plugins_multi.c b/applications/examples/example_plugins/example_plugins_multi.c index 3525b39ea4..40abff5612 100644 --- a/applications/examples/example_plugins/example_plugins_multi.c +++ b/applications/examples/example_plugins/example_plugins_multi.c @@ -1,5 +1,7 @@ -/* - * An example of an advanced plugin host application. +/** + * @file example_plugins_multi.c + * @brief Advanced plugin host application example. + * * It uses PluginManager to load all plugins from a directory */ diff --git a/applications/examples/example_plugins/plugin1.c b/applications/examples/example_plugins/plugin1.c index 1562193533..de8041f343 100644 --- a/applications/examples/example_plugins/plugin1.c +++ b/applications/examples/example_plugins/plugin1.c @@ -1,4 +1,9 @@ -/* A simple plugin implementing example_plugins application's plugin interface */ +/** + * @file plugin1.c + * @brief Plugin example 1. + * + * A simple plugin implementing example_plugins application's plugin interface + */ #include "plugin_interface.h" diff --git a/applications/examples/example_plugins/plugin2.c b/applications/examples/example_plugins/plugin2.c index 0b774dad21..a196437f4b 100644 --- a/applications/examples/example_plugins/plugin2.c +++ b/applications/examples/example_plugins/plugin2.c @@ -1,4 +1,9 @@ -/* Second plugin implementing example_plugins application's plugin interface */ +/** + * @file plugin2.c + * @brief Plugin example 2. + * + * Second plugin implementing example_plugins application's plugin interface + */ #include "plugin_interface.h" diff --git a/applications/examples/example_plugins/plugin_interface.h b/applications/examples/example_plugins/plugin_interface.h index 25d95d2943..85428429ee 100644 --- a/applications/examples/example_plugins/plugin_interface.h +++ b/applications/examples/example_plugins/plugin_interface.h @@ -1,7 +1,11 @@ +/** + * @file plugin_interface.h + * @brief Example plugin interface. + * + * Common interface between a plugin and host application + */ #pragma once -/* Common interface between a plugin and host application */ - #define PLUGIN_APP_ID "example_plugins" #define PLUGIN_API_VERSION 1 diff --git a/applications/examples/example_plugins_advanced/app_api.h b/applications/examples/example_plugins_advanced/app_api.h index 7035b79f52..60a52e6f76 100644 --- a/applications/examples/example_plugins_advanced/app_api.h +++ b/applications/examples/example_plugins_advanced/app_api.h @@ -1,9 +1,12 @@ -#pragma once - -/* +/** + * @file app_api.h + * @brief Application API example. + * * This file contains an API that is internally implemented by the application * It is also exposed to plugins to allow them to use the application's API. */ +#pragma once + #include #ifdef __cplusplus diff --git a/applications/examples/example_plugins_advanced/plugin1.c b/applications/examples/example_plugins_advanced/plugin1.c index bf0ab50b42..9130810079 100644 --- a/applications/examples/example_plugins_advanced/plugin1.c +++ b/applications/examples/example_plugins_advanced/plugin1.c @@ -1,4 +1,7 @@ -/* +/** + * @file plugin1.c + * @brief Plugin example 1. + * * This plugin uses both firmware's API interface and private application headers. * It can be loaded by a plugin manager that uses CompoundApiInterface, * which combines both interfaces. diff --git a/applications/examples/example_plugins_advanced/plugin2.c b/applications/examples/example_plugins_advanced/plugin2.c index f0b2f726db..1ea5590b22 100644 --- a/applications/examples/example_plugins_advanced/plugin2.c +++ b/applications/examples/example_plugins_advanced/plugin2.c @@ -1,4 +1,7 @@ -/* +/** + * @file plugin2.c + * @brief Plugin example 2. + * * This plugin uses both firmware's API interface and private application headers. * It can be loaded by a plugin manager that uses CompoundApiInterface, * which combines both interfaces. diff --git a/applications/examples/example_plugins_advanced/plugin_interface.h b/applications/examples/example_plugins_advanced/plugin_interface.h index d99b335ff0..d78dc9ecc1 100644 --- a/applications/examples/example_plugins_advanced/plugin_interface.h +++ b/applications/examples/example_plugins_advanced/plugin_interface.h @@ -1,7 +1,11 @@ +/** + * @file plugin_interface.h + * @brief Example plugin interface. + * + * Common interface between a plugin and host application + */ #pragma once -/* Common interface between a plugin and host application */ - #define PLUGIN_APP_ID "example_plugins_advanced" #define PLUGIN_API_VERSION 1 diff --git a/applications/examples/example_thermo/README.md b/applications/examples/example_thermo/README.md index d298de6430..4af2e60432 100644 --- a/applications/examples/example_thermo/README.md +++ b/applications/examples/example_thermo/README.md @@ -1,8 +1,14 @@ -# 1-Wire Thermometer +# 1-Wire Thermometer {#example_thermo} + This example application demonstrates the use of the 1-Wire library with a DS18B20 thermometer. It also covers basic GUI, input handling, threads and localisation. +## Source code + +Source code for this example can be found [here](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/examples/example_thermo). + ## Electrical connections + Before launching the application, connect the sensor to Flipper's external GPIO according to the table below: | DS18B20 | Flipper | | :-----: | :-----: | @@ -15,12 +21,14 @@ Before launching the application, connect the sensor to Flipper's external GPIO *NOTE 2*: For any other pin than 17, connect an external 4.7k pull-up resistor to pin 9. ## Launching the application + In order to launch this demo, follow the steps below: 1. Make sure your Flipper has an SD card installed. 2. Connect your Flipper to the computer via a USB cable. 3. Run `./fbt launch APPSRC=example_thermo` in your terminal emulator of choice. ## Changing the data pin + It is possible to use other GPIO pin as a 1-Wire data pin. In order to change it, set the `THERMO_GPIO_PIN` macro to any of the options listed below: ```c diff --git a/applications/examples/example_thermo/example_thermo.c b/applications/examples/example_thermo/example_thermo.c index 5abd963a19..576ece3826 100644 --- a/applications/examples/example_thermo/example_thermo.c +++ b/applications/examples/example_thermo/example_thermo.c @@ -1,4 +1,7 @@ -/* +/** + * @file example_thermo.c + * @brief 1-Wire thermometer example. + * * This file contains an example application that reads and displays * the temperature from a DS18B20 1-wire thermometer. * diff --git a/applications/services/gui/canvas.h b/applications/services/gui/canvas.h index a369e213bd..b1a1b04be2 100644 --- a/applications/services/gui/canvas.h +++ b/applications/services/gui/canvas.h @@ -239,7 +239,6 @@ void canvas_draw_bitmap( * @param x x coordinate * @param y y coordinate * @param icon Icon instance - * @param flip IconFlip * @param rotation IconRotation */ void canvas_draw_icon_ex( diff --git a/applications/services/gui/canvas_i.h b/applications/services/gui/canvas_i.h index 0982830c98..5d2a38ddf7 100644 --- a/applications/services/gui/canvas_i.h +++ b/applications/services/gui/canvas_i.h @@ -107,7 +107,7 @@ CanvasOrientation canvas_get_orientation(const Canvas* canvas); /** Draw a u8g2 bitmap * - * @param canvas Canvas instance + * @param u8g2 u8g2 instance * @param x x coordinate * @param y y coordinate * @param width width diff --git a/applications/services/gui/elements.h b/applications/services/gui/elements.h index 4853351312..dbaf6d6810 100644 --- a/applications/services/gui/elements.h +++ b/applications/services/gui/elements.h @@ -188,8 +188,7 @@ void elements_bubble(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_ * @param canvas Canvas instance * @param x left x coordinates * @param y top y coordinate - * @param width bubble width - * @param height bubble height + * @param text text to display * @param horizontal horizontal aligning * @param vertical aligning */ diff --git a/applications/services/gui/modules/dialog_ex.h b/applications/services/gui/modules/dialog_ex.h index 26a4653545..91424f78c0 100644 --- a/applications/services/gui/modules/dialog_ex.h +++ b/applications/services/gui/modules/dialog_ex.h @@ -114,7 +114,6 @@ void dialog_ex_set_text( * @param x x position * @param y y position * @param icon The icon - * @param name icon to be shown */ void dialog_ex_set_icon(DialogEx* dialog_ex, uint8_t x, uint8_t y, const Icon* icon); diff --git a/applications/services/gui/scene_manager.h b/applications/services/gui/scene_manager.h index c349a12cea..54dfa9cd47 100644 --- a/applications/services/gui/scene_manager.h +++ b/applications/services/gui/scene_manager.h @@ -108,7 +108,6 @@ bool scene_manager_handle_back_event(SceneManager* scene_manager); * Calls Scene event handler with Tick event parameter * * @param scene_manager SceneManager instance - * @return true if event was consumed, false otherwise */ void scene_manager_handle_tick_event(SceneManager* scene_manager); diff --git a/applications/services/gui/view.h b/applications/services/gui/view.h index 7a2003a63b..eafe445fa1 100644 --- a/applications/services/gui/view.h +++ b/applications/services/gui/view.h @@ -34,44 +34,44 @@ typedef enum { typedef struct View View; /** View Draw callback - * @param canvas, pointer to canvas - * @param view_model, pointer to context + * @param canvas pointer to canvas + * @param model pointer to model * @warning called from GUI thread */ typedef void (*ViewDrawCallback)(Canvas* canvas, void* model); /** View Input callback - * @param event, pointer to input event data - * @param context, pointer to context + * @param event pointer to input event data + * @param context pointer to context * @return true if event handled, false if event ignored * @warning called from GUI thread */ typedef bool (*ViewInputCallback)(InputEvent* event, void* context); /** View Custom callback - * @param event, number of custom event - * @param context, pointer to context + * @param event number of custom event + * @param context pointer to context * @return true if event handled, false if event ignored */ typedef bool (*ViewCustomCallback)(uint32_t event, void* context); /** View navigation callback - * @param context, pointer to context + * @param context pointer to context * @return next view id * @warning called from GUI thread */ typedef uint32_t (*ViewNavigationCallback)(void* context); /** View callback - * @param context, pointer to context + * @param context pointer to context * @warning called from GUI thread */ typedef void (*ViewCallback)(void* context); /** View Update Callback Called upon model change, need to be propagated to GUI * throw ViewPort update - * @param view, pointer to view - * @param context, pointer to context + * @param view pointer to view + * @param context pointer to context * @warning called from GUI thread */ typedef void (*ViewUpdateCallback)(View* view, void* context); diff --git a/applications/services/gui/view_stack.h b/applications/services/gui/view_stack.h index cd62b4f6a0..ed17f682f1 100644 --- a/applications/services/gui/view_stack.h +++ b/applications/services/gui/view_stack.h @@ -44,7 +44,7 @@ View* view_stack_get_view(ViewStack* view_stack); * Adds View on top of ViewStack. * * @param view_stack instance - * @view view view to add + * @param view view to add */ void view_stack_add_view(ViewStack* view_stack, View* view); @@ -52,7 +52,7 @@ void view_stack_add_view(ViewStack* view_stack, View* view); * If no View to remove found - ignore. * * @param view_stack instance - * @view view view to remove + * @param view view to remove */ void view_stack_remove_view(ViewStack* view_stack, View* view); diff --git a/applications/services/storage/storage.h b/applications/services/storage/storage.h index 20a371fc08..eaef59cd6d 100644 --- a/applications/services/storage/storage.h +++ b/applications/services/storage/storage.h @@ -339,7 +339,7 @@ FS_Error storage_common_merge(Storage* storage, const char* old_path, const char * @brief Create a directory. * * @param storage pointer to a storage API instance. - * @param fs_path pointer to a zero-terminated string containing the directory path. + * @param path pointer to a zero-terminated string containing the directory path. * @return FSE_OK if the directory has been successfully created, any other error code on failure. */ FS_Error storage_common_mkdir(Storage* storage, const char* path); @@ -366,7 +366,6 @@ FS_Error storage_common_fs_info( * * @param storage pointer to a storage API instance. * @param path pointer to a zero-terminated string containing the path in question. - * @return true if the path was successfully resolved, false otherwise. */ void storage_common_resolve_path_and_ensure_app_directory(Storage* storage, FuriString* path); @@ -526,7 +525,8 @@ FS_Error storage_int_backup(Storage* storage, const char* dstname); * @param converter pointer to a filename conversion function (may be NULL). * @return FSE_OK if the storage was successfully restored, any other error code on failure. */ -FS_Error storage_int_restore(Storage* api, const char* dstname, Storage_name_converter converter); +FS_Error + storage_int_restore(Storage* storage, const char* dstname, Storage_name_converter converter); /***************** Simplified Functions ******************/ diff --git a/applications/services/storage/storage_internal_api.c b/applications/services/storage/storage_internal_api.c index 6d620b9c07..d91c71c0ec 100644 --- a/applications/services/storage/storage_internal_api.c +++ b/applications/services/storage/storage_internal_api.c @@ -2,8 +2,8 @@ #include "storage.h" #include -FS_Error storage_int_backup(Storage* api, const char* dstname) { - TarArchive* archive = tar_archive_alloc(api); +FS_Error storage_int_backup(Storage* storage, const char* dstname) { + TarArchive* archive = tar_archive_alloc(storage); bool success = tar_archive_open(archive, dstname, TAR_OPEN_MODE_WRITE) && tar_archive_add_dir(archive, STORAGE_INT_PATH_PREFIX, "") && tar_archive_finalize(archive); @@ -11,8 +11,9 @@ FS_Error storage_int_backup(Storage* api, const char* dstname) { return success ? FSE_OK : FSE_INTERNAL; } -FS_Error storage_int_restore(Storage* api, const char* srcname, Storage_name_converter converter) { - TarArchive* archive = tar_archive_alloc(api); +FS_Error + storage_int_restore(Storage* storage, const char* srcname, Storage_name_converter converter) { + TarArchive* archive = tar_archive_alloc(storage); bool success = tar_archive_open(archive, srcname, TAR_OPEN_MODE_READ) && tar_archive_unpack_to(archive, STORAGE_INT_PATH_PREFIX, converter); tar_archive_free(archive); diff --git a/assets/ReadMe.md b/assets/ReadMe.md index 84310e731f..50e25a8c90 100644 --- a/assets/ReadMe.md +++ b/assets/ReadMe.md @@ -1,17 +1,19 @@ -# Requirements +# Firmware Assets {#firmware_assets} + +## Requirements - Python3 - Python3 packages: Pillow & heatshrink2 -# Compiling +## Compiling ```bash ./fbt icons proto dolphin_internal dolphin_blocking dolphin_ext resources ``` -# Asset naming rules +## Asset naming rules -## Images and Animations +### Images and Animations `NAME_VARIANT_SIZE` @@ -22,16 +24,16 @@ Image names will be automatically prefixed with `I_`, animation names with `A_`. Icons and Animations will be gathered into `icon.h` and `icon.c`. -## Dolphin and Games assets +### Dolphin and Games assets Rules are same as for Images and Animations plus assets are grouped by level and level prepends `NAME`. Good starting point: https://docs.unrealengine.com/4.27/en-US/ProductionPipelines/AssetNaming/ -# Important notes +## Important notes Don't include assets that you are not using, compiler is not going to strip unused assets. -# Structure +## Structure - `dolphin` - Dolphin game assets sources. Goes to `compiled` and `resources` folders in `build` directory. - `icons` - Icons sources. Goes to `compiled` folder in `build` directory. - `protobuf` - Protobuf sources. Goes to `compiled` folder in `build` directory. diff --git a/assets/dolphin/ReadMe.md b/assets/dolphin/ReadMe.md index e7572571ca..d47dca0f7c 100644 --- a/assets/dolphin/ReadMe.md +++ b/assets/dolphin/ReadMe.md @@ -1,4 +1,4 @@ -# Dolphin assets +# Dolphin assets {#dolphin_assets} Dolphin assets are split into 3 parts: diff --git a/documentation/.gitignore b/documentation/.gitignore index c18ff03bbb..9c7aebc749 100644 --- a/documentation/.gitignore +++ b/documentation/.gitignore @@ -1,2 +1 @@ -/html -/latex \ No newline at end of file +/doxygen/build \ No newline at end of file diff --git a/documentation/AppManifests.md b/documentation/AppManifests.md index 9afdccb0e4..b612df1b79 100644 --- a/documentation/AppManifests.md +++ b/documentation/AppManifests.md @@ -1,14 +1,14 @@ -# Flipper Application Manifests (.fam) +# Flipper Application Manifests (.fam) {#app_manifests} All components of Flipper Zero firmware — services, user applications, and system settings — are developed independently. Each component has a build system manifest file named `application.fam`, which defines the basic properties of that component and its relations to other parts of the system. -When building firmware, **`fbt`** collects all application manifests and processes their dependencies. Then it builds only those components referenced in the current build configuration. See [FBT docs](./fbt.md#firmware-application-set) for details on build configurations. +When building firmware, `fbt` collects all application manifests and processes their dependencies. Then it builds only those components referenced in the current build configuration. See [FBT docs](fbt.md) for details on build configurations. ## Application definition A firmware component's properties are declared in a Python code snippet, forming a call to the `App()` function with various parameters. -Only two parameters are mandatory: **_appid_** and **_apptype_**. Others are optional and may only be meaningful for certain application types. +Only two parameters are mandatory: **appid** and **apptype**. Others are optional and may only be meaningful for certain application types. ### Parameters @@ -34,7 +34,7 @@ Only two parameters are mandatory: **_appid_** and **_apptype_**. Others are opt - **flags**: internal flags for system apps. Do not use. - **cdefines**: C preprocessor definitions to declare globally for other apps when the current application is included in the active build configuration. **For external applications**: specified definitions are used when building the application itself. - **requires**: list of application IDs to include in the build configuration when the current application is referenced in the list of applications to build. -- **conflicts**: list of application IDs with which the current application conflicts. If any of them is found in the constructed application list, **`fbt`** will abort the firmware build process. +- **conflicts**: list of application IDs with which the current application conflicts. If any of them is found in the constructed application list, `fbt` will abort the firmware build process. - **provides**: functionally identical to **_requires_** field. - **stack_size**: stack size in bytes to allocate for an application on its startup. Note that allocating a stack too small for an app to run will cause a system crash due to stack overflow, and allocating too much stack space will reduce usable heap memory size for apps to process data. _Note: you can use `ps` and `free` CLI commands to profile your app's memory usage._ - **icon**: animated icon name from built-in assets to be used when building the app as a part of the firmware. @@ -55,11 +55,11 @@ The following parameters are used only for [FAPs](./AppsOnSDCard.md): - **fap_description**: string, may be empty. Short application description. - **fap_author**: string, may be empty. Application's author. - **fap_weburl**: string, may be empty. Application's homepage. -- **fap_icon_assets**: string. If present, it defines a folder name to be used for gathering image assets for this application. These images will be preprocessed and built alongside the application. See [FAP assets](./AppsOnSDCard.md#fap-assets) for details. -- **fap_extbuild**: provides support for parts of application sources to be built by external tools. Contains a list of `ExtFile(path="file name", command="shell command")` definitions. **`fbt`** will run the specified command for each file in the list. +- **fap_icon_assets**: string. If present, it defines a folder name to be used for gathering image assets for this application. These images will be preprocessed and built alongside the application. See [FAP assets](AppsOnSDCard.md) for details. +- **fap_extbuild**: provides support for parts of application sources to be built by external tools. Contains a list of `ExtFile(path="file name", command="shell command")` definitions. `fbt` will run the specified command for each file in the list. - **fal_embedded**: boolean, default `False`. Applies only to PLUGIN type. If `True`, the plugin will be embedded into host application's .fap file as a resource and extracted to `apps_assets/APPID` folder on its start. This allows plugins to be distributed as a part of the host application. -Note that commands are executed at the firmware root folder, and all intermediate files must be placed in an application's temporary build folder. For that, you can use pattern expansion by **`fbt`**: `${FAP_WORK_DIR}` will be replaced with the path to the application's temporary build folder, and `${FAP_SRC_DIR}` will be replaced with the path to the application's source folder. You can also use other variables defined internally by **`fbt`**. +Note that commands are executed at the firmware root folder, and all intermediate files must be placed in an application's temporary build folder. For that, you can use pattern expansion by `fbt`: `${FAP_WORK_DIR}` will be replaced with the path to the application's temporary build folder, and `${FAP_SRC_DIR}` will be replaced with the path to the application's source folder. You can also use other variables defined internally by `fbt`. Example for building an app from Rust sources: @@ -77,12 +77,12 @@ Example for building an app from Rust sources: Library sources must be placed in a subfolder of the `lib` folder within the application's source folder. Each library is defined as a call to the `Lib()` function, accepting the following parameters: - - **name**: name of the library's folder. Required. - - **fap_include_paths**: list of the library's relative paths to add to the parent fap's include path list. The default value is `["."]`, meaning the library's source root. - - **sources**: list of filename masks to be used for gathering include files for this library. Paths are relative to the library's source root. The default value is `["*.c*"]`. - - **cflags**: list of additional compiler flags to be used for building this library. The default value is `[]`. - - **cdefines**: list of additional preprocessor definitions to be used for building this library. The default value is `[]`. - - **cincludes**: list of additional include paths to be used for building this library. Paths are relative to the application's root. This can be used for providing external search paths for this library's code — for configuration headers. The default value is `[]`. + - **name**: name of the library's folder. Required. + - **fap_include_paths**: list of the library's relative paths to add to the parent fap's include path list. The default value is `["."]`, meaning the library's source root. + - **sources**: list of filename masks to be used for gathering include files for this library. Paths are relative to the library's source root. The default value is `["*.c*"]`. + - **cflags**: list of additional compiler flags to be used for building this library. The default value is `[]`. + - **cdefines**: list of additional preprocessor definitions to be used for building this library. The default value is `[]`. + - **cincludes**: list of additional include paths to be used for building this library. Paths are relative to the application's root. This can be used for providing external search paths for this library's code — for configuration headers. The default value is `[]`. Example for building an app with a private library: @@ -105,12 +105,12 @@ Example for building an app with a private library: ], ``` -For that snippet, **`fbt`** will build 2 libraries: one from sources in `lib/mbedtls` folder and another from sources in the `lib/loclass` folder. For the `mbedtls` library, **`fbt`** will add `lib/mbedtls/include` to the list of include paths for the application and compile only the files specified in the `sources` list. Additionally, **`fbt`** will enable `MBEDTLS_ERROR_C` preprocessor definition for `mbedtls` sources. -For the `loclass` library, **`fbt`** will add `lib/loclass` to the list of the include paths for the application and build all sources in that folder. Also, **`fbt`** will disable treating compiler warnings as errors for the `loclass` library, which can be useful when compiling large 3rd-party codebases. +For that snippet, `fbt` will build 2 libraries: one from sources in `lib/mbedtls` folder and another from sources in the `lib/loclass` folder. For the `mbedtls` library, `fbt` will add `lib/mbedtls/include` to the list of include paths for the application and compile only the files specified in the `sources` list. Additionally, `fbt` will enable `MBEDTLS_ERROR_C` preprocessor definition for `mbedtls` sources. +For the `loclass` library, `fbt` will add `lib/loclass` to the list of the include paths for the application and build all sources in that folder. Also, `fbt` will disable treating compiler warnings as errors for the `loclass` library, which can be useful when compiling large 3rd-party codebases. Both libraries will be linked with the application. -## `.fam` file contents +## .fam file contents The `.fam` file contains one or more application definitions. For example, here's a part of `applications/service/bt/application.fam`: diff --git a/documentation/AppsOnSDCard.md b/documentation/AppsOnSDCard.md index 3f6d51acf0..cb8106fc6c 100644 --- a/documentation/AppsOnSDCard.md +++ b/documentation/AppsOnSDCard.md @@ -1,4 +1,4 @@ -# FAP (Flipper Application Package) +# FAP (Flipper Application Package) {#apps_on_sd_card} [fbt](./fbt.md) supports building applications as FAP files. FAPs are essentially `.elf` executables with extra metadata and resources bundled in. @@ -6,7 +6,7 @@ FAPs are built with the `faps` target. They can also be deployed to the `dist` f FAPs do not depend on being run on a specific firmware version. Compatibility is determined by the FAP's metadata, which includes the required [API version](#api-versioning). -## How to set up an application to be built as a FAP +## How to set up an application to be built as a FAP {#fap-howto} FAPs are created and developed the same way as internal applications that are part of the firmware. @@ -21,15 +21,15 @@ To build your application as a FAP, create a folder with your app's source code FAPs can include static and animated images as private assets. They will be automatically compiled alongside application sources and can be referenced the same way as assets from the main firmware. -To use that feature, put your images in a subfolder inside your application's folder, then reference that folder in your application's manifest in the `fap_icon_assets` field. See [Application Manifests](./AppManifests.md#application-definition) for more details. +To use that feature, put your images in a subfolder inside your application's folder, then reference that folder in your application's manifest in the `fap_icon_assets` field. See [Application Manifests](AppManifests.md) for more details. To use these assets in your application, put `#include "{APPID}_icons.h"` in your application's source code, where `{APPID}` is the `appid` value field from your application's manifest. Then you can use all icons from your application's assets the same way as if they were a part of `assets_icons.h` of the main firmware. -Images and animated icons should follow the same [naming convention](../assets/ReadMe.md#asset-naming-rules) as those from the main firmware. +Images and animated icons should follow the same [naming convention](../assets/ReadMe.md) as those from the main firmware. ## Debugging FAPs -**`fbt`** includes a script for gdb-py to provide debugging support for FAPs, `debug/flipperapps.py`. It is loaded in default debugging configurations by **`fbt`** and stock VS Code configurations. +`fbt` includes a script for gdb-py to provide debugging support for FAPs, `debug/flipperapps.py`. It is loaded in default debugging configurations by `fbt` and stock VS Code configurations. With it, you can debug FAPs as if they were a part of the main firmware — inspect variables, set breakpoints, step through the code, etc. @@ -43,7 +43,7 @@ To debug FAPs, do the following: 1. Build firmware with `./fbt` 2. Flash it with `./fbt flash` -3. [Build your FAP](#how-to-set-up-an-application-to-be-built-as-a-fap) and run it on Flipper +3. [Build your FAP](#fap-howto) and run it on Flipper After that, you can attach with `./fbt debug` or VS Code and use all debug features. @@ -59,25 +59,25 @@ Applications are built for a specific API version. It is a part of the hardware The App Loader allocates memory for the application and copies it to RAM, processing relocations and providing concrete addresses for imported symbols using the [symbol table](#symbol-table). Then it starts the application. -## API versioning +## API versioning {#api-versioning} Not all parts of firmware are available for external applications. A subset of available functions and variables is defined in the "api_symbols.csv" file, which is a part of the firmware target definition in the `targets/` directory. -**`fbt`** uses semantic versioning for the API. The major version is incremented when there are breaking changes in the API. The minor version is incremented when new features are added. +`fbt` uses semantic versioning for the API. The major version is incremented when there are breaking changes in the API. The minor version is incremented when new features are added. Breaking changes include: - Removing a function or a global variable - Changing the signature of a function -API versioning is mostly automated by **`fbt`**. When rebuilding the firmware, **`fbt`** checks if there are any changes in the API exposed by headers gathered from `SDK_HEADERS`. If so, it stops the build, adjusts the API version, and asks the user to go through the changes in the `.csv` file. New entries are marked with a "`?`" mark, and the user is supposed to change the mark to "`+`" for the entry to be exposed for FAPs, or to "`-`" for it to be unavailable. +API versioning is mostly automated by `fbt`. When rebuilding the firmware, `fbt` checks if there are any changes in the API exposed by headers gathered from `SDK_HEADERS`. If so, it stops the build, adjusts the API version, and asks the user to go through the changes in the `.csv` file. New entries are marked with a "`?`" mark, and the user is supposed to change the mark to "`+`" for the entry to be exposed for FAPs, or to "`-`" for it to be unavailable. -**`fbt`** will not allow building a firmware until all "`?`" entries are changed to "`+`" or "`-`". +`fbt` will not allow building a firmware until all "`?`" entries are changed to "`+`" or "`-`". -**NB:** **`fbt`** automatically manages the API version. The only case where manually incrementing the major API version is allowed (and required) is when existing "`+`" entries are to be changed to "`-`". +**NB:** `fbt` automatically manages the API version. The only case where manually incrementing the major API version is allowed (and required) is when existing "`+`" entries are to be changed to "`-`". -### Symbol table +### Symbol table {#symbol-table} -The symbol table is a list of symbols exported by firmware and available for external applications. It is generated by **`fbt`** from the API symbols file and is used by the App Loader to resolve addresses of imported symbols. It is build as a part of the `fap_loader` application. +The symbol table is a list of symbols exported by firmware and available for external applications. It is generated by `fbt` from the API symbols file and is used by the App Loader to resolve addresses of imported symbols. It is build as a part of the `fap_loader` application. -**`fbt`** also checks if all imported symbols are present in the symbol table. If there are any missing symbols, it will issue a warning listing them. The application won't be able to run on the device until all required symbols are provided in the symbol table. +`fbt` also checks if all imported symbols are present in the symbol table. If there are any missing symbols, it will issue a warning listing them. The application won't be able to run on the device until all required symbols are provided in the symbol table. diff --git a/documentation/ExpansionModules.md b/documentation/ExpansionModules.md index c757c0d2b4..470564e574 100644 --- a/documentation/ExpansionModules.md +++ b/documentation/ExpansionModules.md @@ -1,4 +1,4 @@ -# Expansion Module Protocol - Draft +# Expansion Module Protocol {#expansion_protocol} ## Terms and definitions diff --git a/documentation/FuriCheck.md b/documentation/FuriCheck.md index 02f3fc9173..77a44ca84b 100644 --- a/documentation/FuriCheck.md +++ b/documentation/FuriCheck.md @@ -1,4 +1,4 @@ -# Run time checks and forced system crash +# Run time checks and forced system crash {#furi_check} The best way to protect system integrity is to reduce amount cases that we must handle and crash the system as early as possible. For that purpose we have bunch of helpers located in Furi Core check.h. diff --git a/documentation/FuriHalBus.md b/documentation/FuriHalBus.md index 7880c041f6..12c5a70ece 100644 --- a/documentation/FuriHalBus.md +++ b/documentation/FuriHalBus.md @@ -1,4 +1,4 @@ -# Using FuriHalBus API +# Using FuriHalBus API {#furi_hal_bus} ## Basic info diff --git a/documentation/FuriHalDebuging.md b/documentation/FuriHalDebuging.md index da00cbdfb7..5104a99982 100644 --- a/documentation/FuriHalDebuging.md +++ b/documentation/FuriHalDebuging.md @@ -1,4 +1,4 @@ -# Furi HAL Debugging +# Furi HAL Debugging {#furi_hal_debugging} Some Furi subsystems got additional debugging features that can be enabled by adding additional defines to firmware compilation. Usually they are used for low level tracing and profiling or signal redirection/duplication. diff --git a/documentation/HardwareTargets.md b/documentation/HardwareTargets.md index b3213d4f50..9c36088eac 100644 --- a/documentation/HardwareTargets.md +++ b/documentation/HardwareTargets.md @@ -1,4 +1,4 @@ -## What a Firmware Target is +## What a Firmware Target is {#hardware_targets} Flipper's firmware is modular and supports different hardware configurations in a common code base. It encapsulates hardware-specific differences in `furi_hal`, board initialization code, linker files, SDK data and other information in a _target definition_. @@ -29,7 +29,7 @@ A target definition file, `target.json`, is a JSON file that can contain the fol Not all applications are available on different hardware targets. -* For applications built into the firmware, you have to specify a compatible application set using `FIRMWARE_APP_SET=...` fbt option. See [fbt docs](./fbt.md#firmware-application-set) for details on build configurations. +* For applications built into the firmware, you have to specify a compatible application set using `FIRMWARE_APP_SET=...` fbt option. See [fbt docs](./fbt.md) for details on build configurations. * For applications built as external .faps, you have to explicitly specify compatible targets in application's manifest, `application.fam`. For example, to limit application to a single target, add `targets=["f7"],` to the manifest. It won't be built for other targets. diff --git a/documentation/KeyCombo.md b/documentation/KeyCombo.md index 6db5b41135..e3c5e00043 100644 --- a/documentation/KeyCombo.md +++ b/documentation/KeyCombo.md @@ -1,4 +1,4 @@ -# Key Combos +# Key Combos {#key_combos} There are times when your Flipper feels blue and doesn't respond to any of your commands due to a software issue. This guide will help you solve this problem. diff --git a/documentation/LFRFIDRaw.md b/documentation/LFRFIDRaw.md index 5a8cbde60d..526b9a3cba 100644 --- a/documentation/LFRFIDRaw.md +++ b/documentation/LFRFIDRaw.md @@ -1,4 +1,4 @@ -# Reading RAW RFID data +# Reading RAW RFID data {#lfrfid_raw} Flipper Zero has the option to read RAW data from 125 kHz cards that allows you to record the card's data and save it, similar to how a dictaphone records sound. diff --git a/documentation/OTA.md b/documentation/OTA.md index ed75560cfe..9028eff714 100644 --- a/documentation/OTA.md +++ b/documentation/OTA.md @@ -1,20 +1,22 @@ -# Executing code from RAM +# Flipper Zero OTA update process {#ota_updates} + +## Executing code from RAM In Flipper firmware, we have a special boot mode that loads a specially crafted system image into RAM and transfers control to it. System image executing in RAM has full write access to Flipper's entire flash memory — something that's not possible when running main code from the same flash. We leverage that boot mode to perform OTA firmware updates, including operations on a radio stack running on the second MCU core. -# How does Flipper OTA work? +## How does Flipper OTA work? Installation of OTA updates goes through 3 stages: -## 1. Backing up internal storage (`/int`) +### 1. Backing up internal storage (/int) It is a special partition of Flipper's flash memory, taking up all available space not used by the firmware code. Newer versions of firmware may be of different size, and simply installing them would cause flash repartitioning and data loss. So, before taking any action on the firmware, we back up the current configuration from `/int` into a plain tar archive on the SD card. -## 2. Performing device update +### 2. Performing device update The main firmware loads an updater image — a customized build of the main Flipper firmware — into RAM and runs it. Updater performs operations on system flash as described by an Update manifest file. @@ -24,17 +26,17 @@ Then, updater validates and corrects Option Bytes — a special memory region co After that, updater loads a `.dfu` file with firmware to be flashed, checks its integrity using CRC32, writes it to system flash and validates written data. -## 3. Restoring internal storage and updating resources +### 3. Restoring internal storage and updating resources After performing operations on flash memory, the system restarts into newly flashed firmware. Then it performs restoration of previously backed up `/int` contents. If the update package contains an additional resources archive, it is extracted onto the SD card. -# Update manifest +## Update manifest An update package comes with a manifest that contains a description of its contents. The manifest is in Flipper File Format — a simple text file, comprised of key-value pairs. -## Mandatory fields +### Mandatory fields An update manifest must contain the following keys in the given order: @@ -50,7 +52,7 @@ An update manifest must contain the following keys in the given order: - **Loader CRC**: CRC32 of loader file. Note that it is represented in little-endian hex. -## Optional fields +### Optional fields Other fields may have empty values. In this case, updater skips all operations related to these values. @@ -66,7 +68,7 @@ Other fields may have empty values. In this case, updater skips all operations r - **OB reference**, **OB mask**, **OB write mask**: reference values for validating and correcting option bytes. -# OTA update error codes +## OTA update error codes We designed the OTA update process to be as fail-safe as possible. We don't start any risky operations before validating all related pieces of data to ensure we don't leave the device in a partially updated, or bricked, state. @@ -102,21 +104,21 @@ Even if something goes wrong, updater allows you to retry failed operations and | Restoring LFS | **12** | **0-100** | FS read/write error | | Updating resources | **13** | **0-100** | SD card read/write error | -# Building update packages +## Building update packages -## Full package +### Full package To build a full update package, including firmware, radio stack and resources for the SD card, run: `./fbt COMPACT=1 DEBUG=0 updater_package` -## Minimal package +### Minimal package To build a minimal update package, including only firmware, run: `./fbt COMPACT=1 DEBUG=0 updater_minpackage` -## Customizing update bundles +### Customizing update bundles Default update packages are built with Bluetooth Light stack. You can pick a different stack if your firmware version supports it, and build a bundle with it by passing the stack type and binary name to `fbt`: @@ -127,7 +129,7 @@ Note that `COPRO_OB_DATA` must point to a valid file in the `scripts` folder con In certain cases, you might have to confirm your intentions by adding `COPRO_DISCLAIMER=...` to the build command line. -## Building partial update packages +### Building partial update packages You can customize package contents by calling `scripts/update.py` directly. For example, to build a package only for installing BLE FULL stack: diff --git a/documentation/UnitTests.md b/documentation/UnitTests.md index 9352917cdc..b77cd56c6e 100644 --- a/documentation/UnitTests.md +++ b/documentation/UnitTests.md @@ -1,11 +1,11 @@ -# Unit tests +# Unit tests {#unit_tests} ## Intro Unit tests are special pieces of code that apply known inputs to the feature code and check the results to see if they are correct. They are crucial for writing robust, bug-free code. -Flipper Zero firmware includes a separate application called [unit_tests](/applications/debug/unit_tests). +Flipper Zero firmware includes a separate application called [unit_tests](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/debug/unit_tests). It is run directly on Flipper devices in order to employ their hardware features and rule out any platform-related differences. When contributing code to the Flipper Zero firmware, it is highly desirable to supply unit tests along with the proposed features. @@ -20,7 +20,7 @@ To run the unit tests, follow these steps: 3. Launch the CLI session and run the `unit_tests` command. **NOTE:** To run a particular test (and skip all others), specify its name as the command argument. -See [test_index.c](/applications/debug/unit_tests/test_index.c) for the complete list of test names. +See [test_index.c](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/debug/unit_tests/test_index.c) for the complete list of test names. ## Adding unit tests @@ -28,11 +28,11 @@ See [test_index.c](/applications/debug/unit_tests/test_index.c) for the complete #### Entry point -The common entry point for all tests is the [unit_tests](/applications/debug/unit_tests) application. Test-specific code is placed into an arbitrarily named subdirectory and is then called from the [test_index.c](/applications/debug/unit_tests/test_index.c) source file. +The common entry point for all tests is the [unit_tests](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/debug/unit_tests) application. Test-specific code is placed into an arbitrarily named subdirectory and is then called from the [test_index.c](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/debug/unit_tests/test_index.c) source file. #### Test assets -Some unit tests require external data in order to function. These files (commonly called assets) reside in the [unit_tests](/applications/debug/unit_tests/resources/unit_tests) directory in their respective subdirectories. Asset files can be of any type (plain text, FlipperFormat (FFF), binary, etc.). +Some unit tests require external data in order to function. These files (commonly called assets) reside in the [unit_tests](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/debug/unit_tests/resources/unit_tests) directory in their respective subdirectories. Asset files can be of any type (plain text, FlipperFormat (FFF), binary, etc.). ### Application-specific @@ -41,9 +41,9 @@ Some unit tests require external data in order to function. These files (commonl Each infrared protocol has a corresponding set of unit tests, so it makes sense to implement one when adding support for a new protocol. To add unit tests for your protocol, follow these steps: -1. Create a file named `test_.irtest` in the [assets](/applications/debug/unit_tests/resources/unit_tests/infrared) directory. +1. Create a file named `test_.irtest` in the [assets](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/debug/unit_tests/resources/unit_tests/infrared) directory. 2. Fill it with the test data (more on it below). -3. Add the test code to [infrared_test.c](/applications/debug/unit_tests/infrared/infrared_test.c). +3. Add the test code to [infrared_test.c](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/debug/unit_tests/infrared/infrared_test.c). 4. Build and install firmware with resources, install it on your Flipper and run the tests to see if they pass. ##### Test data format diff --git a/documentation/UniversalRemotes.md b/documentation/UniversalRemotes.md index 213709afbf..360d8a0abb 100644 --- a/documentation/UniversalRemotes.md +++ b/documentation/UniversalRemotes.md @@ -1,4 +1,4 @@ -# Universal Remotes +# Universal Remotes {#universal_remotes} ## Televisions @@ -13,7 +13,7 @@ Each signal is recorded using the following algorithm: The signal names are self-explanatory. Remember to make sure that every recorded signal does what it's supposed to. -If everything checks out, append these signals **to the end** of the [TV universal remote file](/applications/main/infrared/resources/infrared/assets/tv.ir). +If everything checks out, append these signals **to the end** of the [TV universal remote file](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/main/infrared/resources/infrared/assets/tv.ir). ## Audio players @@ -23,7 +23,7 @@ The signal names are self-explanatory. On many remotes, the `Play` button doubles as `Pause`. In this case, record it as `Play` omitting the `Pause`. Make sure that every signal does what it's supposed to. -If everything checks out, append these signals **to the end** of the [audio player universal remote file](/applications/main/infrared/resources/infrared/assets/audio.ir). +If everything checks out, append these signals **to the end** of the [audio player universal remote file](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/main/infrared/resources/infrared/assets/audio.ir). ## Projectors @@ -67,7 +67,7 @@ Finally, record the `Off` signal: The resulting remote file should now contain 6 signals. You can omit any of them, but you then won't be able to use their functionality. Test the file against the actual device. Make sure that every signal does what it's supposed to. -If everything checks out, append these signals **to the end** of the [A/C universal remote file](/applications/main/infrared/resources/infrared/assets/ac.ir). +If everything checks out, append these signals **to the end** of the [A/C universal remote file](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/main/infrared/resources/infrared/assets/ac.ir). ## Final steps diff --git a/documentation/devboard/Firmware update on Developer Board.md b/documentation/devboard/Firmware update on Developer Board.md new file mode 100644 index 0000000000..c62c653444 --- /dev/null +++ b/documentation/devboard/Firmware update on Developer Board.md @@ -0,0 +1,246 @@ +# Firmware update on Developer Board {#dev_board_fw_update} + +It's important to regularly update your Developer Board to keep it up to date. This tutorial will guide you through the necessary steps to successfully update the firmware of your Developer Board. + +This tutorial assumes that you're familiar with the basics of the command line. If you’re unfamiliar with the command line, please refer to the [Windows](https://www.digitalcitizen.life/command-prompt-how-use-basic-commands/) or [MacOS/Linux](https://ubuntu.com/tutorials/command-line-for-beginners#1-overview) command line tutorials. + +*** + +## Downloading the latest firmware + +The first thing you need to do is to download the latest Developer Board firmware. + +To get the latest pre-built firmware, do the following: + +1. Go to the [Update Server page](https://update.flipperzero.one/builds/blackmagic-firmware). +![The Update Server page hosts different versions of the Developer Board firmware](https://archbee-image-uploads.s3.amazonaws.com/3StCFqarJkJQZV-7N79yY/gIXVO9VrE4LK05CmcMSSD_monosnap-miro-2023-07-19-17-36-23.jpg) + + There, you can find the following version of the Developer Board firmware: + + * **Release:** The most stable version of the firmware, which went through rigorous testing. The Release firmware version has the following format: **X.Y.Z/**, where X, Y, and Z are the build numbers. We recommend installing this version of the firmware. + + * **Release-candidate:** The firmware version that hasn't been tested yet and may contain bugs. The Release-candidate firmware version has the following format: **X.Y.Z-rc/**, where X, Y, and Z are the build numbers. + + * **Development:** The firmware version which builds every day and contains the latest features but might be unstable. + +2. Open the folder with the latest Release firmware and download the `blackmagic-firmware-s2-full-X.Y.Z.tgz` file. + +*** + +## Extracting the firmware + +After downloading the firmware archive, extract it into a folder: + +* On Windows, you can use any archive manager for this, for example, [7-Zip](https://www.7-zip.org/). + +* On MacOS and Linux, you can use the `tar` command: + + ```text + tar -xzf blackmagic-firmware-s2-full-X.Y.Z.tgz -C + ``` + +Don't forget to replace `X.Y.Z` with the actual version number and set the destination directory! + +*** + +## Installing the prerequisites for flashing + +Install the tools below if you haven't already. + +### Python + +Download and install [Python3](https://www.python.org/downloads/). Make sure to check the “Add Python to PATH” option during installation. + +### pip + +To install the pip package manager, run the following command in the Terminal: + +```text +python3 -m ensurepip --upgrade +``` + +If this command fails, please refer to the [official pip documentation](https://pip.pypa.io/en/stable/installation/) for alternative installation methods. + +### esptool + +esptool is a command-line utility for flashing ESP8266 and ESP32 microcontrollers, including the ESP32-S2 in your Developer Board. + +To install esptool, run the following command in the Terminal: + +```text +pip3 install esptool +``` + +If this command fails, try using **pip** instead of **pip3**. If this didn’t help, please refer to the [official esptool installation manual](https://docs.espressif.com/projects/esptool/en/latest/esp32/installation.html). + +*** + +## Connecting the Developer Board to your computer + +1. List all of the serial devices on your computer. + + * ***Windows*** + + On Windows, go to Device Manager and expand the Ports (COM & LPT) section. + + * ***macOS*** + + On macOS, you can run the following command in the Terminal: + + ```text + ls /dev/cu.* + ``` + + * ***Linux*** + + On Linux, you can run the following command in the Terminal: + + ```text + ls /dev/tty* + ``` + + View the devices in the list. + +2. Connect the Developer Board to your computer using a USB-C cable.\ +![The Developer Board in Wired mode](https://archbee-image-uploads.s3.amazonaws.com/3StCFqarJkJQZV-7N79yY/Aq7gfMI-m_5H6sGGjwb4I_monosnap-miro-2023-07-19-19-47-39.jpg) + +3. Switch your Developer Board to Bootloader mode: + + 3.1. Press and hold the **BOOT** button. + + 3.2. Press the **RESET** button while holding the **BOOT** button. + + 3.3. Release the **BOOT** button. +![You can easily switch the Dev Board to Bootloader mode](https://archbee-image-uploads.s3.amazonaws.com/3StCFqarJkJQZV-7N79yY/KynP9iT6sJ3mXLaLyI82__image.png) + +4. Repeat Step 1 and view the name of your Developer Board that appeared in the list. + + For example, on macOS: + + ```text + /dev/cu.usbmodem01 + ``` + +*** + +## Flashing the firmware + +### Getting the flash command + +1. Run the Terminal and navigate to the folder with the extracted firmware. + +2. Run the following command to read the file with the flash command: + + ```text + cat flash.command + ``` + + If you see a similar output, you can proceed to the Flashing step: + + ```text + esptool.py -p (PORT) -b 460800 --before default_reset --after hard_reset --chip esp32s2 write_flash --flash_mode dio --flash_freq 80m --flash_size 4MB 0x1000 bootloader.bin 0x10000 blackmagic.bin 0x8000 partition-table.bin + ``` + + Don't use the exact command above for your Developer Board in the next step since it's just an example and may not match your firmware version! + + If you get an error, ensure you’re in the correct directory and extracted the firmware archive correctly. + +*** + +### Flashing + +1. Copy the command you got from the previous step and replace the `(PORT)` part with the name of the serial device you learned earlier. + + For Windows, replace `(PORT)` with the COM port number—for example, `COM3`. + +2. Run the command in the Terminal. + + Your command should look similar to this: + + ```text + esptool.py -p /dev/cu.usbmodem01 -b 460800 --before default_reset --after hard_reset --chip esp32s2 write_flash --flash_mode dio --flash_freq 80m --flash_size 4MB 0x1000 bootloader.bin 0x10000 blackmagic.bin 0x8000 partition-table.bin + ``` + + If you get an error, ensure that you’ve entered the correct serial device name and that the Developer Board is in Bootloader mode. + +3. Wait till the firmware flashing is over. The flashing process takes about 30 seconds. + + The Terminal output should look similar to this: + + ```text + esptool.py v4.6.1 + Serial port /dev/cu.usbmodem01 + Connecting... + Chip is ESP32-S2 (revision v0.0) + Features: WiFi, No Embedded Flash, No Embedded PSRAM, ADC and temperature sensor + calibration in BLK2 of efuse V2 + Crystal is 40MHz + MAC: 00:11:22:33:44:55 + Uploading stub... + Running stub... + Stub running... + Changing baud rate to 460800 + Changed. + Configuring flash size... + Flash will be erased from 0x00001000 to 0x00004fff... + Flash will be erased from 0x00010000 to 0x000ecfff... + Flash will be erased from 0x00008000 to 0x00008fff... + Compressed 13248 bytes to 9298... + Wrote 13248 bytes (9298 compressed) at 0x00001000 in 0.3 seconds (effective 402.7 kbit/s)... + Hash of data verified. + Compressed 904288 bytes to 562550... + Wrote 904288 bytes (562550 compressed) at 0x00010000 in 6.7 seconds (effective 1076.5 kbit/s)... + Hash of data verified. + Compressed 3072 bytes to 124... + Wrote 3072 bytes (124 compressed) at 0x00008000 in 0.1 seconds (effective 360.8 kbit/s)... + Hash of data verified. + Leaving... + Hard resetting via RTS pin... + ``` + + If the Terminal output has these two lines at the end, your Developer Board has been successfully updated: + + ```text + Leaving... + Hard resetting via RTS pin... + ``` + + If you get this warning, you can safely ignore it: + + ```text + WARNING: ESP32-S2 (revision v0.0) chip was placed into download mode using GPIO0. + esptool.py can not exit the download mode over USB. To run the app, reset the chip manually. + To suppress this note, set --after option to 'no_reset + ``` + +#### If flashing failed + +If you get an error message during the flashing process, such as: + +```text +A fatal error occurred: Serial data stream stopped: Possible serial noise or corruption. +``` + +or + +```text +FileNotFoundError: [Errno 2] No such file or directory: '/dev/cu.usbmodem01' +``` + +Try doing the following: + +* Disconnect the Developer Board from your computer, then reconnect it. + +* Use a different USB port on your computer. + +* Use a different USB-C cable. + +*** + +## Finishing the installation + +After flashing the firmware, you can reboot the Developer Board by pressing the **RESET** button. + +![Reset the Developer Board](https://archbee-image-uploads.s3.amazonaws.com/3StCFqarJkJQZV-7N79yY/rcQeKARgrVwa51tLoo-qY_monosnap-miro-2023-07-20-18-29-33.jpg) + +The Developer Board should appear as a serial device on your computer. Now, you can use it with the Black Magic Debug client of your choice. diff --git a/documentation/devboard/Get started with the Dev Board.md b/documentation/devboard/Get started with the Dev Board.md new file mode 100644 index 0000000000..eb230663e5 --- /dev/null +++ b/documentation/devboard/Get started with the Dev Board.md @@ -0,0 +1,175 @@ +# Get started with the Dev Board {#dev_board_get_started} + +The Wi-Fi Developer Board serves as a tool to debug the Flipper Zero firmware. To debug the firmware, the initial step involves compiling the firmware from its source code. This process enables the debugging functionality within the firmware and generates all the necessary files required for debugging purposes. + +> **NOTE:** Building and debugging the Flipper Zero firmware is fully supported on MacOS and Linux. Support for Windows is in beta test. + +*** + +## Updating the firmware of your Developer Board + +Update the firmware of your Developer Board before using it. For more information, visit [Firmware update on Developer Board](https://docs.flipperzero.one/development/hardware/wifi-debugger-module/update). + +*** + +## Installing Git + +You’ll need Git installed on your computer to clone the firmware repository. If you don’t have Git, install it by doing the following: + +* **MacOS** + + On MacOS, install the **Xcode Command Line Tools** package, which includes Git as one of the pre-installed command-line utilities, by running in the Terminal the following command: + + ```text + xcode-select --install + ``` + +* **Linux** + + On Linux, you can install Git using your package manager. For example, on Ubuntu, run in the Terminal the following command: + + ```text + sudo apt install git + ``` + +For other distributions, refer to your package manager documentation. + +*** + +## Building the firmware + +First, clone the firmware repository: + +```text +git clone --recursive https://github.com/flipperdevices/flipperzero-firmware.git +cd flipperzero-firmware +``` + +Then, run the **Flipper Build Tool** (FBT) to build the firmware: + +```text +./fbt +``` + +*** + +## Connecting the Developer Board + +The Developer Board can work in the **Wired** mode and two **Wireless** modes: **Wi-Fi access point (AP)** mode and **Wi-Fi client (STA)** mode. The Wired mode is the simplest to set up, but requires a USB Type-C cable. The Wireless modes are more complex to set up, but they allow you to debug your Flipper Zero wirelessly. + +> **NOTE:** Use the following credentials when connecting to the Developer Board in **Wi-Fi access point** mode: Name: **blackmagic**, Password: **iamwitcher** + +## Wired + +![The Developer Board in Wired mode](https://archbee-image-uploads.s3.amazonaws.com/3StCFqarJkJQZV-7N79yY/jZdVlRTPVdSQVegzCyXp7_monosnap-miro-2023-06-22-16-28-06.jpg) + +To connect the Developer Board in **Wired** mode, do the following: + +1. Cold-plug the Developer Board by turning off your Flipper Zero and connecting the Developer Board, and then turning it back on. + +2. On your computer, open the **Terminal** and run the following: + + * **MacOS** + + ```text + ls /dev/cu.* + ``` + + * **Linux** + + ```text + ls /dev/tty* + ``` + + Note the list of devices. + +3. Connect the Developer Board to your computer via a USB-C cable. + +4. Rerun the command. Two new devices have to appear: this is the Developer Board. + +> **NOTE:** If the Developer Board doesn’t appear in the list of devices, try using a different cable, USB port, or computer. +> +> **NOTE:** Flipper Zero logs can only be viewed when the Developer Board is connected via USB. The option to view logs over Wi-Fi will be added in future updates. For more information, visit [Reading logs via the Dev Board](https://docs.flipperzero.one/development/hardware/wifi-debugger-module/reading-logs). + +## Wireless + +### Wi-Fi access point (AP) mode + +![The Developer Board in Wi-Fi access point mode](https://archbee-image-uploads.s3.amazonaws.com/3StCFqarJkJQZV-7N79yY/tKRTMHAuruiLSEce2a8Ve_monosnap-miro-2023-06-22-16-39-17.jpg) + +Out of the box, the Developer Board is configured to work as a **Wi-Fi access point**. This means it will create its own Wi-Fi network to which you can connect. If your Developer Board doesn’t create a Wi-Fi network, it is probably configured to work in **Wi-Fi client** mode. To reset your Developer Board back to **Wi-Fi access point** mode, press and hold the **BOOT** button for 10 seconds, then wait for the module to reboot. + +![You can reconfigure the Developer Board mode by pressing and holding the BOOT button](https://archbee-image-uploads.s3.amazonaws.com/3StCFqarJkJQZV-7N79yY/57eELJsAwMxeZCEA1NMJw_monosnap-miro-2023-06-22-20-33-27.jpg) + +To connect the Developer Board in **Wi-Fi access point** mode, do the following: + +1. Cold-plug the Developer Board by turning off your Flipper Zero and connecting the Developer Board, and then turning it back on. + +2. Open Wi-Fi settings on your client device (phone, laptop, or other). + +3. Connect to the network: + + * Name: **blackmagic** + * Password: **iamwitcher** + +4. To configure the Developer Board, open a browser and go to `http://192.168.4.1`. + +#### Wi-Fi client (STA) mode + +![The Developer Board in Wi-Fi client mode](https://archbee-image-uploads.s3.amazonaws.com/3StCFqarJkJQZV-7N79yY/xLQpFyYPfUS5Cx0uQhrNd_monosnap-miro-2023-06-23-12-34-36.jpg) + +To connect the Developer Board in **Wi-Fi client** mode, you need to configure it to connect to your Wi-Fi network by doing the following: + +1. Cold-plug the Developer Board by turning off your Flipper Zero and connecting the Developer Board, and then turning it back on. + +2. Connect to the Developer Board in **Wi-Fi access point** mode. + +3. In a browser, go to the configuration page on `http://192.168.4.1`. + +4. Select the **STA** mode and enter your network’s **SSID** (name) and **password**. For convenience, you can click the **+** button to see the list of nearby networks. + +5. Save the configuration and reboot the Developer Board. + +![In the Wi-Fi tab, you can set the Developer Board mode](https://archbee-image-uploads.s3.amazonaws.com/3StCFqarJkJQZV-7N79yY/klbLVj8lz2bEvm7j4wRaj_monosnap-miro-2023-06-23-13-06-32.jpg) + +After rebooting, the Developer Board connects to your Wi-Fi network. You can connect to the device using the mDNS name [blackmagic.local](http://blackmagic.local) or the IP address it got from your router (you’ll have to figure this out yourself, every router is different). + +After connecting to your debugger via [blackmagic.local](http://blackmagic.local), you can find its IP address in the **SYS** tab. You can also change the debugger’s mode to **AP** or **STA** there. + +![In the SYS tab, you can view the IP address of your Developer Board](https://archbee-image-uploads.s3.amazonaws.com/3StCFqarJkJQZV-7N79yY/5XbUptlfqzlV0p6hRUqiG_monosnap-miro-2023-06-22-18-11-30.jpg) + +*** + +## Debugging the firmware + +Open the **Terminal** in the **flipperzero-firmware** directory that you cloned earlier and run the following command: + +```text +./fbt flash_blackmagic +``` + +This will upload the firmware you’ve just built to your Flipper Zero via the Developer Board. After that, you can start debugging the firmware using the [GDB](https://www.gnu.org/software/gdb/) debugger. We recommend using **VSCode** with the recommended extensions, and we have pre-made configurations for it. + +To debug in **VSCode**, do the following: + +1. In VSCode, open the **flipperzero-firmware** directory. + +2. You should see a notification about recommended extensions. Install them. + + If there were no notifications, open the **Extensions** tab, enter `@recommended` in the search bar, and install the workspace recommendations. + +3. In the **Terminal**, run the `./fbt vscode_dist` command. This will generate the VSCode configuration files needed for debugging. + +4. In VSCode, open the **Run and Debug** tab and select **Attach FW (blackmagic)** from the dropdown menu. + +5. If needed, flash your Flipper Zero with the `./fbt flash_blackmagic` command, then click the **Play** button in the debug sidebar to start the debugging session. + +6. Note that starting a debug session halts the execution of the firmware, so you’ll need to click the **Continue** button on the toolbar at the top of your VSCode window to continue execution. + +![Click Continue in the toolbar to continue execution of the firmware](https://archbee-image-uploads.s3.amazonaws.com/3StCFqarJkJQZV-7N79yY/lp8ygGaZ3DvWD3OSI9yGO_monosnap-miro-2023-06-23-17-58-09.jpg) + +To learn about debugging, visit the following pages: + +* [Debugging with GDB](https://sourceware.org/gdb/current/onlinedocs/gdb.pdf) + +* [Debugging in VS Code](https://code.visualstudio.com/docs/editor/debugging) diff --git a/documentation/devboard/Reading logs via the Dev Board.md b/documentation/devboard/Reading logs via the Dev Board.md new file mode 100644 index 0000000000..112e59a19b --- /dev/null +++ b/documentation/devboard/Reading logs via the Dev Board.md @@ -0,0 +1,152 @@ +# Reading logs via the Dev Board {#dev_board_reading_logs} + +The Developer Board allows you to read Flipper Zero logs via UART. Unlike reading logs via the command-line interface (CLI), the Developer Board enables you to collect logs from the device directly to a serial console independently from the operating system of Flipper Zero. It allows you to see the device's logs when it's loading, updating, or crashing. It's useful for debugging and troubleshooting during software development. + +> **NOTE:** Flipper Zero logs can only be viewed when the developer board is connected via USB. The option to view logs over Wi-Fi will be added in future updates. + +## Setting the log level + +Depending on your needs, you can set the log level by going to Main Menu -> Settings -> Log Level. To learn more about logging levels, visit [Settings](https://docs.flipperzero.one/basics/settings#d5TAt). + +![You can manually set the preferred log level](https://archbee-image-uploads.s3.amazonaws.com/3StCFqarJkJQZV-7N79yY/INzQMw8QUsG9PXi30WFS0_monosnap-miro-2023-07-11-13-29-47.jpg) + +*** + +## Viewing Flipper Zero logs + +Depending on your operating system, you need to install an additional application on your computer to read logs via the Developer Board: + +### MacOS + +On MacOS, you need to install the **minicom** communication program by doing the following: + +1. [Install Homebrew](https://brew.sh/) by running in the Terminal the following command: + + ```text + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + ``` + +2. After installation of Homebrew, run the following command to install minicom: + + ```text + brew install minicom + ``` + +After installation of minicom on your macOS computer, you can connect to the Developer Board to read Flipper Zero logs by doing the following: + +1. Cold-plug the Developer Board into your Flipper Zero by turning off the Flipper Zero, connecting the developer board, and then turning it back on. + +2. On your computer, open the Terminal and run the following command: + + ```text + ls /dev/cu.* + ``` + + Note the list of devices. + +3. Connect the developer board to your computer using a USB Type-C cable.\ +![The Developer Board in Wired mode](https://archbee-image-uploads.s3.amazonaws.com/3StCFqarJkJQZV-7N79yY/iPpsMt2-is4aIjiVeFu5t_hjxs2i1oovrnps74v5jgsimage.png) + +4. Rerun the command. Two new devices have to appear: this is the Developer Board. + + ```text + /dev/cu.usbmodemblackmagic1 + /dev/cu.usbmodemblackmagic3 + ``` + + Your Developer Board might have different names. + +5. Run the following command: + + ```text + minicom -D /dev/ -b 230400 + ``` + + Where `` is the name of your device with a bigger number. + + Example: + + ```text + minicom -D /dev/cu.usbmodemblackmagic3 -b 230400 + ``` + +6. View logs of your Flipper Zero in the Terminal. + +7. To quit, close the minicom window or quit via the minicom menu. + +### Linux + +On Linux, you need to install the **minicom** communication program. For example, on Ubuntu, run in the Terminal the following command: + +```text +sudo apt install minicom +``` + +After installation of minicom on your Linux computer, you can connect to the Developer Board to read Flipper Zero logs by doing the following: + +1. Cold-plug the Developer Board into your Flipper Zero by turning off the Flipper Zero, connecting the developer board, and then turning it back on. + +2. On your computer, open the Terminal and run the following command: + + ```text + ls /dev/tty* + ``` + + Note the list of devices. + +3. Connect the developer board to your computer using a USB Type-C cable. +![The Developer Board in Wired mode](https://archbee-image-uploads.s3.amazonaws.com/3StCFqarJkJQZV-7N79yY/iPpsMt2-is4aIjiVeFu5t_hjxs2i1oovrnps74v5jgsimage.png) + +4. Rerun the command. Two new devices have to appear: this is the Developer Board. + + ```text + /dev/ttyACM0 + /dev/ttyACM1 + ``` + + Your Developer Board might have different names. + +5. Run the following command: + + ```text + minicom -D /dev/ \-b 230400 + ``` + + Where `` is the name of your device with a bigger number. + + Example: + + ```text + minicom -D /dev/ttyACM1 \-b 230400 + ``` + +6. View logs of your Flipper Zero in the Terminal. + + > **NOTE:** If no logs are shown in the Terminal, try running the command from Step 5 with another device name. + +7. To quit, close the minicom window or quit via the minicom menu. + +### Windows + +On Windows, do the following: + +1. On your computer, [install the PuTTY application](https://www.chiark.greenend.org.uk/\~sgtatham/putty/latest.html). + +2. Cold-plug the Developer Board into your Flipper Zero by turning off the Flipper Zero, connecting the developer board, and then turning it back on. + +3. Connect the developer board to your computer using a USB Type-C cable. +![The Developer Board in Wired mode](https://archbee-image-uploads.s3.amazonaws.com/3StCFqarJkJQZV-7N79yY/iPpsMt2-is4aIjiVeFu5t_hjxs2i1oovrnps74v5jgsimage.png) + +4. Find the serial port that the developer board is connected to by going to **Device Manager -> Ports (COM & LPT)** and looking for a new port that appears when you connect the Wi-Fi developer board. +![Go to Device Manager -> Ports (COM & LPT)](https://archbee-image-uploads.s3.amazonaws.com/3StCFqarJkJQZV-7N79yY/KKLQJK1lvqmI5iab3d__C_image.png) + +5. Run the PuTTY application and select **Serial** as the connection type. + +6. Enter the port number you found in the previous step into the **Serial line** field. + +7. Set the **Speed** parameter to **230400** and click **Open**. +![Set the required parameters](https://archbee-image-uploads.s3.amazonaws.com/3StCFqarJkJQZV-7N79yY/ROBSJyfQ_CXiy4GUZcPbs_monosnap-miro-2023-07-12-13-56-47.jpg) + +8. View logs of your Flipper Zero in the PuTTY terminal window. + +9. To quit, close the PuTTY window. diff --git a/documentation/doxygen/Doxyfile-awesome.cfg b/documentation/doxygen/Doxyfile-awesome.cfg new file mode 100644 index 0000000000..e4c4c95cd7 --- /dev/null +++ b/documentation/doxygen/Doxyfile-awesome.cfg @@ -0,0 +1,11 @@ +@INCLUDE = doxygen/Doxyfile.cfg +GENERATE_TREEVIEW = YES # required! +DISABLE_INDEX = NO +FULL_SIDEBAR = NO +HTML_EXTRA_STYLESHEET = doxygen/doxygen-awesome-css/doxygen-awesome.css \ + doxygen/doxygen-awesome-css/doxygen-awesome-sidebar-only.css \ + doxygen/doxygen-awesome-css/doxygen-awesome-sidebar-only-darkmode-toggle.css +HTML_COLORSTYLE = LIGHT # required with Doxygen >= 1.9.5 +HTML_HEADER = doxygen/header.html +HTML_EXTRA_FILES = doxygen/doxygen-awesome-css/doxygen-awesome-darkmode-toggle.js + diff --git a/documentation/Doxyfile b/documentation/doxygen/Doxyfile.cfg similarity index 98% rename from documentation/Doxyfile rename to documentation/doxygen/Doxyfile.cfg index f31cbb9d86..28ac19e029 100644 --- a/documentation/Doxyfile +++ b/documentation/doxygen/Doxyfile.cfg @@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "FlipperZero Firmware" +PROJECT_NAME = "Flipper Zero Firmware" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version @@ -51,14 +51,19 @@ PROJECT_BRIEF = # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. -PROJECT_LOGO = +PROJECT_LOGO = doxygen/logo.png + +# With the PROJECT_ICON tag one can specify an icon that is included in the tabs +# when the HTML document is shown. Doxygen will copy the logo to the output directory. + +PROJECT_ICON = doxygen/favicon.ico # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = documentation +OUTPUT_DIRECTORY = doxygen/build # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and @@ -150,7 +155,7 @@ INLINE_INHERITED_MEMB = NO # shortest path that makes the file name unique will be used # The default value is: YES. -FULL_PATH_NAMES = YES +FULL_PATH_NAMES = NO # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand @@ -162,7 +167,7 @@ FULL_PATH_NAMES = YES # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. -STRIP_FROM_PATH = +STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which @@ -171,7 +176,7 @@ STRIP_FROM_PATH = # specify the list of include paths that are normally passed to the compiler # using the -I flag. -STRIP_FROM_INC_PATH = +STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't @@ -347,7 +352,7 @@ TOC_INCLUDE_HEADINGS = 5 # globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. -AUTOLINK_SUPPORT = YES +AUTOLINK_SUPPORT = NO # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this @@ -463,7 +468,7 @@ LOOKUP_CACHE_SIZE = 0 # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. -NUM_PROC_THREADS = 1 +NUM_PROC_THREADS = 4 #--------------------------------------------------------------------------- # Build related configuration options @@ -748,7 +753,7 @@ SHOW_FILES = YES # Folder Tree View (if specified). # The default value is: YES. -SHOW_NAMESPACES = YES +SHOW_NAMESPACES = NO # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from @@ -871,10 +876,13 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = applications \ - lib \ - firmware \ - furi +INPUT = ../applications \ + ../documentation \ + ../targets \ + ../assets \ + ../lib \ + ../furi \ + ../.vscode \ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -912,7 +920,9 @@ FILE_PATTERNS = *.c \ *.hh \ *.hxx \ *.hpp \ - *.h++ + *.h++ \ + *.md \ + *.dox # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. @@ -928,18 +938,46 @@ RECURSIVE = YES # run. EXCLUDE = \ - lib/mlib \ - lib/stm32wb_cmsis \ - lib/stm32wb_copro \ - lib/stm32wb_hal_driver \ - lib/littlefs \ - lib/nanopb \ - assets/protobuf \ - lib/libusb_stm32 \ - lib/FreeRTOS-Kernel \ - lib/microtar \ - lib/mbedtls \ - lib/cxxheaderparser + ../lib/mlib \ + ../lib/STM32CubeWB \ + ../lib/littlefs \ + ../lib/nanopb \ + ../assets/protobuf \ + ../lib/libusb_stm32 \ + ../lib/FreeRTOS-Kernel \ + ../lib/microtar \ + ../lib/mbedtls \ + ../lib/cxxheaderparser \ + ../lib/ST25RFAL002 \ + ../lib/fatfs \ + ../lib/mlib \ + ../lib/stm32wb_cmsis \ + ../lib/stm32wb_copro \ + ../lib/stm32wb_hal_driver \ + ../lib/stm32wb_hal \ + ../lib/cmsis_core \ + ../targets/f7/fatfs/ \ + ../applications/plugins/dap_link/lib/free-dap \ + ../applications/debug \ + ../applications/main \ + ../applications/settings \ + ../lib/micro-ecc \ + ../lib/ReadMe.md \ + ../lib/callback-connector \ + ../lib/app-scened-template \ + ../applications/ReadMe.md \ + ../targets/ReadMe.md \ + ../web \ + ../assets/protobuf \ + ../lib/libusb_stm32 \ + ../lib/FreeRTOS-Kernel \ + ../lib/microtar \ + ../lib/mbedtls \ + ../lib/cxxheaderparser \ + ../applications/external/dap_link/lib/free-dap \ + ../lib/heatshrink \ + ./doxygen/doxygen-awesome-css + # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded diff --git a/documentation/doxygen/applications.dox b/documentation/doxygen/applications.dox new file mode 100644 index 0000000000..ad0dfba8d8 --- /dev/null +++ b/documentation/doxygen/applications.dox @@ -0,0 +1,12 @@ +/** +@page applications Application Programming + +Flipper Zero features full support for custom applications which (usually) do not require any changes to the firmware. + +For easy application development, a software tool called [uFBT](https://github.com/flipperdevices/flipperzero-ufbt) is available. + +- @subpage vscode - Flipper Zero integration for VS Code +- @subpage apps_on_sd_card - Creating apps that can be dynamically loaded from the SD card +- @subpage app_manifests - How applications announce themselves to the system +- @subpage app_examples - Various application examples, complete with the source code +*/ diff --git a/documentation/doxygen/dev_board.dox b/documentation/doxygen/dev_board.dox new file mode 100644 index 0000000000..f9363ed069 --- /dev/null +++ b/documentation/doxygen/dev_board.dox @@ -0,0 +1,10 @@ +/** +@page dev_board Developer Board + +[ESP32-based development board](https://shop.flipperzero.one/collections/flipper-zero-accessories/products/wifi-devboard). + +- @subpage dev_board_get_started - Quick start for new users +- @subpage dev_board_reading_logs - Find out what is currently happening on the system +- @subpage dev_board_fw_update - Keep the developer board up to date + +*/ diff --git a/documentation/doxygen/dev_tools.dox b/documentation/doxygen/dev_tools.dox new file mode 100644 index 0000000000..bd7a5c704f --- /dev/null +++ b/documentation/doxygen/dev_tools.dox @@ -0,0 +1,9 @@ +/** +@page dev_tools Developer Tools + +Hardware and software tools for all kinds of programming. + +- @subpage fbt - Official build and deployment tool for Flipper Zero +- @subpage dev_board - ESP32-based development board +- @subpage ota_updates - Standalone firmware self-update mechanism +*/ diff --git a/documentation/doxygen/doxygen-awesome-css b/documentation/doxygen/doxygen-awesome-css new file mode 160000 index 0000000000..df88fe4fdd --- /dev/null +++ b/documentation/doxygen/doxygen-awesome-css @@ -0,0 +1 @@ +Subproject commit df88fe4fdd97714fadfd3ef17de0b4401f804052 diff --git a/documentation/doxygen/examples.dox b/documentation/doxygen/examples.dox new file mode 100644 index 0000000000..9743549a2d --- /dev/null +++ b/documentation/doxygen/examples.dox @@ -0,0 +1,10 @@ +/** +@page app_examples Application Examples + +A collection of examples covering various aspects of application programming for Flipper Zero. + +- @subpage example_app_images - Using images and icons in an application +- @subpage example_app_assets - Using application-specific asset folders +- @subpage example_app_data - Using application-specific data folders +- @subpage example_thermo - Reading data from a 1-Wire thermometer +*/ diff --git a/documentation/doxygen/expansion_modules.dox b/documentation/doxygen/expansion_modules.dox new file mode 100644 index 0000000000..c38bb2923f --- /dev/null +++ b/documentation/doxygen/expansion_modules.dox @@ -0,0 +1,8 @@ +/** +@page expansion Expansion Modules + +Expansion modules are special pieces of hardware designed to interface with Flipper's GPIO connector, such as the [Video Game Module](https://shop.flipperzero.one/collections/flipper-zero-accessories/products/video-game-module-for-flipper-zero). + +- @subpage expansion_protocol - Transport protocol for smart expansion modules + +*/ diff --git a/documentation/doxygen/favicon.ico b/documentation/doxygen/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..3fbdd33f20fdf6e7db8049f78c7eda63a67fe3fa GIT binary patch literal 2734 zcmeH}J(3ea429M57F$I$eFC~B+Hj+di8uyD$T55diUav54mkpu`JU9>9vfqzD2PyP zTP^A7r=^~mH7mBWWm(`4_EgMX%&fJPzqr|ZbkENze|%;3?!oK{%px31e)s70=aZdI zr+bhe+g7Mmb8)LnFsoI3ZI&F%+K|lK>b^E=_J)g<3U6%SZa@lqcUz4>-kP@xd@W0< zI!vv#u%JbI$PPQRJ?H;jl8=1Vr^b5A`dOdqr$6XZb|~6XbQX`TewvPBQ1Tl)nhUQZ$#UU^ik<_a+0nlC;{GG`L z#he&k!y#14Pb6TPH6a+@kV64LGS`B^LMscg2pB5>IKZ8IBY^IS@DOR% zmHUF0nz(SKW<`P_Vha)=;3=x-WLIq>fi$K8?mTQ^8oT5ZnBvA$3dFBwZ;ob<2eaKC zw3huP8u1V0I+9blb_FkIcD( z{^GqA4tnj#8I9AP$c=&i_c(C&!=Sl1OK(M4_)JTRdErL=*j@H&Lk|z7%ata8()%!p zgbKt>NQ!WnR6neDADbth@hJCz&nJfVfo?sD*dh;|2Zu9U1`3dEjBxtM08RPc<|5)5 z$cOwUjqmxj@w*xlph|8;Le0C5x1HHh{E=_G_>TV(#ChWmF7ZG8dI$D`Zu)nSOka=U I^m+9C8=LaGd;kCd literal 0 HcmV?d00001 diff --git a/documentation/doxygen/file_formats.dox b/documentation/doxygen/file_formats.dox new file mode 100644 index 0000000000..47c2362cf7 --- /dev/null +++ b/documentation/doxygen/file_formats.dox @@ -0,0 +1,13 @@ +/** +@page file_formats File Formats + +Descriptions of various file formats used in Flipper Zero, grouped by applications that use them. + +- @subpage badusb_file_format +- @subpage ibutton_file_format +- @subpage infrared_file_format +- @subpage lfrfid_file_format +- @subpage nfc_file_format +- @subpage subghz_file_format + +*/ diff --git a/documentation/doxygen/header.html b/documentation/doxygen/header.html new file mode 100644 index 0000000000..cd3ea49e75 --- /dev/null +++ b/documentation/doxygen/header.html @@ -0,0 +1,84 @@ + + + + + + + + +$projectname: $title +$title + + + + + + + + + + + + + + +$treeview +$search +$mathjax +$darkmode + +$extrastylesheet + + + + + + +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
$projectname $projectnumber +
+
$projectbrief
+
+
$projectbrief
+
$searchbox
$searchbox
+
+ + diff --git a/documentation/doxygen/index.dox b/documentation/doxygen/index.dox new file mode 100644 index 0000000000..6fb4d5a717 --- /dev/null +++ b/documentation/doxygen/index.dox @@ -0,0 +1,25 @@ +/** +@mainpage Overview + +Welcome to the Flipper Zero Firmware Developer Documentation! + +This documentation is intended for developers who want to modify the firmware of the Flipper Zero. + +If you are looking for the user manual, please visit the [User Documentation](https://docs.flipperzero.one/) instead. + +The documentation is divided into several sections, with all of them accessible from the sidebar on the left: + +- @ref applications - Writing applications for Flipper Zero +- @ref system - Understanding the firmware's internals +- @ref file_formats - Saving and loading data to and from files +- @ref dev_tools - Hardware and software tools for all kinds of programming +- @ref expansion - Additional modules to expand Flipper's consciousness +- @ref misc - Various useful pieces of information + +Aside from the manually-written documentation files, there's also a few automatically-generated ones at the bottom of the sidebar: + +- [Data Structures](annotated.html) - Every data structure in a list +- [Files](files.html) - Source file tree with easy navigation + +These are generated from the source code and are useful for quickly finding the source code or API documentation for a particular function or data structure. +*/ diff --git a/documentation/doxygen/logo.png b/documentation/doxygen/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a38cdab4d6448d06e9d3576bcdbfaaa9e912aa7b GIT binary patch literal 873 zcmV-v1D5=WP)EX>4Tx04R}tkv&MmKpe$iKcpfR1uKX+WT;LSMMWI73Pq?8YK2xEOfLO`CJjl7 zi=*ILaPVWX>fqw6tAnc`2!4P#J2)x2NQwVT3N2zhIPS;0dyl(!fWKa5su>&yRLwF{ ziMWu-t_q=71Q0|o`Y|RkQ=b#XBs|C0J$!tC`-NgjguFvE0V2XsEe3JSuIyt^Pc>L;heUz%ypVWNMI355FtQD6(y8mAx5i4iis5M$36Umj$b5~Os*0b zITlcX3d!+<|H1EW&HUtqn-q=%9WS>1F${!ufkw@?zmILZaRLOMfh(=$uhfB=Ptt2G zEqVm>Yy%h9Elu77E_Z-|CtWfmM+(sN=kvh(8GTb0=(`2F*4*Bj`#607($rP*1~@nb zMhcX@?(y!<_TK(I)9mjDS44800{@mh00006VoOIv0RI600RN!9r;`8x010qNS#tmY z3ljhU3ljkVnw%H_000McNliru=mP{0CJ(Vj;|%}+02y>eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{00BWsL_t(o!^Kxi7Q-M26rXp^nLI6Br1lX60X5c5Ka$Qc znIRRDu&^dfFD$qcbl;r-9ua}l;G6{f1Duj5&cflsx?byPd@{|G#QZq zTw0is39^=aaf&4$J~?8$_#7ET;ka$~(cEHzFR%VC5!YOzB^;buP#|Z^6v; z1adLHVM6s8@I9JkQVhr9C>>6jl=y}U(((KQ($Fa@gMO#X00000NkvXXu0mjfI;w3s literal 0 HcmV?d00001 diff --git a/documentation/doxygen/misc.dox b/documentation/doxygen/misc.dox new file mode 100644 index 0000000000..0ef232ba24 --- /dev/null +++ b/documentation/doxygen/misc.dox @@ -0,0 +1,9 @@ +/** +@page misc Miscellaneous + +Various pieces of information that do not fall into other categories. + +- @subpage lfrfid_raw - Collecting raw data from LFRFID tags +- @subpage key_combos - Different key combination shortcuts for Flipper Zero +- @subpage universal_remotes - Creating and improving IR universal remote libraries +*/ diff --git a/documentation/doxygen/system.dox b/documentation/doxygen/system.dox new file mode 100644 index 0000000000..328717ea21 --- /dev/null +++ b/documentation/doxygen/system.dox @@ -0,0 +1,13 @@ +/** +@page system System Programming + +Lower level aspects of software development for Flipper Zero. + +- @subpage unit_tests - Automated testing, a crucial part of the development process +- @subpage furi_check - Hard checks for exceptional situations +- @subpage furi_hal_bus - Access the on-chip peripherals in a safe way +- @subpage furi_hal_debugging - Low level debugging features +- @subpage hardware_targets - Support for different hardware platforms +- @subpage firmware_assets - Various files required for building the firmware +- @subpage dolphin_assets - Animations for the Dolphin game +*/ diff --git a/documentation/fbt.md b/documentation/fbt.md index 0220178a2d..a7df5615b9 100644 --- a/documentation/fbt.md +++ b/documentation/fbt.md @@ -1,4 +1,4 @@ -# Flipper Build Tool +# Flipper Build Tool {#fbt} FBT is the entry point for firmware-related commands and utilities. It is invoked by `./fbt` in the firmware project root directory. Internally, it is a wrapper around [scons](https://scons.org/) build system. @@ -58,7 +58,7 @@ To use language servers other than the default VS Code C/C++ language server, us ## FBT targets -**`fbt`** keeps track of internal dependencies, so you only need to build the highest-level target you need, and **`fbt`** will make sure everything they depend on is up-to-date. +`fbt` keeps track of internal dependencies, so you only need to build the highest-level target you need, and `fbt` will make sure everything they depend on is up-to-date. ### High-level (what you most likely need) @@ -83,8 +83,8 @@ To use language servers other than the default VS Code C/C++ language server, us ### Firmware targets -- `faps` - build all external & plugin apps as [`.faps`](./AppsOnSDCard.md#fap-flipper-application-package). -- **`fbt`** also defines per-app targets. For example, for an app with `appid=snake_game` target names are: +- `faps` - build all external & plugin apps as [`.faps`](AppsOnSDCard.md). +- `fbt` also defines per-app targets. For example, for an app with `appid=snake_game` target names are: - `fap_snake_game`, etc. - build single app as `.fap` by its application ID. - Check out [`--extra-ext-apps`](#command-line-parameters) for force adding extra apps to external build. - `fap_snake_game_list`, etc - generate source + assembler listing for app's `.fap`. @@ -103,7 +103,7 @@ To use language servers other than the default VS Code C/C++ language server, us - `proto_ver` - generate `.h` with a protobuf version - `dolphin_internal`, `dolphin_blocking` - generate `.c+.h` for corresponding dolphin assets -## Command-line parameters +## Command-line parameters {#command-line-parameters} - `--options optionfile.py` (default value `fbt_options.py`) - load a file with multiple configuration values - `--extra-int-apps=app1,app2,appN` - force listed apps to be built as internal with the `firmware` target diff --git a/documentation/file_formats/BadUsbScriptFormat.md b/documentation/file_formats/BadUsbScriptFormat.md index 1eb8eb5180..3bda306172 100644 --- a/documentation/file_formats/BadUsbScriptFormat.md +++ b/documentation/file_formats/BadUsbScriptFormat.md @@ -1,21 +1,23 @@ -# Command syntax +# BadUSB File Format {#badusb_file_format} + +## Command syntax BadUsb app uses extended Duckyscript syntax. It is compatible with classic USB Rubber Ducky 1.0 scripts but provides some additional commands and features, such as custom USB ID, ALT+Numpad input method, SYSRQ command, and more functional keys. -# Script file format +## Script file format BadUsb app can execute only text scripts from `.txt` files, no compilation is required. Both `\n` and `\r\n` line endings are supported. Empty lines are allowed. You can use spaces or tabs for line indentation. -# Command set +## Command set -## Comment line +### Comment line Just a single comment line. The interpreter will ignore all text after the REM command. | Command | Parameters | Notes | | ------- | ------------ | ----- | | REM | Comment text | | -## Delay +### Delay Pause script execution by a defined time. | Command | Parameters | Notes | @@ -24,7 +26,7 @@ Pause script execution by a defined time. | DEFAULT_DELAY | Delay value in ms | Add delay before every next command | | DEFAULTDELAY | Delay value in ms | Same as DEFAULT_DELAY | -## Special keys +### Special keys | Command | Notes | | ------------------ | ---------------- | @@ -53,7 +55,7 @@ Pause script execution by a defined time. | APP | Same as MENU | | Fx | F1-F12 keys | -## Modifier keys +### Modifier keys Can be combined with a special key command or a single character. | Command | Notes | @@ -85,7 +87,7 @@ Will wait indefinitely for a button to be pressed | WAIT_FOR_BUTTON_PRESS | None | Will wait for the user to press a button to continue script execution | -## String +### String | Command | Parameters | Notes | | ------- | ----------- | ----------------- | @@ -100,13 +102,13 @@ Delay between keypresses. | STRING_DELAY | Delay value in ms | Applied once to next appearing STRING command | | STRINGDELAY | Delay value in ms | Same as STRING_DELAY | -## Repeat +### Repeat | Command | Parameters | Notes | | ------- | ---------------------------- | ----------------------- | | REPEAT | Number of additional repeats | Repeat previous command | -## ALT+Numpad input +### ALT+Numpad input On Windows and some Linux systems, you can print characters by holding `ALT` key and entering its code on Numpad. | Command | Parameters | Notes | @@ -115,14 +117,14 @@ On Windows and some Linux systems, you can print characters by holding `ALT` key | ALTSTRING | Text string | Print text string using ALT+Numpad method | | ALTCODE | Text string | Same as ALTSTRING, presents in some Duckyscript implementations | -## SysRq +### SysRq Send [SysRq command](https://en.wikipedia.org/wiki/Magic_SysRq_key) | Command | Parameters | Notes | | ------- | ---------------- | ----- | | SYSRQ | Single character | | -## USB device ID +### USB device ID You can set the custom ID of the Flipper USB HID device. ID command should be in the **first line** of script, it is executed before script run. diff --git a/documentation/file_formats/InfraredFileFormats.md b/documentation/file_formats/InfraredFileFormats.md index 4d43bd5b8e..a416a5894c 100644 --- a/documentation/file_formats/InfraredFileFormats.md +++ b/documentation/file_formats/InfraredFileFormats.md @@ -1,7 +1,7 @@ -# Infrared Flipper File Formats +# Infrared Flipper File Formats {#infrared_file_format} +## Supported protocols list for "type: parsed" -## Supported protocols list for `type: parsed` ``` NEC NECext @@ -17,6 +17,7 @@ Kaseikyo RCA ``` + ## Infrared Remote File Format ### Example @@ -51,7 +52,7 @@ Each button is separated from others by a comment character (`#`) for better rea Known protocols are represented in the `parsed` form, whereas non-recognized signals may be saved and re-transmitted as `raw` data. -#### Version history: +#### Version history 1. Initial version. @@ -72,19 +73,19 @@ Known protocols are represented in the `parsed` form, whereas non-recognized sig ### Examples -- [TV Universal Library](/applications/main/infrared/resources/infrared/assets/tv.ir) -- [A/C Universal Library](/applications/main/infrared/resources/infrared/assets/ac.ir) -- [Audio Universal Library](/applications/main/infrared/resources/infrared/assets/audio.ir) +- [TV Universal Library](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/main/infrared/resources/infrared/assets/tv.ir) +- [A/C Universal Library](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/main/infrared/resources/infrared/assets/ac.ir) +- [Audio Universal Library](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/main/infrared/resources/infrared/assets/audio.ir) ### Description Filename extension: `.ir` -This file format is used to store universal remote libraries. It is identical to the previous format, differing only in the `Filetype` field.\ +This file format is used to store universal remote libraries. It is identical to the previous format, differing only in the `Filetype` field. It also has predefined button names for each universal library type, so that the universal remote application can understand them. -See [Universal Remotes](/documentation/UniversalRemotes.md) for more information. +See [Universal Remotes](../UniversalRemotes.md) for more information. -### Version history: +### Version history 1. Initial version. @@ -92,7 +93,7 @@ See [Universal Remotes](/documentation/UniversalRemotes.md) for more information ### Examples -See [Infrared Unit Tests](/applications/debug/unit_tests/resources/unit_tests/infrared/) for various examples. +See [Infrared Unit Tests](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/debug/unit_tests/resources/unit_tests/infrared) for various examples. ### Description @@ -103,10 +104,10 @@ It is mostly similar to the two previous formats, with the main difference being Each infrared protocol must have corresponding unit tests complete with an `.irtest` file. -Known protocols are represented in the `parsed_array` form, whereas raw data has the `raw` type.\ +Known protocols are represented in the `parsed_array` form, whereas raw data has the `raw` type. Note: a single parsed signal must be represented as an array of size 1. -### Version history: +### Version history 1. Initial version. @@ -141,4 +142,4 @@ and the number is a sequential integer: 1, 2, 3, etc., which produces names like | decoder_expected | parsed_array | An array of parsed signals containing the expected decoder output. Also used as the encoder input. | | encoder_decoder_input | parsed_array | An array of parsed signals containing both the encoder-decoder input and expected output. | -See [Unit Tests](/documentation/UnitTests.md#infrared) for more info. +See [Unit Tests](../UnitTests.md) for more info. diff --git a/documentation/file_formats/LfRfidFileFormat.md b/documentation/file_formats/LfRfidFileFormat.md index 5143d8bc1e..2463195e40 100644 --- a/documentation/file_formats/LfRfidFileFormat.md +++ b/documentation/file_formats/LfRfidFileFormat.md @@ -1,4 +1,4 @@ -# LF RFID key file format +# LF RFID key file format {#lfrfid_file_format} ## Example diff --git a/documentation/file_formats/NfcFileFormats.md b/documentation/file_formats/NfcFileFormats.md index f752cdb901..5b08c3471f 100644 --- a/documentation/file_formats/NfcFileFormats.md +++ b/documentation/file_formats/NfcFileFormats.md @@ -1,4 +1,4 @@ -# NFC Flipper File Formats +# NFC Flipper File Formats {#nfc_file_format} ## UID + Header (General format) diff --git a/documentation/file_formats/SubGhzFileFormats.md b/documentation/file_formats/SubGhzFileFormats.md index c22f97f8df..c4d63835e9 100644 --- a/documentation/file_formats/SubGhzFileFormats.md +++ b/documentation/file_formats/SubGhzFileFormats.md @@ -1,20 +1,20 @@ -# File Formats for Flipper's SubGhz Subsystem +# SubGhz Subsystem File Formats {#subghz_file_format} -## `.sub` File Format +## .sub File Format -Flipper uses `.sub` files to store SubGhz transmissions. These are text files in Flipper File Format. `.sub` files can contain either a SubGhz Key with a certain protocol or SubGhz RAW data. +Flipper uses `.sub` files to store SubGhz signals. These files use the Flipper File Format. `.sub` files can contain either a SubGhz Key with a certain protocol or SubGhz RAW data. -A `.sub` files consist of 3 parts: +A `.sub` file consist of 3 parts: -- **header**, contains file type, version, and frequency +- **header**, contains the file type, version, and frequency - **preset information**, preset type and, in case of a custom preset, transceiver configuration data - **protocol and its data**, contains protocol name and its specific data, such as key, bit length, etc., or RAW data -Flipper's SubGhz subsystem uses presets to configure the radio transceiver. Presets are used to configure modulation, bandwidth, filters, etc. There are several presets available in stock firmware, and there is a way to create custom presets. See [SubGhz Presets](#adding-a-custom-preset) for more details. +Flipper's SubGhz subsystem uses presets to configure the radio transceiver. Presets are used to configure modulation, bandwidth, filters, etc. There are several presets available in stock firmware, and there is a way to create custom presets. See [SubGhz Presets](#adding-a-custom-preset) section for more details. ## Header format -Header is a mandatory part of `.sub` file. It contains file type, version, and frequency. +Header is a mandatory part of a `.sub` file. It contains the file type, version, and frequency. | Field | Type | Description | | ----------- | ------ | ----------------------------------------------------------------- | @@ -41,7 +41,7 @@ Built-in presets: - `FuriHalSubGhzPreset2FSKDev238Async` — 2 Frequency Shift Keying, deviation 2kHz, 270kHz bandwidth, async(IO throw GP0) - `FuriHalSubGhzPreset2FSKDev476Async` — 2 Frequency Shift Keying, deviation 47kHz, 270kHz bandwidth, async(IO throw GP0) -### Transceiver Configuration Data +### Transceiver Configuration Data {#transceiver-configuration-data} Transceiver configuration data is a string of bytes, encoded in hex format, separated by spaces. For CC1101 data structure is: `XX YY XX YY .. 00 00 ZZ ZZ ZZ ZZ ZZ ZZ ZZ ZZ`, where: @@ -54,7 +54,7 @@ You can find more details in the [CC1101 datasheet](https://www.ti.com/lit/ds/sy ## File Data -`.sub` file data section contains either key data — protocol name and its specific data, bit length, etc., or RAW data — an array of signal timings, recorded without any protocol-specific processing. +`.sub` file data section can either contain key data, consisting of a protocol name and its specific data, bit length, etc., or RAW data, which consists of an array of signal timings, recorded without any protocol-specific processing. ### Key Files @@ -88,20 +88,20 @@ RAW `.sub` files contain raw signal data that is not processed through protocol- For RAW files, 2 fields are required: - **Protocol**, must be `RAW` -- **RAW_Data**, contains an array of timings, specified in microseconds Values must be non-zero, start with a positive number, and interleaved (change sign with each value). Up to 512 values per line. Can be specified multiple times to store multiple lines of data. +- **RAW_Data**, contains an array of timings, specified in microseconds. Values must be non-zero, start with a positive number, and interleaved (change sign with each value). Up to 512 values per line. Can be specified multiple times to store multiple lines of data. Example of RAW data: Protocol: RAW RAW_Data: 29262 361 -68 2635 -66 24113 -66 11 ... -Long payload not fitting into internal memory buffer and consisting of short duration timings (< 10us) may not be read fast enough from the SD card. That might cause the signal transmission to stop before reaching the end of the payload. Ensure that your SD Card has good performance before transmitting long or complex RAW payloads. +A long payload that doesn't fit into the internal memory buffer and consists of short duration timings (< 10us) may not be read fast enough from the SD card. That might cause the signal transmission to stop before reaching the end of the payload. Ensure that your SD Card has good performance before transmitting long or complex RAW payloads. ### BIN_RAW Files BinRAW `.sub` files and `RAW` files both contain data that has not been decoded by any protocol. However, unlike `RAW`, `BinRAW` files only record a useful repeating sequence of durations with a restored byte transfer rate and without broadcast noise. These files can emulate nearly all static protocols, whether Flipper knows them or not. -- Usually, you have to receive the signal a little longer so that Flipper accumulates sufficient data for correct analysis. +- Usually, you have to receive the signal a little longer so that Flipper accumulates sufficient data to analyze it correctly. For `BinRAW` files, the following parameters are required and must be aligned to the left: @@ -188,7 +188,7 @@ Data_RAW: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 DE 02 D3 54 D5 4C D2 C SubGhz application provides support for adding extra radio presets and additional keys for decoding transmissions in certain protocols. -## SubGhz `keeloq_mfcodes_user` file +## SubGhz keeloq_mfcodes_user file This file contains additional manufacturer keys for Keeloq protocol. It is used to decode Keeloq transmissions. This file is loaded at subghz application start and is located at path `/ext/subghz/assets/keeloq_mfcodes_user`. @@ -228,7 +228,7 @@ For each key, a name and encryption method must be specified, according to comme AABBCCDDEEFFAABB:1:Test1 AABBCCDDEEFFAABB:1:Test2 -## SubGhz `setting_user` file +## SubGhz setting_user file This file contains additional radio presets and frequencies for SubGhz application. It is used to add new presets and frequencies for existing presets. This file is being loaded on subghz application start and is located at path `/ext/subghz/assets/setting_user`. @@ -256,7 +256,7 @@ Header must contain the following fields: Repeating the same frequency will cause Flipper to listen to this frequency more often. -#### Adding a Custom Preset +#### Adding a Custom Preset {#adding-a-custom-preset} You can have as many presets as you want. Presets are embedded into `.sub` files, so another Flipper can load them directly from that file. Each preset is defined by the following fields: diff --git a/documentation/file_formats/iButtonFileFormat.md b/documentation/file_formats/iButtonFileFormat.md index 63743f0637..414d73045f 100644 --- a/documentation/file_formats/iButtonFileFormat.md +++ b/documentation/file_formats/iButtonFileFormat.md @@ -1,4 +1,4 @@ -# iButton key file format +# iButton key file format {#ibutton_file_format} ## Example diff --git a/furi/core/check.h b/furi/core/check.h index 2d5df4cf6c..e782380fdd 100644 --- a/furi/core/check.h +++ b/furi/core/check.h @@ -43,7 +43,7 @@ FURI_NORETURN void __furi_halt_implementation(); /** Crash system * - * @param optional message (const char*) + * @param ... optional message (const char*) */ #define furi_crash(...) M_APPLY(__furi_crash, M_IF_EMPTY(__VA_ARGS__)((NULL), (__VA_ARGS__))) @@ -57,7 +57,7 @@ FURI_NORETURN void __furi_halt_implementation(); /** Halt system * - * @param optional message (const char*) + * @param ... optional message (const char*) */ #define furi_halt(...) M_APPLY(__furi_halt, M_IF_EMPTY(__VA_ARGS__)((NULL), (__VA_ARGS__))) @@ -71,8 +71,7 @@ FURI_NORETURN void __furi_halt_implementation(); /** Check condition and crash if failed * - * @param condition to check - * @param optional message (const char*) + * @param ... condition to check and optional message (const char*) */ #define furi_check(...) \ M_APPLY(__furi_check, M_DEFAULT_ARGS(2, (__FURI_CHECK_MESSAGE_FLAG), __VA_ARGS__)) @@ -97,8 +96,7 @@ FURI_NORETURN void __furi_halt_implementation(); * * @warning only will do check if firmware compiled in debug mode * - * @param condition to check - * @param optional message (const char*) + * @param ... condition to check and optional message (const char*) */ #define furi_assert(...) \ M_APPLY(__furi_assert, M_DEFAULT_ARGS(2, (__FURI_ASSERT_MESSAGE_FLAG), __VA_ARGS__)) diff --git a/furi/core/kernel.h b/furi/core/kernel.h index c962402efd..592f01d57d 100644 --- a/furi/core/kernel.h +++ b/furi/core/kernel.h @@ -79,7 +79,7 @@ void furi_delay_tick(uint32_t ticks); * * @warning This should never be called in interrupt request context. * - * @param[in] ticks The tick until which kerel should delay task execution + * @param[in] tick The tick until which kerel should delay task execution * * @return The furi status. */ diff --git a/furi/core/log.h b/furi/core/log.h index a587d8ab27..3ce88db5b2 100644 --- a/furi/core/log.h +++ b/furi/core/log.h @@ -51,7 +51,7 @@ void furi_log_init(void); /** Add log TX callback * - * @param[in] callback The callback + * @param[in] handler The callback and its context * * @return true on success, false otherwise */ @@ -59,7 +59,7 @@ bool furi_log_add_handler(FuriLogHandler handler); /** Remove log TX callback * - * @param[in] callback The callback + * @param[in] handler The callback and its context * * @return true on success, false otherwise */ @@ -112,15 +112,16 @@ FuriLogLevel furi_log_get_level(void); /** Log level to string * * @param[in] level The level + * @param[out] str String representation of the level * - * @return The string + * @return True if success, False otherwise */ bool furi_log_level_to_string(FuriLogLevel level, const char** str); /** Log level from string * * @param[in] str The string - * @param level The level + * @param[out] level The level * * @return True if success, False otherwise */ diff --git a/furi/core/memmgr_heap.h b/furi/core/memmgr_heap.h index 9aacba1ca7..660c5c6bf0 100644 --- a/furi/core/memmgr_heap.h +++ b/furi/core/memmgr_heap.h @@ -18,13 +18,13 @@ extern "C" { * * @param thread_id - thread id to track */ -void memmgr_heap_enable_thread_trace(FuriThreadId taks_handle); +void memmgr_heap_enable_thread_trace(FuriThreadId thread_id); /** Memmgr heap disable thread allocation tracking * * @param thread_id - thread id to track */ -void memmgr_heap_disable_thread_trace(FuriThreadId taks_handle); +void memmgr_heap_disable_thread_trace(FuriThreadId thread_id); /** Memmgr heap get allocatred thread memory * @@ -32,7 +32,7 @@ void memmgr_heap_disable_thread_trace(FuriThreadId taks_handle); * * @return bytes allocated right now */ -size_t memmgr_heap_get_thread_memory(FuriThreadId taks_handle); +size_t memmgr_heap_get_thread_memory(FuriThreadId thread_id); /** Memmgr heap get the max contiguous block size on the heap * diff --git a/furi/core/message_queue.h b/furi/core/message_queue.h index 392a145f10..8d7f389e12 100644 --- a/furi/core/message_queue.h +++ b/furi/core/message_queue.h @@ -32,7 +32,6 @@ void furi_message_queue_free(FuriMessageQueue* instance); * @param instance pointer to FuriMessageQueue instance * @param[in] msg_ptr The message pointer * @param[in] timeout The timeout - * @param[in] msg_prio The message prio * * @return The furi status. */ @@ -43,7 +42,6 @@ FuriStatus * * @param instance pointer to FuriMessageQueue instance * @param msg_ptr The message pointer - * @param msg_prio The message prioority * @param[in] timeout The timeout * * @return The furi status. diff --git a/furi/core/string.h b/furi/core/string.h index 0e3e6a88e6..77ae9da6dc 100644 --- a/furi/core/string.h +++ b/furi/core/string.h @@ -102,7 +102,7 @@ void furi_string_reserve(FuriString* string, size_t size); /** * @brief Reset string. * Make the string empty. - * @param s + * @param string */ void furi_string_reset(FuriString* string); diff --git a/furi/core/thread.h b/furi/core/thread.h index 83c051cc22..489a468448 100644 --- a/furi/core/thread.h +++ b/furi/core/thread.h @@ -235,8 +235,6 @@ int32_t furi_thread_get_return_code(FuriThread* thread); /** Thread related methods that doesn't involve FuriThread directly */ /** Get FreeRTOS FuriThreadId for current thread - * - * @param thread FuriThread instance * * @return FuriThreadId or NULL */ @@ -263,7 +261,7 @@ uint32_t furi_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeo * @brief Enumerate threads * * @param thread_array array of FuriThreadId, where thread ids will be stored - * @param array_items array size + * @param array_item_count array size * @return uint32_t threads count */ uint32_t furi_thread_enumerate(FuriThreadId* thread_array, uint32_t array_item_count); diff --git a/lib/flipper_application/elf/elf_file.h b/lib/flipper_application/elf/elf_file.h index 631fe122f9..921b506bdb 100644 --- a/lib/flipper_application/elf/elf_file.h +++ b/lib/flipper_application/elf/elf_file.h @@ -98,8 +98,7 @@ bool elf_file_is_init_complete(ELFFile* elf); /** * @brief Get actual entry point for ELF file * @param elf_file - * @param args - * @return int32_t + * @return void* */ void* elf_file_get_entry_point(ELFFile* elf_file); @@ -148,4 +147,4 @@ ElfProcessSectionResult elf_process_section( #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/lib/nfc/nfc.h b/lib/nfc/nfc.h index 4f7980b026..6f3e25a5d0 100644 --- a/lib/nfc/nfc.h +++ b/lib/nfc/nfc.h @@ -170,7 +170,7 @@ void nfc_set_fdt_listen_fc(Nfc* instance, uint32_t fdt_listen_fc); * @brief Set mask receive time. * * @param[in,out] instance pointer to the instance to be modified. - * @param[in] mask_rx_time mask receive time, in carrier cycles. + * @param[in] mask_rx_time_fc mask receive time, in carrier cycles. */ void nfc_set_mask_receive_time_fc(Nfc* instance, uint32_t mask_rx_time_fc); diff --git a/lib/nfc/nfc_scanner.h b/lib/nfc/nfc_scanner.h index a1b4aabcda..c13a58b741 100644 --- a/lib/nfc/nfc_scanner.h +++ b/lib/nfc/nfc_scanner.h @@ -72,14 +72,14 @@ NfcScanner* nfc_scanner_alloc(Nfc* nfc); /** * @brief Delete an NfcScanner instance. * - * @param[in,out] pointer to the instance to be deleted. + * @param[in,out] instance pointer to the instance to be deleted. */ void nfc_scanner_free(NfcScanner* instance); /** * @brief Start an NfcScanner. * - * @param[in,out] pointer to the instance to be started. + * @param[in,out] instance pointer to the instance to be started. * @param[in] callback pointer to the callback function (will be called upon a detection event). * @param[in] context pointer to the caller-specific context (will be passed to the callback). */ @@ -88,7 +88,7 @@ void nfc_scanner_start(NfcScanner* instance, NfcScannerCallback callback, void* /** * @brief Stop an NfcScanner. * - * @param[in,out] pointer to the instance to be stopped. + * @param[in,out] instance pointer to the instance to be stopped. */ void nfc_scanner_stop(NfcScanner* instance); diff --git a/targets/f7/furi_hal/furi_hal_rtc.h b/targets/f7/furi_hal/furi_hal_rtc.h index c871d5060a..353bd34946 100644 --- a/targets/f7/furi_hal/furi_hal_rtc.h +++ b/targets/f7/furi_hal/furi_hal_rtc.h @@ -199,7 +199,7 @@ FuriHalRtcHeapTrackMode furi_hal_rtc_get_heap_track_mode(void); /** Set locale units * - * @param[in] mode The RTC Locale Units + * @param[in] value The RTC Locale Units */ void furi_hal_rtc_set_locale_units(FuriHalRtcLocaleUnits value); diff --git a/targets/furi_hal_include/furi_hal_bt.h b/targets/furi_hal_include/furi_hal_bt.h index 939c9a30dd..266db85888 100644 --- a/targets/furi_hal_include/furi_hal_bt.h +++ b/targets/furi_hal_include/furi_hal_bt.h @@ -97,11 +97,12 @@ void furi_hal_bt_reinit(); /** Change BLE app * Restarts 2nd core * - * @param profile FuriHalBleProfileTemplate instance - * @param event_cb GapEventCallback instance - * @param context pointer to context + * @param profile_template FuriHalBleProfileTemplate instance + * @param profile_params Parameters to pass to the profile. Can be NULL + * @param event_cb GapEventCallback instance + * @param context pointer to context * - * @return instance of profile, NULL on failure + * @return instance of profile, NULL on failure */ FURI_WARN_UNUSED FuriHalBleProfileBase* furi_hal_bt_change_app( const FuriHalBleProfileTemplate* profile_template, diff --git a/targets/furi_hal_include/furi_hal_i2c.h b/targets/furi_hal_include/furi_hal_i2c.h index f493655b4d..44f647cefc 100644 --- a/targets/furi_hal_include/furi_hal_i2c.h +++ b/targets/furi_hal_include/furi_hal_i2c.h @@ -91,7 +91,7 @@ bool furi_hal_i2c_tx( * @param size Size of data buffer * @param begin How to begin the transaction * @param end How to end the transaction - * @param timer Timeout timer + * @param timeout Timeout in milliseconds * * @return true on successful transfer, false otherwise */ @@ -131,7 +131,7 @@ bool furi_hal_i2c_rx( * @param size Size of data buffer * @param begin How to begin the transaction * @param end How to end the transaction - * @param timer Timeout timer + * @param timeout Timeout in milliseconds * * @return true on successful transfer, false otherwise */ diff --git a/targets/furi_hal_include/furi_hal_infrared.h b/targets/furi_hal_include/furi_hal_infrared.h index 5fcea06615..ce2e4328e6 100644 --- a/targets/furi_hal_include/furi_hal_infrared.h +++ b/targets/furi_hal_include/furi_hal_infrared.h @@ -36,15 +36,15 @@ typedef void (*FuriHalInfraredTxSignalSentISRCallback)(void* context); /** Signature of callback function for receiving continuous INFRARED rx signal. * - * @param ctx[in] context to pass to callback - * @param level[in] level of input INFRARED rx signal - * @param duration[in] duration of continuous rx signal level in us + * @param[in] ctx context to pass to callback + * @param[in] level level of input INFRARED rx signal + * @param[in] duration duration of continuous rx signal level in us */ typedef void (*FuriHalInfraredRxCaptureCallback)(void* ctx, bool level, uint32_t duration); /** Signature of callback function for reaching silence timeout on INFRARED port. * - * @param ctx[in] context to pass to callback + * @param[in] ctx context to pass to callback */ typedef void (*FuriHalInfraredRxTimeoutCallback)(void* ctx); diff --git a/targets/furi_hal_include/furi_hal_nfc.h b/targets/furi_hal_include/furi_hal_nfc.h index 3d145d1002..3d8ff394ef 100644 --- a/targets/furi_hal_include/furi_hal_nfc.h +++ b/targets/furi_hal_include/furi_hal_nfc.h @@ -228,9 +228,9 @@ FuriHalNfcError furi_hal_nfc_poller_tx(const uint8_t* tx_data, size_t tx_bits); * * The receive buffer must be big enough to accomodate all of the expected data. * - * @param rx_data[out] pointer to a byte array to be filled with received data. - * @param rx_data_size[in] maximum received data size, in bytes. - * @param rx_bits[out] pointer to the variable to hold received data size, in bits. + * @param[out] rx_data pointer to a byte array to be filled with received data. + * @param[in] rx_data_size maximum received data size, in bytes. + * @param[out] rx_bits pointer to the variable to hold received data size, in bits. * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_poller_rx(uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits); @@ -249,9 +249,9 @@ FuriHalNfcError furi_hal_nfc_listener_tx(const uint8_t* tx_data, size_t tx_bits) * * The receive buffer must be big enough to accomodate all of the expected data. * - * @param rx_data[out] pointer to a byte array to be filled with received data. - * @param rx_data_size[in] maximum received data size, in bytes. - * @param rx_bits[out] pointer to the variable to hold received data size, in bits. + * @param[out] rx_data pointer to a byte array to be filled with received data. + * @param[in] rx_data_size maximum received data size, in bytes. + * @param[out] rx_bits pointer to the variable to hold received data size, in bits. * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_listener_rx(uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits); @@ -395,9 +395,9 @@ FuriHalNfcError furi_hal_nfc_iso14443a_tx_sdd_frame(const uint8_t* tx_data, size * * The receive buffer must be big enough to accomodate all of the expected data. * - * @param rx_data[out] pointer to a byte array to be filled with received data. - * @param rx_data_size[in] maximum received data size, in bytes. - * @param rx_bits[out] pointer to the variable to hold received data size, in bits. + * @param[in] rx_data pointer to a byte array to be filled with received data. + * @param[in] rx_data_size maximum received data size, in bytes. + * @param[in] rx_bits pointer to the variable to hold received data size, in bits. * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError diff --git a/targets/furi_hal_include/furi_hal_power.h b/targets/furi_hal_include/furi_hal_power.h index ebe0fe6149..fa5e179c00 100644 --- a/targets/furi_hal_include/furi_hal_power.h +++ b/targets/furi_hal_include/furi_hal_power.h @@ -135,9 +135,7 @@ float furi_hal_power_get_battery_charge_voltage_limit(); * * Invalid values will be clamped downward to the nearest valid value. * - * @param voltage[in] voltage in V - * - * @return voltage in V + * @param[in] voltage voltage in V */ void furi_hal_power_set_battery_charge_voltage_limit(float voltage); @@ -161,7 +159,7 @@ uint32_t furi_hal_power_get_battery_design_capacity(); /** Get battery voltage in V * - * @param ic FuriHalPowerIc to get measurment + * @param[in] ic FuriHalPowerIc to get measurment * * @return voltage in V */ @@ -169,7 +167,7 @@ float furi_hal_power_get_battery_voltage(FuriHalPowerIC ic); /** Get battery current in A * - * @param ic FuriHalPowerIc to get measurment + * @param[in] ic FuriHalPowerIc to get measurment * * @return current in A */ @@ -177,7 +175,7 @@ float furi_hal_power_get_battery_current(FuriHalPowerIC ic); /** Get temperature in C * - * @param ic FuriHalPowerIc to get measurment + * @param[in] ic FuriHalPowerIc to get measurment * * @return temperature in C */ From ab9c10c4360a7bba5ceef75973aaaf4338709b6f Mon Sep 17 00:00:00 2001 From: Spooks <62370103+Spooks4576@users.noreply.github.com> Date: Tue, 5 Mar 2024 23:59:06 -0700 Subject: [PATCH 03/22] Added Ble Beacon --- applications/system/js_app/application.fam | 8 + .../js_app/examples/apps/Scripts/blebeacon.js | 88 ++++++++ .../system/js_app/modules/js_blebeacon.c | 213 ++++++++++++++++++ 3 files changed, 309 insertions(+) create mode 100644 applications/system/js_app/examples/apps/Scripts/blebeacon.js create mode 100644 applications/system/js_app/modules/js_blebeacon.c diff --git a/applications/system/js_app/application.fam b/applications/system/js_app/application.fam index 588ad03fae..40fe06cff5 100644 --- a/applications/system/js_app/application.fam +++ b/applications/system/js_app/application.fam @@ -70,3 +70,11 @@ App( requires=["js_app"], sources=["modules/js_submenu.c"], ) + +App( + appid="js_blebeacon", + apptype=FlipperAppType.PLUGIN, + entry_point="js_blebeacon_ep", + requires=["js_app"], + sources=["modules/js_blebeacon.c"], +) diff --git a/applications/system/js_app/examples/apps/Scripts/blebeacon.js b/applications/system/js_app/examples/apps/Scripts/blebeacon.js new file mode 100644 index 0000000000..3f853f2cb8 --- /dev/null +++ b/applications/system/js_app/examples/apps/Scripts/blebeacon.js @@ -0,0 +1,88 @@ +let bleBeacon = require("blebeacon"); + +let currentIndex = 0; +let currentByteValue = 0; +let watchValues = [ + 0x1A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x11, 0x12, 0x13, 0x14, 0x15, + 0x16, 0x17, 0x18, 0xE4, 0xE5, 0x1B, 0x1C, 0x1D, 0x1E, + 0x20, 0xEC, 0xEF +]; + +function byteToHex(byte) { + let hexChars = '0123456789abcdef'; + let hex = ''; + if (byte >= 0 && byte <= 255) { + hex = hexChars[(byte >> 4) & 0x0F] + hexChars[byte & 0x0F]; + } + return hex; +} + +function getNextByteValue() { + let value = currentByteValue; + currentByteValue = (currentByteValue + 1) % 256; + return value; +} + +function generateRandomMac() { + let mac = ''; + for (let i = 0; i < 6; i++) { + if (mac.length) mac += ':'; + let byte = getNextByteValue(); + mac += byteToHex(byte); + } + return mac; +} + +function bytesToHexString(bytes) { + if (!bytes) { + print("Invalid input for bytesToHexString"); + return ''; + } + + let hexString = ''; + for (let i = 0; i < bytes.length; i++) { + hexString += byteToHex(bytes[i]); + } + return hexString; +} + +function sendRandomModelAdvertisement() { + if (!watchValues || watchValues.length === 0) { + print("watchValues array is empty or undefined."); + return; + } + + let model = watchValues[currentIndex]; + + let packet = [ + 14, 0xFF, 0x75, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0xFF, 0x00, 0x00, 0x43, + model + ]; + + let packetString = bytesToHexString(packet); + if (!packetString) { + print("Failed to generate packet string."); + return; + } + + bleBeacon.setMac(generateRandomMac()); + bleBeacon.setData(packetString); + bleBeacon.send(); + + print("Sent data for model ID " + to_string(model)); + + currentIndex = (currentIndex + 1) % watchValues.length; + + delay(500); // 500 Ms is the fatest we can manage to recieve + // in C Flipper is way faster at spamming this + + bleBeacon.stop(); + + bleBeacon +} + +while (true) +{ + sendRandomModelAdvertisement(); +} \ No newline at end of file diff --git a/applications/system/js_app/modules/js_blebeacon.c b/applications/system/js_app/modules/js_blebeacon.c new file mode 100644 index 0000000000..5bbcb01122 --- /dev/null +++ b/applications/system/js_app/modules/js_blebeacon.c @@ -0,0 +1,213 @@ +#include "../js_modules.h" +#include +#include + +typedef struct { + char* data; + char* mac_addr; + size_t beacon_data_len; + GapExtraBeaconConfig beacon_config; +} JSblebeaconInst; + +static JSblebeaconInst* get_this_ctx(struct mjs* mjs) { + mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); + JSblebeaconInst* storage = mjs_get_ptr(mjs, obj_inst); + furi_assert(storage); + return storage; +} + +static void ret_bad_args(struct mjs* mjs, const char* error) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "%s", error); + mjs_return(mjs, MJS_UNDEFINED); +} + +static bool check_arg_count(struct mjs* mjs, size_t count) { + size_t num_args = mjs_nargs(mjs); + if(num_args != count) { + ret_bad_args(mjs, "Wrong argument count"); + return false; + } + return true; +} + +static bool get_str_arg(struct mjs* mjs, size_t index, char** value) { + mjs_val_t str_obj = mjs_arg(mjs, index); + if(!mjs_is_string(str_obj)) { + ret_bad_args(mjs, "Argument must be a string"); + return false; + } + size_t str_len; + const char* temp = mjs_get_string(mjs, &str_obj, &str_len); + if(str_len == 0 || !temp) { + ret_bad_args(mjs, "Bad string argument"); + return false; + } + + *value = (char*)malloc(str_len + 1); + if (!*value) { + ret_bad_args(mjs, "Memory allocation failed"); + return false; + } + strncpy(*value, temp, str_len); + (*value)[str_len] = '\0'; // Ensure null termination + return true; +} + +static uint8_t hex_char_to_uint(char c) { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return 10 + c - 'a'; + if (c >= 'A' && c <= 'F') return 10 + c - 'A'; + return 0; +} + +static uint8_t* macstr_to_uint8(const char* macstr) { + if (strlen(macstr) != 17) return NULL; + + uint8_t* mac_bytes = (uint8_t*)malloc(6 * sizeof(uint8_t)); + if (!mac_bytes) return NULL; + + for (size_t i = 0, j = 0; i < 17; i += 3, ++j) { + + mac_bytes[j] = (hex_char_to_uint(macstr[i]) << 4) | hex_char_to_uint(macstr[i + 1]); + + if (i < 15 && macstr[i + 2] != ':' && macstr[i + 2] != '-') { + free(mac_bytes); + return NULL; + } + } + + return mac_bytes; +} + +static uint8_t* hexstr_to_uint8(const char* hexstr, size_t* out_length) { + size_t len = strlen(hexstr); + if (len % 2 != 0) return NULL; + + if (len > EXTRA_BEACON_MAX_DATA_SIZE + 1) return NULL; + + *out_length = len / 2; + uint8_t* bytes = (uint8_t*)malloc(*out_length); + if (!bytes) return NULL; + + for (size_t i = 0; i < *out_length; ++i) { + bytes[i] = (hex_char_to_uint(hexstr[i * 2]) << 4) | hex_char_to_uint(hexstr[i * 2 + 1]); + } + + return bytes; +} + +static void js_blebeacon_set_data(struct mjs *mjs) { + FURI_LOG_D("BLE", "Setting data"); + if (!check_arg_count(mjs, 1)) return; + JSblebeaconInst* inst = get_this_ctx(mjs); + if (!inst) { + FURI_LOG_D("BLE", "Beacon instance is null"); + ret_bad_args(mjs, "Beacon instance is null"); + return; + } + + if (inst->data) { + FURI_LOG_D("BLE", "Freeing existing data"); + free(inst->data); + inst->data = NULL; + } + + + if (!get_str_arg(mjs, 0, &(inst->data))) return; // get_str_arg now modifies inst->data directly + + size_t data_len = 0; + uint8_t* beacon_data = hexstr_to_uint8(inst->data, &data_len); + if (!beacon_data) { + FURI_LOG_D("BLE", "Failed to convert data to hex"); + ret_bad_args(mjs, "Failed to convert data to hex"); + return; + } + + FURI_LOG_D("BLE", "Successfully set beacon data"); + furi_hal_bt_extra_beacon_set_data(beacon_data, data_len); + free(beacon_data); + mjs_return(mjs, MJS_UNDEFINED); +} + +static void js_blebeacon_set_mac(struct mjs *mjs) { + FURI_LOG_D("BLE", "Setting Mac"); + if (!check_arg_count(mjs, 1)) + { + ret_bad_args(mjs, "Bad args"); + return; + } + + JSblebeaconInst* inst = get_this_ctx(mjs); + char* mac_addr = ""; + if (!get_str_arg(mjs, 0, &mac_addr)) return; + inst->mac_addr = mac_addr; + inst->beacon_config.min_adv_interval_ms = 50; + inst->beacon_config.max_adv_interval_ms = 150; + + inst->beacon_config.adv_channel_map = GapAdvChannelMapAll; + inst->beacon_config.adv_power_level = GapAdvPowerLevel_0dBm; + + inst->beacon_config.address_type = GapAddressTypePublic; + + uint8_t* mac = macstr_to_uint8(mac_addr); + if (mac) { + memcpy(inst->beacon_config.address, mac, 6); + furi_hal_bt_extra_beacon_set_config(&inst->beacon_config); + mjs_return(mjs, MJS_UNDEFINED); + } else { + FURI_LOG_D("BLE", "Bad MacAddress"); + ret_bad_args(mjs, "Bad Mac Address"); + return; + } +} + +static void js_blebeacon_send(struct mjs *mjs) { + + furi_hal_bt_extra_beacon_start(); + + mjs_return(mjs, MJS_UNDEFINED); +} + +static void js_blebeacon_stop(struct mjs *mjs) { + + furi_hal_bt_extra_beacon_stop(); + + mjs_return(mjs, MJS_UNDEFINED); +} + +static void* js_blebeacon_create(struct mjs *mjs, mjs_val_t* object) { + JSblebeaconInst* inst = malloc(sizeof(JSblebeaconInst)); + mjs_val_t blebeacon_obj = mjs_mk_object(mjs); + mjs_set(mjs, blebeacon_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, inst)); + mjs_set(mjs, blebeacon_obj, "setData", ~0, MJS_MK_FN(js_blebeacon_set_data)); + mjs_set(mjs, blebeacon_obj, "setMac", ~0, MJS_MK_FN(js_blebeacon_set_mac)); + mjs_set(mjs, blebeacon_obj, "send", ~0, MJS_MK_FN(js_blebeacon_send)); + mjs_set(mjs, blebeacon_obj, "stop", ~0, MJS_MK_FN(js_blebeacon_stop)); + *object = blebeacon_obj; + return inst; +} + +static void js_blebeacon_destroy(void *ptr) { + JSblebeaconInst* inst = (JSblebeaconInst*)ptr; + if (inst) { + free(inst->data); + free(inst->mac_addr); + free(inst); + } +} + +static const JsModuleDescriptor js_blebeacon_desc = { + "blebeacon", + js_blebeacon_create, + js_blebeacon_destroy, +}; + +static const FlipperAppPluginDescriptor blebeacon_plugin_descriptor = { + .appid = PLUGIN_APP_ID, + .ep_api_version = PLUGIN_API_VERSION, + .entry_point = &js_blebeacon_desc, +}; + +const FlipperAppPluginDescriptor* js_blebeacon_ep(void) { + return &blebeacon_plugin_descriptor; +} \ No newline at end of file From 913127854c67a7b696ab4b57276789dc36feb42c Mon Sep 17 00:00:00 2001 From: Spooks <62370103+Spooks4576@users.noreply.github.com> Date: Wed, 6 Mar 2024 02:05:03 -0700 Subject: [PATCH 04/22] Moved Mac Address Gen to API --- .../js_app/examples/apps/Scripts/blebeacon.js | 20 +---- .../system/js_app/modules/js_blebeacon.c | 85 +++++++++++++++++++ 2 files changed, 88 insertions(+), 17 deletions(-) diff --git a/applications/system/js_app/examples/apps/Scripts/blebeacon.js b/applications/system/js_app/examples/apps/Scripts/blebeacon.js index 3f853f2cb8..e9c2869414 100644 --- a/applications/system/js_app/examples/apps/Scripts/blebeacon.js +++ b/applications/system/js_app/examples/apps/Scripts/blebeacon.js @@ -18,22 +18,6 @@ function byteToHex(byte) { return hex; } -function getNextByteValue() { - let value = currentByteValue; - currentByteValue = (currentByteValue + 1) % 256; - return value; -} - -function generateRandomMac() { - let mac = ''; - for (let i = 0; i < 6; i++) { - if (mac.length) mac += ':'; - let byte = getNextByteValue(); - mac += byteToHex(byte); - } - return mac; -} - function bytesToHexString(bytes) { if (!bytes) { print("Invalid input for bytesToHexString"); @@ -66,7 +50,9 @@ function sendRandomModelAdvertisement() { return; } - bleBeacon.setMac(generateRandomMac()); + let Mac = bleBeacon.genMac(); + + bleBeacon.setMac(Mac); bleBeacon.setData(packetString); bleBeacon.send(); diff --git a/applications/system/js_app/modules/js_blebeacon.c b/applications/system/js_app/modules/js_blebeacon.c index 5bbcb01122..3fbf7f6dad 100644 --- a/applications/system/js_app/modules/js_blebeacon.c +++ b/applications/system/js_app/modules/js_blebeacon.c @@ -9,6 +9,78 @@ typedef struct { GapExtraBeaconConfig beacon_config; } JSblebeaconInst; +// Define the OUI Map as a constant array of structs +struct OUI_MAP_ENTRY { + const char *brand; + const char *oui; +}; + +struct OUI_MAP_ENTRY OUI_MAP[] = { + {"Apple", "00:1F:7F"}, + {"Dell", "00:14:5F"}, + {"HP", "00:4C:6F"}, + {"Lenovo", "00:50:C2"}, + {"Microsoft", "00:0C:29"}, + {"Samsung", "00:1C:42"}, + {"Sony", "00:0A:95"}, + {"Acer", "00:26:A9"}, + {"Asus", "00:19:D8"}, + {"Google", "08:00:27"}, + {"HTC", "00:1F:B5"}, + {"Intel", "00:19:5D"}, + {"LG", "00:1C:61"}, + {"Motorola", "00:1F:42"}, + {"Toshiba", "00:1E:67"}, + {"Xiaomi", "00:26:A8"}, +}; + +#define OUI_MAP_SIZE (sizeof(OUI_MAP) / sizeof(OUI_MAP[0])) + + +int rand_range(int min, int max) { + return min + rand() / (RAND_MAX / (max - min + 1) + 1); +} + +void byte_to_hex(char *output, unsigned char byte) { + static const char hex_chars[] = "0123456789ABCDEF"; + output[0] = hex_chars[byte >> 4]; + output[1] = hex_chars[byte & 0x0F]; +} + +static char* generate_mac_address(const char *brand) { + char *mac_address = (char*)malloc(18 * sizeof(char)); + if (mac_address == NULL) { + FURI_LOG_D("BLE", "Memory allocation failed.\n"); + return NULL; + } + + const char *oui = NULL; + for (unsigned int i = 0; i < OUI_MAP_SIZE; ++i) { + if (strcmp(brand, OUI_MAP[i].brand) == 0) { + oui = OUI_MAP[i].oui; + break; + } + } + + if (oui == NULL) { + FURI_LOG_D("BLE", "Brand not found.\n"); + free(mac_address); + return NULL; + } + + + char last_bytes[6]; + for (int i = 0; i < 3; ++i) { + unsigned char byte = rand_range(0x00, 0xFF); + byte_to_hex(&last_bytes[i * 2], byte); + } + + strcpy(mac_address, oui); + strcat(mac_address, ":"); + strcat(mac_address, last_bytes); + return mac_address; +} + static JSblebeaconInst* get_this_ctx(struct mjs* mjs) { mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); JSblebeaconInst* storage = mjs_get_ptr(mjs, obj_inst); @@ -129,6 +201,18 @@ static void js_blebeacon_set_data(struct mjs *mjs) { mjs_return(mjs, MJS_UNDEFINED); } +static void js_blebeacon_generate_mac(struct mjs *mjs) +{ + char* company = ""; + if (!get_str_arg(mjs, 0, &company)) return; + + char* mac = generate_mac_address(company); + + mjs_val_t js_mac_address = mjs_mk_string(mjs, mac, strlen(mac), 1); + + return mjs_return(mjs, js_mac_address); +} + static void js_blebeacon_set_mac(struct mjs *mjs) { FURI_LOG_D("BLE", "Setting Mac"); if (!check_arg_count(mjs, 1)) @@ -183,6 +267,7 @@ static void* js_blebeacon_create(struct mjs *mjs, mjs_val_t* object) { mjs_set(mjs, blebeacon_obj, "setMac", ~0, MJS_MK_FN(js_blebeacon_set_mac)); mjs_set(mjs, blebeacon_obj, "send", ~0, MJS_MK_FN(js_blebeacon_send)); mjs_set(mjs, blebeacon_obj, "stop", ~0, MJS_MK_FN(js_blebeacon_stop)); + mjs_set(mjs, blebeacon_obj, "genMac", ~0, MJS_MK_FN(js_blebeacon_generate_mac)); *object = blebeacon_obj; return inst; } From c43d9d3bddc2bd6537e2a2f959477a7b5c3d56c6 Mon Sep 17 00:00:00 2001 From: Spooks <62370103+Spooks4576@users.noreply.github.com> Date: Wed, 6 Mar 2024 11:03:26 -0700 Subject: [PATCH 05/22] Math --- applications/system/js_app/application.fam | 10 + .../js_app/examples/apps/Scripts/math.js | 47 +++ .../system/js_app/modules/js_blebeacon.c | 2 +- applications/system/js_app/modules/js_math.c | 309 ++++++++++++++++++ 4 files changed, 367 insertions(+), 1 deletion(-) create mode 100644 applications/system/js_app/examples/apps/Scripts/math.js create mode 100644 applications/system/js_app/modules/js_math.c diff --git a/applications/system/js_app/application.fam b/applications/system/js_app/application.fam index 40fe06cff5..779ecd8ad7 100644 --- a/applications/system/js_app/application.fam +++ b/applications/system/js_app/application.fam @@ -78,3 +78,13 @@ App( requires=["js_app"], sources=["modules/js_blebeacon.c"], ) + +App( + appid="js_math", + apptype=FlipperAppType.PLUGIN, + entry_point="js_math_ep", + requires=["js_app"], + sources=["modules/js_math.c"], +) + + diff --git a/applications/system/js_app/examples/apps/Scripts/math.js b/applications/system/js_app/examples/apps/Scripts/math.js new file mode 100644 index 0000000000..49212f904a --- /dev/null +++ b/applications/system/js_app/examples/apps/Scripts/math.js @@ -0,0 +1,47 @@ +let math = require("math"); + +let absResult = math.abs(-5); +let acosResult = math.acos(0.5); +let acoshResult = math.acosh(2); +let asinResult = math.asin(0.5); +let asinhResult = math.asinh(2); +let atanResult = math.atan(1); +let atan2Result = math.atan2(1, 1); +let atanhResult = math.atanh(0.5); +let cbrtResult = math.cbrt(27); +let ceilResult = math.ceil(5.3); +let clz32Result = math.clz32(1); +let cosResult = math.cos(math.PI); +let expResult = math.exp(1); +let floorResult = math.floor(5.7); +let maxResult = math.max(3, 5); +let minResult = math.min(3, 5); +let powResult = math.pow(2, 3); +let randomResult = math.random(); +let signResult = math.sign(-5); +let sinResult = math.sin(math.PI / 2); +let sqrtResult = math.sqrt(25); +let truncResult = math.trunc(5.7); + +print("math.abs(-5):", absResult); +print("math.acos(0.5):", acosResult); +print("math.acosh(2):", acoshResult); +print("math.asin(0.5):", asinResult); +print("math.asinh(2):", asinhResult); +print("math.atan(1):", atanResult); +print("math.atan2(1, 1):", atan2Result); +print("math.atanh(0.5):", atanhResult); +print("math.cbrt(27):", cbrtResult); +print("math.ceil(5.3):", ceilResult); +print("math.clz32(1):", clz32Result); +print("math.cos(math.PI):", cosResult); +print("math.exp(1):", expResult); +print("math.floor(5.7):", floorResult); +print("math.max(3, 5):", maxResult); +print("math.min(3, 5):", minResult); +print("math.pow(2, 3):", powResult); +print("math.random():", randomResult); +print("math.sign(-5):", signResult); +print("math.sin(math.PI/2):", sinResult); +print("math.sqrt(25):", sqrtResult); +print("math.trunc(5.7):", truncResult); \ No newline at end of file diff --git a/applications/system/js_app/modules/js_blebeacon.c b/applications/system/js_app/modules/js_blebeacon.c index 3fbf7f6dad..ff7176c526 100644 --- a/applications/system/js_app/modules/js_blebeacon.c +++ b/applications/system/js_app/modules/js_blebeacon.c @@ -9,7 +9,7 @@ typedef struct { GapExtraBeaconConfig beacon_config; } JSblebeaconInst; -// Define the OUI Map as a constant array of structs + struct OUI_MAP_ENTRY { const char *brand; const char *oui; diff --git a/applications/system/js_app/modules/js_math.c b/applications/system/js_app/modules/js_math.c new file mode 100644 index 0000000000..d17b72b888 --- /dev/null +++ b/applications/system/js_app/modules/js_math.c @@ -0,0 +1,309 @@ +#include "../js_modules.h" +#include "furi_hal_random.h" + +#define MJS_PI (double)3.14159265358979323846 +#define MJS_E (double)2.7182818284590452354 + +static void ret_bad_args(struct mjs* mjs, const char* error) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "%s", error); + mjs_return(mjs, mjs_mk_undefined()); +} + +static bool check_arg_count(struct mjs* mjs, size_t count) { + size_t num_args = mjs_nargs(mjs); + if(num_args != count) { + ret_bad_args(mjs, "Wrong argument count"); + return false; + } + return true; +} + +void mjs_abs(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + mjs_return(mjs, x < 0 ? mjs_mk_number(mjs, -x) : mjs_arg(mjs, 0)); +} + +void mjs_acos(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + if (x < -1 || x > 1) { + ret_bad_args(mjs, "Invalid input value for Math.acos"); + mjs_return(mjs, MJS_UNDEFINED); + } + mjs_return(mjs, mjs_mk_number(mjs, MJS_PI / (double)2 - atan(x / sqrt(1 - x * x)))); +} + +void mjs_acosh(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + if (x < 1) { + ret_bad_args(mjs, "Invalid input value for Math.acosh"); + mjs_return(mjs, MJS_UNDEFINED); + } + mjs_return(mjs, mjs_mk_number(mjs, log(x + sqrt(x * x - 1)))); +} + +void mjs_asin(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + mjs_return(mjs, mjs_mk_number(mjs, atan(x / sqrt(1 - x * x)))); +} + +void mjs_asinh(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + mjs_return(mjs, mjs_mk_number(mjs, log(x + sqrt(x * x + 1)))); +} + +void mjs_atan(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + mjs_return(mjs, mjs_mk_number(mjs, atan(x))); +} + +void mjs_atan2(struct mjs* mjs) { + if (!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) || !mjs_is_number(mjs_arg(mjs, 1))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double y = mjs_get_double(mjs, mjs_arg(mjs, 0)); + double x = mjs_get_double(mjs, mjs_arg(mjs, 1)); + mjs_return(mjs, mjs_mk_number(mjs, atan2(y, x))); +} + +void mjs_atanh(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + if (x <= -1 || x >= 1) { + ret_bad_args(mjs, "Invalid input value for Math.atanh"); + mjs_return(mjs, MJS_UNDEFINED); + } + mjs_return(mjs, mjs_mk_number(mjs, (double)0.5 * log((1 + x) / (1 - x)))); +} + +void mjs_cbrt(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + mjs_return(mjs, mjs_mk_number(mjs, pow(x, 1.0 / 3.0))); +} + +void mjs_ceil(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + mjs_return(mjs, mjs_mk_number(mjs, (int)(x + (double)0.5))); +} + +void mjs_clz32(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + unsigned int x = (unsigned int)mjs_get_int(mjs, mjs_arg(mjs, 0)); + int count = 0; + while (x) { + x >>= 1; + count++; + } + mjs_return(mjs, mjs_mk_number(mjs, 32 - count)); +} + +void mjs_cos(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + mjs_return(mjs, mjs_mk_number(mjs, cos(x))); +} + +void mjs_exp(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + double result = 1; + double term = 1; + for (int i = 1; i < 100; i++) { + term *= x / i; + result += term; + } + mjs_return(mjs, mjs_mk_number(mjs, result)); +} + +void mjs_floor(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + mjs_return(mjs, mjs_mk_number(mjs, (int)x)); +} + +void mjs_log(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + if (x <= 0) { + ret_bad_args(mjs, "Invalid input value for Math.log"); + mjs_return(mjs, MJS_UNDEFINED); + } + double result = 0; + while (x >= MJS_E) { + x /= MJS_E; + result++; + } + mjs_return(mjs, mjs_mk_number(mjs, result + log(x))); +} + +void mjs_max(struct mjs* mjs) { + if (!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) || !mjs_is_number(mjs_arg(mjs, 1))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + double y = mjs_get_double(mjs, mjs_arg(mjs, 1)); + mjs_return(mjs, mjs_mk_number(mjs, x > y ? x : y)); +} + +void mjs_min(struct mjs* mjs) { + if (!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) || !mjs_is_number(mjs_arg(mjs, 1))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + double y = mjs_get_double(mjs, mjs_arg(mjs, 1)); + mjs_return(mjs, mjs_mk_number(mjs, x < y ? x : y)); +} + +void mjs_pow(struct mjs* mjs) { + if (!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) || !mjs_is_number(mjs_arg(mjs, 1))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double base = mjs_get_double(mjs, mjs_arg(mjs, 0)); + double exponent = mjs_get_double(mjs, mjs_arg(mjs, 1)); + double result = 1; + for (int i = 0; i < exponent; i++) { + result *= base; + } + mjs_return(mjs, mjs_mk_number(mjs, result)); +} + +void mjs_random(struct mjs* mjs) { + if (!check_arg_count(mjs, 0)) { + mjs_return(mjs, MJS_UNDEFINED); + } + const uint32_t random_val = furi_hal_random_get(); + double rnd = (double)random_val / RAND_MAX; + mjs_return(mjs, mjs_mk_number(mjs, rnd)); +} + +void mjs_sign(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + mjs_return(mjs, mjs_mk_number(mjs, x == 0 ? 0 : (x < 0 ? -1 : 1))); +} + +void mjs_sin(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + double result = x; + double term = x; + for (int i = 1; i < 10; i++) { + term *= -x * x / ((2 * i) * (2 * i + 1)); + result += term; + } + mjs_return(mjs, mjs_mk_number(mjs, result)); +} + +void mjs_sqrt(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + if (x < 0) { + ret_bad_args(mjs, "Invalid input value for Math.sqrt"); + mjs_return(mjs, MJS_UNDEFINED); + } + double result = 1; + while (result * result < x) { + result += (double)0.001; + } + mjs_return(mjs, mjs_mk_number(mjs, result)); +} + +void mjs_trunc(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + mjs_return(mjs, mjs_mk_number(mjs, x < 0 ? ceil(x) : floor(x))); +} + + +static void* js_math_create(struct mjs *mjs, mjs_val_t* object) { + mjs_val_t math_obj = mjs_mk_object(mjs); + mjs_set(mjs, math_obj, "abs", ~0, MJS_MK_FN(mjs_abs)); + mjs_set(mjs, math_obj, "acos", ~0, MJS_MK_FN(mjs_acos)); + mjs_set(mjs, math_obj, "acosh", ~0, MJS_MK_FN(mjs_acosh)); + mjs_set(mjs, math_obj, "asin", ~0, MJS_MK_FN(mjs_asin)); + mjs_set(mjs, math_obj, "asinh", ~0, MJS_MK_FN(mjs_asinh)); + mjs_set(mjs, math_obj, "atan", ~0, MJS_MK_FN(mjs_atan)); + mjs_set(mjs, math_obj, "atan2", ~0, MJS_MK_FN(mjs_atan2)); + mjs_set(mjs, math_obj, "atanh", ~0, MJS_MK_FN(mjs_atanh)); + mjs_set(mjs, math_obj, "cbrt", ~0, MJS_MK_FN(mjs_cbrt)); + mjs_set(mjs, math_obj, "ceil", ~0, MJS_MK_FN(mjs_ceil)); + mjs_set(mjs, math_obj, "clz32", ~0, MJS_MK_FN(mjs_clz32)); + mjs_set(mjs, math_obj, "cos", ~0, MJS_MK_FN(mjs_cos)); + mjs_set(mjs, math_obj, "exp", ~0, MJS_MK_FN(mjs_exp)); + mjs_set(mjs, math_obj, "floor", ~0, MJS_MK_FN(mjs_floor)); + mjs_set(mjs, math_obj, "log", ~0, MJS_MK_FN(mjs_log)); + mjs_set(mjs, math_obj, "max", ~0, MJS_MK_FN(mjs_max)); + mjs_set(mjs, math_obj, "min", ~0, MJS_MK_FN(mjs_min)); + mjs_set(mjs, math_obj, "pow", ~0, MJS_MK_FN(mjs_pow)); + mjs_set(mjs, math_obj, "random", ~0, MJS_MK_FN(mjs_random)); + mjs_set(mjs, math_obj, "sign", ~0, MJS_MK_FN(mjs_sign)); + mjs_set(mjs, math_obj, "sin", ~0, MJS_MK_FN(mjs_sin)); + mjs_set(mjs, math_obj, "sqrt", ~0, MJS_MK_FN(mjs_sqrt)); + mjs_set(mjs, math_obj, "trunc", ~0, MJS_MK_FN(mjs_trunc)); + mjs_set(mjs, math_obj, "PI", ~0, mjs_mk_number(mjs, MJS_PI)); + *object = math_obj; + return object; +} + +static void js_math_destroy(void *ptr) { + UNUSED(ptr); +} + +static const JsModuleDescriptor js_math_desc = { + "math", + js_math_create, + js_math_destroy, +}; + +static const FlipperAppPluginDescriptor btkicker_plugin_descriptor = { + .appid = PLUGIN_APP_ID, + .ep_api_version = PLUGIN_API_VERSION, + .entry_point = &js_math_desc, +}; + +const FlipperAppPluginDescriptor* js_math_ep(void) { + return &btkicker_plugin_descriptor; +} \ No newline at end of file From 2ff6dc8c1deb82fea63821d13ba37cd790b3dbae Mon Sep 17 00:00:00 2001 From: Spooks <62370103+Spooks4576@users.noreply.github.com> Date: Wed, 6 Mar 2024 15:02:00 -0700 Subject: [PATCH 06/22] Keyboard WIP (Byte Input Doesn't Work) --- applications/system/js_app/application.fam | 8 + .../js_app/examples/apps/Scripts/keyboard.js | 5 + .../system/js_app/modules/js_keyboard.c | 206 ++++++++++++++++++ 3 files changed, 219 insertions(+) create mode 100644 applications/system/js_app/examples/apps/Scripts/keyboard.js create mode 100644 applications/system/js_app/modules/js_keyboard.c diff --git a/applications/system/js_app/application.fam b/applications/system/js_app/application.fam index 779ecd8ad7..8bd2f4a2f4 100644 --- a/applications/system/js_app/application.fam +++ b/applications/system/js_app/application.fam @@ -87,4 +87,12 @@ App( sources=["modules/js_math.c"], ) +App( + appid="js_keyboard", + apptype=FlipperAppType.PLUGIN, + entry_point="js_keyboard_ep", + requires=["js_app"], + sources=["modules/js_keyboard.c"], +) + diff --git a/applications/system/js_app/examples/apps/Scripts/keyboard.js b/applications/system/js_app/examples/apps/Scripts/keyboard.js new file mode 100644 index 0000000000..6e7b8332a1 --- /dev/null +++ b/applications/system/js_app/examples/apps/Scripts/keyboard.js @@ -0,0 +1,5 @@ +let keyboard = require("keyboard"); + +let text = keyboard.text(100, "Please Input Shit", 1); + +print(text); \ No newline at end of file diff --git a/applications/system/js_app/modules/js_keyboard.c b/applications/system/js_app/modules/js_keyboard.c new file mode 100644 index 0000000000..1988fda25b --- /dev/null +++ b/applications/system/js_app/modules/js_keyboard.c @@ -0,0 +1,206 @@ +#include "../js_modules.h" +#include +#include +#include + +#define membersof(x) (sizeof(x) / sizeof(x[0])) + +typedef struct { + char* data; + TextInput* textinput; + ByteInput* byteinputview; + ViewDispatcher* view_dispatcher; + uint8_t* byteinput; +} JSkeyboardInst; + +static void ret_bad_args(struct mjs* mjs, const char* error) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "%s", error); + mjs_return(mjs, MJS_UNDEFINED); +} + +char nibble_to_hex_character(uint8_t nibble) { + nibble &= 0xF; + if (nibble < 10) { + return '0' + nibble; + } else { + return 'A' + (nibble - 10); + } +} + +char* bytes_to_hex_string(uint8_t* bytes, size_t num_bytes) { + char* hex_string = (char*)malloc(num_bytes * 2 + 1); + if (hex_string == NULL) { + FURI_LOG_D("LOG", "Memory allocation failed\n"); + return NULL; + } + + for (size_t i = 0; i < num_bytes; ++i) { + hex_string[i * 2] = nibble_to_hex_character(bytes[i] >> 4); + hex_string[i * 2 + 1] = nibble_to_hex_character(bytes[i] & 0xF); + } + hex_string[num_bytes * 2] = '\0'; + + return hex_string; +} + +static bool get_str_arg(struct mjs* mjs, size_t index, const char** value) { + mjs_val_t str_obj = mjs_arg(mjs, index); + if(!mjs_is_string(str_obj)) { + ret_bad_args(mjs, "Argument must be a string"); + return false; + } + size_t str_len = 0; + *value = mjs_get_string(mjs, &str_obj, &str_len); + if((str_len == 0) || (*value == NULL)) { + ret_bad_args(mjs, "Bad string argument"); + return false; + } + return true; +} + +static bool get_int_arg(struct mjs* mjs, size_t index, int* value) { + mjs_val_t int_obj = mjs_arg(mjs, index); + if(!mjs_is_number(int_obj)) { + return false; + } + *value = (int)mjs_get_int(mjs, int_obj); + return true; +} + +static JSkeyboardInst* get_this_ctx(struct mjs* mjs) { + mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); + JSkeyboardInst* storage = mjs_get_ptr(mjs, obj_inst); + furi_assert(storage); + return storage; +} + +void text_input_callback(void* context) +{ + JSkeyboardInst* keyboardinst = (JSkeyboardInst*)context; + view_dispatcher_stop(keyboardinst->view_dispatcher); +} + +void byte_input_callback(void* context) +{ + JSkeyboardInst* keyboardinst = (JSkeyboardInst*)context; + view_dispatcher_stop(keyboardinst->view_dispatcher); +} + +static void js_keyboard_text(struct mjs *mjs) { + + JSkeyboardInst* keyboardinst = get_this_ctx(mjs); + + int MaxInputLength; + if(!get_int_arg(mjs, 0, &MaxInputLength)) return; + + const char* defaultText = ""; + get_str_arg(mjs, 1, &defaultText); + + int ShouldSelect; + mjs_val_t bool_obj = mjs_arg(mjs, 2); + ShouldSelect = (int)mjs_get_bool(mjs, bool_obj); + + if (keyboardinst->textinput && keyboardinst->view_dispatcher) + { + if (strlen(defaultText) > 0) + { + text_input_set_header_text(keyboardinst->textinput, defaultText); + } + + view_dispatcher_attach_to_gui( + keyboardinst->view_dispatcher, furi_record_open(RECORD_GUI), ViewDispatcherTypeFullscreen); + furi_record_close(RECORD_GUI); + + text_input_set_result_callback(keyboardinst->textinput, text_input_callback, keyboardinst, keyboardinst->data, MaxInputLength, ShouldSelect); + + view_dispatcher_switch_to_view(keyboardinst->view_dispatcher, 0); + + view_dispatcher_run(keyboardinst->view_dispatcher); + } + + text_input_reset(keyboardinst->textinput); + + mjs_return(mjs, mjs_mk_string(mjs, keyboardinst->data, strlen(keyboardinst->data), 1)); +} + +static void js_keyboard_byte(struct mjs *mjs) { + + + + JSkeyboardInst* keyboardinst = get_this_ctx(mjs); + + int MaxInputLength; + if(!get_int_arg(mjs, 0, &MaxInputLength)) return; + + const char* defaultText; + get_str_arg(mjs, 1, &defaultText); + + if (keyboardinst->byteinputview && keyboardinst->view_dispatcher) + { + if (strlen(defaultText) > 0) + { + byte_input_set_header_text(keyboardinst->byteinputview, defaultText); + } + + view_dispatcher_attach_to_gui( + keyboardinst->view_dispatcher, furi_record_open(RECORD_GUI), ViewDispatcherTypeFullscreen); + furi_record_close(RECORD_GUI); + + byte_input_set_result_callback(keyboardinst->byteinputview, byte_input_callback, NULL, keyboardinst, keyboardinst->byteinput, 10); + + view_dispatcher_switch_to_view(keyboardinst->view_dispatcher, 1); + + view_dispatcher_run(keyboardinst->view_dispatcher); + } + + FURI_LOG_D("SHIT", "DID THING"); + keyboardinst->data = bytes_to_hex_string(keyboardinst->byteinput, 10); + FURI_LOG_D("SHIT", "DID THING 1"); + mjs_return(mjs, mjs_mk_string(mjs, keyboardinst->data, strlen(keyboardinst->data), 1)); + FURI_LOG_D("SHIT", "DID THING 2"); +} + + +static void* js_keyboard_create(struct mjs* mjs, mjs_val_t* object) { + JSkeyboardInst* keyboardinst = malloc(sizeof(JSkeyboardInst)); + mjs_val_t keyboard_obj = mjs_mk_object(mjs); + mjs_set(mjs, keyboard_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, keyboardinst)); + mjs_set(mjs, keyboard_obj, "text", ~0, MJS_MK_FN(js_keyboard_text)); + //mjs_set(mjs, keyboard_obj, "byte", ~0, MJS_MK_FN(js_keyboard_byte)); // NULL POINTER DEREFERENCE + keyboardinst->byteinputview = byte_input_alloc(); + keyboardinst->textinput = text_input_alloc(); + keyboardinst->view_dispatcher = view_dispatcher_alloc(); + keyboardinst->data = malloc(100); + keyboardinst->byteinput = malloc(100); + view_dispatcher_enable_queue(keyboardinst->view_dispatcher); + view_dispatcher_add_view(keyboardinst->view_dispatcher, 0, text_input_get_view(keyboardinst->textinput)); + view_dispatcher_add_view(keyboardinst->view_dispatcher, 1, byte_input_get_view(keyboardinst->byteinputview)); + *object = keyboard_obj; + return keyboardinst; +} + +static void js_keyboard_destroy(void* inst) { + JSkeyboardInst* insts = (JSkeyboardInst*)inst; + byte_input_free(insts->byteinputview); + text_input_free(insts->textinput); + view_dispatcher_free(insts->view_dispatcher); + free(insts->data); + free(insts); +} + +static const JsModuleDescriptor js_keyboard_desc = { + "keyboard", + js_keyboard_create, + js_keyboard_destroy, +}; + +static const FlipperAppPluginDescriptor keyboard_plugin_descriptor = { + .appid = PLUGIN_APP_ID, + .ep_api_version = PLUGIN_API_VERSION, + .entry_point = &js_keyboard_desc, +}; + +const FlipperAppPluginDescriptor* js_keyboard_ep(void) { + UNUSED(js_keyboard_byte); + return &keyboard_plugin_descriptor; +} \ No newline at end of file From 337486df873b2abddea5f729b0edb3c919eaad84 Mon Sep 17 00:00:00 2001 From: Spooks <62370103+Spooks4576@users.noreply.github.com> Date: Wed, 6 Mar 2024 15:48:53 -0700 Subject: [PATCH 07/22] Update blebeacon.js --- .../js_app/examples/apps/Scripts/blebeacon.js | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/applications/system/js_app/examples/apps/Scripts/blebeacon.js b/applications/system/js_app/examples/apps/Scripts/blebeacon.js index e9c2869414..f324fafd96 100644 --- a/applications/system/js_app/examples/apps/Scripts/blebeacon.js +++ b/applications/system/js_app/examples/apps/Scripts/blebeacon.js @@ -18,6 +18,22 @@ function byteToHex(byte) { return hex; } +function getNextByteValue() { + let value = currentByteValue; + currentByteValue = (currentByteValue + 1) % 256; + return value; +} + +function generateRandomMac() { + let mac = ''; + for (let i = 0; i < 6; i++) { + if (mac.length) mac += ':'; + let byte = getNextByteValue(); + mac += byteToHex(byte); + } + return mac; +} + function bytesToHexString(bytes) { if (!bytes) { print("Invalid input for bytesToHexString"); @@ -50,9 +66,7 @@ function sendRandomModelAdvertisement() { return; } - let Mac = bleBeacon.genMac(); - - bleBeacon.setMac(Mac); + bleBeacon.setMac(generateRandomMac()); bleBeacon.setData(packetString); bleBeacon.send(); @@ -60,8 +74,7 @@ function sendRandomModelAdvertisement() { currentIndex = (currentIndex + 1) % watchValues.length; - delay(500); // 500 Ms is the fatest we can manage to recieve - // in C Flipper is way faster at spamming this + delay(500); bleBeacon.stop(); From ae39c99d67f2deda7cf2b14e0537addaf19dc3c1 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 7 Mar 2024 04:34:53 +0000 Subject: [PATCH 08/22] BleSpam: Fix hang on lock keyboard --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index d2f94be5ef..ea5d532d8c 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit d2f94be5ef4ca54b2df16879880d05562f6f267c +Subproject commit ea5d532d8ccdbba597b620d1cb44d5a83bd6f700 From 86986278d3ccfcd6ad3d8fde974f54942578d4b1 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 7 Mar 2024 04:56:53 +0000 Subject: [PATCH 09/22] Format --- applications/system/js_app/application.fam | 2 - .../system/js_app/modules/js_blebeacon.c | 97 +++++++++---------- .../system/js_app/modules/js_keyboard.c | 63 ++++++------ applications/system/js_app/modules/js_math.c | 79 +++++++-------- 4 files changed, 120 insertions(+), 121 deletions(-) diff --git a/applications/system/js_app/application.fam b/applications/system/js_app/application.fam index 8bd2f4a2f4..74be8273db 100644 --- a/applications/system/js_app/application.fam +++ b/applications/system/js_app/application.fam @@ -94,5 +94,3 @@ App( requires=["js_app"], sources=["modules/js_keyboard.c"], ) - - diff --git a/applications/system/js_app/modules/js_blebeacon.c b/applications/system/js_app/modules/js_blebeacon.c index ff7176c526..0e77e56484 100644 --- a/applications/system/js_app/modules/js_blebeacon.c +++ b/applications/system/js_app/modules/js_blebeacon.c @@ -9,10 +9,9 @@ typedef struct { GapExtraBeaconConfig beacon_config; } JSblebeaconInst; - struct OUI_MAP_ENTRY { - const char *brand; - const char *oui; + const char* brand; + const char* oui; }; struct OUI_MAP_ENTRY OUI_MAP[] = { @@ -36,41 +35,39 @@ struct OUI_MAP_ENTRY OUI_MAP[] = { #define OUI_MAP_SIZE (sizeof(OUI_MAP) / sizeof(OUI_MAP[0])) - int rand_range(int min, int max) { return min + rand() / (RAND_MAX / (max - min + 1) + 1); } -void byte_to_hex(char *output, unsigned char byte) { +void byte_to_hex(char* output, unsigned char byte) { static const char hex_chars[] = "0123456789ABCDEF"; output[0] = hex_chars[byte >> 4]; output[1] = hex_chars[byte & 0x0F]; } -static char* generate_mac_address(const char *brand) { - char *mac_address = (char*)malloc(18 * sizeof(char)); - if (mac_address == NULL) { +static char* generate_mac_address(const char* brand) { + char* mac_address = (char*)malloc(18 * sizeof(char)); + if(mac_address == NULL) { FURI_LOG_D("BLE", "Memory allocation failed.\n"); return NULL; } - const char *oui = NULL; - for (unsigned int i = 0; i < OUI_MAP_SIZE; ++i) { - if (strcmp(brand, OUI_MAP[i].brand) == 0) { + const char* oui = NULL; + for(unsigned int i = 0; i < OUI_MAP_SIZE; ++i) { + if(strcmp(brand, OUI_MAP[i].brand) == 0) { oui = OUI_MAP[i].oui; break; } } - if (oui == NULL) { + if(oui == NULL) { FURI_LOG_D("BLE", "Brand not found.\n"); free(mac_address); return NULL; } - char last_bytes[6]; - for (int i = 0; i < 3; ++i) { + for(int i = 0; i < 3; ++i) { unsigned char byte = rand_range(0x00, 0xFF); byte_to_hex(&last_bytes[i * 2], byte); } @@ -114,9 +111,9 @@ static bool get_str_arg(struct mjs* mjs, size_t index, char** value) { ret_bad_args(mjs, "Bad string argument"); return false; } - + *value = (char*)malloc(str_len + 1); - if (!*value) { + if(!*value) { ret_bad_args(mjs, "Memory allocation failed"); return false; } @@ -126,24 +123,23 @@ static bool get_str_arg(struct mjs* mjs, size_t index, char** value) { } static uint8_t hex_char_to_uint(char c) { - if (c >= '0' && c <= '9') return c - '0'; - if (c >= 'a' && c <= 'f') return 10 + c - 'a'; - if (c >= 'A' && c <= 'F') return 10 + c - 'A'; + if(c >= '0' && c <= '9') return c - '0'; + if(c >= 'a' && c <= 'f') return 10 + c - 'a'; + if(c >= 'A' && c <= 'F') return 10 + c - 'A'; return 0; } static uint8_t* macstr_to_uint8(const char* macstr) { - if (strlen(macstr) != 17) return NULL; + if(strlen(macstr) != 17) return NULL; uint8_t* mac_bytes = (uint8_t*)malloc(6 * sizeof(uint8_t)); - if (!mac_bytes) return NULL; - - for (size_t i = 0, j = 0; i < 17; i += 3, ++j) { + if(!mac_bytes) return NULL; + for(size_t i = 0, j = 0; i < 17; i += 3, ++j) { mac_bytes[j] = (hex_char_to_uint(macstr[i]) << 4) | hex_char_to_uint(macstr[i + 1]); - if (i < 15 && macstr[i + 2] != ':' && macstr[i + 2] != '-') { - free(mac_bytes); + if(i < 15 && macstr[i + 2] != ':' && macstr[i + 2] != '-') { + free(mac_bytes); return NULL; } } @@ -153,43 +149,42 @@ static uint8_t* macstr_to_uint8(const char* macstr) { static uint8_t* hexstr_to_uint8(const char* hexstr, size_t* out_length) { size_t len = strlen(hexstr); - if (len % 2 != 0) return NULL; + if(len % 2 != 0) return NULL; - if (len > EXTRA_BEACON_MAX_DATA_SIZE + 1) return NULL; + if(len > EXTRA_BEACON_MAX_DATA_SIZE + 1) return NULL; *out_length = len / 2; uint8_t* bytes = (uint8_t*)malloc(*out_length); - if (!bytes) return NULL; + if(!bytes) return NULL; - for (size_t i = 0; i < *out_length; ++i) { + for(size_t i = 0; i < *out_length; ++i) { bytes[i] = (hex_char_to_uint(hexstr[i * 2]) << 4) | hex_char_to_uint(hexstr[i * 2 + 1]); } return bytes; } -static void js_blebeacon_set_data(struct mjs *mjs) { +static void js_blebeacon_set_data(struct mjs* mjs) { FURI_LOG_D("BLE", "Setting data"); - if (!check_arg_count(mjs, 1)) return; + if(!check_arg_count(mjs, 1)) return; JSblebeaconInst* inst = get_this_ctx(mjs); - if (!inst) { + if(!inst) { FURI_LOG_D("BLE", "Beacon instance is null"); ret_bad_args(mjs, "Beacon instance is null"); return; } - if (inst->data) { + if(inst->data) { FURI_LOG_D("BLE", "Freeing existing data"); free(inst->data); - inst->data = NULL; + inst->data = NULL; } - - if (!get_str_arg(mjs, 0, &(inst->data))) return; // get_str_arg now modifies inst->data directly + if(!get_str_arg(mjs, 0, &(inst->data))) return; // get_str_arg now modifies inst->data directly size_t data_len = 0; uint8_t* beacon_data = hexstr_to_uint8(inst->data, &data_len); - if (!beacon_data) { + if(!beacon_data) { FURI_LOG_D("BLE", "Failed to convert data to hex"); ret_bad_args(mjs, "Failed to convert data to hex"); return; @@ -201,10 +196,9 @@ static void js_blebeacon_set_data(struct mjs *mjs) { mjs_return(mjs, MJS_UNDEFINED); } -static void js_blebeacon_generate_mac(struct mjs *mjs) -{ +static void js_blebeacon_generate_mac(struct mjs* mjs) { char* company = ""; - if (!get_str_arg(mjs, 0, &company)) return; + if(!get_str_arg(mjs, 0, &company)) return; char* mac = generate_mac_address(company); @@ -213,17 +207,16 @@ static void js_blebeacon_generate_mac(struct mjs *mjs) return mjs_return(mjs, js_mac_address); } -static void js_blebeacon_set_mac(struct mjs *mjs) { +static void js_blebeacon_set_mac(struct mjs* mjs) { FURI_LOG_D("BLE", "Setting Mac"); - if (!check_arg_count(mjs, 1)) - { + if(!check_arg_count(mjs, 1)) { ret_bad_args(mjs, "Bad args"); return; } - + JSblebeaconInst* inst = get_this_ctx(mjs); char* mac_addr = ""; - if (!get_str_arg(mjs, 0, &mac_addr)) return; + if(!get_str_arg(mjs, 0, &mac_addr)) return; inst->mac_addr = mac_addr; inst->beacon_config.min_adv_interval_ms = 50; inst->beacon_config.max_adv_interval_ms = 150; @@ -234,7 +227,7 @@ static void js_blebeacon_set_mac(struct mjs *mjs) { inst->beacon_config.address_type = GapAddressTypePublic; uint8_t* mac = macstr_to_uint8(mac_addr); - if (mac) { + if(mac) { memcpy(inst->beacon_config.address, mac, 6); furi_hal_bt_extra_beacon_set_config(&inst->beacon_config); mjs_return(mjs, MJS_UNDEFINED); @@ -245,21 +238,19 @@ static void js_blebeacon_set_mac(struct mjs *mjs) { } } -static void js_blebeacon_send(struct mjs *mjs) { - +static void js_blebeacon_send(struct mjs* mjs) { furi_hal_bt_extra_beacon_start(); mjs_return(mjs, MJS_UNDEFINED); } -static void js_blebeacon_stop(struct mjs *mjs) { - +static void js_blebeacon_stop(struct mjs* mjs) { furi_hal_bt_extra_beacon_stop(); mjs_return(mjs, MJS_UNDEFINED); } -static void* js_blebeacon_create(struct mjs *mjs, mjs_val_t* object) { +static void* js_blebeacon_create(struct mjs* mjs, mjs_val_t* object) { JSblebeaconInst* inst = malloc(sizeof(JSblebeaconInst)); mjs_val_t blebeacon_obj = mjs_mk_object(mjs); mjs_set(mjs, blebeacon_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, inst)); @@ -272,9 +263,9 @@ static void* js_blebeacon_create(struct mjs *mjs, mjs_val_t* object) { return inst; } -static void js_blebeacon_destroy(void *ptr) { +static void js_blebeacon_destroy(void* ptr) { JSblebeaconInst* inst = (JSblebeaconInst*)ptr; - if (inst) { + if(inst) { free(inst->data); free(inst->mac_addr); free(inst); diff --git a/applications/system/js_app/modules/js_keyboard.c b/applications/system/js_app/modules/js_keyboard.c index 1988fda25b..d4e9fcba18 100644 --- a/applications/system/js_app/modules/js_keyboard.c +++ b/applications/system/js_app/modules/js_keyboard.c @@ -20,7 +20,7 @@ static void ret_bad_args(struct mjs* mjs, const char* error) { char nibble_to_hex_character(uint8_t nibble) { nibble &= 0xF; - if (nibble < 10) { + if(nibble < 10) { return '0' + nibble; } else { return 'A' + (nibble - 10); @@ -29,12 +29,12 @@ char nibble_to_hex_character(uint8_t nibble) { char* bytes_to_hex_string(uint8_t* bytes, size_t num_bytes) { char* hex_string = (char*)malloc(num_bytes * 2 + 1); - if (hex_string == NULL) { + if(hex_string == NULL) { FURI_LOG_D("LOG", "Memory allocation failed\n"); return NULL; } - for (size_t i = 0; i < num_bytes; ++i) { + for(size_t i = 0; i < num_bytes; ++i) { hex_string[i * 2] = nibble_to_hex_character(bytes[i] >> 4); hex_string[i * 2 + 1] = nibble_to_hex_character(bytes[i] & 0xF); } @@ -74,20 +74,17 @@ static JSkeyboardInst* get_this_ctx(struct mjs* mjs) { return storage; } -void text_input_callback(void* context) -{ +void text_input_callback(void* context) { JSkeyboardInst* keyboardinst = (JSkeyboardInst*)context; view_dispatcher_stop(keyboardinst->view_dispatcher); } -void byte_input_callback(void* context) -{ +void byte_input_callback(void* context) { JSkeyboardInst* keyboardinst = (JSkeyboardInst*)context; view_dispatcher_stop(keyboardinst->view_dispatcher); } -static void js_keyboard_text(struct mjs *mjs) { - +static void js_keyboard_text(struct mjs* mjs) { JSkeyboardInst* keyboardinst = get_this_ctx(mjs); int MaxInputLength; @@ -100,18 +97,24 @@ static void js_keyboard_text(struct mjs *mjs) { mjs_val_t bool_obj = mjs_arg(mjs, 2); ShouldSelect = (int)mjs_get_bool(mjs, bool_obj); - if (keyboardinst->textinput && keyboardinst->view_dispatcher) - { - if (strlen(defaultText) > 0) - { + if(keyboardinst->textinput && keyboardinst->view_dispatcher) { + if(strlen(defaultText) > 0) { text_input_set_header_text(keyboardinst->textinput, defaultText); } view_dispatcher_attach_to_gui( - keyboardinst->view_dispatcher, furi_record_open(RECORD_GUI), ViewDispatcherTypeFullscreen); + keyboardinst->view_dispatcher, + furi_record_open(RECORD_GUI), + ViewDispatcherTypeFullscreen); furi_record_close(RECORD_GUI); - text_input_set_result_callback(keyboardinst->textinput, text_input_callback, keyboardinst, keyboardinst->data, MaxInputLength, ShouldSelect); + text_input_set_result_callback( + keyboardinst->textinput, + text_input_callback, + keyboardinst, + keyboardinst->data, + MaxInputLength, + ShouldSelect); view_dispatcher_switch_to_view(keyboardinst->view_dispatcher, 0); @@ -123,10 +126,7 @@ static void js_keyboard_text(struct mjs *mjs) { mjs_return(mjs, mjs_mk_string(mjs, keyboardinst->data, strlen(keyboardinst->data), 1)); } -static void js_keyboard_byte(struct mjs *mjs) { - - - +static void js_keyboard_byte(struct mjs* mjs) { JSkeyboardInst* keyboardinst = get_this_ctx(mjs); int MaxInputLength; @@ -135,18 +135,24 @@ static void js_keyboard_byte(struct mjs *mjs) { const char* defaultText; get_str_arg(mjs, 1, &defaultText); - if (keyboardinst->byteinputview && keyboardinst->view_dispatcher) - { - if (strlen(defaultText) > 0) - { + if(keyboardinst->byteinputview && keyboardinst->view_dispatcher) { + if(strlen(defaultText) > 0) { byte_input_set_header_text(keyboardinst->byteinputview, defaultText); } view_dispatcher_attach_to_gui( - keyboardinst->view_dispatcher, furi_record_open(RECORD_GUI), ViewDispatcherTypeFullscreen); + keyboardinst->view_dispatcher, + furi_record_open(RECORD_GUI), + ViewDispatcherTypeFullscreen); furi_record_close(RECORD_GUI); - byte_input_set_result_callback(keyboardinst->byteinputview, byte_input_callback, NULL, keyboardinst, keyboardinst->byteinput, 10); + byte_input_set_result_callback( + keyboardinst->byteinputview, + byte_input_callback, + NULL, + keyboardinst, + keyboardinst->byteinput, + 10); view_dispatcher_switch_to_view(keyboardinst->view_dispatcher, 1); @@ -160,7 +166,6 @@ static void js_keyboard_byte(struct mjs *mjs) { FURI_LOG_D("SHIT", "DID THING 2"); } - static void* js_keyboard_create(struct mjs* mjs, mjs_val_t* object) { JSkeyboardInst* keyboardinst = malloc(sizeof(JSkeyboardInst)); mjs_val_t keyboard_obj = mjs_mk_object(mjs); @@ -173,8 +178,10 @@ static void* js_keyboard_create(struct mjs* mjs, mjs_val_t* object) { keyboardinst->data = malloc(100); keyboardinst->byteinput = malloc(100); view_dispatcher_enable_queue(keyboardinst->view_dispatcher); - view_dispatcher_add_view(keyboardinst->view_dispatcher, 0, text_input_get_view(keyboardinst->textinput)); - view_dispatcher_add_view(keyboardinst->view_dispatcher, 1, byte_input_get_view(keyboardinst->byteinputview)); + view_dispatcher_add_view( + keyboardinst->view_dispatcher, 0, text_input_get_view(keyboardinst->textinput)); + view_dispatcher_add_view( + keyboardinst->view_dispatcher, 1, byte_input_get_view(keyboardinst->byteinputview)); *object = keyboard_obj; return keyboardinst; } diff --git a/applications/system/js_app/modules/js_math.c b/applications/system/js_app/modules/js_math.c index d17b72b888..5e78fc661f 100644 --- a/applications/system/js_app/modules/js_math.c +++ b/applications/system/js_app/modules/js_math.c @@ -19,7 +19,7 @@ static bool check_arg_count(struct mjs* mjs, size_t count) { } void mjs_abs(struct mjs* mjs) { - if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); @@ -27,11 +27,11 @@ void mjs_abs(struct mjs* mjs) { } void mjs_acos(struct mjs* mjs) { - if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); - if (x < -1 || x > 1) { + if(x < -1 || x > 1) { ret_bad_args(mjs, "Invalid input value for Math.acos"); mjs_return(mjs, MJS_UNDEFINED); } @@ -39,11 +39,11 @@ void mjs_acos(struct mjs* mjs) { } void mjs_acosh(struct mjs* mjs) { - if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); - if (x < 1) { + if(x < 1) { ret_bad_args(mjs, "Invalid input value for Math.acosh"); mjs_return(mjs, MJS_UNDEFINED); } @@ -51,7 +51,7 @@ void mjs_acosh(struct mjs* mjs) { } void mjs_asin(struct mjs* mjs) { - if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); @@ -59,7 +59,7 @@ void mjs_asin(struct mjs* mjs) { } void mjs_asinh(struct mjs* mjs) { - if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); @@ -67,7 +67,7 @@ void mjs_asinh(struct mjs* mjs) { } void mjs_atan(struct mjs* mjs) { - if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); @@ -75,7 +75,8 @@ void mjs_atan(struct mjs* mjs) { } void mjs_atan2(struct mjs* mjs) { - if (!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) || !mjs_is_number(mjs_arg(mjs, 1))) { + if(!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) || + !mjs_is_number(mjs_arg(mjs, 1))) { mjs_return(mjs, MJS_UNDEFINED); } double y = mjs_get_double(mjs, mjs_arg(mjs, 0)); @@ -84,11 +85,11 @@ void mjs_atan2(struct mjs* mjs) { } void mjs_atanh(struct mjs* mjs) { - if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); - if (x <= -1 || x >= 1) { + if(x <= -1 || x >= 1) { ret_bad_args(mjs, "Invalid input value for Math.atanh"); mjs_return(mjs, MJS_UNDEFINED); } @@ -96,7 +97,7 @@ void mjs_atanh(struct mjs* mjs) { } void mjs_cbrt(struct mjs* mjs) { - if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); @@ -104,7 +105,7 @@ void mjs_cbrt(struct mjs* mjs) { } void mjs_ceil(struct mjs* mjs) { - if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); @@ -112,12 +113,12 @@ void mjs_ceil(struct mjs* mjs) { } void mjs_clz32(struct mjs* mjs) { - if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } unsigned int x = (unsigned int)mjs_get_int(mjs, mjs_arg(mjs, 0)); int count = 0; - while (x) { + while(x) { x >>= 1; count++; } @@ -125,7 +126,7 @@ void mjs_clz32(struct mjs* mjs) { } void mjs_cos(struct mjs* mjs) { - if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); @@ -133,13 +134,13 @@ void mjs_cos(struct mjs* mjs) { } void mjs_exp(struct mjs* mjs) { - if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); double result = 1; double term = 1; - for (int i = 1; i < 100; i++) { + for(int i = 1; i < 100; i++) { term *= x / i; result += term; } @@ -147,7 +148,7 @@ void mjs_exp(struct mjs* mjs) { } void mjs_floor(struct mjs* mjs) { - if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); @@ -155,16 +156,16 @@ void mjs_floor(struct mjs* mjs) { } void mjs_log(struct mjs* mjs) { - if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); - if (x <= 0) { + if(x <= 0) { ret_bad_args(mjs, "Invalid input value for Math.log"); mjs_return(mjs, MJS_UNDEFINED); } double result = 0; - while (x >= MJS_E) { + while(x >= MJS_E) { x /= MJS_E; result++; } @@ -172,7 +173,8 @@ void mjs_log(struct mjs* mjs) { } void mjs_max(struct mjs* mjs) { - if (!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) || !mjs_is_number(mjs_arg(mjs, 1))) { + if(!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) || + !mjs_is_number(mjs_arg(mjs, 1))) { mjs_return(mjs, MJS_UNDEFINED); } double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); @@ -181,7 +183,8 @@ void mjs_max(struct mjs* mjs) { } void mjs_min(struct mjs* mjs) { - if (!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) || !mjs_is_number(mjs_arg(mjs, 1))) { + if(!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) || + !mjs_is_number(mjs_arg(mjs, 1))) { mjs_return(mjs, MJS_UNDEFINED); } double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); @@ -190,20 +193,21 @@ void mjs_min(struct mjs* mjs) { } void mjs_pow(struct mjs* mjs) { - if (!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) || !mjs_is_number(mjs_arg(mjs, 1))) { + if(!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) || + !mjs_is_number(mjs_arg(mjs, 1))) { mjs_return(mjs, MJS_UNDEFINED); } double base = mjs_get_double(mjs, mjs_arg(mjs, 0)); double exponent = mjs_get_double(mjs, mjs_arg(mjs, 1)); double result = 1; - for (int i = 0; i < exponent; i++) { + for(int i = 0; i < exponent; i++) { result *= base; } mjs_return(mjs, mjs_mk_number(mjs, result)); } void mjs_random(struct mjs* mjs) { - if (!check_arg_count(mjs, 0)) { + if(!check_arg_count(mjs, 0)) { mjs_return(mjs, MJS_UNDEFINED); } const uint32_t random_val = furi_hal_random_get(); @@ -212,7 +216,7 @@ void mjs_random(struct mjs* mjs) { } void mjs_sign(struct mjs* mjs) { - if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); @@ -220,13 +224,13 @@ void mjs_sign(struct mjs* mjs) { } void mjs_sin(struct mjs* mjs) { - if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); double result = x; double term = x; - for (int i = 1; i < 10; i++) { + for(int i = 1; i < 10; i++) { term *= -x * x / ((2 * i) * (2 * i + 1)); result += term; } @@ -234,31 +238,30 @@ void mjs_sin(struct mjs* mjs) { } void mjs_sqrt(struct mjs* mjs) { - if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); - if (x < 0) { + if(x < 0) { ret_bad_args(mjs, "Invalid input value for Math.sqrt"); mjs_return(mjs, MJS_UNDEFINED); } double result = 1; - while (result * result < x) { + while(result * result < x) { result += (double)0.001; } mjs_return(mjs, mjs_mk_number(mjs, result)); } void mjs_trunc(struct mjs* mjs) { - if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); mjs_return(mjs, mjs_mk_number(mjs, x < 0 ? ceil(x) : floor(x))); } - -static void* js_math_create(struct mjs *mjs, mjs_val_t* object) { +static void* js_math_create(struct mjs* mjs, mjs_val_t* object) { mjs_val_t math_obj = mjs_mk_object(mjs); mjs_set(mjs, math_obj, "abs", ~0, MJS_MK_FN(mjs_abs)); mjs_set(mjs, math_obj, "acos", ~0, MJS_MK_FN(mjs_acos)); @@ -286,9 +289,9 @@ static void* js_math_create(struct mjs *mjs, mjs_val_t* object) { mjs_set(mjs, math_obj, "PI", ~0, mjs_mk_number(mjs, MJS_PI)); *object = math_obj; return object; -} +} -static void js_math_destroy(void *ptr) { +static void js_math_destroy(void* ptr) { UNUSED(ptr); } From be014b5103cf03371e789608fb1483ed793e94fd Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 7 Mar 2024 05:15:01 +0000 Subject: [PATCH 10/22] Small JS bindings tweaks - no destroy for math - tweak example print results - namespace naming consistency --- .../js_app/examples/apps/Scripts/keyboard.js | 2 +- .../js_app/examples/apps/Scripts/submenu.js | 2 +- .../system/js_app/modules/js_blebeacon.c | 18 +-- .../system/js_app/modules/js_keyboard.c | 23 ++-- applications/system/js_app/modules/js_math.c | 116 +++++++++--------- 5 files changed, 78 insertions(+), 83 deletions(-) diff --git a/applications/system/js_app/examples/apps/Scripts/keyboard.js b/applications/system/js_app/examples/apps/Scripts/keyboard.js index 6e7b8332a1..ddcb40a614 100644 --- a/applications/system/js_app/examples/apps/Scripts/keyboard.js +++ b/applications/system/js_app/examples/apps/Scripts/keyboard.js @@ -2,4 +2,4 @@ let keyboard = require("keyboard"); let text = keyboard.text(100, "Please Input Shit", 1); -print(text); \ No newline at end of file +print("Got text:", text); \ No newline at end of file diff --git a/applications/system/js_app/examples/apps/Scripts/submenu.js b/applications/system/js_app/examples/apps/Scripts/submenu.js index 4c0cd79d03..6744ca4526 100644 --- a/applications/system/js_app/examples/apps/Scripts/submenu.js +++ b/applications/system/js_app/examples/apps/Scripts/submenu.js @@ -8,4 +8,4 @@ submenu.setHeader("Select an option:"); let result = submenu.show(); -print("Result: ", result); +print("Result:", result); diff --git a/applications/system/js_app/modules/js_blebeacon.c b/applications/system/js_app/modules/js_blebeacon.c index 0e77e56484..946a99babc 100644 --- a/applications/system/js_app/modules/js_blebeacon.c +++ b/applications/system/js_app/modules/js_blebeacon.c @@ -7,7 +7,7 @@ typedef struct { char* mac_addr; size_t beacon_data_len; GapExtraBeaconConfig beacon_config; -} JSblebeaconInst; +} JsBlebeaconInst; struct OUI_MAP_ENTRY { const char* brand; @@ -78,9 +78,9 @@ static char* generate_mac_address(const char* brand) { return mac_address; } -static JSblebeaconInst* get_this_ctx(struct mjs* mjs) { +static JsBlebeaconInst* get_this_ctx(struct mjs* mjs) { mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); - JSblebeaconInst* storage = mjs_get_ptr(mjs, obj_inst); + JsBlebeaconInst* storage = mjs_get_ptr(mjs, obj_inst); furi_assert(storage); return storage; } @@ -167,7 +167,7 @@ static uint8_t* hexstr_to_uint8(const char* hexstr, size_t* out_length) { static void js_blebeacon_set_data(struct mjs* mjs) { FURI_LOG_D("BLE", "Setting data"); if(!check_arg_count(mjs, 1)) return; - JSblebeaconInst* inst = get_this_ctx(mjs); + JsBlebeaconInst* inst = get_this_ctx(mjs); if(!inst) { FURI_LOG_D("BLE", "Beacon instance is null"); ret_bad_args(mjs, "Beacon instance is null"); @@ -214,7 +214,7 @@ static void js_blebeacon_set_mac(struct mjs* mjs) { return; } - JSblebeaconInst* inst = get_this_ctx(mjs); + JsBlebeaconInst* inst = get_this_ctx(mjs); char* mac_addr = ""; if(!get_str_arg(mjs, 0, &mac_addr)) return; inst->mac_addr = mac_addr; @@ -251,7 +251,7 @@ static void js_blebeacon_stop(struct mjs* mjs) { } static void* js_blebeacon_create(struct mjs* mjs, mjs_val_t* object) { - JSblebeaconInst* inst = malloc(sizeof(JSblebeaconInst)); + JsBlebeaconInst* inst = malloc(sizeof(JsBlebeaconInst)); mjs_val_t blebeacon_obj = mjs_mk_object(mjs); mjs_set(mjs, blebeacon_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, inst)); mjs_set(mjs, blebeacon_obj, "setData", ~0, MJS_MK_FN(js_blebeacon_set_data)); @@ -264,7 +264,7 @@ static void* js_blebeacon_create(struct mjs* mjs, mjs_val_t* object) { } static void js_blebeacon_destroy(void* ptr) { - JSblebeaconInst* inst = (JSblebeaconInst*)ptr; + JsBlebeaconInst* inst = (JsBlebeaconInst*)ptr; if(inst) { free(inst->data); free(inst->mac_addr); @@ -278,12 +278,12 @@ static const JsModuleDescriptor js_blebeacon_desc = { js_blebeacon_destroy, }; -static const FlipperAppPluginDescriptor blebeacon_plugin_descriptor = { +static const FlipperAppPluginDescriptor plugin_descriptor = { .appid = PLUGIN_APP_ID, .ep_api_version = PLUGIN_API_VERSION, .entry_point = &js_blebeacon_desc, }; const FlipperAppPluginDescriptor* js_blebeacon_ep(void) { - return &blebeacon_plugin_descriptor; + return &plugin_descriptor; } \ No newline at end of file diff --git a/applications/system/js_app/modules/js_keyboard.c b/applications/system/js_app/modules/js_keyboard.c index d4e9fcba18..e120ee0d64 100644 --- a/applications/system/js_app/modules/js_keyboard.c +++ b/applications/system/js_app/modules/js_keyboard.c @@ -11,7 +11,7 @@ typedef struct { ByteInput* byteinputview; ViewDispatcher* view_dispatcher; uint8_t* byteinput; -} JSkeyboardInst; +} JsKeyboardInst; static void ret_bad_args(struct mjs* mjs, const char* error) { mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "%s", error); @@ -67,25 +67,25 @@ static bool get_int_arg(struct mjs* mjs, size_t index, int* value) { return true; } -static JSkeyboardInst* get_this_ctx(struct mjs* mjs) { +static JsKeyboardInst* get_this_ctx(struct mjs* mjs) { mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); - JSkeyboardInst* storage = mjs_get_ptr(mjs, obj_inst); + JsKeyboardInst* storage = mjs_get_ptr(mjs, obj_inst); furi_assert(storage); return storage; } void text_input_callback(void* context) { - JSkeyboardInst* keyboardinst = (JSkeyboardInst*)context; + JsKeyboardInst* keyboardinst = (JsKeyboardInst*)context; view_dispatcher_stop(keyboardinst->view_dispatcher); } void byte_input_callback(void* context) { - JSkeyboardInst* keyboardinst = (JSkeyboardInst*)context; + JsKeyboardInst* keyboardinst = (JsKeyboardInst*)context; view_dispatcher_stop(keyboardinst->view_dispatcher); } static void js_keyboard_text(struct mjs* mjs) { - JSkeyboardInst* keyboardinst = get_this_ctx(mjs); + JsKeyboardInst* keyboardinst = get_this_ctx(mjs); int MaxInputLength; if(!get_int_arg(mjs, 0, &MaxInputLength)) return; @@ -127,7 +127,7 @@ static void js_keyboard_text(struct mjs* mjs) { } static void js_keyboard_byte(struct mjs* mjs) { - JSkeyboardInst* keyboardinst = get_this_ctx(mjs); + JsKeyboardInst* keyboardinst = get_this_ctx(mjs); int MaxInputLength; if(!get_int_arg(mjs, 0, &MaxInputLength)) return; @@ -167,7 +167,7 @@ static void js_keyboard_byte(struct mjs* mjs) { } static void* js_keyboard_create(struct mjs* mjs, mjs_val_t* object) { - JSkeyboardInst* keyboardinst = malloc(sizeof(JSkeyboardInst)); + JsKeyboardInst* keyboardinst = malloc(sizeof(JsKeyboardInst)); mjs_val_t keyboard_obj = mjs_mk_object(mjs); mjs_set(mjs, keyboard_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, keyboardinst)); mjs_set(mjs, keyboard_obj, "text", ~0, MJS_MK_FN(js_keyboard_text)); @@ -187,7 +187,7 @@ static void* js_keyboard_create(struct mjs* mjs, mjs_val_t* object) { } static void js_keyboard_destroy(void* inst) { - JSkeyboardInst* insts = (JSkeyboardInst*)inst; + JsKeyboardInst* insts = (JsKeyboardInst*)inst; byte_input_free(insts->byteinputview); text_input_free(insts->textinput); view_dispatcher_free(insts->view_dispatcher); @@ -201,13 +201,12 @@ static const JsModuleDescriptor js_keyboard_desc = { js_keyboard_destroy, }; -static const FlipperAppPluginDescriptor keyboard_plugin_descriptor = { +static const FlipperAppPluginDescriptor plugin_descriptor = { .appid = PLUGIN_APP_ID, .ep_api_version = PLUGIN_API_VERSION, .entry_point = &js_keyboard_desc, }; const FlipperAppPluginDescriptor* js_keyboard_ep(void) { - UNUSED(js_keyboard_byte); - return &keyboard_plugin_descriptor; + return &plugin_descriptor; } \ No newline at end of file diff --git a/applications/system/js_app/modules/js_math.c b/applications/system/js_app/modules/js_math.c index 5e78fc661f..2af989908d 100644 --- a/applications/system/js_app/modules/js_math.c +++ b/applications/system/js_app/modules/js_math.c @@ -1,8 +1,8 @@ #include "../js_modules.h" #include "furi_hal_random.h" -#define MJS_PI (double)3.14159265358979323846 -#define MJS_E (double)2.7182818284590452354 +#define JS_MATH_PI (double)3.14159265358979323846 +#define JS_MATH_E (double)2.7182818284590452354 static void ret_bad_args(struct mjs* mjs, const char* error) { mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "%s", error); @@ -18,7 +18,7 @@ static bool check_arg_count(struct mjs* mjs, size_t count) { return true; } -void mjs_abs(struct mjs* mjs) { +void js_math_abs(struct mjs* mjs) { if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } @@ -26,7 +26,7 @@ void mjs_abs(struct mjs* mjs) { mjs_return(mjs, x < 0 ? mjs_mk_number(mjs, -x) : mjs_arg(mjs, 0)); } -void mjs_acos(struct mjs* mjs) { +void js_math_acos(struct mjs* mjs) { if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } @@ -35,10 +35,10 @@ void mjs_acos(struct mjs* mjs) { ret_bad_args(mjs, "Invalid input value for Math.acos"); mjs_return(mjs, MJS_UNDEFINED); } - mjs_return(mjs, mjs_mk_number(mjs, MJS_PI / (double)2 - atan(x / sqrt(1 - x * x)))); + mjs_return(mjs, mjs_mk_number(mjs, JS_MATH_PI / (double)2 - atan(x / sqrt(1 - x * x)))); } -void mjs_acosh(struct mjs* mjs) { +void js_math_acosh(struct mjs* mjs) { if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } @@ -50,7 +50,7 @@ void mjs_acosh(struct mjs* mjs) { mjs_return(mjs, mjs_mk_number(mjs, log(x + sqrt(x * x - 1)))); } -void mjs_asin(struct mjs* mjs) { +void js_math_asin(struct mjs* mjs) { if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } @@ -58,7 +58,7 @@ void mjs_asin(struct mjs* mjs) { mjs_return(mjs, mjs_mk_number(mjs, atan(x / sqrt(1 - x * x)))); } -void mjs_asinh(struct mjs* mjs) { +void js_math_asinh(struct mjs* mjs) { if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } @@ -66,7 +66,7 @@ void mjs_asinh(struct mjs* mjs) { mjs_return(mjs, mjs_mk_number(mjs, log(x + sqrt(x * x + 1)))); } -void mjs_atan(struct mjs* mjs) { +void js_math_atan(struct mjs* mjs) { if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } @@ -74,7 +74,7 @@ void mjs_atan(struct mjs* mjs) { mjs_return(mjs, mjs_mk_number(mjs, atan(x))); } -void mjs_atan2(struct mjs* mjs) { +void js_math_atan2(struct mjs* mjs) { if(!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) || !mjs_is_number(mjs_arg(mjs, 1))) { mjs_return(mjs, MJS_UNDEFINED); @@ -84,7 +84,7 @@ void mjs_atan2(struct mjs* mjs) { mjs_return(mjs, mjs_mk_number(mjs, atan2(y, x))); } -void mjs_atanh(struct mjs* mjs) { +void js_math_atanh(struct mjs* mjs) { if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } @@ -96,7 +96,7 @@ void mjs_atanh(struct mjs* mjs) { mjs_return(mjs, mjs_mk_number(mjs, (double)0.5 * log((1 + x) / (1 - x)))); } -void mjs_cbrt(struct mjs* mjs) { +void js_math_cbrt(struct mjs* mjs) { if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } @@ -104,7 +104,7 @@ void mjs_cbrt(struct mjs* mjs) { mjs_return(mjs, mjs_mk_number(mjs, pow(x, 1.0 / 3.0))); } -void mjs_ceil(struct mjs* mjs) { +void js_math_ceil(struct mjs* mjs) { if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } @@ -112,7 +112,7 @@ void mjs_ceil(struct mjs* mjs) { mjs_return(mjs, mjs_mk_number(mjs, (int)(x + (double)0.5))); } -void mjs_clz32(struct mjs* mjs) { +void js_math_clz32(struct mjs* mjs) { if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } @@ -125,7 +125,7 @@ void mjs_clz32(struct mjs* mjs) { mjs_return(mjs, mjs_mk_number(mjs, 32 - count)); } -void mjs_cos(struct mjs* mjs) { +void js_math_cos(struct mjs* mjs) { if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } @@ -133,7 +133,7 @@ void mjs_cos(struct mjs* mjs) { mjs_return(mjs, mjs_mk_number(mjs, cos(x))); } -void mjs_exp(struct mjs* mjs) { +void js_math_exp(struct mjs* mjs) { if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } @@ -147,7 +147,7 @@ void mjs_exp(struct mjs* mjs) { mjs_return(mjs, mjs_mk_number(mjs, result)); } -void mjs_floor(struct mjs* mjs) { +void js_math_floor(struct mjs* mjs) { if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } @@ -155,7 +155,7 @@ void mjs_floor(struct mjs* mjs) { mjs_return(mjs, mjs_mk_number(mjs, (int)x)); } -void mjs_log(struct mjs* mjs) { +void js_math_log(struct mjs* mjs) { if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } @@ -165,14 +165,14 @@ void mjs_log(struct mjs* mjs) { mjs_return(mjs, MJS_UNDEFINED); } double result = 0; - while(x >= MJS_E) { - x /= MJS_E; + while(x >= JS_MATH_E) { + x /= JS_MATH_E; result++; } mjs_return(mjs, mjs_mk_number(mjs, result + log(x))); } -void mjs_max(struct mjs* mjs) { +void js_math_max(struct mjs* mjs) { if(!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) || !mjs_is_number(mjs_arg(mjs, 1))) { mjs_return(mjs, MJS_UNDEFINED); @@ -182,7 +182,7 @@ void mjs_max(struct mjs* mjs) { mjs_return(mjs, mjs_mk_number(mjs, x > y ? x : y)); } -void mjs_min(struct mjs* mjs) { +void js_math_min(struct mjs* mjs) { if(!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) || !mjs_is_number(mjs_arg(mjs, 1))) { mjs_return(mjs, MJS_UNDEFINED); @@ -192,7 +192,7 @@ void mjs_min(struct mjs* mjs) { mjs_return(mjs, mjs_mk_number(mjs, x < y ? x : y)); } -void mjs_pow(struct mjs* mjs) { +void js_math_pow(struct mjs* mjs) { if(!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) || !mjs_is_number(mjs_arg(mjs, 1))) { mjs_return(mjs, MJS_UNDEFINED); @@ -206,7 +206,7 @@ void mjs_pow(struct mjs* mjs) { mjs_return(mjs, mjs_mk_number(mjs, result)); } -void mjs_random(struct mjs* mjs) { +void js_math_random(struct mjs* mjs) { if(!check_arg_count(mjs, 0)) { mjs_return(mjs, MJS_UNDEFINED); } @@ -215,7 +215,7 @@ void mjs_random(struct mjs* mjs) { mjs_return(mjs, mjs_mk_number(mjs, rnd)); } -void mjs_sign(struct mjs* mjs) { +void js_math_sign(struct mjs* mjs) { if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } @@ -223,7 +223,7 @@ void mjs_sign(struct mjs* mjs) { mjs_return(mjs, mjs_mk_number(mjs, x == 0 ? 0 : (x < 0 ? -1 : 1))); } -void mjs_sin(struct mjs* mjs) { +void js_math_sin(struct mjs* mjs) { if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } @@ -237,7 +237,7 @@ void mjs_sin(struct mjs* mjs) { mjs_return(mjs, mjs_mk_number(mjs, result)); } -void mjs_sqrt(struct mjs* mjs) { +void js_math_sqrt(struct mjs* mjs) { if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } @@ -253,7 +253,7 @@ void mjs_sqrt(struct mjs* mjs) { mjs_return(mjs, mjs_mk_number(mjs, result)); } -void mjs_trunc(struct mjs* mjs) { +void js_math_trunc(struct mjs* mjs) { if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { mjs_return(mjs, MJS_UNDEFINED); } @@ -263,50 +263,46 @@ void mjs_trunc(struct mjs* mjs) { static void* js_math_create(struct mjs* mjs, mjs_val_t* object) { mjs_val_t math_obj = mjs_mk_object(mjs); - mjs_set(mjs, math_obj, "abs", ~0, MJS_MK_FN(mjs_abs)); - mjs_set(mjs, math_obj, "acos", ~0, MJS_MK_FN(mjs_acos)); - mjs_set(mjs, math_obj, "acosh", ~0, MJS_MK_FN(mjs_acosh)); - mjs_set(mjs, math_obj, "asin", ~0, MJS_MK_FN(mjs_asin)); - mjs_set(mjs, math_obj, "asinh", ~0, MJS_MK_FN(mjs_asinh)); - mjs_set(mjs, math_obj, "atan", ~0, MJS_MK_FN(mjs_atan)); - mjs_set(mjs, math_obj, "atan2", ~0, MJS_MK_FN(mjs_atan2)); - mjs_set(mjs, math_obj, "atanh", ~0, MJS_MK_FN(mjs_atanh)); - mjs_set(mjs, math_obj, "cbrt", ~0, MJS_MK_FN(mjs_cbrt)); - mjs_set(mjs, math_obj, "ceil", ~0, MJS_MK_FN(mjs_ceil)); - mjs_set(mjs, math_obj, "clz32", ~0, MJS_MK_FN(mjs_clz32)); - mjs_set(mjs, math_obj, "cos", ~0, MJS_MK_FN(mjs_cos)); - mjs_set(mjs, math_obj, "exp", ~0, MJS_MK_FN(mjs_exp)); - mjs_set(mjs, math_obj, "floor", ~0, MJS_MK_FN(mjs_floor)); - mjs_set(mjs, math_obj, "log", ~0, MJS_MK_FN(mjs_log)); - mjs_set(mjs, math_obj, "max", ~0, MJS_MK_FN(mjs_max)); - mjs_set(mjs, math_obj, "min", ~0, MJS_MK_FN(mjs_min)); - mjs_set(mjs, math_obj, "pow", ~0, MJS_MK_FN(mjs_pow)); - mjs_set(mjs, math_obj, "random", ~0, MJS_MK_FN(mjs_random)); - mjs_set(mjs, math_obj, "sign", ~0, MJS_MK_FN(mjs_sign)); - mjs_set(mjs, math_obj, "sin", ~0, MJS_MK_FN(mjs_sin)); - mjs_set(mjs, math_obj, "sqrt", ~0, MJS_MK_FN(mjs_sqrt)); - mjs_set(mjs, math_obj, "trunc", ~0, MJS_MK_FN(mjs_trunc)); - mjs_set(mjs, math_obj, "PI", ~0, mjs_mk_number(mjs, MJS_PI)); + mjs_set(mjs, math_obj, "abs", ~0, MJS_MK_FN(js_math_abs)); + mjs_set(mjs, math_obj, "acos", ~0, MJS_MK_FN(js_math_acos)); + mjs_set(mjs, math_obj, "acosh", ~0, MJS_MK_FN(js_math_acosh)); + mjs_set(mjs, math_obj, "asin", ~0, MJS_MK_FN(js_math_asin)); + mjs_set(mjs, math_obj, "asinh", ~0, MJS_MK_FN(js_math_asinh)); + mjs_set(mjs, math_obj, "atan", ~0, MJS_MK_FN(js_math_atan)); + mjs_set(mjs, math_obj, "atan2", ~0, MJS_MK_FN(js_math_atan2)); + mjs_set(mjs, math_obj, "atanh", ~0, MJS_MK_FN(js_math_atanh)); + mjs_set(mjs, math_obj, "cbrt", ~0, MJS_MK_FN(js_math_cbrt)); + mjs_set(mjs, math_obj, "ceil", ~0, MJS_MK_FN(js_math_ceil)); + mjs_set(mjs, math_obj, "clz32", ~0, MJS_MK_FN(js_math_clz32)); + mjs_set(mjs, math_obj, "cos", ~0, MJS_MK_FN(js_math_cos)); + mjs_set(mjs, math_obj, "exp", ~0, MJS_MK_FN(js_math_exp)); + mjs_set(mjs, math_obj, "floor", ~0, MJS_MK_FN(js_math_floor)); + mjs_set(mjs, math_obj, "log", ~0, MJS_MK_FN(js_math_log)); + mjs_set(mjs, math_obj, "max", ~0, MJS_MK_FN(js_math_max)); + mjs_set(mjs, math_obj, "min", ~0, MJS_MK_FN(js_math_min)); + mjs_set(mjs, math_obj, "pow", ~0, MJS_MK_FN(js_math_pow)); + mjs_set(mjs, math_obj, "random", ~0, MJS_MK_FN(js_math_random)); + mjs_set(mjs, math_obj, "sign", ~0, MJS_MK_FN(js_math_sign)); + mjs_set(mjs, math_obj, "sin", ~0, MJS_MK_FN(js_math_sin)); + mjs_set(mjs, math_obj, "sqrt", ~0, MJS_MK_FN(js_math_sqrt)); + mjs_set(mjs, math_obj, "trunc", ~0, MJS_MK_FN(js_math_trunc)); + mjs_set(mjs, math_obj, "PI", ~0, mjs_mk_number(mjs, JS_MATH_PI)); *object = math_obj; - return object; -} - -static void js_math_destroy(void* ptr) { - UNUSED(ptr); + return (void*)1; } static const JsModuleDescriptor js_math_desc = { "math", js_math_create, - js_math_destroy, + NULL, }; -static const FlipperAppPluginDescriptor btkicker_plugin_descriptor = { +static const FlipperAppPluginDescriptor plugin_descriptor = { .appid = PLUGIN_APP_ID, .ep_api_version = PLUGIN_API_VERSION, .entry_point = &js_math_desc, }; const FlipperAppPluginDescriptor* js_math_ep(void) { - return &btkicker_plugin_descriptor; + return &plugin_descriptor; } \ No newline at end of file From 3d6baaef1c345ab23bb65107ee4e7b67dc03230a Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 7 Mar 2024 05:16:34 +0000 Subject: [PATCH 11/22] Expose math.E value --- applications/system/js_app/modules/js_math.c | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/system/js_app/modules/js_math.c b/applications/system/js_app/modules/js_math.c index 2af989908d..80d97fb9cf 100644 --- a/applications/system/js_app/modules/js_math.c +++ b/applications/system/js_app/modules/js_math.c @@ -287,6 +287,7 @@ static void* js_math_create(struct mjs* mjs, mjs_val_t* object) { mjs_set(mjs, math_obj, "sqrt", ~0, MJS_MK_FN(js_math_sqrt)); mjs_set(mjs, math_obj, "trunc", ~0, MJS_MK_FN(js_math_trunc)); mjs_set(mjs, math_obj, "PI", ~0, mjs_mk_number(mjs, JS_MATH_PI)); + mjs_set(mjs, math_obj, "E", ~0, mjs_mk_number(mjs, JS_MATH_E)); *object = math_obj; return (void*)1; } From fcef129698ee0707fbbf7ffc6b6994c73d7e4c6c Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 7 Mar 2024 05:21:23 +0000 Subject: [PATCH 12/22] Naming consistency for keyboard --- .../system/js_app/modules/js_keyboard.c | 85 +++++++++---------- 1 file changed, 38 insertions(+), 47 deletions(-) diff --git a/applications/system/js_app/modules/js_keyboard.c b/applications/system/js_app/modules/js_keyboard.c index e120ee0d64..29b5c07149 100644 --- a/applications/system/js_app/modules/js_keyboard.c +++ b/applications/system/js_app/modules/js_keyboard.c @@ -7,8 +7,8 @@ typedef struct { char* data; - TextInput* textinput; - ByteInput* byteinputview; + TextInput* text_input; + ByteInput* byte_input; ViewDispatcher* view_dispatcher; uint8_t* byteinput; } JsKeyboardInst; @@ -75,17 +75,17 @@ static JsKeyboardInst* get_this_ctx(struct mjs* mjs) { } void text_input_callback(void* context) { - JsKeyboardInst* keyboardinst = (JsKeyboardInst*)context; - view_dispatcher_stop(keyboardinst->view_dispatcher); + JsKeyboardInst* keyboard = (JsKeyboardInst*)context; + view_dispatcher_stop(keyboard->view_dispatcher); } void byte_input_callback(void* context) { - JsKeyboardInst* keyboardinst = (JsKeyboardInst*)context; - view_dispatcher_stop(keyboardinst->view_dispatcher); + JsKeyboardInst* keyboard = (JsKeyboardInst*)context; + view_dispatcher_stop(keyboard->view_dispatcher); } static void js_keyboard_text(struct mjs* mjs) { - JsKeyboardInst* keyboardinst = get_this_ctx(mjs); + JsKeyboardInst* keyboard = get_this_ctx(mjs); int MaxInputLength; if(!get_int_arg(mjs, 0, &MaxInputLength)) return; @@ -97,37 +97,35 @@ static void js_keyboard_text(struct mjs* mjs) { mjs_val_t bool_obj = mjs_arg(mjs, 2); ShouldSelect = (int)mjs_get_bool(mjs, bool_obj); - if(keyboardinst->textinput && keyboardinst->view_dispatcher) { + if(keyboard->text_input && keyboard->view_dispatcher) { if(strlen(defaultText) > 0) { - text_input_set_header_text(keyboardinst->textinput, defaultText); + text_input_set_header_text(keyboard->text_input, defaultText); } view_dispatcher_attach_to_gui( - keyboardinst->view_dispatcher, - furi_record_open(RECORD_GUI), - ViewDispatcherTypeFullscreen); + keyboard->view_dispatcher, furi_record_open(RECORD_GUI), ViewDispatcherTypeFullscreen); furi_record_close(RECORD_GUI); text_input_set_result_callback( - keyboardinst->textinput, + keyboard->text_input, text_input_callback, - keyboardinst, - keyboardinst->data, + keyboard, + keyboard->data, MaxInputLength, ShouldSelect); - view_dispatcher_switch_to_view(keyboardinst->view_dispatcher, 0); + view_dispatcher_switch_to_view(keyboard->view_dispatcher, 0); - view_dispatcher_run(keyboardinst->view_dispatcher); + view_dispatcher_run(keyboard->view_dispatcher); } - text_input_reset(keyboardinst->textinput); + text_input_reset(keyboard->text_input); - mjs_return(mjs, mjs_mk_string(mjs, keyboardinst->data, strlen(keyboardinst->data), 1)); + mjs_return(mjs, mjs_mk_string(mjs, keyboard->data, strlen(keyboard->data), 1)); } static void js_keyboard_byte(struct mjs* mjs) { - JsKeyboardInst* keyboardinst = get_this_ctx(mjs); + JsKeyboardInst* keyboard = get_this_ctx(mjs); int MaxInputLength; if(!get_int_arg(mjs, 0, &MaxInputLength)) return; @@ -135,61 +133,54 @@ static void js_keyboard_byte(struct mjs* mjs) { const char* defaultText; get_str_arg(mjs, 1, &defaultText); - if(keyboardinst->byteinputview && keyboardinst->view_dispatcher) { + if(keyboard->byte_input && keyboard->view_dispatcher) { if(strlen(defaultText) > 0) { - byte_input_set_header_text(keyboardinst->byteinputview, defaultText); + byte_input_set_header_text(keyboard->byte_input, defaultText); } view_dispatcher_attach_to_gui( - keyboardinst->view_dispatcher, - furi_record_open(RECORD_GUI), - ViewDispatcherTypeFullscreen); + keyboard->view_dispatcher, furi_record_open(RECORD_GUI), ViewDispatcherTypeFullscreen); furi_record_close(RECORD_GUI); byte_input_set_result_callback( - keyboardinst->byteinputview, - byte_input_callback, - NULL, - keyboardinst, - keyboardinst->byteinput, - 10); + keyboard->byte_input, byte_input_callback, NULL, keyboard, keyboard->byteinput, 10); - view_dispatcher_switch_to_view(keyboardinst->view_dispatcher, 1); + view_dispatcher_switch_to_view(keyboard->view_dispatcher, 1); - view_dispatcher_run(keyboardinst->view_dispatcher); + view_dispatcher_run(keyboard->view_dispatcher); } FURI_LOG_D("SHIT", "DID THING"); - keyboardinst->data = bytes_to_hex_string(keyboardinst->byteinput, 10); + keyboard->data = bytes_to_hex_string(keyboard->byteinput, 10); FURI_LOG_D("SHIT", "DID THING 1"); - mjs_return(mjs, mjs_mk_string(mjs, keyboardinst->data, strlen(keyboardinst->data), 1)); + mjs_return(mjs, mjs_mk_string(mjs, keyboard->data, strlen(keyboard->data), 1)); FURI_LOG_D("SHIT", "DID THING 2"); } static void* js_keyboard_create(struct mjs* mjs, mjs_val_t* object) { - JsKeyboardInst* keyboardinst = malloc(sizeof(JsKeyboardInst)); + JsKeyboardInst* keyboard = malloc(sizeof(JsKeyboardInst)); mjs_val_t keyboard_obj = mjs_mk_object(mjs); - mjs_set(mjs, keyboard_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, keyboardinst)); + mjs_set(mjs, keyboard_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, keyboard)); mjs_set(mjs, keyboard_obj, "text", ~0, MJS_MK_FN(js_keyboard_text)); - //mjs_set(mjs, keyboard_obj, "byte", ~0, MJS_MK_FN(js_keyboard_byte)); // NULL POINTER DEREFERENCE - keyboardinst->byteinputview = byte_input_alloc(); - keyboardinst->textinput = text_input_alloc(); - keyboardinst->view_dispatcher = view_dispatcher_alloc(); + mjs_set(mjs, keyboard_obj, "byte", ~0, MJS_MK_FN(js_keyboard_byte)); + keyboard->byte_input = byte_input_alloc(); + keyboard->text_input = text_input_alloc(); + keyboard->view_dispatcher = view_dispatcher_alloc(); keyboardinst->data = malloc(100); keyboardinst->byteinput = malloc(100); - view_dispatcher_enable_queue(keyboardinst->view_dispatcher); + view_dispatcher_enable_queue(keyboard->view_dispatcher); view_dispatcher_add_view( - keyboardinst->view_dispatcher, 0, text_input_get_view(keyboardinst->textinput)); + keyboard->view_dispatcher, 0, text_input_get_view(keyboard->text_input)); view_dispatcher_add_view( - keyboardinst->view_dispatcher, 1, byte_input_get_view(keyboardinst->byteinputview)); + keyboard->view_dispatcher, 1, byte_input_get_view(keyboard->byte_input)); *object = keyboard_obj; - return keyboardinst; + return keyboard; } static void js_keyboard_destroy(void* inst) { JsKeyboardInst* insts = (JsKeyboardInst*)inst; - byte_input_free(insts->byteinputview); - text_input_free(insts->textinput); + byte_input_free(insts->byte_input); + text_input_free(insts->text_input); view_dispatcher_free(insts->view_dispatcher); free(insts->data); free(insts); From 43781d9332f9e9953eaa290c102cd388473aca7e Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 7 Mar 2024 05:24:31 +0000 Subject: [PATCH 13/22] Use view IDs --- .../system/js_app/modules/js_keyboard.c | 17 +++++++++++++---- applications/system/js_app/modules/js_submenu.c | 9 +++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/applications/system/js_app/modules/js_keyboard.c b/applications/system/js_app/modules/js_keyboard.c index 29b5c07149..6c546039f2 100644 --- a/applications/system/js_app/modules/js_keyboard.c +++ b/applications/system/js_app/modules/js_keyboard.c @@ -13,6 +13,11 @@ typedef struct { uint8_t* byteinput; } JsKeyboardInst; +typedef enum { + JsKeyboardViewTextInput, + JsKeyboardViewByteInput, +} JsKeyboardView; + static void ret_bad_args(struct mjs* mjs, const char* error) { mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "%s", error); mjs_return(mjs, MJS_UNDEFINED); @@ -114,7 +119,7 @@ static void js_keyboard_text(struct mjs* mjs) { MaxInputLength, ShouldSelect); - view_dispatcher_switch_to_view(keyboard->view_dispatcher, 0); + view_dispatcher_switch_to_view(keyboard->view_dispatcher, JsKeyboardViewTextInput); view_dispatcher_run(keyboard->view_dispatcher); } @@ -145,7 +150,7 @@ static void js_keyboard_byte(struct mjs* mjs) { byte_input_set_result_callback( keyboard->byte_input, byte_input_callback, NULL, keyboard, keyboard->byteinput, 10); - view_dispatcher_switch_to_view(keyboard->view_dispatcher, 1); + view_dispatcher_switch_to_view(keyboard->view_dispatcher, JsKeyboardViewByteInput); view_dispatcher_run(keyboard->view_dispatcher); } @@ -170,9 +175,13 @@ static void* js_keyboard_create(struct mjs* mjs, mjs_val_t* object) { keyboardinst->byteinput = malloc(100); view_dispatcher_enable_queue(keyboard->view_dispatcher); view_dispatcher_add_view( - keyboard->view_dispatcher, 0, text_input_get_view(keyboard->text_input)); + keyboard->view_dispatcher, + JsKeyboardViewTextInput, + text_input_get_view(keyboard->text_input)); view_dispatcher_add_view( - keyboard->view_dispatcher, 1, byte_input_get_view(keyboard->byte_input)); + keyboard->view_dispatcher, + JsKeyboardViewByteInput, + byte_input_get_view(keyboard->byte_input)); *object = keyboard_obj; return keyboard; } diff --git a/applications/system/js_app/modules/js_submenu.c b/applications/system/js_app/modules/js_submenu.c index 08c62f404f..88478364bc 100644 --- a/applications/system/js_app/modules/js_submenu.c +++ b/applications/system/js_app/modules/js_submenu.c @@ -9,6 +9,10 @@ typedef struct { uint32_t result; } JsSubmenuInst; +typedef enum { + JsSubmenuViewSubmenu, +} JsSubmenuView; + static JsSubmenuInst* get_this_ctx(struct mjs* mjs) { mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); JsSubmenuInst* storage = mjs_get_ptr(mjs, obj_inst); @@ -98,7 +102,7 @@ static void js_submenu_show(struct mjs* mjs) { submenu->view_dispatcher, furi_record_open(RECORD_GUI), ViewDispatcherTypeFullscreen); furi_record_close(RECORD_GUI); - view_dispatcher_switch_to_view(submenu->view_dispatcher, 0); + view_dispatcher_switch_to_view(submenu->view_dispatcher, JsSubmenuViewSubmenu); view_dispatcher_run(submenu->view_dispatcher); @@ -117,7 +121,8 @@ static void* js_submenu_create(struct mjs* mjs, mjs_val_t* object) { submenu->submenu = submenu_alloc(); submenu->view_dispatcher = view_dispatcher_alloc(); view_dispatcher_enable_queue(submenu->view_dispatcher); - view_dispatcher_add_view(submenu->view_dispatcher, 0, submenu_get_view(submenu->submenu)); + view_dispatcher_add_view( + submenu->view_dispatcher, JsSubmenuViewSubmenu, submenu_get_view(submenu->submenu)); *object = submenu_obj; return submenu; } From d5e4af482fc0dc6925cbcdcbf83bf3632951e660 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 7 Mar 2024 05:26:40 +0000 Subject: [PATCH 14/22] Fix view memory leaks --- applications/system/js_app/modules/js_keyboard.c | 14 ++++++++------ applications/system/js_app/modules/js_submenu.c | 1 + 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/applications/system/js_app/modules/js_keyboard.c b/applications/system/js_app/modules/js_keyboard.c index 6c546039f2..dc7de06484 100644 --- a/applications/system/js_app/modules/js_keyboard.c +++ b/applications/system/js_app/modules/js_keyboard.c @@ -187,12 +187,14 @@ static void* js_keyboard_create(struct mjs* mjs, mjs_val_t* object) { } static void js_keyboard_destroy(void* inst) { - JsKeyboardInst* insts = (JsKeyboardInst*)inst; - byte_input_free(insts->byte_input); - text_input_free(insts->text_input); - view_dispatcher_free(insts->view_dispatcher); - free(insts->data); - free(insts); + JsKeyboardInst* keyboard = (JsKeyboardInst*)inst; + view_dispatcher_remove_view(keyboard->view_dispatcher, JsKeyboardViewByteInput); + byte_input_free(keyboard->byte_input); + view_dispatcher_remove_view(keyboard->view_dispatcher, JsKeyboardViewTextInput); + text_input_free(keyboard->text_input); + view_dispatcher_free(keyboard->view_dispatcher); + free(keyboard->data); + free(keyboard); } static const JsModuleDescriptor js_keyboard_desc = { diff --git a/applications/system/js_app/modules/js_submenu.c b/applications/system/js_app/modules/js_submenu.c index 88478364bc..b87f34fa8b 100644 --- a/applications/system/js_app/modules/js_submenu.c +++ b/applications/system/js_app/modules/js_submenu.c @@ -129,6 +129,7 @@ static void* js_submenu_create(struct mjs* mjs, mjs_val_t* object) { static void js_submenu_destroy(void* inst) { JsSubmenuInst* submenu = inst; + view_dispatcher_remove_view(submenu->view_dispatcher, JsSubmenuViewSubmenu); submenu_free(submenu->submenu); view_dispatcher_free(submenu->view_dispatcher); free(submenu); From bf8e1845f023b2345d0d450faac74e4f0038cf17 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 7 Mar 2024 05:53:03 +0000 Subject: [PATCH 15/22] JS Keyboard improve memory and error handling --- .../js_app/examples/apps/Scripts/keyboard.js | 3 +- .../system/js_app/modules/js_keyboard.c | 129 +++++++----------- 2 files changed, 53 insertions(+), 79 deletions(-) diff --git a/applications/system/js_app/examples/apps/Scripts/keyboard.js b/applications/system/js_app/examples/apps/Scripts/keyboard.js index ddcb40a614..91510111ac 100644 --- a/applications/system/js_app/examples/apps/Scripts/keyboard.js +++ b/applications/system/js_app/examples/apps/Scripts/keyboard.js @@ -1,5 +1,6 @@ let keyboard = require("keyboard"); -let text = keyboard.text(100, "Please Input Shit", 1); +// Default text is optional +let text = keyboard.text(100, "Some default text", true); print("Got text:", text); \ No newline at end of file diff --git a/applications/system/js_app/modules/js_keyboard.c b/applications/system/js_app/modules/js_keyboard.c index dc7de06484..542bdf2fdf 100644 --- a/applications/system/js_app/modules/js_keyboard.c +++ b/applications/system/js_app/modules/js_keyboard.c @@ -23,49 +23,25 @@ static void ret_bad_args(struct mjs* mjs, const char* error) { mjs_return(mjs, MJS_UNDEFINED); } -char nibble_to_hex_character(uint8_t nibble) { - nibble &= 0xF; - if(nibble < 10) { - return '0' + nibble; - } else { - return 'A' + (nibble - 10); - } -} - -char* bytes_to_hex_string(uint8_t* bytes, size_t num_bytes) { - char* hex_string = (char*)malloc(num_bytes * 2 + 1); - if(hex_string == NULL) { - FURI_LOG_D("LOG", "Memory allocation failed\n"); - return NULL; - } - - for(size_t i = 0; i < num_bytes; ++i) { - hex_string[i * 2] = nibble_to_hex_character(bytes[i] >> 4); - hex_string[i * 2 + 1] = nibble_to_hex_character(bytes[i] & 0xF); - } - hex_string[num_bytes * 2] = '\0'; - - return hex_string; -} - -static bool get_str_arg(struct mjs* mjs, size_t index, const char** value) { +static bool get_str_arg(struct mjs* mjs, size_t index, const char** value, bool error) { mjs_val_t str_obj = mjs_arg(mjs, index); if(!mjs_is_string(str_obj)) { - ret_bad_args(mjs, "Argument must be a string"); + if(error) ret_bad_args(mjs, "Argument must be a string"); return false; } size_t str_len = 0; *value = mjs_get_string(mjs, &str_obj, &str_len); if((str_len == 0) || (*value == NULL)) { - ret_bad_args(mjs, "Bad string argument"); + if(error) ret_bad_args(mjs, "Bad string argument"); return false; } return true; } -static bool get_int_arg(struct mjs* mjs, size_t index, int* value) { +static bool get_int_arg(struct mjs* mjs, size_t index, int* value, bool error) { mjs_val_t int_obj = mjs_arg(mjs, index); if(!mjs_is_number(int_obj)) { + if(error) ret_bad_args(mjs, "Argument must be a number"); return false; } *value = (int)mjs_get_int(mjs, int_obj); @@ -92,74 +68,73 @@ void byte_input_callback(void* context) { static void js_keyboard_text(struct mjs* mjs) { JsKeyboardInst* keyboard = get_this_ctx(mjs); - int MaxInputLength; - if(!get_int_arg(mjs, 0, &MaxInputLength)) return; + int input_length; + if(!get_int_arg(mjs, 0, &input_length, true)) return; + char* buffer = malloc(input_length); - const char* defaultText = ""; - get_str_arg(mjs, 1, &defaultText); + const char* default_text = ""; + bool clear_default = false; + if(get_str_arg(mjs, 1, &default_text, false)) { + strlcpy(buffer, default_text, input_length); + mjs_val_t bool_obj = mjs_arg(mjs, 2); + clear_default = mjs_get_bool(mjs, bool_obj); + } - int ShouldSelect; - mjs_val_t bool_obj = mjs_arg(mjs, 2); - ShouldSelect = (int)mjs_get_bool(mjs, bool_obj); + // TODO: own function + // if(strlen(default_text) > 0) { + // text_input_set_header_text(keyboard->text_input, default_text); + // } - if(keyboard->text_input && keyboard->view_dispatcher) { - if(strlen(defaultText) > 0) { - text_input_set_header_text(keyboard->text_input, defaultText); - } + view_dispatcher_attach_to_gui( + keyboard->view_dispatcher, furi_record_open(RECORD_GUI), ViewDispatcherTypeFullscreen); + furi_record_close(RECORD_GUI); - view_dispatcher_attach_to_gui( - keyboard->view_dispatcher, furi_record_open(RECORD_GUI), ViewDispatcherTypeFullscreen); - furi_record_close(RECORD_GUI); + text_input_set_result_callback( + keyboard->text_input, text_input_callback, keyboard, buffer, input_length, clear_default); - text_input_set_result_callback( - keyboard->text_input, - text_input_callback, - keyboard, - keyboard->data, - MaxInputLength, - ShouldSelect); + view_dispatcher_switch_to_view(keyboard->view_dispatcher, JsKeyboardViewTextInput); - view_dispatcher_switch_to_view(keyboard->view_dispatcher, JsKeyboardViewTextInput); - - view_dispatcher_run(keyboard->view_dispatcher); - } + view_dispatcher_run(keyboard->view_dispatcher); text_input_reset(keyboard->text_input); - mjs_return(mjs, mjs_mk_string(mjs, keyboard->data, strlen(keyboard->data), 1)); + mjs_return(mjs, mjs_mk_string(mjs, buffer, ~0, true)); + free(buffer); } static void js_keyboard_byte(struct mjs* mjs) { JsKeyboardInst* keyboard = get_this_ctx(mjs); - int MaxInputLength; - if(!get_int_arg(mjs, 0, &MaxInputLength)) return; + int input_length; + if(!get_int_arg(mjs, 0, &input_length, true)) return; + uint8_t* buffer = malloc(input_length); - const char* defaultText; - get_str_arg(mjs, 1, &defaultText); + // const char* default_data; + // if(get_str_arg(mjs, 1, &default_data, false)) { + // strlcpy(buffer, default_data, input_length); + // } - if(keyboard->byte_input && keyboard->view_dispatcher) { - if(strlen(defaultText) > 0) { - byte_input_set_header_text(keyboard->byte_input, defaultText); - } + // TODO: own function + // if(strlen(default_data) > 0) { + // byte_input_set_header_text(keyboard->byte_input, default_data); + // } - view_dispatcher_attach_to_gui( - keyboard->view_dispatcher, furi_record_open(RECORD_GUI), ViewDispatcherTypeFullscreen); - furi_record_close(RECORD_GUI); + view_dispatcher_attach_to_gui( + keyboard->view_dispatcher, furi_record_open(RECORD_GUI), ViewDispatcherTypeFullscreen); + furi_record_close(RECORD_GUI); - byte_input_set_result_callback( - keyboard->byte_input, byte_input_callback, NULL, keyboard, keyboard->byteinput, 10); + byte_input_set_result_callback( + keyboard->byte_input, byte_input_callback, NULL, keyboard, buffer, input_length); - view_dispatcher_switch_to_view(keyboard->view_dispatcher, JsKeyboardViewByteInput); + view_dispatcher_switch_to_view(keyboard->view_dispatcher, JsKeyboardViewByteInput); - view_dispatcher_run(keyboard->view_dispatcher); - } + view_dispatcher_run(keyboard->view_dispatcher); + + byte_input_set_result_callback(keyboard->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(keyboard->byte_input, ""); - FURI_LOG_D("SHIT", "DID THING"); - keyboard->data = bytes_to_hex_string(keyboard->byteinput, 10); - FURI_LOG_D("SHIT", "DID THING 1"); - mjs_return(mjs, mjs_mk_string(mjs, keyboard->data, strlen(keyboard->data), 1)); - FURI_LOG_D("SHIT", "DID THING 2"); + mjs_return(mjs, mjs_mk_string(mjs, (char*)buffer, ~0, true)); + free(buffer); } static void* js_keyboard_create(struct mjs* mjs, mjs_val_t* object) { @@ -171,8 +146,6 @@ static void* js_keyboard_create(struct mjs* mjs, mjs_val_t* object) { keyboard->byte_input = byte_input_alloc(); keyboard->text_input = text_input_alloc(); keyboard->view_dispatcher = view_dispatcher_alloc(); - keyboardinst->data = malloc(100); - keyboardinst->byteinput = malloc(100); view_dispatcher_enable_queue(keyboard->view_dispatcher); view_dispatcher_add_view( keyboard->view_dispatcher, From 0405849ca615cc992eae374c6a3c4fcd16feea68 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 7 Mar 2024 05:59:33 +0000 Subject: [PATCH 16/22] JS Keyboard add setHeader() --- .../js_app/examples/apps/Scripts/keyboard.js | 5 ++-- .../system/js_app/modules/js_keyboard.c | 23 +++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/applications/system/js_app/examples/apps/Scripts/keyboard.js b/applications/system/js_app/examples/apps/Scripts/keyboard.js index 91510111ac..784ddf2a00 100644 --- a/applications/system/js_app/examples/apps/Scripts/keyboard.js +++ b/applications/system/js_app/examples/apps/Scripts/keyboard.js @@ -1,6 +1,7 @@ let keyboard = require("keyboard"); -// Default text is optional -let text = keyboard.text(100, "Some default text", true); +keyboard.setHeader("Example Text Input"); +// Default text is optional +let text = keyboard.text(100, "Default text", true); print("Got text:", text); \ No newline at end of file diff --git a/applications/system/js_app/modules/js_keyboard.c b/applications/system/js_app/modules/js_keyboard.c index 542bdf2fdf..4a7b13546b 100644 --- a/applications/system/js_app/modules/js_keyboard.c +++ b/applications/system/js_app/modules/js_keyboard.c @@ -65,6 +65,18 @@ void byte_input_callback(void* context) { view_dispatcher_stop(keyboard->view_dispatcher); } +static void js_keyboard_set_header(struct mjs* mjs) { + JsKeyboardInst* keyboard = get_this_ctx(mjs); + + const char* header; + if(!get_str_arg(mjs, 0, &header, true)) return; + + text_input_set_header_text(keyboard->text_input, header); + byte_input_set_header_text(keyboard->byte_input, header); + + mjs_return(mjs, MJS_UNDEFINED); +} + static void js_keyboard_text(struct mjs* mjs) { JsKeyboardInst* keyboard = get_this_ctx(mjs); @@ -80,11 +92,6 @@ static void js_keyboard_text(struct mjs* mjs) { clear_default = mjs_get_bool(mjs, bool_obj); } - // TODO: own function - // if(strlen(default_text) > 0) { - // text_input_set_header_text(keyboard->text_input, default_text); - // } - view_dispatcher_attach_to_gui( keyboard->view_dispatcher, furi_record_open(RECORD_GUI), ViewDispatcherTypeFullscreen); furi_record_close(RECORD_GUI); @@ -114,11 +121,6 @@ static void js_keyboard_byte(struct mjs* mjs) { // strlcpy(buffer, default_data, input_length); // } - // TODO: own function - // if(strlen(default_data) > 0) { - // byte_input_set_header_text(keyboard->byte_input, default_data); - // } - view_dispatcher_attach_to_gui( keyboard->view_dispatcher, furi_record_open(RECORD_GUI), ViewDispatcherTypeFullscreen); furi_record_close(RECORD_GUI); @@ -141,6 +143,7 @@ static void* js_keyboard_create(struct mjs* mjs, mjs_val_t* object) { JsKeyboardInst* keyboard = malloc(sizeof(JsKeyboardInst)); mjs_val_t keyboard_obj = mjs_mk_object(mjs); mjs_set(mjs, keyboard_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, keyboard)); + mjs_set(mjs, keyboard_obj, "setHeader", ~0, MJS_MK_FN(js_keyboard_set_header)); mjs_set(mjs, keyboard_obj, "text", ~0, MJS_MK_FN(js_keyboard_text)); mjs_set(mjs, keyboard_obj, "byte", ~0, MJS_MK_FN(js_keyboard_byte)); keyboard->byte_input = byte_input_alloc(); From 03f780f43ccf8e0572db1b39deb3c679d538595f Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 7 Mar 2024 06:20:14 +0000 Subject: [PATCH 17/22] Use correct array buf for js byte keyboard --- .../js_app/examples/apps/Scripts/keyboard.js | 14 ++++++++++- .../system/js_app/modules/js_keyboard.c | 23 +++++++++++-------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/applications/system/js_app/examples/apps/Scripts/keyboard.js b/applications/system/js_app/examples/apps/Scripts/keyboard.js index 784ddf2a00..a34607c299 100644 --- a/applications/system/js_app/examples/apps/Scripts/keyboard.js +++ b/applications/system/js_app/examples/apps/Scripts/keyboard.js @@ -4,4 +4,16 @@ keyboard.setHeader("Example Text Input"); // Default text is optional let text = keyboard.text(100, "Default text", true); -print("Got text:", text); \ No newline at end of file +print("Got text:", text); + +keyboard.setHeader("Example Byte Input"); + +// Default data is optional +let data = keyboard.byte(6, Uint8Array([1, 2, 3, 4, 5, 6])); +data = Uint8Array(data); +let result = "0x"; +for (let i = 0; i < data.byteLength; i++) { + if (data[i] < 0x10) result += "0"; + result += to_hex_string(data[i]); +} +print("Got data:", result); \ No newline at end of file diff --git a/applications/system/js_app/modules/js_keyboard.c b/applications/system/js_app/modules/js_keyboard.c index 4a7b13546b..e6c0b5a548 100644 --- a/applications/system/js_app/modules/js_keyboard.c +++ b/applications/system/js_app/modules/js_keyboard.c @@ -38,13 +38,13 @@ static bool get_str_arg(struct mjs* mjs, size_t index, const char** value, bool return true; } -static bool get_int_arg(struct mjs* mjs, size_t index, int* value, bool error) { +static bool get_int_arg(struct mjs* mjs, size_t index, size_t* value, bool error) { mjs_val_t int_obj = mjs_arg(mjs, index); if(!mjs_is_number(int_obj)) { if(error) ret_bad_args(mjs, "Argument must be a number"); return false; } - *value = (int)mjs_get_int(mjs, int_obj); + *value = mjs_get_int(mjs, int_obj); return true; } @@ -80,7 +80,7 @@ static void js_keyboard_set_header(struct mjs* mjs) { static void js_keyboard_text(struct mjs* mjs) { JsKeyboardInst* keyboard = get_this_ctx(mjs); - int input_length; + size_t input_length; if(!get_int_arg(mjs, 0, &input_length, true)) return; char* buffer = malloc(input_length); @@ -112,14 +112,19 @@ static void js_keyboard_text(struct mjs* mjs) { static void js_keyboard_byte(struct mjs* mjs) { JsKeyboardInst* keyboard = get_this_ctx(mjs); - int input_length; + size_t input_length; if(!get_int_arg(mjs, 0, &input_length, true)) return; uint8_t* buffer = malloc(input_length); - // const char* default_data; - // if(get_str_arg(mjs, 1, &default_data, false)) { - // strlcpy(buffer, default_data, input_length); - // } + mjs_val_t default_data_arg = mjs_arg(mjs, 1); + if(mjs_is_typed_array(default_data_arg)) { + if(mjs_is_data_view(default_data_arg)) { + default_data_arg = mjs_dataview_get_buf(mjs, default_data_arg); + } + size_t default_data_len = 0; + char* default_data = mjs_array_buf_get_ptr(mjs, default_data_arg, &default_data_len); + memcpy(buffer, (uint8_t*)default_data, MIN((size_t)input_length, default_data_len)); + } view_dispatcher_attach_to_gui( keyboard->view_dispatcher, furi_record_open(RECORD_GUI), ViewDispatcherTypeFullscreen); @@ -135,7 +140,7 @@ static void js_keyboard_byte(struct mjs* mjs) { byte_input_set_result_callback(keyboard->byte_input, NULL, NULL, NULL, NULL, 0); byte_input_set_header_text(keyboard->byte_input, ""); - mjs_return(mjs, mjs_mk_string(mjs, (char*)buffer, ~0, true)); + mjs_return(mjs, mjs_mk_array_buf(mjs, (char*)buffer, input_length)); free(buffer); } From eedeee4941ab6ea733fa45b4a782660da5b59639 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 7 Mar 2024 07:44:11 +0000 Subject: [PATCH 18/22] JS Blebeacon keep/not prev cfg, use arraybuf, more options --- .../js_app/examples/apps/Scripts/blebeacon.js | 72 +--- .../system/js_app/modules/js_blebeacon.c | 322 +++++++----------- .../system/js_app/modules/js_keyboard.c | 2 +- 3 files changed, 142 insertions(+), 254 deletions(-) diff --git a/applications/system/js_app/examples/apps/Scripts/blebeacon.js b/applications/system/js_app/examples/apps/Scripts/blebeacon.js index f324fafd96..5e29ccbc12 100644 --- a/applications/system/js_app/examples/apps/Scripts/blebeacon.js +++ b/applications/system/js_app/examples/apps/Scripts/blebeacon.js @@ -1,7 +1,7 @@ -let bleBeacon = require("blebeacon"); +let blebeacon = require("blebeacon"); +let math = require("math"); let currentIndex = 0; -let currentByteValue = 0; let watchValues = [ 0x1A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x11, 0x12, 0x13, 0x14, 0x15, @@ -9,50 +9,15 @@ let watchValues = [ 0x20, 0xEC, 0xEF ]; -function byteToHex(byte) { - let hexChars = '0123456789abcdef'; - let hex = ''; - if (byte >= 0 && byte <= 255) { - hex = hexChars[(byte >> 4) & 0x0F] + hexChars[byte & 0x0F]; - } - return hex; -} - -function getNextByteValue() { - let value = currentByteValue; - currentByteValue = (currentByteValue + 1) % 256; - return value; -} - function generateRandomMac() { - let mac = ''; + let mac = []; for (let i = 0; i < 6; i++) { - if (mac.length) mac += ':'; - let byte = getNextByteValue(); - mac += byteToHex(byte); + mac.push(math.floor(math.random() * 256)); } - return mac; -} - -function bytesToHexString(bytes) { - if (!bytes) { - print("Invalid input for bytesToHexString"); - return ''; - } - - let hexString = ''; - for (let i = 0; i < bytes.length; i++) { - hexString += byteToHex(bytes[i]); - } - return hexString; + return Uint8Array(mac); } function sendRandomModelAdvertisement() { - if (!watchValues || watchValues.length === 0) { - print("watchValues array is empty or undefined."); - return; - } - let model = watchValues[currentIndex]; let packet = [ @@ -60,28 +25,27 @@ function sendRandomModelAdvertisement() { model ]; - let packetString = bytesToHexString(packet); - if (!packetString) { - print("Failed to generate packet string."); - return; - } + let intervalMs = 50; - bleBeacon.setMac(generateRandomMac()); - bleBeacon.setData(packetString); - bleBeacon.send(); + // Power level, min interval and max interval are optional + blebeacon.setConfig(generateRandomMac(), 0x1F, intervalMs, intervalMs * 3); + + blebeacon.setData(Uint8Array(packet)); + + blebeacon.start(); print("Sent data for model ID " + to_string(model)); currentIndex = (currentIndex + 1) % watchValues.length; - delay(500); + delay(intervalMs); - bleBeacon.stop(); - - bleBeacon + blebeacon.stop(); } -while (true) -{ +// Make sure it resets at script exit, true will keep advertising in background +blebeacon.keepAlive(true); + +while (true) { sendRandomModelAdvertisement(); } \ No newline at end of file diff --git a/applications/system/js_app/modules/js_blebeacon.c b/applications/system/js_app/modules/js_blebeacon.c index 946a99babc..35dbbb47b9 100644 --- a/applications/system/js_app/modules/js_blebeacon.c +++ b/applications/system/js_app/modules/js_blebeacon.c @@ -3,80 +3,19 @@ #include typedef struct { - char* data; - char* mac_addr; - size_t beacon_data_len; - GapExtraBeaconConfig beacon_config; -} JsBlebeaconInst; - -struct OUI_MAP_ENTRY { - const char* brand; - const char* oui; -}; - -struct OUI_MAP_ENTRY OUI_MAP[] = { - {"Apple", "00:1F:7F"}, - {"Dell", "00:14:5F"}, - {"HP", "00:4C:6F"}, - {"Lenovo", "00:50:C2"}, - {"Microsoft", "00:0C:29"}, - {"Samsung", "00:1C:42"}, - {"Sony", "00:0A:95"}, - {"Acer", "00:26:A9"}, - {"Asus", "00:19:D8"}, - {"Google", "08:00:27"}, - {"HTC", "00:1F:B5"}, - {"Intel", "00:19:5D"}, - {"LG", "00:1C:61"}, - {"Motorola", "00:1F:42"}, - {"Toshiba", "00:1E:67"}, - {"Xiaomi", "00:26:A8"}, -}; + bool saved_prev_cfg; + bool prev_cfg_set; + GapExtraBeaconConfig prev_cfg; -#define OUI_MAP_SIZE (sizeof(OUI_MAP) / sizeof(OUI_MAP[0])) - -int rand_range(int min, int max) { - return min + rand() / (RAND_MAX / (max - min + 1) + 1); -} - -void byte_to_hex(char* output, unsigned char byte) { - static const char hex_chars[] = "0123456789ABCDEF"; - output[0] = hex_chars[byte >> 4]; - output[1] = hex_chars[byte & 0x0F]; -} + bool saved_prev_data; + uint8_t prev_data[EXTRA_BEACON_MAX_DATA_SIZE]; + uint8_t prev_data_len; -static char* generate_mac_address(const char* brand) { - char* mac_address = (char*)malloc(18 * sizeof(char)); - if(mac_address == NULL) { - FURI_LOG_D("BLE", "Memory allocation failed.\n"); - return NULL; - } - - const char* oui = NULL; - for(unsigned int i = 0; i < OUI_MAP_SIZE; ++i) { - if(strcmp(brand, OUI_MAP[i].brand) == 0) { - oui = OUI_MAP[i].oui; - break; - } - } - - if(oui == NULL) { - FURI_LOG_D("BLE", "Brand not found.\n"); - free(mac_address); - return NULL; - } - - char last_bytes[6]; - for(int i = 0; i < 3; ++i) { - unsigned char byte = rand_range(0x00, 0xFF); - byte_to_hex(&last_bytes[i * 2], byte); - } + bool saved_prev_active; + bool prev_active; - strcpy(mac_address, oui); - strcat(mac_address, ":"); - strcat(mac_address, last_bytes); - return mac_address; -} + bool keep_alive; +} JsBlebeaconInst; static JsBlebeaconInst* get_this_ctx(struct mjs* mjs) { mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); @@ -99,177 +38,162 @@ static bool check_arg_count(struct mjs* mjs, size_t count) { return true; } -static bool get_str_arg(struct mjs* mjs, size_t index, char** value) { - mjs_val_t str_obj = mjs_arg(mjs, index); - if(!mjs_is_string(str_obj)) { - ret_bad_args(mjs, "Argument must be a string"); +static bool get_int_arg(struct mjs* mjs, size_t index, uint8_t* value, bool error) { + mjs_val_t int_obj = mjs_arg(mjs, index); + if(!mjs_is_number(int_obj)) { + if(error) ret_bad_args(mjs, "Argument must be a number"); return false; } - size_t str_len; - const char* temp = mjs_get_string(mjs, &str_obj, &str_len); - if(str_len == 0 || !temp) { - ret_bad_args(mjs, "Bad string argument"); - return false; - } - - *value = (char*)malloc(str_len + 1); - if(!*value) { - ret_bad_args(mjs, "Memory allocation failed"); - return false; - } - strncpy(*value, temp, str_len); - (*value)[str_len] = '\0'; // Ensure null termination + *value = mjs_get_int(mjs, int_obj); return true; } -static uint8_t hex_char_to_uint(char c) { - if(c >= '0' && c <= '9') return c - '0'; - if(c >= 'a' && c <= 'f') return 10 + c - 'a'; - if(c >= 'A' && c <= 'F') return 10 + c - 'A'; - return 0; -} - -static uint8_t* macstr_to_uint8(const char* macstr) { - if(strlen(macstr) != 17) return NULL; - - uint8_t* mac_bytes = (uint8_t*)malloc(6 * sizeof(uint8_t)); - if(!mac_bytes) return NULL; - - for(size_t i = 0, j = 0; i < 17; i += 3, ++j) { - mac_bytes[j] = (hex_char_to_uint(macstr[i]) << 4) | hex_char_to_uint(macstr[i + 1]); +static void js_blebeacon_set_config(struct mjs* mjs) { + JsBlebeaconInst* blebeacon = get_this_ctx(mjs); + if(mjs_nargs(mjs) < 1 || mjs_nargs(mjs) > 4) { + ret_bad_args(mjs, "Wrong argument count"); + return; + } - if(i < 15 && macstr[i + 2] != ':' && macstr[i + 2] != '-') { - free(mac_bytes); - return NULL; + char* mac = NULL; + size_t mac_len = 0; + mjs_val_t mac_arg = mjs_arg(mjs, 0); + if(mjs_is_typed_array(mac_arg)) { + if(mjs_is_data_view(mac_arg)) { + mac_arg = mjs_dataview_get_buf(mjs, mac_arg); } + mac = mjs_array_buf_get_ptr(mjs, mac_arg, &mac_len); } - - return mac_bytes; -} - -static uint8_t* hexstr_to_uint8(const char* hexstr, size_t* out_length) { - size_t len = strlen(hexstr); - if(len % 2 != 0) return NULL; - - if(len > EXTRA_BEACON_MAX_DATA_SIZE + 1) return NULL; - - *out_length = len / 2; - uint8_t* bytes = (uint8_t*)malloc(*out_length); - if(!bytes) return NULL; - - for(size_t i = 0; i < *out_length; ++i) { - bytes[i] = (hex_char_to_uint(hexstr[i * 2]) << 4) | hex_char_to_uint(hexstr[i * 2 + 1]); + if(!mac || mac_len != EXTRA_BEACON_MAC_ADDR_SIZE) { + ret_bad_args(mjs, "Wrong MAC address"); + return; } - return bytes; + uint8_t power = GapAdvPowerLevel_0dBm; + get_int_arg(mjs, 1, &power, false); + power = CLAMP(power, GapAdvPowerLevel_6dBm, GapAdvPowerLevel_Neg40dBm); + + uint8_t intv_min = 50; + get_int_arg(mjs, 2, &intv_min, false); + intv_min = MAX(intv_min, 20); + + uint8_t intv_max = 150; + get_int_arg(mjs, 3, &intv_max, false); + intv_max = MAX(intv_max, intv_min); + + GapExtraBeaconConfig config = { + .min_adv_interval_ms = intv_min, + .max_adv_interval_ms = intv_max, + .adv_channel_map = GapAdvChannelMapAll, + .adv_power_level = power, + .address_type = GapAddressTypePublic, + }; + memcpy(config.address, (uint8_t*)mac, sizeof(config.address)); + + if(!blebeacon->saved_prev_cfg) { + blebeacon->saved_prev_cfg = true; + const GapExtraBeaconConfig* prev_cfg_ptr = furi_hal_bt_extra_beacon_get_config(); + if(prev_cfg_ptr) { + blebeacon->prev_cfg_set = true; + memcpy(&blebeacon->prev_cfg, prev_cfg_ptr, sizeof(blebeacon->prev_cfg)); + } else { + blebeacon->prev_cfg_set = false; + } + } + furi_check(furi_hal_bt_extra_beacon_set_config(&config)); } static void js_blebeacon_set_data(struct mjs* mjs) { - FURI_LOG_D("BLE", "Setting data"); + JsBlebeaconInst* blebeacon = get_this_ctx(mjs); if(!check_arg_count(mjs, 1)) return; - JsBlebeaconInst* inst = get_this_ctx(mjs); - if(!inst) { - FURI_LOG_D("BLE", "Beacon instance is null"); - ret_bad_args(mjs, "Beacon instance is null"); - return; - } - - if(inst->data) { - FURI_LOG_D("BLE", "Freeing existing data"); - free(inst->data); - inst->data = NULL; - } - - if(!get_str_arg(mjs, 0, &(inst->data))) return; // get_str_arg now modifies inst->data directly + char* data = NULL; size_t data_len = 0; - uint8_t* beacon_data = hexstr_to_uint8(inst->data, &data_len); - if(!beacon_data) { - FURI_LOG_D("BLE", "Failed to convert data to hex"); - ret_bad_args(mjs, "Failed to convert data to hex"); + mjs_val_t data_arg = mjs_arg(mjs, 0); + if(mjs_is_typed_array(data_arg)) { + if(mjs_is_data_view(data_arg)) { + data_arg = mjs_dataview_get_buf(mjs, data_arg); + } + data = mjs_array_buf_get_ptr(mjs, data_arg, &data_len); + } + if(!data) { + ret_bad_args(mjs, "Data must be a Uint8Array"); return; } - FURI_LOG_D("BLE", "Successfully set beacon data"); - furi_hal_bt_extra_beacon_set_data(beacon_data, data_len); - free(beacon_data); + if(!blebeacon->saved_prev_data) { + blebeacon->saved_prev_data = true; + blebeacon->prev_data_len = furi_hal_bt_extra_beacon_get_data(blebeacon->prev_data); + } + furi_check(furi_hal_bt_extra_beacon_set_data((uint8_t*)data, data_len)); + mjs_return(mjs, MJS_UNDEFINED); } -static void js_blebeacon_generate_mac(struct mjs* mjs) { - char* company = ""; - if(!get_str_arg(mjs, 0, &company)) return; - - char* mac = generate_mac_address(company); - - mjs_val_t js_mac_address = mjs_mk_string(mjs, mac, strlen(mac), 1); +static void js_blebeacon_start(struct mjs* mjs) { + JsBlebeaconInst* blebeacon = get_this_ctx(mjs); + if(!check_arg_count(mjs, 0)) return; - return mjs_return(mjs, js_mac_address); -} - -static void js_blebeacon_set_mac(struct mjs* mjs) { - FURI_LOG_D("BLE", "Setting Mac"); - if(!check_arg_count(mjs, 1)) { - ret_bad_args(mjs, "Bad args"); - return; + if(!blebeacon->saved_prev_active) { + blebeacon->saved_prev_active = true; + blebeacon->prev_active = furi_hal_bt_extra_beacon_is_active(); } + furi_check(furi_hal_bt_extra_beacon_start()); - JsBlebeaconInst* inst = get_this_ctx(mjs); - char* mac_addr = ""; - if(!get_str_arg(mjs, 0, &mac_addr)) return; - inst->mac_addr = mac_addr; - inst->beacon_config.min_adv_interval_ms = 50; - inst->beacon_config.max_adv_interval_ms = 150; - - inst->beacon_config.adv_channel_map = GapAdvChannelMapAll; - inst->beacon_config.adv_power_level = GapAdvPowerLevel_0dBm; - - inst->beacon_config.address_type = GapAddressTypePublic; - - uint8_t* mac = macstr_to_uint8(mac_addr); - if(mac) { - memcpy(inst->beacon_config.address, mac, 6); - furi_hal_bt_extra_beacon_set_config(&inst->beacon_config); - mjs_return(mjs, MJS_UNDEFINED); - } else { - FURI_LOG_D("BLE", "Bad MacAddress"); - ret_bad_args(mjs, "Bad Mac Address"); - return; - } + mjs_return(mjs, MJS_UNDEFINED); } -static void js_blebeacon_send(struct mjs* mjs) { - furi_hal_bt_extra_beacon_start(); +static void js_blebeacon_stop(struct mjs* mjs) { + JsBlebeaconInst* blebeacon = get_this_ctx(mjs); + if(!check_arg_count(mjs, 0)) return; + UNUSED(blebeacon); + + furi_hal_bt_extra_beacon_stop(); mjs_return(mjs, MJS_UNDEFINED); } -static void js_blebeacon_stop(struct mjs* mjs) { - furi_hal_bt_extra_beacon_stop(); +static void js_blebeacon_keep_alive(struct mjs* mjs) { + JsBlebeaconInst* blebeacon = get_this_ctx(mjs); + if(!check_arg_count(mjs, 1)) return; + + mjs_val_t bool_obj = mjs_arg(mjs, 0); + blebeacon->keep_alive = mjs_get_bool(mjs, bool_obj); mjs_return(mjs, MJS_UNDEFINED); } static void* js_blebeacon_create(struct mjs* mjs, mjs_val_t* object) { - JsBlebeaconInst* inst = malloc(sizeof(JsBlebeaconInst)); + JsBlebeaconInst* blebeacon = malloc(sizeof(JsBlebeaconInst)); mjs_val_t blebeacon_obj = mjs_mk_object(mjs); - mjs_set(mjs, blebeacon_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, inst)); + mjs_set(mjs, blebeacon_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, blebeacon)); + mjs_set(mjs, blebeacon_obj, "setConfig", ~0, MJS_MK_FN(js_blebeacon_set_config)); mjs_set(mjs, blebeacon_obj, "setData", ~0, MJS_MK_FN(js_blebeacon_set_data)); - mjs_set(mjs, blebeacon_obj, "setMac", ~0, MJS_MK_FN(js_blebeacon_set_mac)); - mjs_set(mjs, blebeacon_obj, "send", ~0, MJS_MK_FN(js_blebeacon_send)); + mjs_set(mjs, blebeacon_obj, "start", ~0, MJS_MK_FN(js_blebeacon_start)); mjs_set(mjs, blebeacon_obj, "stop", ~0, MJS_MK_FN(js_blebeacon_stop)); - mjs_set(mjs, blebeacon_obj, "genMac", ~0, MJS_MK_FN(js_blebeacon_generate_mac)); + mjs_set(mjs, blebeacon_obj, "keepAlive", ~0, MJS_MK_FN(js_blebeacon_keep_alive)); *object = blebeacon_obj; - return inst; + return blebeacon; } -static void js_blebeacon_destroy(void* ptr) { - JsBlebeaconInst* inst = (JsBlebeaconInst*)ptr; - if(inst) { - free(inst->data); - free(inst->mac_addr); - free(inst); +static void js_blebeacon_destroy(void* inst) { + JsBlebeaconInst* blebeacon = inst; + if(!blebeacon->keep_alive) { + if(furi_hal_bt_extra_beacon_is_active()) { + furi_hal_bt_extra_beacon_stop(); + } + if(blebeacon->saved_prev_cfg && blebeacon->prev_cfg_set) { + furi_check(furi_hal_bt_extra_beacon_set_config(&blebeacon->prev_cfg)); + } + if(blebeacon->saved_prev_data) { + furi_check( + furi_hal_bt_extra_beacon_set_data(blebeacon->prev_data, blebeacon->prev_data_len)); + } + if(blebeacon->prev_active) { + furi_check(furi_hal_bt_extra_beacon_start()); + } } + free(blebeacon); } static const JsModuleDescriptor js_blebeacon_desc = { diff --git a/applications/system/js_app/modules/js_keyboard.c b/applications/system/js_app/modules/js_keyboard.c index e6c0b5a548..8958dcaf8c 100644 --- a/applications/system/js_app/modules/js_keyboard.c +++ b/applications/system/js_app/modules/js_keyboard.c @@ -168,7 +168,7 @@ static void* js_keyboard_create(struct mjs* mjs, mjs_val_t* object) { } static void js_keyboard_destroy(void* inst) { - JsKeyboardInst* keyboard = (JsKeyboardInst*)inst; + JsKeyboardInst* keyboard = inst; view_dispatcher_remove_view(keyboard->view_dispatcher, JsKeyboardViewByteInput); byte_input_free(keyboard->byte_input); view_dispatcher_remove_view(keyboard->view_dispatcher, JsKeyboardViewTextInput); From 724be02ee5ab9ba28cd1cc363476330dda245d87 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 7 Mar 2024 19:35:45 +0000 Subject: [PATCH 19/22] JS: Blebeacon: Add isActive(), fix some state logic --- .../js_app/examples/apps/Scripts/blebeacon.js | 14 +++++++++++--- applications/system/js_app/modules/js_blebeacon.c | 13 +++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/applications/system/js_app/examples/apps/Scripts/blebeacon.js b/applications/system/js_app/examples/apps/Scripts/blebeacon.js index 5e29ccbc12..53983a7455 100644 --- a/applications/system/js_app/examples/apps/Scripts/blebeacon.js +++ b/applications/system/js_app/examples/apps/Scripts/blebeacon.js @@ -1,4 +1,15 @@ let blebeacon = require("blebeacon"); + +// Stop if previous background beacon is active +if (blebeacon.isActive()) { + blebeacon.stop(); +} + +// Make sure it resets at script exit, true will keep advertising in background +// This is false by default, can be omitted +blebeacon.keepAlive(false); + + let math = require("math"); let currentIndex = 0; @@ -43,9 +54,6 @@ function sendRandomModelAdvertisement() { blebeacon.stop(); } -// Make sure it resets at script exit, true will keep advertising in background -blebeacon.keepAlive(true); - while (true) { sendRandomModelAdvertisement(); } \ No newline at end of file diff --git a/applications/system/js_app/modules/js_blebeacon.c b/applications/system/js_app/modules/js_blebeacon.c index 35dbbb47b9..10b1da6db2 100644 --- a/applications/system/js_app/modules/js_blebeacon.c +++ b/applications/system/js_app/modules/js_blebeacon.c @@ -48,6 +48,14 @@ static bool get_int_arg(struct mjs* mjs, size_t index, uint8_t* value, bool erro return true; } +static void js_blebeacon_is_active(struct mjs* mjs) { + JsBlebeaconInst* blebeacon = get_this_ctx(mjs); + if(!check_arg_count(mjs, 0)) return; + UNUSED(blebeacon); + + mjs_return(mjs, mjs_mk_boolean(mjs, furi_hal_bt_extra_beacon_is_active())); +} + static void js_blebeacon_set_config(struct mjs* mjs) { JsBlebeaconInst* blebeacon = get_this_ctx(mjs); if(mjs_nargs(mjs) < 1 || mjs_nargs(mjs) > 4) { @@ -148,6 +156,10 @@ static void js_blebeacon_stop(struct mjs* mjs) { if(!check_arg_count(mjs, 0)) return; UNUSED(blebeacon); + if(!blebeacon->saved_prev_active) { + blebeacon->saved_prev_active = true; + blebeacon->prev_active = furi_hal_bt_extra_beacon_is_active(); + } furi_hal_bt_extra_beacon_stop(); mjs_return(mjs, MJS_UNDEFINED); @@ -167,6 +179,7 @@ static void* js_blebeacon_create(struct mjs* mjs, mjs_val_t* object) { JsBlebeaconInst* blebeacon = malloc(sizeof(JsBlebeaconInst)); mjs_val_t blebeacon_obj = mjs_mk_object(mjs); mjs_set(mjs, blebeacon_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, blebeacon)); + mjs_set(mjs, blebeacon_obj, "isActive", ~0, MJS_MK_FN(js_blebeacon_is_active)); mjs_set(mjs, blebeacon_obj, "setConfig", ~0, MJS_MK_FN(js_blebeacon_set_config)); mjs_set(mjs, blebeacon_obj, "setData", ~0, MJS_MK_FN(js_blebeacon_set_data)); mjs_set(mjs, blebeacon_obj, "start", ~0, MJS_MK_FN(js_blebeacon_start)); From b009a42591ec05494589f50f17f8b31b333d5ab0 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 7 Mar 2024 19:41:57 +0000 Subject: [PATCH 20/22] JS: Blebeacon: Give js errors instead of furi_check() --- .../system/js_app/modules/js_blebeacon.c | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/applications/system/js_app/modules/js_blebeacon.c b/applications/system/js_app/modules/js_blebeacon.c index 10b1da6db2..a87f4d2291 100644 --- a/applications/system/js_app/modules/js_blebeacon.c +++ b/applications/system/js_app/modules/js_blebeacon.c @@ -29,6 +29,11 @@ static void ret_bad_args(struct mjs* mjs, const char* error) { mjs_return(mjs, MJS_UNDEFINED); } +static void ret_int_err(struct mjs* mjs, const char* error) { + mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "%s", error); + mjs_return(mjs, MJS_UNDEFINED); +} + static bool check_arg_count(struct mjs* mjs, size_t count) { size_t num_args = mjs_nargs(mjs); if(num_args != count) { @@ -108,7 +113,12 @@ static void js_blebeacon_set_config(struct mjs* mjs) { blebeacon->prev_cfg_set = false; } } - furi_check(furi_hal_bt_extra_beacon_set_config(&config)); + if(!furi_hal_bt_extra_beacon_set_config(&config)) { + ret_int_err(mjs, "Failed setting beacon config"); + return; + } + + mjs_return(mjs, MJS_UNDEFINED); } static void js_blebeacon_set_data(struct mjs* mjs) { @@ -133,7 +143,10 @@ static void js_blebeacon_set_data(struct mjs* mjs) { blebeacon->saved_prev_data = true; blebeacon->prev_data_len = furi_hal_bt_extra_beacon_get_data(blebeacon->prev_data); } - furi_check(furi_hal_bt_extra_beacon_set_data((uint8_t*)data, data_len)); + if(!furi_hal_bt_extra_beacon_set_data((uint8_t*)data, data_len)) { + ret_int_err(mjs, "Failed setting beacon data"); + return; + } mjs_return(mjs, MJS_UNDEFINED); } @@ -146,7 +159,10 @@ static void js_blebeacon_start(struct mjs* mjs) { blebeacon->saved_prev_active = true; blebeacon->prev_active = furi_hal_bt_extra_beacon_is_active(); } - furi_check(furi_hal_bt_extra_beacon_start()); + if(!furi_hal_bt_extra_beacon_start()) { + ret_int_err(mjs, "Failed starting beacon"); + return; + } mjs_return(mjs, MJS_UNDEFINED); } From f8aad11dbcfd97a16a08bb5fdf18fc3458b9bc09 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 7 Mar 2024 19:55:18 +0000 Subject: [PATCH 21/22] Fix intro slideshow --- assets/slideshow/firstboot/frame_01.png | Bin 746 -> 4988 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/slideshow/firstboot/frame_01.png b/assets/slideshow/firstboot/frame_01.png index c138bad541b8db75af285b01234113b30544ca9a..6bfe937ee605af49e8a6511f70425aaf9f9d7f0f 100644 GIT binary patch literal 4988 zcmeHKYg7~07M>6hL_t)vD&7zyRuPk#BtuA2B2iF?66Ga=U~w{;ga~<%3=n7)8sAIg zq1FfB>ncJ~v0mSzg7t~E3TUa1YEiVHR;faTs+FDz2-w@T?sBc$e`MCl+2@>nzP-=4 z_u0v;44xU_=rGa&f*?m(pnoXnGeDEt_XXcIwdv`gt0E$0Dw%8oWDf>Flhy~K0VxAC zTBj@pvJH(6(Luiqw8228bf`9xW^3&;f$Z8TuLJV`zt)yEj4>NbV&^EZvl z8oa8I6LEawm-5x-JmIbd;)nf3*|V=i7aUr!`A}13>~Kz_=jE6pk;P@${yzO!F4sdQ z4;9=$_;_dJ#i~U$7kqztHP-0ypd=#~r+LNY%=2w?d-JSz!c2LZ?4Eno>-$2t9n!du z;=D#RyuOh*cbxRs5l3r2c{_K=#^2pfR#z(mD(VD=#5>jNBy~o+;b}Z&SaKm#zCE(f z-K&lHFQgBKHO4ll#W&Tr&@P6~dE{DG=RDjqlR}!$bn11)wJ2ANM_dQlsi7>pkD773*<&Is(jf;lHQ- z?(9}&pe2`!{q=YfWmM)bVNw2ym3myG6kA11q2LQtJd}+q_!t`{1S+;r>CIy+`2q|V zsuVmCU)cjnrZbV44ksum0M5|>91K_Bd=)NYdwVIkY?RB#*&>{evbnedL2+DwV+hg% zVwO<@vJy+^=@kW~1W;T|AyA4C1zUv*1#DCVvO^)nyx1a?tH5w?KJd}Y3Pr_1JULh< zVevQ};MSSo1dLSajamsSNTW+KcdLeLv_u$*QKWIb5s?t(@r}^$ z3L_FbMjgWhjrA%qnbu1IV%Tv66P8SL#2a9BEa7pOPECO7(V4FA<~4tl3QDffOTZ@( zwgSWoWRn7AV=5(=jSDdp5h#Rw0s0qolU_xpU`ArH8t@3X0_kb}fntugM$p5`S(h&< zaRe0}Ko}e0vk~vV5(al37^Z5*yA%7s|HX-qRiQ_b0sA`UK=A^#5bi35otyy={>e{g zEdEIoOlGf<58}6%u3ox6h=C7M?#-@Vx;}`34^r;UuKycd4&AR)gbw@*N&zoRxnB)@ z3SPAODrN-uL+#YF?PSqfFw@Tv7-fPWr!!QeUCmZ)z@Q%2FF2df3J|eZGG~ zb?$?OZ?8wjW?rS`Um6Big`WKao_B8VvVBV#(0p#1(d=ZrIGJVnT*P4>R3o*8IBh z&ej`_D?-Aa^PjZE?7Ll(GJkVW-p|mQ(i)v58+lt&xMlj_8#Vb(qZ1033`2r%9*w^c zL38QzqrSE%%@UgVQ`FtEIkcAMKU{AfUCYZUZK)sE>m7M8xmNGlcj3Mp|qmUC(Ge&Y37OLpircLEMiR75X=4W>wnLpp544o9^_l zpjXf{AgG~oYXbre1kZ~g&Q*tL&HWirA1EUa%3&=1&;6`X>_ur!VI>oHiu1Ssu#0c0 z37Z_TYDa9_{$TNu{mHMLb663f(HoqiChWDTychJSsD(imELon*$h0iEH0N6M%q`nz zOP^e-cUoIlT8+;U60(Nzn~96xg}DAyuT7g3;_>{qrEb6Raz614IPs}6aK>*7 zX$NAzd$rUz+r?{W{D`%m)q7|9-k@i^WF557pd9rpIzvWl^N=oH4K5BOn>y3Kbjp0o Ep9i!N1^@s6 delta 733 zcmV<30wVqVCh7%{8Gix*003RI=qLaH010qNS#tmY4#NNd4#NS*Z>VGd00M$ZL_t(| z+U=alcEcbHMfv}qrmLn6gY+cCWIru>avY*!uF+)KDgR1JDF;bY)z$hFAg0ND2Bh$F z5R}}j|Fgus4>15h)cA$aJCp15|Npl5ivajr?)pQfVB4ZzXn)WI)xRPDCjfPG)T=CP z9RirU1A0h`Z_}>RnCAwj&K&mv#qmPJ1}B_CWs4IwMM1UwX^vkMh6Y}|f~p1c$MXPi zd|Hsmgqsdu=)E`*vjGS_Us4Xo4OBxqnPy; z0BT)n9DpOHw0{Cn6Qx5)Uz-4fx3Kq0W?p+I;G7{xc$rT z+Q=0E@S4(bQ4qM+X98%h$Yo)uP>CJ!0pry%JdC!6xo0`yk+4z?1RV@s0YE-D9)_+2 zaHR=q+&ySTM1cVC#d?Jk?nR#ngEIl#8ivZ@zdH(A1ApN9M674`$Mb*%KH&ONguG`5 z!_bugt}MnaC`ZrE0|z%Enk z`(OC4ntwO+O`S+fcfjFZ4(ymm00?@H0MP6Qm3tt1pR?Gs+V}8~AhZ>mELfrHRse{D zX-<7`2UtyzIztse#-C^(@p4EH7HF^4$H8SQVOAZV`!50T0m$0xDW~KCY4R(CFOAAp zBw~1H{fzn#Ghqq=5CA1aegFzG$R1mowuArx01jnXNoGw=04e|g00;m8000000Mb*F P00000NkvXXu0mjfxwkgF From 4104dd31cf3a7d8eaac5dc6d39ac77a0e50ee3d3 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 7 Mar 2024 20:05:27 +0000 Subject: [PATCH 22/22] Sync apps (minor changes to picopass) --- applications/external | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external b/applications/external index ea5d532d8c..3da3a125fe 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit ea5d532d8ccdbba597b620d1cb44d5a83bd6f700 +Subproject commit 3da3a125fe9d9a286d265d0209a403dd728fb2b1