From ee2c7bed9264ae19451bf947bcc42a588f0f69c7 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Wed, 21 Feb 2024 14:15:52 +0000 Subject: [PATCH 01/10] Update ac.ir Updated AC --- .../infrared/resources/infrared/assets/ac.ir | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/applications/main/infrared/resources/infrared/assets/ac.ir b/applications/main/infrared/resources/infrared/assets/ac.ir index c26d282c4c..2f076fbabd 100644 --- a/applications/main/infrared/resources/infrared/assets/ac.ir +++ b/applications/main/infrared/resources/infrared/assets/ac.ir @@ -1,43 +1,43 @@ Filetype: IR library file Version: 1 -# Last Updated 16th Aug, 2023 -# Last Checked 16th Aug, 2023 +# Last Updated 21st Feb, 2024 +# Last Checked 21st Feb, 2024 # # Model: Electrolux EACM-16 HP/N3 name: Off type: raw frequency: 38000 -duty_cycle: 0.33 +duty_cycle: 0.330000 data: 502 3436 510 475 509 476 508 477 507 477 507 479 505 480 504 480 504 490 504 481 502 482 501 483 563 420 511 474 510 475 509 476 508 485 561 423 508 476 508 477 507 478 506 479 505 480 504 481 503 517 508 476 508 478 506 479 505 479 505 481 503 483 521 1456 501 498 507 479 505 480 504 481 503 482 501 483 563 421 562 422 509 499 506 479 505 480 504 481 503 482 502 484 510 1451 506 479 505 1542 562 1396 509 471 502 476 508 469 504 3425 511 # name: Dh type: raw frequency: 38000 -duty_cycle: 0.33 +duty_cycle: 0.330000 data: 507 3430 506 479 505 480 504 481 503 481 503 483 501 485 509 1453 504 1465 503 482 502 483 511 473 500 485 509 476 508 477 507 478 506 487 507 477 507 478 506 479 505 480 504 482 502 483 501 484 500 523 503 482 502 484 500 485 509 476 508 476 508 478 506 1456 501 501 504 482 502 483 501 484 500 485 509 476 508 477 507 1455 502 509 506 479 505 1457 500 485 509 476 508 1454 503 482 502 483 501 568 499 1459 509 1450 507 471 502 474 510 3421 505 # name: Cool_hi type: raw frequency: 38000 -duty_cycle: 0.33 +duty_cycle: 0.330000 data: 504 3433 503 482 502 484 510 474 510 475 509 476 508 478 506 1456 564 1405 510 475 509 476 508 502 482 477 507 478 506 479 505 480 504 489 505 480 504 481 503 482 502 483 511 473 511 474 510 475 509 509 506 479 505 480 504 481 503 482 512 473 511 474 510 476 508 1469 509 475 509 476 508 477 507 478 506 479 505 480 504 481 503 505 510 475 509 502 482 503 481 504 480 505 478 507 477 1459 509 560 507 1451 506 473 511 493 480 1450 507 3422 503 # name: Cool_lo type: raw frequency: 38000 -duty_cycle: 0.33 +duty_cycle: 0.330000 data: 525 3615 530 506 561 474 562 474 562 473 563 473 531 505 562 1502 528 1542 562 474 562 474 530 505 531 504 532 504 532 504 616 419 533 510 589 447 526 509 527 509 527 509 527 508 528 508 528 507 529 542 525 510 526 509 527 509 527 509 527 508 528 508 528 1535 527 524 533 503 533 503 533 502 534 502 534 502 534 501 535 501 525 534 533 502 534 502 534 501 535 502 534 1529 533 503 533 503 533 587 533 497 528 501 524 1536 526 501 524 3609 526 # name: Heat_hi type: raw frequency: 38000 -duty_cycle: 0.33 +duty_cycle: 0.330000 data: 531 3406 530 455 529 456 528 457 537 447 537 448 535 450 534 1429 528 1442 536 448 536 449 534 451 532 452 532 453 530 454 530 455 529 464 530 454 529 456 528 457 537 448 536 449 535 450 533 451 533 490 535 449 534 450 534 451 533 452 532 453 531 455 529 1433 534 1443 535 449 535 450 534 452 531 453 530 454 530 455 529 456 538 472 532 452 532 454 530 1433 535 1427 530 1432 536 1427 530 1431 537 1511 530 448 536 1422 535 1423 534 1422 535 3395 530 # name: Heat_lo type: raw frequency: 38000 -duty_cycle: 0.33 +duty_cycle: 0.330000 data: 506 3430 506 478 506 479 505 480 504 481 503 482 502 484 500 1463 505 1465 503 482 502 483 501 484 500 485 509 476 508 477 507 478 506 486 508 477 507 478 506 479 505 480 504 481 503 482 502 483 500 523 502 482 502 483 501 484 500 485 509 476 508 478 506 1455 502 498 507 478 506 479 505 481 503 482 501 483 500 484 500 485 509 500 505 481 502 482 502 1461 507 1455 502 1459 509 476 508 477 507 563 504 1453 504 1454 503 1454 503 1453 504 3426 499 # # Model: Hisense Generic @@ -959,3 +959,27 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 664 17757 3057 8901 528 469 550 1434 556 465 553 440 529 465 554 439 555 439 555 438 555 440 553 1435 552 443 550 470 524 1466 522 472 522 472 521 1467 523 1466 549 1440 549 1440 548 1440 548 445 549 445 549 445 549 446 548 470 524 471 523 471 523 471 523 472 522 472 522 473 522 472 523 472 548 446 549 445 550 445 548 446 548 446 548 446 548 446 548 447 547 470 524 471 523 471 523 471 523 471 523 474 519 474 521 474 521 473 522 473 546 447 547 1441 548 1442 546 1442 547 1442 546 2947 3023 8935 522 1466 522 472 522 498 496 498 495 499 496 498 496 498 521 473 522 471 523 1466 522 471 523 471 522 1466 523 471 523 1467 522 1467 522 1467 521 1493 495 1494 496 1493 521 473 522 472 522 471 523 472 522 471 523 472 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 499 495 499 495 499 495 499 496 498 522 473 522 472 523 471 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 473 521 473 521 473 521 499 495 500 494 500 495 499 496 498 522 2947 3023 8937 521 1468 520 473 521 473 521 473 521 473 521 473 521 473 521 474 520 474 520 1470 518 500 494 500 494 500 494 500 494 1494 521 1468 521 473 521 1468 520 1468 520 1469 520 1469 520 1470 518 1495 493 1496 493 1495 494 500 519 475 520 474 520 1469 519 1469 520 1469 519 474 520 474 520 475 519 477 517 500 494 1496 493 1496 493 1496 493 500 495 1495 519 475 518 475 519 475 518 476 518 475 519 1470 519 500 494 500 494 501 493 501 493 501 493 1499 490 1497 493 1497 492 1496 518 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 148 110377 9082 4422 707 499 706 500 704 1603 703 1606 701 505 700 507 699 506 700 507 699 506 700 1607 700 1608 700 1609 699 506 699 507 699 506 699 507 699 507 699 506 700 507 699 507 699 507 699 1608 699 1607 699 507 699 507 699 507 699 507 699 507 699 1609 699 507 699 1608 699 507 699 507 699 1609 699 507 699 19940 700 506 700 506 699 507 699 507 699 506 700 506 700 507 699 507 699 507 700 507 699 506 699 507 699 507 699 507 699 507 699 507 699 507 699 507 699 507 698 507 700 507 699 507 699 507 699 507 699 507 699 508 698 508 698 507 699 507 699 508 698 1610 699 508 698 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 315 101050 3094 3056 3093 4437 580 1648 572 534 576 1649 582 525 574 530 580 1646 574 1653 578 529 570 534 576 529 571 534 576 529 570 1655 576 1651 580 527 572 532 578 1647 573 1654 577 1651 580 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 525 574 531 579 525 574 531 579 1646 574 532 578 526 573 531 579 526 573 531 579 526 573 1652 579 527 572 1653 578 528 571 534 576 528 571 533 577 528 571 533 577 528 572 533 577 528 572 532 578 527 572 532 578 527 572 532 578 526 573 1652 579 527 572 532 578 527 572 532 578 527 572 532 578 526 573 531 579 526 573 531 579 526 573 531 579 525 574 530 580 525 574 530 580 525 574 530 580 524 575 529 581 524 575 529 571 534 576 528 571 533 577 528 571 533 577 528 571 533 577 527 572 532 578 527 572 532 578 526 573 531 579 526 573 531 579 525 574 531 579 525 574 530 580 525 574 1650 581 525 574 1651 580 1647 573 533 577 527 572 1653 578 528 572 1654 577 1650 581 1646 574 71637 254 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 284 19161 3098 3053 3096 4435 572 1656 575 532 578 1648 572 534 576 530 570 1682 549 1652 579 527 572 534 576 1649 571 1656 575 1652 579 1649 571 1656 575 531 579 527 572 1653 578 1649 571 1656 575 531 579 527 572 532 578 527 572 533 577 527 572 533 577 527 573 532 578 527 572 532 578 527 573 532 578 527 572 1652 579 527 572 533 577 528 571 533 577 528 571 533 577 1648 572 533 577 1649 571 535 575 530 569 536 574 531 569 536 574 530 569 536 574 530 570 535 575 530 570 535 575 530 569 535 575 530 569 535 575 1649 571 535 575 531 568 536 574 531 568 536 574 531 568 536 574 531 569 536 574 530 569 536 574 530 569 535 575 530 569 535 575 530 569 535 575 530 570 535 575 529 570 534 576 529 570 534 576 529 570 534 576 528 571 534 576 528 571 534 576 528 571 534 576 528 571 534 576 528 571 533 577 528 571 533 577 528 572 533 577 528 571 533 577 528 572 1652 579 527 572 1653 578 529 570 534 576 529 570 535 575 529 570 1654 577 1677 554 1673 547 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 301 132136 5036 2167 337 1766 361 689 358 692 366 684 363 1770 357 692 366 684 363 718 329 690 357 1776 361 687 360 1773 364 1767 360 689 358 1775 362 1769 357 1774 363 1768 359 1773 364 684 363 718 329 1773 364 684 363 718 329 691 356 694 364 716 331 719 328 1775 362 1769 358 1774 363 1768 359 1772 365 714 333 1770 357 1774 363 716 331 719 328 722 336 715 332 718 329 721 326 724 334 716 331 719 328 722 336 715 332 718 329 1773 364 1767 360 1772 354 1777 360 719 328 721 326 725 333 717 330 29455 5036 2139 354 1777 360 688 359 691 367 714 333 1770 356 692 366 684 363 687 360 690 357 1776 361 688 359 1773 364 1768 359 689 358 1775 362 1769 357 1774 363 1768 359 1773 364 684 363 687 360 1773 364 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 688 359 691 356 1777 360 1771 355 693 365 685 362 1771 355 693 365 1768 359 1773 364 1767 360 689 358 692 366 685 362 1771 355 1775 362 687 360 690 357 1775 362 687 360 690 357 693 365 716 331 689 358 1774 363 686 361 689 358 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1771 355 693 365 1768 358 1773 364 684 363 687 360 690 357 693 365 1768 359 690 357 1776 361 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1770 356 693 365 685 362 688 359 691 356 1777 360 1771 355 693 365 686 361 689 358 692 366 685 362 1770 356 From 33fe146a57db4fead25d774009f2eca590644150 Mon Sep 17 00:00:00 2001 From: MizumasuShoichi <153268042+MizumasuShoichi@users.noreply.github.com> Date: Tue, 27 Feb 2024 16:02:51 +0800 Subject: [PATCH 02/10] Update subghz_setting.c Added frequency: 430.50 --- lib/subghz/subghz_setting.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c index a9f6b7c6b8..a72c482d4b 100644 --- a/lib/subghz/subghz_setting.c +++ b/lib/subghz/subghz_setting.c @@ -47,6 +47,7 @@ static const uint32_t subghz_frequency_list[] = { 390000000, 418000000, 430000000, + 430500000, 431000000, 431500000, 433075000, /* LPD433 first */ 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 03/10] [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 4f7eb770711f0f6d07c66a1018618918a604806f Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Thu, 7 Mar 2024 08:57:32 +0200 Subject: [PATCH 04/10] Fix troika 4K keys (#3499) --- .../main/nfc/plugins/supported_cards/troika.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/troika.c b/applications/main/nfc/plugins/supported_cards/troika.c index 50806e137d..758f824b70 100644 --- a/applications/main/nfc/plugins/supported_cards/troika.c +++ b/applications/main/nfc/plugins/supported_cards/troika.c @@ -73,14 +73,14 @@ static const MfClassicKeyPair troika_4k_keys[] = { {.a = 0x7A38E3511A38, .b = 0xAB16584C972A}, //30 {.a = 0x7545DF809202, .b = 0xECF751084A80}, //31 {.a = 0x5125974CD391, .b = 0xD3EAFB5DF46D}, //32 - {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //33 - {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //34 - {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //35 - {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //36 - {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //37 - {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //38 - {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //39 - {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //40 + {.a = 0x7A86AA203788, .b = 0xE41242278CA2}, //33 + {.a = 0xAFCEF64C9913, .b = 0x9DB96DCA4324}, //34 + {.a = 0x04EAA462F70B, .b = 0xAC17B93E2FAE}, //35 + {.a = 0xE734C210F27E, .b = 0x29BA8C3E9FDA}, //36 + {.a = 0xD5524F591EED, .b = 0x5DAF42861B4D}, //37 + {.a = 0xE4821A377B75, .b = 0xE8709E486465}, //38 + {.a = 0x518DC6EEA089, .b = 0x97C64AC98CA4}, //39 + {.a = 0xBB52F8CCE07F, .b = 0x6B6119752C70}, //40 }; static bool troika_get_card_config(TroikaCardConfig* config, MfClassicType type) { From 58cd56a439fa3cd33cd32222ea7e5a878f7fd145 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 10 Mar 2024 23:35:44 +0300 Subject: [PATCH 05/10] Merge branch 'ofw_dev' into dev p1 --- .gitmodules | 3 + .vscode/ReadMe.md | 2 +- .../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 +- .../main/nfc/plugins/supported_cards/troika.c | 16 +- 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 | 76 ++++++ .../Firmware update on Developer Board.md | 246 ++++++++++++++++++ .../Get started with the Dev Board.md | 175 +++++++++++++ .../Reading logs via the Dev Board.md | 152 +++++++++++ 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 +- 73 files changed, 958 insertions(+), 229 deletions(-) create mode 100644 documentation/UniversalRemotes.md 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 diff --git a/.gitmodules b/.gitmodules index 6f73e125ac..9b5e3b16a6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -42,3 +42,6 @@ path = applications/main/subghz_remote url = https://github.com/DarkFlippers/SubGHz_Remote.git branch = ufw_main_app +[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/applications/drivers/subghz/cc1101_ext/cc1101_ext.h b/applications/drivers/subghz/cc1101_ext/cc1101_ext.h index 130402f8ef..64b7d4cc1a 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/main/nfc/plugins/supported_cards/troika.c b/applications/main/nfc/plugins/supported_cards/troika.c index 50806e137d..758f824b70 100644 --- a/applications/main/nfc/plugins/supported_cards/troika.c +++ b/applications/main/nfc/plugins/supported_cards/troika.c @@ -73,14 +73,14 @@ static const MfClassicKeyPair troika_4k_keys[] = { {.a = 0x7A38E3511A38, .b = 0xAB16584C972A}, //30 {.a = 0x7545DF809202, .b = 0xECF751084A80}, //31 {.a = 0x5125974CD391, .b = 0xD3EAFB5DF46D}, //32 - {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //33 - {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //34 - {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //35 - {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //36 - {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //37 - {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //38 - {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //39 - {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //40 + {.a = 0x7A86AA203788, .b = 0xE41242278CA2}, //33 + {.a = 0xAFCEF64C9913, .b = 0x9DB96DCA4324}, //34 + {.a = 0x04EAA462F70B, .b = 0xAC17B93E2FAE}, //35 + {.a = 0xE734C210F27E, .b = 0x29BA8C3E9FDA}, //36 + {.a = 0xD5524F591EED, .b = 0x5DAF42861B4D}, //37 + {.a = 0xE4821A377B75, .b = 0xE8709E486465}, //38 + {.a = 0x518DC6EEA089, .b = 0x97C64AC98CA4}, //39 + {.a = 0xBB52F8CCE07F, .b = 0x6B6119752C70}, //40 }; static bool troika_get_card_config(TroikaCardConfig* config, MfClassicType type) { diff --git a/applications/services/gui/canvas.h b/applications/services/gui/canvas.h index bda730ff28..5f31676973 100644 --- a/applications/services/gui/canvas.h +++ b/applications/services/gui/canvas.h @@ -248,7 +248,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 bdc43d09a4..066735d4cb 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 f16f5febc3..ebe65ab88f 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 new file mode 100644 index 0000000000..360d8a0abb --- /dev/null +++ b/documentation/UniversalRemotes.md @@ -0,0 +1,76 @@ +# Universal Remotes {#universal_remotes} + +## Televisions + +Adding your TV set to the universal remote is quite straightforward. Up to 6 signals can be recorded: `Power`, `Mute`, `Vol_up`, `Vol_dn`, `Ch_next`, and `Ch_prev`. Any of them can be omitted if not supported by your TV. + +Each signal is recorded using the following algorithm: + +1. Get the remote and point it to Flipper's IR receiver. +2. Start learning a new remote if it's the first button or press `+` to add a new button otherwise. +3. Press a remote button and save it under a corresponding name. +4. Repeat steps 2-3 until all required signals are saved. + +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](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/main/infrared/resources/infrared/assets/tv.ir). + +## Audio players + +Adding your audio player to the universal remote is done in the same manner as described above. Up to 8 signals can be recorded: `Power`, `Play`, `Pause`, `Vol_up`, `Vol_dn`, `Next`, `Prev`, and `Mute`. Any of them can be omitted if not supported by the player. + +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](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/main/infrared/resources/infrared/assets/audio.ir). + +## Projectors + +Adding your projector to the universal remote is really simple. Up to 4 signals can be recorded: `Power`, `Mute`, `Vol_up`, `Vol_dn`. Any of them can be omitted if not supported by your projector. +To save time, please make sure every recording has been named accordingly. +In case of omitting, on most projectors with the 4 following buttons, you should not have a problem. + + +## Air conditioners + +Air conditioners differ from most other infrared-controlled devices because their state is tracked by the remote. +The majority of A/C remotes have a small display that shows the current mode, temperature, and other settings. +When the user presses a button, a whole set of parameters is transmitted to the device, which must be recorded and used as a whole. + +In order to add a particular air conditioner to the universal remote, 6 signals must be recorded: `Off`, `Dh`, `Cool_hi`, `Cool_lo`, `Heat_hi`, and `Heat_lo`. +Each signal (except `Off`) is recorded using the following algorithm: + +1. Get the remote and press the **Power Button** so that the display shows that A/C is ON. +2. Set the A/C to the corresponding mode (see table below), leaving other parameters such as fan speed or vane on **AUTO** (if applicable). +3. Press the **POWER** button to switch the A/C off. +4. Start learning a new remote on Flipper if it's the first button or press `+` to add a new button otherwise. +5. Point the remote to Flipper's IR receiver as directed and press **POWER** button once again. +6. Save the resulting signal under the specified name. +7. Repeat steps 2-6 for each signal from the table below. + +| Signal | Mode | Temperature | Note | +| :-----: | :--------: | :---------: | ----------------------------------- | +| Dh | Dehumidify | N/A | | +| Cool_hi | Cooling | See note | Lowest temperature in cooling mode | +| Cool_lo | Cooling | 23°C | | +| Heat_hi | Heating | See note | Highest temperature in heating mode | +| Heat_lo | Heating | 23°C | | + +Finally, record the `Off` signal: + +1. Make sure the display shows that the A/C is ON. +2. Start learning a new signal on Flipper and point the remote towards the IR receiver. +3. Press the **POWER** button so that the remote shows the OFF state. +4. Save the resulting signal under the name `Off`. + +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](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/main/infrared/resources/infrared/assets/ac.ir). + +## Final steps + +The order of signals is not important, but they should be preceded by the following comment: `# Model: ` in order to keep the library organized. + +When done, open a pull request containing the changed file. 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/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 35a6e31555..80047faf7a 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 225072629f..01919b9c6d 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 e498b15862..cc81e09547 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 bac3aba1eb..3f7d42c64e 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 df57ecd7862c9c3822b5fcd4bd0cd9531f0968b9 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 10 Mar 2024 23:37:30 +0300 Subject: [PATCH 06/10] rem module that is not used in UL for now --- .gitmodules | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitmodules b/.gitmodules index 9b5e3b16a6..2193287be4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -41,7 +41,4 @@ [submodule "subghz_remote"] path = applications/main/subghz_remote url = https://github.com/DarkFlippers/SubGHz_Remote.git - branch = ufw_main_app -[submodule "documentation/doxygen/doxygen-awesome-css"] - path = documentation/doxygen/doxygen-awesome-css - url = https://github.com/jothepro/doxygen-awesome-css.git + branch = ufw_main_app \ No newline at end of file From 25d4c4f8298240cae6b5a048ad6a6006b7bfffd6 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 10 Mar 2024 23:56:23 +0300 Subject: [PATCH 07/10] upd subremote --- applications/main/subghz_remote | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/subghz_remote b/applications/main/subghz_remote index 2ea0fac185..21b2c18d92 160000 --- a/applications/main/subghz_remote +++ b/applications/main/subghz_remote @@ -1 +1 @@ -Subproject commit 2ea0fac1857f27fdbcff236ce375c82038e381b7 +Subproject commit 21b2c18d92d3d87e46f6381dce3de3c2818de151 From 1bd56fb19062a2d91dcd89a2a2c2fc591d65ddd2 Mon Sep 17 00:00:00 2001 From: Methodius Date: Sat, 9 Mar 2024 01:53:02 +0900 Subject: [PATCH 08/10] bip parser refactor --- .../main/nfc/plugins/supported_cards/bip.c | 401 +++++++++--------- 1 file changed, 190 insertions(+), 211 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/bip.c b/applications/main/nfc/plugins/supported_cards/bip.c index f6fed6774b..7487d38448 100644 --- a/applications/main/nfc/plugins/supported_cards/bip.c +++ b/applications/main/nfc/plugins/supported_cards/bip.c @@ -1,70 +1,139 @@ #include "nfc_supported_card_plugin.h" +#include -#include -#include -#include +#include "protocols/mf_classic/mf_classic.h" #include +#include +#include + #define TAG "Bip" -#define SECTOR_BLOCK_OFFSET(sector, block) (((sector) * 4) + (block)) - -static const uint64_t bip_keys_a[] = { - 0x3a42f33af429, - 0x6338a371c0ed, - 0xf124c2578ad0, - 0x32ac3b90ac13, - 0x4ad1e273eaf1, - 0xe2c42591368a, - 0x2a3c347a1200, - 0x16f3d5ab1139, - 0x937a4fff3011, - 0x35c3d2caee88, - 0x693143f10368, - 0xa3f97428dd01, - 0x63f17a449af0, - 0xc4652c54261c, - 0xd49e2826664f, - 0x3df14c8000a1, -}; +#define BIP_CARD_ID_SECTOR_NUMBER (0) +#define BIP_BALANCE_SECTOR_NUMBER (8) +#define BIP_TRIP_TIME_WINDOW_SECTOR_NUMBER (5) +#define BIP_LAST_TOP_UPS_SECTOR_NUMBER (10) +#define BIP_TRIPS_INFO_SECTOR_NUMBER (11) -static const uint64_t bip_keys_b[] = { - 0x1fc235ac1309, - 0x243f160918d1, - 0x9afc42372af1, - 0x682d401abb09, - 0x067db45454a9, - 0x15fc4c7613fe, - 0x68d30288910a, - 0xf59a36a2546d, - 0x64e3c10394c2, - 0xb736412614af, - 0x324f5df65310, - 0x643fb6de2217, - 0x82f435dedf01, - 0x0263de1278f3, - 0x51284c3686a6, - 0x6a470d54127c, +typedef struct { + DateTime datetime; + uint16_t amount; +} BipTransaction; + +typedef struct { + uint64_t a; + uint64_t b; +} MfClassicKeyPair; + +static const MfClassicKeyPair bip_1k_keys[] = { + {.a = 0x3a42f33af429, .b = 0x1fc235ac1309}, + {.a = 0x6338a371c0ed, .b = 0x243f160918d1}, + {.a = 0xf124c2578ad0, .b = 0x9afc42372af1}, + {.a = 0x32ac3b90ac13, .b = 0x682d401abb09}, + {.a = 0x4ad1e273eaf1, .b = 0x067db45454a9}, + {.a = 0xe2c42591368a, .b = 0x15fc4c7613fe}, + {.a = 0x2a3c347a1200, .b = 0x68d30288910a}, + {.a = 0x16f3d5ab1139, .b = 0xf59a36a2546d}, + {.a = 0x937a4fff3011, .b = 0x64e3c10394c2}, + {.a = 0x35c3d2caee88, .b = 0xb736412614af}, + {.a = 0x693143f10368, .b = 0x324f5df65310}, + {.a = 0xa3f97428dd01, .b = 0x643fb6de2217}, + {.a = 0x63f17a449af0, .b = 0x82f435dedf01}, + {.a = 0xc4652c54261c, .b = 0x0263de1278f3}, + {.a = 0xd49e2826664f, .b = 0x51284c3686a6}, + {.a = 0x3df14c8000a1, .b = 0x6a470d54127c}, }; -bool bip_verify(Nfc* nfc) { - bool verified = true; +static void bip_parse_datetime(const MfClassicBlock* block, DateTime* parsed_data) { + furi_assert(block); + furi_assert(parsed_data); + + parsed_data->day = (((block->data[1] << 8) + block->data[0]) >> 6) & 0x1f; + parsed_data->month = (((block->data[1] << 8) + block->data[0]) >> 11) & 0xf; + parsed_data->year = 2000 + ((((block->data[2] << 8) + block->data[1]) >> 7) & 0x1f); + parsed_data->hour = (((block->data[3] << 8) + block->data[2]) >> 4) & 0x1f; + parsed_data->minute = (((block->data[3] << 8) + block->data[2]) >> 9) & 0x3f; + parsed_data->second = (((block->data[4] << 8) + block->data[3]) >> 7) & 0x3f; +} + +static void bip_print_datetime(const DateTime* datetime, FuriString* str) { + furi_assert(datetime); + furi_assert(str); - const uint8_t verify_sector = 0; - uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector); - FURI_LOG_D(TAG, "Verifying sector %u", verify_sector); + LocaleDateFormat date_format = locale_get_date_format(); + const char* separator = (date_format == LocaleDateFormatDMY) ? "." : "/"; - MfClassicKey key_a_0 = {}; - bit_lib_num_to_bytes_be(bip_keys_a[0], COUNT_OF(key_a_0.data), key_a_0.data); + FuriString* date_str = furi_string_alloc(); + locale_format_date(date_str, datetime, date_format, separator); - MfClassicAuthContext auth_ctx = {}; - MfClassicError error = - mf_classic_poller_sync_auth(nfc, block_num, &key_a_0, MfClassicKeyTypeA, &auth_ctx); + FuriString* time_str = furi_string_alloc(); + locale_format_time(time_str, datetime, locale_get_time_format(), false); - if(error == MfClassicErrorNotPresent) { - FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error); - verified = false; + furi_string_cat_printf( + str, "%s %s", furi_string_get_cstr(date_str), furi_string_get_cstr(time_str)); + + furi_string_free(date_str); + furi_string_free(time_str); +} + +static int datetime_cmp(const DateTime* dt_1, const DateTime* dt_2) { + furi_assert(dt_1); + furi_assert(dt_2); + + if(dt_1->year != dt_2->year) { + return dt_1->year - dt_2->year; + } + if(dt_1->month != dt_2->month) { + return dt_1->month - dt_2->month; + } + if(dt_1->day != dt_2->day) { + return dt_1->day - dt_2->day; } + if(dt_1->hour != dt_2->hour) { + return dt_1->hour - dt_2->hour; + } + if(dt_1->minute != dt_2->minute) { + return dt_1->minute - dt_2->minute; + } + if(dt_1->second != dt_2->second) { + return dt_1->second - dt_2->second; + } + return 0; +} + +static bool is_bip_block_empty(const MfClassicBlock* block) { + furi_assert(block); + // check if all but last byte are zero (last is checksum) + for(size_t i = 0; i < sizeof(block->data) - 1; i++) { + if(block->data[i] != 0) { + return false; + } + } + return true; +} + +bool bip_verify(Nfc* nfc) { + bool verified = false; + + do { + const uint8_t verify_sector = 0; + uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector); + FURI_LOG_D(TAG, "Verifying sector %u", verify_sector); + + MfClassicKey key = {}; + bit_lib_num_to_bytes_be(bip_1k_keys[0].a, COUNT_OF(key.data), key.data); + + MfClassicAuthContext auth_ctx = {}; + MfClassicError error = + mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_ctx); + + if(error == MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error); + break; + } + + verified = true; + } while(false); return verified; } @@ -79,31 +148,33 @@ static bool bip_read(Nfc* nfc, NfcDevice* device) { nfc_device_copy_data(device, NfcProtocolMfClassic, data); do { - MfClassicType type = MfClassicType1k; + MfClassicType type = MfClassicTypeMini; MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type); - if(error == MfClassicErrorNotPresent) { - FURI_LOG_W(TAG, "Card not MIFARE Classic 1k"); - break; - } + if(error != MfClassicErrorNone) break; data->type = type; - MfClassicDeviceKeys keys = {}; + if(type != MfClassicType1k) break; + + MfClassicDeviceKeys keys = { + .key_a_mask = 0, + .key_b_mask = 0, + }; for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { - bit_lib_num_to_bytes_be(bip_keys_a[i], sizeof(MfClassicKey), keys.key_a[i].data); + bit_lib_num_to_bytes_be(bip_1k_keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); FURI_BIT_SET(keys.key_a_mask, i); - bit_lib_num_to_bytes_be(bip_keys_b[i], sizeof(MfClassicKey), keys.key_b[i].data); + bit_lib_num_to_bytes_be(bip_1k_keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); FURI_BIT_SET(keys.key_b_mask, i); } error = mf_classic_poller_sync_read(nfc, &keys, data); if(error == MfClassicErrorNotPresent) { - FURI_LOG_W(TAG, "Failed to read data. Bad keys?"); + FURI_LOG_W(TAG, "Failed to read data"); break; } nfc_device_set_data(device, NfcProtocolMfClassic, data); - is_read = true; + is_read = (error == MfClassicErrorNone); } while(false); mf_classic_free(data); @@ -111,183 +182,91 @@ static bool bip_read(Nfc* nfc, NfcDevice* device) { return is_read; } -typedef struct { - uint16_t year; - uint8_t month; - uint8_t day; - uint8_t hour; - uint8_t minute; - uint8_t second; -} BipTimestamp; - -static void parse_bip_timestamp(const MfClassicBlock* block, BipTimestamp* timestamp) { - furi_assert(block); - furi_assert(timestamp); - - timestamp->day = (((block->data[1] << 8) + block->data[0]) >> 6) & 0x1f; - timestamp->month = (((block->data[1] << 8) + block->data[0]) >> 11) & 0xf; - timestamp->year = 2000 + ((((block->data[2] << 8) + block->data[1]) >> 7) & 0x1f); - timestamp->hour = (((block->data[3] << 8) + block->data[2]) >> 4) & 0x1f; - timestamp->minute = (((block->data[3] << 8) + block->data[2]) >> 9) & 0x3f; - timestamp->second = (((block->data[4] << 8) + block->data[3]) >> 7) & 0x3f; -} - -static int compare_bip_timestamp(const BipTimestamp* t1, const BipTimestamp* t2) { - furi_assert(t1); - furi_assert(t2); - if(t1->year != t2->year) { - return t1->year - t2->year; - } - if(t1->month != t2->month) { - return t1->month - t2->month; - } - if(t1->day != t2->day) { - return t1->day - t2->day; - } - if(t1->hour != t2->hour) { - return t1->hour - t2->hour; - } - if(t1->minute != t2->minute) { - return t1->minute - t2->minute; - } - if(t1->second != t2->second) { - return t1->second - t2->second; - } - return 0; -} - -static void print_bip_timestamp(const BipTimestamp* timestamp, FuriString* str) { - furi_assert(timestamp); - furi_assert(str); - furi_string_cat_printf( - str, - "%04u-%02u-%02u %02u:%02u:%02u", - timestamp->year, - timestamp->month, - timestamp->day, - timestamp->hour, - timestamp->minute, - timestamp->second); -} - -static bool is_bip_block_empty(const MfClassicBlock* block) { - furi_assert(block); - // check if all but last byte are zero (last is checksum) - for(size_t i = 0; i < sizeof(block->data) - 1; i++) { - if(block->data[i] != 0) { - return false; - } - } - return true; -} - -static void parse_uint16_le(const uint8_t* data, uint16_t* value) { - furi_assert(data); - furi_assert(value); - - *value = (data[0]) | (data[1] << 8); -} - -static void parse_uint32_le(const uint8_t* data, uint32_t* value) { - furi_assert(data); - furi_assert(value); - - *value = (data[0]) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); -} - -static void parse_uint16_txn_amount(const uint8_t* data, uint16_t* value) { - furi_assert(data); - furi_assert(value); - - parse_uint16_le(data, value); - *value = *value >> 2; -} - -typedef struct { - BipTimestamp timestamp; - uint16_t amount; -} BipTransaction; - static bool bip_parse(const NfcDevice* device, FuriString* parsed_data) { furi_assert(device); furi_assert(parsed_data); - bool parsed = true; - struct { uint32_t card_id; uint16_t balance; uint16_t flags; - BipTimestamp trip_time_window; + DateTime trip_time_window; BipTransaction top_ups[3]; BipTransaction charges[3]; - } bip_data = { - .card_id = 0, - .balance = 0, - .flags = 0, - .trip_time_window = {0, 0, 0, 0, 0, 0}, - .top_ups = - { - {{0, 0, 0, 0, 0, 0}, 0}, - {{0, 0, 0, 0, 0, 0}, 0}, - {{0, 0, 0, 0, 0, 0}, 0}, - }, - .charges = - { - {{0, 0, 0, 0, 0, 0}, 0}, - {{0, 0, 0, 0, 0, 0}, 0}, - {{0, 0, 0, 0, 0, 0}, 0}, - }, - }; + } bip_data = {0}; const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + bool parsed = false; + do { - // verify first sector keys + // verify sector 0 key A MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 0); + + if(data->type != MfClassicType1k) break; + uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6); - if(key != bip_keys_a[0]) { - parsed = false; + if(key != bip_1k_keys[0].a) { break; } + + // verify sector 0 key B key = bit_lib_bytes_to_num_be(sec_tr->key_b.data, 6); - if(key != bip_keys_b[0]) { - parsed = false; + if(key != bip_1k_keys[0].b) { break; } // Get Card ID, little-endian 4 bytes at sector 0 block 1, bytes 4-7 - parse_uint32_le(&data->block[SECTOR_BLOCK_OFFSET(0, 1)].data[4], &bip_data.card_id); + const uint8_t card_id_start_block_num = + mf_classic_get_first_block_num_of_sector(BIP_CARD_ID_SECTOR_NUMBER); + const uint8_t* block_start_ptr = &data->block[card_id_start_block_num + 1].data[0]; + + bip_data.card_id = bit_lib_bytes_to_num_le(block_start_ptr + 4, 4); // Get balance, little-endian 2 bytes at sector 8 block 1, bytes 0-1 - parse_uint16_le(&data->block[SECTOR_BLOCK_OFFSET(8, 1)].data[0], &bip_data.balance); + const uint8_t balance_start_block_num = + mf_classic_get_first_block_num_of_sector(BIP_BALANCE_SECTOR_NUMBER); + block_start_ptr = &data->block[balance_start_block_num + 1].data[0]; + + bip_data.balance = bit_lib_bytes_to_num_le(block_start_ptr, 2); // Get balance flags (negative balance, etc.), little-endian 2 bytes at sector 8 block 1, bytes 2-3 - parse_uint16_le(&data->block[SECTOR_BLOCK_OFFSET(8, 1)].data[2], &bip_data.flags); + bip_data.flags = bit_lib_bytes_to_num_le(block_start_ptr + 2, 2); // Get trip time window, proprietary format, at sector 5 block 1, bytes 0-7 - parse_bip_timestamp(&data->block[SECTOR_BLOCK_OFFSET(5, 1)], &bip_data.trip_time_window); + const uint8_t trip_time_window_start_block_num = + mf_classic_get_first_block_num_of_sector(BIP_TRIP_TIME_WINDOW_SECTOR_NUMBER); + const MfClassicBlock* trip_window_block_ptr = + &data->block[trip_time_window_start_block_num + 1]; + + bip_parse_datetime(trip_window_block_ptr, &bip_data.trip_time_window); // Last 3 top-ups: sector 10, ring-buffer of 3 blocks, timestamp in bytes 0-7, amount in bytes 9-10 + const uint8_t top_ups_start_block_num = + mf_classic_get_first_block_num_of_sector(BIP_LAST_TOP_UPS_SECTOR_NUMBER); for(size_t i = 0; i < 3; i++) { - if(is_bip_block_empty(&data->block[SECTOR_BLOCK_OFFSET(10, i)])) { - continue; - } + const MfClassicBlock* block = &data->block[top_ups_start_block_num + i]; + + if(is_bip_block_empty(block)) continue; + BipTransaction* top_up = &bip_data.top_ups[i]; - parse_bip_timestamp(&data->block[SECTOR_BLOCK_OFFSET(10, i)], &top_up->timestamp); - parse_uint16_txn_amount( - &data->block[SECTOR_BLOCK_OFFSET(10, i)].data[9], &top_up->amount); + bip_parse_datetime(block, &top_up->datetime); + + top_up->amount = bit_lib_bytes_to_num_le(&block->data[9], 2) >> 2; } // Last 3 charges (i.e. trips), sector 11, ring-buffer of 3 blocks, timestamp in bytes 0-7, amount in bytes 10-11 + const uint8_t trips_start_block_num = + mf_classic_get_first_block_num_of_sector(BIP_TRIPS_INFO_SECTOR_NUMBER); for(size_t i = 0; i < 3; i++) { - if(is_bip_block_empty(&data->block[SECTOR_BLOCK_OFFSET(11, i)])) { - continue; - } + const MfClassicBlock* block = &data->block[trips_start_block_num + i]; + + if(is_bip_block_empty(block)) continue; + BipTransaction* charge = &bip_data.charges[i]; - parse_bip_timestamp(&data->block[SECTOR_BLOCK_OFFSET(11, i)], &charge->timestamp); - parse_uint16_txn_amount( - &data->block[SECTOR_BLOCK_OFFSET(11, i)].data[10], &charge->amount); + bip_parse_datetime(block, &charge->datetime); + + charge->amount = bit_lib_bytes_to_num_le(&block->data[10], 2) >> 2; } // All data is now parsed and stored in bip_data, now print it @@ -303,14 +282,14 @@ static bool bip_parse(const NfcDevice* device, FuriString* parsed_data) { bip_data.balance, bip_data.flags); - print_bip_timestamp(&bip_data.trip_time_window, parsed_data); + bip_print_datetime(&bip_data.trip_time_window, parsed_data); // Find newest top-up size_t newest_top_up = 0; for(size_t i = 1; i < 3; i++) { - const BipTimestamp* newest = &bip_data.top_ups[newest_top_up].timestamp; - const BipTimestamp* current = &bip_data.top_ups[i].timestamp; - if(compare_bip_timestamp(current, newest) > 0) { + const DateTime* newest = &bip_data.top_ups[newest_top_up].datetime; + const DateTime* current = &bip_data.top_ups[i].datetime; + if(datetime_cmp(current, newest) > 0) { newest_top_up = i; } } @@ -320,15 +299,15 @@ static bool bip_parse(const NfcDevice* device, FuriString* parsed_data) { for(size_t i = 0; i < 3; i++) { const BipTransaction* top_up = &bip_data.top_ups[(3u + newest_top_up - i) % 3]; furi_string_cat_printf(parsed_data, "\n+$%d\n @", top_up->amount); - print_bip_timestamp(&top_up->timestamp, parsed_data); + bip_print_datetime(&top_up->datetime, parsed_data); } // Find newest charge size_t newest_charge = 0; for(size_t i = 1; i < 3; i++) { - const BipTimestamp* newest = &bip_data.charges[newest_charge].timestamp; - const BipTimestamp* current = &bip_data.charges[i].timestamp; - if(compare_bip_timestamp(current, newest) > 0) { + const DateTime* newest = &bip_data.charges[newest_charge].datetime; + const DateTime* current = &bip_data.charges[i].datetime; + if(datetime_cmp(current, newest) > 0) { newest_charge = i; } } @@ -338,7 +317,7 @@ static bool bip_parse(const NfcDevice* device, FuriString* parsed_data) { for(size_t i = 0; i < 3; i++) { const BipTransaction* charge = &bip_data.charges[(3u + newest_charge - i) % 3]; furi_string_cat_printf(parsed_data, "\n-$%d\n @", charge->amount); - print_bip_timestamp(&charge->timestamp, parsed_data); + bip_print_datetime(&charge->datetime, parsed_data); } parsed = true; From c9434492b87187763acaea00cde03cd2123e9d19 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 11 Mar 2024 00:37:29 +0300 Subject: [PATCH 09/10] Various changes, in expansion, rgb, js by Willy-JL , Spooks4576 , Sil333033 , HaxSam --- .ci_files/rgb.patch | 4 +- applications/services/expansion/expansion.c | 69 +++- applications/services/expansion/expansion.h | 9 + .../services/expansion/expansion_worker.c | 3 +- .../services/expansion/expansion_worker.h | 8 +- applications/system/js_app/application.fam | 39 ++ .../js_app/examples/apps/Scripts/blebeacon.js | 59 +++ .../js_app/examples/apps/Scripts/keyboard.js | 19 + .../js_app/examples/apps/Scripts/math.js | 47 +++ .../js_app/examples/apps/Scripts/subghz.js | 37 ++ .../js_app/examples/apps/Scripts/submenu.js | 11 + .../system/js_app/modules/js_blebeacon.c | 245 +++++++++++ .../system/js_app/modules/js_keyboard.c | 195 +++++++++ applications/system/js_app/modules/js_math.c | 309 ++++++++++++++ .../js_app/modules/js_subghz/js_subghz.c | 391 ++++++++++++++++++ .../modules/js_subghz/radio_device_loader.c | 64 +++ .../modules/js_subghz/radio_device_loader.h | 15 + .../system/js_app/modules/js_submenu.c | 152 +++++++ targets/f7/api_symbols.csv | 1 + 19 files changed, 1665 insertions(+), 12 deletions(-) create mode 100644 applications/system/js_app/examples/apps/Scripts/blebeacon.js create mode 100644 applications/system/js_app/examples/apps/Scripts/keyboard.js create mode 100644 applications/system/js_app/examples/apps/Scripts/math.js create mode 100644 applications/system/js_app/examples/apps/Scripts/subghz.js create mode 100644 applications/system/js_app/examples/apps/Scripts/submenu.js create mode 100644 applications/system/js_app/modules/js_blebeacon.c create mode 100644 applications/system/js_app/modules/js_keyboard.c create mode 100644 applications/system/js_app/modules/js_math.c create mode 100644 applications/system/js_app/modules/js_subghz/js_subghz.c create mode 100644 applications/system/js_app/modules/js_subghz/radio_device_loader.c create mode 100644 applications/system/js_app/modules/js_subghz/radio_device_loader.h create mode 100644 applications/system/js_app/modules/js_submenu.c diff --git a/.ci_files/rgb.patch b/.ci_files/rgb.patch index a9908125f4..ad0a8d80df 100644 --- a/.ci_files/rgb.patch +++ b/.ci_files/rgb.patch @@ -527,7 +527,7 @@ index 0000000..572e1df + +void SK6805_update(void) { + SK6805_init(); -+ furi_kernel_lock(); ++ FURI_CRITICAL_ENTER(); + uint32_t end; + /* Последовательная отправка цветов светодиодов */ + for(uint8_t lednumber = 0; lednumber < SK6805_LED_COUNT; lednumber++) { @@ -567,7 +567,7 @@ index 0000000..572e1df + } + } + } -+ furi_kernel_unlock(); ++ FURI_CRITICAL_EXIT(); +} diff --git a/lib/drivers/SK6805.h b/lib/drivers/SK6805.h new file mode 100644 diff --git a/applications/services/expansion/expansion.c b/applications/services/expansion/expansion.c index 5b834b48d3..9c64d0b5a1 100644 --- a/applications/services/expansion/expansion.c +++ b/applications/services/expansion/expansion.c @@ -18,6 +18,7 @@ typedef enum { ExpansionStateDisabled, ExpansionStateEnabled, ExpansionStateRunning, + ExpansionStateConnectionEstablished, } ExpansionState; typedef enum { @@ -26,10 +27,15 @@ typedef enum { ExpansionMessageTypeSetListenSerial, ExpansionMessageTypeModuleConnected, ExpansionMessageTypeModuleDisconnected, + ExpansionMessageTypeConnectionEstablished, + ExpansionMessageTypeIsConnected, } ExpansionMessageType; typedef union { - FuriHalSerialId serial_id; + union { + FuriHalSerialId serial_id; + bool* is_connected; + }; } ExpansionMessageData; typedef struct { @@ -68,13 +74,21 @@ static void expansion_detect_callback(void* context) { UNUSED(status); } -static void expansion_worker_callback(void* context) { +static void expansion_worker_callback(void* context, ExpansionWorkerCallbackReason reason) { furi_assert(context); Expansion* instance = context; - ExpansionMessage message = { - .type = ExpansionMessageTypeModuleDisconnected, - .api_lock = NULL, // Not locking the API here to avoid a deadlock + ExpansionMessage message; + switch(reason) { + case ExpansionWorkerCallbackReasonExit: + message.type = ExpansionMessageTypeModuleDisconnected; + message.api_lock = NULL; // Not locking the API here to avoid a deadlock + break; + + case ExpansionWorkerCallbackReasonConnected: + message.type = ExpansionMessageTypeConnectionEstablished; + message.api_lock = api_lock_alloc_locked(); + break; }; const FuriStatus status = furi_message_queue_put(instance->queue, &message, FuriWaitForever); @@ -105,7 +119,9 @@ static void if(instance->state == ExpansionStateDisabled) { return; - } else if(instance->state == ExpansionStateRunning) { + } else if( + instance->state == ExpansionStateRunning || + instance->state == ExpansionStateConnectionEstablished) { expansion_worker_stop(instance->worker); expansion_worker_free(instance->worker); } else { @@ -122,7 +138,8 @@ static void expansion_control_handler_set_listen_serial( const ExpansionMessageData* data) { furi_check(data->serial_id < FuriHalSerialIdMax); - if(instance->state == ExpansionStateRunning) { + if(instance->state == ExpansionStateRunning || + instance->state == ExpansionStateConnectionEstablished) { expansion_worker_stop(instance->worker); expansion_worker_free(instance->worker); @@ -160,7 +177,8 @@ static void expansion_control_handler_module_disconnected( Expansion* instance, const ExpansionMessageData* data) { UNUSED(data); - if(instance->state != ExpansionStateRunning) { + if(instance->state != ExpansionStateRunning && + instance->state != ExpansionStateConnectionEstablished) { return; } @@ -170,6 +188,23 @@ static void expansion_control_handler_module_disconnected( instance->serial_id, expansion_detect_callback, instance); } +static void expansion_control_handler_connection_established( + Expansion* instance, + const ExpansionMessageData* data) { + UNUSED(data); + if(instance->state != ExpansionStateRunning && + instance->state != ExpansionStateConnectionEstablished) { + return; + } + + instance->state = ExpansionStateConnectionEstablished; +} + +static void + expansion_control_handler_is_connected(Expansion* instance, const ExpansionMessageData* data) { + *data->is_connected = instance->state == ExpansionStateConnectionEstablished; +} + typedef void (*ExpansionControlHandler)(Expansion*, const ExpansionMessageData*); static const ExpansionControlHandler expansion_control_handlers[] = { @@ -178,6 +213,8 @@ static const ExpansionControlHandler expansion_control_handlers[] = { [ExpansionMessageTypeSetListenSerial] = expansion_control_handler_set_listen_serial, [ExpansionMessageTypeModuleConnected] = expansion_control_handler_module_connected, [ExpansionMessageTypeModuleDisconnected] = expansion_control_handler_module_disconnected, + [ExpansionMessageTypeConnectionEstablished] = expansion_control_handler_connection_established, + [ExpansionMessageTypeIsConnected] = expansion_control_handler_is_connected, }; static int32_t expansion_control(void* context) { @@ -249,6 +286,22 @@ void expansion_disable(Expansion* instance) { api_lock_wait_unlock_and_free(message.api_lock); } +bool expansion_is_connected(Expansion* instance) { + furi_check(instance); + bool is_connected; + + ExpansionMessage message = { + .type = ExpansionMessageTypeIsConnected, + .data.is_connected = &is_connected, + .api_lock = api_lock_alloc_locked(), + }; + + furi_message_queue_put(instance->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); + + return is_connected; +} + void expansion_set_listen_serial(Expansion* instance, FuriHalSerialId serial_id) { furi_check(instance); furi_check(serial_id < FuriHalSerialIdMax); diff --git a/applications/services/expansion/expansion.h b/applications/services/expansion/expansion.h index e169b3c15d..1b0879b1ec 100644 --- a/applications/services/expansion/expansion.h +++ b/applications/services/expansion/expansion.h @@ -50,6 +50,15 @@ void expansion_enable(Expansion* instance); */ void expansion_disable(Expansion* instance); +/** + * @brief Check if an expansion module is connected. + * + * @param[in,out] instance pointer to the Expansion instance. + * + * @returns true if the module is connected and initialized, false otherwise. + */ +bool expansion_is_connected(Expansion* instance); + /** * @brief Enable support for expansion modules on designated serial port. * diff --git a/applications/services/expansion/expansion_worker.c b/applications/services/expansion/expansion_worker.c index fd92063d25..4047212eb8 100644 --- a/applications/services/expansion/expansion_worker.c +++ b/applications/services/expansion/expansion_worker.c @@ -223,6 +223,7 @@ static bool expansion_worker_handle_state_handshake( if(furi_hal_serial_is_baud_rate_supported(instance->serial_handle, baud_rate)) { instance->state = ExpansionWorkerStateConnected; + instance->callback(instance->cb_context, ExpansionWorkerCallbackReasonConnected); // Send response at previous baud rate if(!expansion_worker_send_status_response(instance, ExpansionFrameErrorNone)) break; furi_hal_serial_set_br(instance->serial_handle, baud_rate); @@ -351,7 +352,7 @@ static int32_t expansion_worker(void* context) { // Do not invoke worker callback on user-requested exit if((instance->exit_reason != ExpansionWorkerExitReasonUser) && (instance->callback != NULL)) { - instance->callback(instance->cb_context); + instance->callback(instance->cb_context, ExpansionWorkerCallbackReasonExit); } return 0; diff --git a/applications/services/expansion/expansion_worker.h b/applications/services/expansion/expansion_worker.h index 761f79c1d9..faab2887f3 100644 --- a/applications/services/expansion/expansion_worker.h +++ b/applications/services/expansion/expansion_worker.h @@ -17,14 +17,20 @@ */ typedef struct ExpansionWorker ExpansionWorker; +typedef enum { + ExpansionWorkerCallbackReasonExit, + ExpansionWorkerCallbackReasonConnected, +} ExpansionWorkerCallbackReason; + /** * @brief Worker callback type. * * @see expansion_worker_set_callback() * * @param[in,out] context pointer to a user-defined object. + * @param[in] reason reason for the callback. */ -typedef void (*ExpansionWorkerCallback)(void* context); +typedef void (*ExpansionWorkerCallback)(void* context, ExpansionWorkerCallbackReason reason); /** * @brief Create an expansion worker instance. diff --git a/applications/system/js_app/application.fam b/applications/system/js_app/application.fam index b586f16237..0b9b1f51f3 100644 --- a/applications/system/js_app/application.fam +++ b/applications/system/js_app/application.fam @@ -47,3 +47,42 @@ 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"], +) + +App( + appid="js_blebeacon", + apptype=FlipperAppType.PLUGIN, + entry_point="js_blebeacon_ep", + 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"], +) + +App( + appid="js_keyboard", + apptype=FlipperAppType.PLUGIN, + entry_point="js_keyboard_ep", + requires=["js_app"], + sources=["modules/js_keyboard.c"], +) +App( + appid="js_subghz", + apptype=FlipperAppType.PLUGIN, + entry_point="js_subghz_ep", + requires=["js_app"], + sources=["modules/js_subghz/*.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..53983a7455 --- /dev/null +++ b/applications/system/js_app/examples/apps/Scripts/blebeacon.js @@ -0,0 +1,59 @@ +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; +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 generateRandomMac() { + let mac = []; + for (let i = 0; i < 6; i++) { + mac.push(math.floor(math.random() * 256)); + } + return Uint8Array(mac); +} + +function sendRandomModelAdvertisement() { + let model = watchValues[currentIndex]; + + let packet = [ + 14, 0xFF, 0x75, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0xFF, 0x00, 0x00, 0x43, + model + ]; + + let intervalMs = 50; + + // 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(intervalMs); + + blebeacon.stop(); +} + +while (true) { + sendRandomModelAdvertisement(); +} \ No newline at end of file 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..a34607c299 --- /dev/null +++ b/applications/system/js_app/examples/apps/Scripts/keyboard.js @@ -0,0 +1,19 @@ +let keyboard = require("keyboard"); + +keyboard.setHeader("Example Text Input"); + +// Default text is optional +let text = keyboard.text(100, "Default text", true); +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/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/examples/apps/Scripts/subghz.js b/applications/system/js_app/examples/apps/Scripts/subghz.js new file mode 100644 index 0000000000..39dadf0709 --- /dev/null +++ b/applications/system/js_app/examples/apps/Scripts/subghz.js @@ -0,0 +1,37 @@ +let subghz = require("subghz"); +subghz.setup(); + +function printRXline() { + if (subghz.getState() !== "RX") { + subghz.setRx(); // to RX + } + + let rssi = subghz.getRssi(); + let freq = subghz.getFrequency(); + let ext = subghz.isExternal(); + + print("rssi: ", rssi, "dBm", "@", freq, "MHz", "ext: ", ext); +} + +function changeFrequency(freq) { + if (subghz.getState() !== "IDLE") { + subghz.setIdle(); // need to be idle to change frequency + } + subghz.setFrequency(freq); +} + +subghz.setIdle(); +print(subghz.getState()); // "IDLE" +subghz.setRx(); +print(subghz.getState()); // "RX" + +changeFrequency(433920000); +printRXline(); +delay(1000); + +let result = subghz.transmitFile("/ext/subghz/0.sub"); +print(result ? "Send success" : "Send failed"); +delay(1000); + +changeFrequency(315000000); +printRXline(); \ 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 new file mode 100644 index 0000000000..6744ca4526 --- /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_blebeacon.c b/applications/system/js_app/modules/js_blebeacon.c new file mode 100644 index 0000000000..4d19accb1c --- /dev/null +++ b/applications/system/js_app/modules/js_blebeacon.c @@ -0,0 +1,245 @@ +#include "../js_modules.h" +#include +#include + +typedef struct { + bool saved_prev_cfg; + bool prev_cfg_set; + GapExtraBeaconConfig prev_cfg; + + bool saved_prev_data; + uint8_t prev_data[EXTRA_BEACON_MAX_DATA_SIZE]; + uint8_t prev_data_len; + + bool saved_prev_active; + bool prev_active; + + 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); + 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 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) { + ret_bad_args(mjs, "Wrong argument count"); + return false; + } + return true; +} + +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; + } + *value = mjs_get_int(mjs, int_obj); + 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) { + ret_bad_args(mjs, "Wrong argument count"); + return; + } + + 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); + } + if(!mac || mac_len != EXTRA_BEACON_MAC_ADDR_SIZE) { + ret_bad_args(mjs, "Wrong MAC address"); + return; + } + + 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; + } + } + 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) { + JsBlebeaconInst* blebeacon = get_this_ctx(mjs); + if(!check_arg_count(mjs, 1)) return; + + char* data = NULL; + size_t data_len = 0; + 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; + } + + if(!blebeacon->saved_prev_data) { + blebeacon->saved_prev_data = true; + blebeacon->prev_data_len = furi_hal_bt_extra_beacon_get_data(blebeacon->prev_data); + } + 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); +} + +static void js_blebeacon_start(struct mjs* mjs) { + JsBlebeaconInst* blebeacon = get_this_ctx(mjs); + if(!check_arg_count(mjs, 0)) return; + + if(!blebeacon->saved_prev_active) { + blebeacon->saved_prev_active = true; + blebeacon->prev_active = furi_hal_bt_extra_beacon_is_active(); + } + if(!furi_hal_bt_extra_beacon_start()) { + ret_int_err(mjs, "Failed starting beacon"); + return; + } + + mjs_return(mjs, MJS_UNDEFINED); +} + +static void js_blebeacon_stop(struct mjs* mjs) { + JsBlebeaconInst* blebeacon = get_this_ctx(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(); + } + if(!furi_hal_bt_extra_beacon_stop()) { + ret_int_err(mjs, "Failed stopping beacon"); + return; + } + + mjs_return(mjs, MJS_UNDEFINED); +} + +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* 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)); + mjs_set(mjs, blebeacon_obj, "stop", ~0, MJS_MK_FN(js_blebeacon_stop)); + mjs_set(mjs, blebeacon_obj, "keepAlive", ~0, MJS_MK_FN(js_blebeacon_keep_alive)); + *object = blebeacon_obj; + return blebeacon; +} + +static void js_blebeacon_destroy(void* inst) { + JsBlebeaconInst* blebeacon = inst; + if(!blebeacon->keep_alive) { + if(furi_hal_bt_extra_beacon_is_active()) { + furi_check(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 = { + "blebeacon", + js_blebeacon_create, + js_blebeacon_destroy, +}; + +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 &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 new file mode 100644 index 0000000000..8958dcaf8c --- /dev/null +++ b/applications/system/js_app/modules/js_keyboard.c @@ -0,0 +1,195 @@ +#include "../js_modules.h" +#include +#include +#include + +#define membersof(x) (sizeof(x) / sizeof(x[0])) + +typedef struct { + char* data; + TextInput* text_input; + ByteInput* byte_input; + ViewDispatcher* view_dispatcher; + 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); +} + +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)) { + 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)) { + if(error) ret_bad_args(mjs, "Bad string argument"); + return false; + } + return true; +} + +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 = 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* keyboard = (JsKeyboardInst*)context; + view_dispatcher_stop(keyboard->view_dispatcher); +} + +void byte_input_callback(void* context) { + JsKeyboardInst* keyboard = (JsKeyboardInst*)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); + + size_t input_length; + if(!get_int_arg(mjs, 0, &input_length, true)) return; + char* buffer = malloc(input_length); + + 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); + } + + 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); + + view_dispatcher_switch_to_view(keyboard->view_dispatcher, JsKeyboardViewTextInput); + + view_dispatcher_run(keyboard->view_dispatcher); + + text_input_reset(keyboard->text_input); + + 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); + + size_t input_length; + if(!get_int_arg(mjs, 0, &input_length, true)) return; + uint8_t* buffer = malloc(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); + furi_record_close(RECORD_GUI); + + 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_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, ""); + + mjs_return(mjs, mjs_mk_array_buf(mjs, (char*)buffer, input_length)); + free(buffer); +} + +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(); + keyboard->text_input = text_input_alloc(); + keyboard->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(keyboard->view_dispatcher); + view_dispatcher_add_view( + keyboard->view_dispatcher, + JsKeyboardViewTextInput, + text_input_get_view(keyboard->text_input)); + view_dispatcher_add_view( + keyboard->view_dispatcher, + JsKeyboardViewByteInput, + byte_input_get_view(keyboard->byte_input)); + *object = keyboard_obj; + return keyboard; +} + +static void js_keyboard_destroy(void* 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); + text_input_free(keyboard->text_input); + view_dispatcher_free(keyboard->view_dispatcher); + free(keyboard->data); + free(keyboard); +} + +static const JsModuleDescriptor js_keyboard_desc = { + "keyboard", + js_keyboard_create, + js_keyboard_destroy, +}; + +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) { + 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 new file mode 100644 index 0000000000..80d97fb9cf --- /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 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); + 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 js_math_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 js_math_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, JS_MATH_PI / (double)2 - atan(x / sqrt(1 - x * x)))); +} + +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); + } + 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 js_math_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 js_math_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 js_math_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 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); + } + 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 js_math_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 js_math_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 js_math_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 js_math_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 js_math_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 js_math_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 js_math_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 js_math_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 >= JS_MATH_E) { + x /= JS_MATH_E; + result++; + } + mjs_return(mjs, mjs_mk_number(mjs, result + log(x))); +} + +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); + } + 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 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); + } + 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 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); + } + 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 js_math_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 js_math_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 js_math_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 js_math_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 js_math_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(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)); + mjs_set(mjs, math_obj, "E", ~0, mjs_mk_number(mjs, JS_MATH_E)); + *object = math_obj; + return (void*)1; +} + +static const JsModuleDescriptor js_math_desc = { + "math", + js_math_create, + NULL, +}; + +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 &plugin_descriptor; +} \ No newline at end of file diff --git a/applications/system/js_app/modules/js_subghz/js_subghz.c b/applications/system/js_app/modules/js_subghz/js_subghz.c new file mode 100644 index 0000000000..913f8c670d --- /dev/null +++ b/applications/system/js_app/modules/js_subghz/js_subghz.c @@ -0,0 +1,391 @@ +#include "../../js_modules.h" +#include "radio_device_loader.h" + +#include +#include +#include + +#include + +#define tag "js_subghz" + +typedef enum { + JsSubghzRadioStateRX, + JsSubghzRadioStateTX, + JsSubghzRadioStateIDLE, +} JsSubghzRadioState; + +typedef struct { + const SubGhzDevice* radio_device; + int frequency; + bool is_external; + JsSubghzRadioState state; +} JsSubghzInst; + +// from subghz cli +static FuriHalSubGhzPreset js_subghz_get_preset_name(const char* preset_name) { + FuriHalSubGhzPreset preset = FuriHalSubGhzPresetIDLE; + if(!strcmp(preset_name, "FuriHalSubGhzPresetOok270Async")) { + preset = FuriHalSubGhzPresetOok270Async; + } else if(!strcmp(preset_name, "FuriHalSubGhzPresetOok650Async")) { + preset = FuriHalSubGhzPresetOok650Async; + } else if(!strcmp(preset_name, "FuriHalSubGhzPreset2FSKDev238Async")) { + preset = FuriHalSubGhzPreset2FSKDev238Async; + } else if(!strcmp(preset_name, "FuriHalSubGhzPreset2FSKDev476Async")) { + preset = FuriHalSubGhzPreset2FSKDev476Async; + } else if(!strcmp(preset_name, "FuriHalSubGhzPresetCustom")) { + preset = FuriHalSubGhzPresetCustom; + } else { + FURI_LOG_I(tag, "unknown preset"); + } + return preset; +} + +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)) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Argument must be a number"); + mjs_return(mjs, MJS_UNDEFINED); + return false; + } + *value = mjs_get_int(mjs, int_obj); + return true; +} + +static void js_subghz_set_rx(struct mjs* mjs) { + mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); + JsSubghzInst* js_subghz = mjs_get_ptr(mjs, obj_inst); + furi_assert(js_subghz); + + if(js_subghz->state == JsSubghzRadioStateRX) { + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + subghz_devices_set_rx(js_subghz->radio_device); + js_subghz->state = JsSubghzRadioStateRX; +} + +static void js_subgjz_set_idle(struct mjs* mjs) { + mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); + JsSubghzInst* js_subghz = mjs_get_ptr(mjs, obj_inst); + furi_assert(js_subghz); + + if(js_subghz->state == JsSubghzRadioStateIDLE) { + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + subghz_devices_idle(js_subghz->radio_device); + js_subghz->state = JsSubghzRadioStateIDLE; +} + +static void js_subghz_get_rssi(struct mjs* mjs) { + mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); + JsSubghzInst* js_subghz = mjs_get_ptr(mjs, obj_inst); + furi_assert(js_subghz); + + if(js_subghz->state != JsSubghzRadioStateRX) { + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + float rssi = subghz_devices_get_rssi(js_subghz->radio_device); + mjs_return(mjs, mjs_mk_number(mjs, (double)rssi)); +} + +static void js_subghz_get_state(struct mjs* mjs) { + mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); + JsSubghzInst* js_subghz = mjs_get_ptr(mjs, obj_inst); + furi_assert(js_subghz); + + const char* state; + switch(js_subghz->state) { + case JsSubghzRadioStateRX: + state = "RX"; + break; + case JsSubghzRadioStateTX: + state = "TX"; + break; + case JsSubghzRadioStateIDLE: + state = "IDLE"; + break; + default: + state = ""; + break; + } + + mjs_return(mjs, mjs_mk_string(mjs, state, ~0, true)); +} + +static void js_subghz_is_external(struct mjs* mjs) { + mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); + JsSubghzInst* js_subghz = mjs_get_ptr(mjs, obj_inst); + furi_assert(js_subghz); + + mjs_return(mjs, mjs_mk_boolean(mjs, js_subghz->is_external)); +} + +static void js_subghz_set_frequency(struct mjs* mjs) { + mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); + JsSubghzInst* js_subghz = mjs_get_ptr(mjs, obj_inst); + furi_assert(js_subghz); + + if(js_subghz->state != JsSubghzRadioStateIDLE) { + mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Radio is not in IDLE state"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + int32_t frequency; + if(!get_int_arg(mjs, 0, &frequency)) return; + + if(!subghz_devices_is_frequency_valid(js_subghz->radio_device, frequency)) { + mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Invalid frequency"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + js_subghz->frequency = subghz_devices_set_frequency(js_subghz->radio_device, frequency); + + mjs_return(mjs, mjs_mk_number(mjs, (double)js_subghz->frequency)); +} + +static void js_subghz_get_frequency(struct mjs* mjs) { + mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); + JsSubghzInst* js_subghz = mjs_get_ptr(mjs, obj_inst); + furi_assert(js_subghz); + + mjs_return(mjs, mjs_mk_number(mjs, (double)js_subghz->frequency)); +} + +static void js_subghz_transmit_file(struct mjs* mjs) { + mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); + JsSubghzInst* js_subghz = mjs_get_ptr(mjs, obj_inst); + furi_assert(js_subghz); + + mjs_val_t file = mjs_arg(mjs, 0); + + if(!mjs_is_string(file)) { + mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "File must be a string"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + const char* file_path = mjs_get_string(mjs, &file, NULL); + if(!file_path) { + mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Failed to get file path"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* fff_file = flipper_format_file_alloc(storage); + FlipperFormat* fff_data_raw = flipper_format_string_alloc(); + + if(!flipper_format_file_open_existing(fff_file, file_path)) { + flipper_format_free(fff_file); + furi_record_close(RECORD_STORAGE); + mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Failed to open file"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + SubGhzEnvironment* environment = subghz_environment_alloc(); + if(!subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME)) { + FURI_LOG_I(tag, "Load_keystore keeloq_mfcodes \033[0;31mERROR\033[0m\r\n"); + } + if(!subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME)) { + FURI_LOG_I(tag, "Load_keystore keeloq_mfcodes_user \033[0;33mAbsent\033[0m\r\n"); + } + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); + subghz_environment_set_nice_flor_s_rainbow_table_file_name( + environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); + subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); + + FuriString* temp_str = furi_string_alloc(); + SubGhzTransmitter* transmitter = NULL; + bool is_init_protocol = true; + bool is_sent = false; + uint32_t frequency = 0; + uint32_t repeat = 10; + + do { + //Load frequency + if(!flipper_format_read_uint32(fff_file, "Frequency", &frequency, 1)) { + mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Failed to read frequency from file"); + mjs_return(mjs, MJS_UNDEFINED); + break; + } + + if(!subghz_devices_is_frequency_valid(js_subghz->radio_device, frequency)) { + mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Invalid frequency"); + mjs_return(mjs, MJS_UNDEFINED); + break; + } + + if(!flipper_format_read_string(fff_file, "Preset", temp_str)) { + mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Failed to read preset from file"); + mjs_return(mjs, MJS_UNDEFINED); + break; + } + + subghz_devices_reset(js_subghz->radio_device); + + if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { + mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Custom presets are not supported (yet)"); + mjs_return(mjs, MJS_UNDEFINED); + break; + } else { + subghz_devices_load_preset( + js_subghz->radio_device, + js_subghz_get_preset_name(furi_string_get_cstr(temp_str)), + NULL); + } + + js_subghz->frequency = subghz_devices_set_frequency(js_subghz->radio_device, frequency); + + if(!flipper_format_read_string(fff_file, "Protocol", temp_str)) { + mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Failed to read protocol from file"); + mjs_return(mjs, MJS_UNDEFINED); + break; + } + + SubGhzProtocolStatus status; + bool is_raw = false; + + if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) { + subghz_protocol_raw_gen_fff_data( + fff_data_raw, file_path, subghz_devices_get_name(js_subghz->radio_device)); + is_raw = true; + } + + transmitter = subghz_transmitter_alloc_init(environment, furi_string_get_cstr(temp_str)); + if(transmitter == NULL) { + is_init_protocol = false; + } + + if(is_init_protocol) { + status = subghz_transmitter_deserialize(transmitter, is_raw ? fff_data_raw : fff_file); + if(status != SubGhzProtocolStatusOk) { + FURI_LOG_I(tag, "failed to deserialize transmitter"); + is_init_protocol = false; + } + } else { + FURI_LOG_I(tag, "failed to allocate transmitter"); + subghz_devices_idle(js_subghz->radio_device); + js_subghz->state = JsSubghzRadioStateIDLE; + } + } while(false); + + if(is_init_protocol) { + if(!js_subghz->is_external) { + furi_hal_power_suppress_charge_enter(); + } + + FURI_LOG_I(tag, "transmitting file %s", file_path); + + do { + furi_delay_ms(200); + if(subghz_devices_start_async_tx( + js_subghz->radio_device, subghz_transmitter_yield, transmitter)) { + while(!subghz_devices_is_async_complete_tx(js_subghz->radio_device)) { + furi_delay_ms(333); + } + subghz_devices_stop_async_tx(js_subghz->radio_device); + is_sent = true; + } else { + FURI_LOG_E(tag, "failed to start async tx"); + } + + } while(repeat && !strcmp(furi_string_get_cstr(temp_str), "RAW")); + + subghz_devices_idle(js_subghz->radio_device); + js_subghz->state = JsSubghzRadioStateIDLE; + + if(!js_subghz->is_external) { + furi_hal_power_suppress_charge_exit(); + } + } + + furi_string_free(temp_str); + flipper_format_free(fff_file); + flipper_format_free(fff_data_raw); + furi_record_close(RECORD_STORAGE); + + subghz_environment_reset_keeloq(environment); + subghz_environment_free(environment); + subghz_transmitter_free(transmitter); + + mjs_return(mjs, mjs_mk_boolean(mjs, is_sent)); +} + +static void js_subghz_setup(struct mjs* mjs) { + mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); + JsSubghzInst* js_subghz = mjs_get_ptr(mjs, obj_inst); + furi_assert(js_subghz); + + js_subghz->radio_device = + radio_device_loader_set(js_subghz->radio_device, SubGhzRadioDeviceTypeExternalCC1101); + + if(!subghz_devices_is_connect(js_subghz->radio_device)) { + js_subghz->is_external = true; + } else { + js_subghz->is_external = false; + } + + js_subghz->state = JsSubghzRadioStateIDLE; + js_subghz->frequency = 433920000; + + subghz_devices_reset(js_subghz->radio_device); + subghz_devices_idle(js_subghz->radio_device); + + mjs_return(mjs, MJS_UNDEFINED); +} + +static void* js_subghz_create(struct mjs* mjs, mjs_val_t* object) { + JsSubghzInst* js_subghz = malloc(sizeof(JsSubghzInst)); + mjs_val_t subghz_obj = mjs_mk_object(mjs); + + subghz_devices_init(); + + mjs_set(mjs, subghz_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, js_subghz)); + mjs_set(mjs, subghz_obj, "setup", ~0, MJS_MK_FN(js_subghz_setup)); + mjs_set(mjs, subghz_obj, "setRx", ~0, MJS_MK_FN(js_subghz_set_rx)); + mjs_set(mjs, subghz_obj, "setIdle", ~0, MJS_MK_FN(js_subgjz_set_idle)); + mjs_set(mjs, subghz_obj, "getRssi", ~0, MJS_MK_FN(js_subghz_get_rssi)); + mjs_set(mjs, subghz_obj, "getState", ~0, MJS_MK_FN(js_subghz_get_state)); + mjs_set(mjs, subghz_obj, "getFrequency", ~0, MJS_MK_FN(js_subghz_get_frequency)); + mjs_set(mjs, subghz_obj, "setFrequency", ~0, MJS_MK_FN(js_subghz_set_frequency)); + mjs_set(mjs, subghz_obj, "isExternal", ~0, MJS_MK_FN(js_subghz_is_external)); + mjs_set(mjs, subghz_obj, "transmitFile", ~0, MJS_MK_FN(js_subghz_transmit_file)); + + *object = subghz_obj; + + return js_subghz; +} + +static void js_subghz_destroy(void* inst) { + JsSubghzInst* js_subghz = inst; + + subghz_devices_deinit(); + + free(js_subghz); +} + +static const JsModuleDescriptor js_subghz_desc = { + "subghz", + js_subghz_create, + js_subghz_destroy, +}; + +static const FlipperAppPluginDescriptor plugin_descriptor = { + .appid = PLUGIN_APP_ID, + .ep_api_version = PLUGIN_API_VERSION, + .entry_point = &js_subghz_desc, +}; + +const FlipperAppPluginDescriptor* js_subghz_ep(void) { + return &plugin_descriptor; +} diff --git a/applications/system/js_app/modules/js_subghz/radio_device_loader.c b/applications/system/js_app/modules/js_subghz/radio_device_loader.c new file mode 100644 index 0000000000..d2cffde583 --- /dev/null +++ b/applications/system/js_app/modules/js_subghz/radio_device_loader.c @@ -0,0 +1,64 @@ +#include "radio_device_loader.h" + +#include +#include + +static void radio_device_loader_power_on() { + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + //CC1101 power-up time + furi_delay_ms(10); + } +} + +static void radio_device_loader_power_off() { + if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg(); +} + +bool radio_device_loader_is_connect_external(const char* name) { + bool is_connect = false; + bool is_otg_enabled = furi_hal_power_is_otg_enabled(); + + if(!is_otg_enabled) { + radio_device_loader_power_on(); + } + + const SubGhzDevice* device = subghz_devices_get_by_name(name); + if(device) { + is_connect = subghz_devices_is_connect(device); + } + + if(!is_otg_enabled) { + radio_device_loader_power_off(); + } + return is_connect; +} + +const SubGhzDevice* radio_device_loader_set( + const SubGhzDevice* current_radio_device, + SubGhzRadioDeviceType radio_device_type) { + const SubGhzDevice* radio_device; + + if(radio_device_type == SubGhzRadioDeviceTypeExternalCC1101 && + radio_device_loader_is_connect_external(SUBGHZ_DEVICE_CC1101_EXT_NAME)) { + radio_device_loader_power_on(); + radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME); + subghz_devices_begin(radio_device); + } else if(current_radio_device == NULL) { + radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME); + } else { + radio_device_loader_end(current_radio_device); + radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME); + } + + return radio_device; +} + +void radio_device_loader_end(const SubGhzDevice* radio_device) { + furi_assert(radio_device); + radio_device_loader_power_off(); + if(radio_device != subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME)) { + subghz_devices_end(radio_device); + } +} \ No newline at end of file diff --git a/applications/system/js_app/modules/js_subghz/radio_device_loader.h b/applications/system/js_app/modules/js_subghz/radio_device_loader.h new file mode 100644 index 0000000000..bee4e2c362 --- /dev/null +++ b/applications/system/js_app/modules/js_subghz/radio_device_loader.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +/** SubGhzRadioDeviceType */ +typedef enum { + SubGhzRadioDeviceTypeInternal, + SubGhzRadioDeviceTypeExternalCC1101, +} SubGhzRadioDeviceType; + +const SubGhzDevice* radio_device_loader_set( + const SubGhzDevice* current_radio_device, + SubGhzRadioDeviceType radio_device_type); + +void radio_device_loader_end(const SubGhzDevice* radio_device); \ No newline at end of file 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..b87f34fa8b --- /dev/null +++ b/applications/system/js_app/modules/js_submenu.c @@ -0,0 +1,152 @@ +#include +#include +#include +#include "../js_modules.h" + +typedef struct { + Submenu* submenu; + ViewDispatcher* view_dispatcher; + 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); + 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, JsSubmenuViewSubmenu); + + 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, JsSubmenuViewSubmenu, submenu_get_view(submenu->submenu)); + *object = submenu_obj; + return submenu; +} + +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); +} + +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 diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 03c12a66be..85f25d00e8 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -991,6 +991,7 @@ Function,-,exp2f,float,float Function,-,exp2l,long double,long double Function,+,expansion_disable,void,Expansion* Function,+,expansion_enable,void,Expansion* +Function,+,expansion_is_connected,_Bool,Expansion* Function,+,expansion_set_listen_serial,void,"Expansion*, FuriHalSerialId" Function,-,expf,float,float Function,-,expl,long double,long double From 7b09a22a5c05f70f37c4f213e94367e3cf1c254d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 11 Mar 2024 02:37:37 +0300 Subject: [PATCH 10/10] fix bipki [ci skip] --- applications/main/nfc/plugins/supported_cards/bip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/nfc/plugins/supported_cards/bip.c b/applications/main/nfc/plugins/supported_cards/bip.c index 7487d38448..c3587fbd33 100644 --- a/applications/main/nfc/plugins/supported_cards/bip.c +++ b/applications/main/nfc/plugins/supported_cards/bip.c @@ -67,7 +67,7 @@ static void bip_print_datetime(const DateTime* datetime, FuriString* str) { locale_format_date(date_str, datetime, date_format, separator); FuriString* time_str = furi_string_alloc(); - locale_format_time(time_str, datetime, locale_get_time_format(), false); + locale_format_time(time_str, datetime, locale_get_time_format(), true); furi_string_cat_printf( str, "%s %s", furi_string_get_cstr(date_str), furi_string_get_cstr(time_str));