浏览代码

Add mp_flipper from https://github.com/xMasterX/all-the-plugins

git-subtree-dir: mp_flipper
git-subtree-mainline: eb831f767788535d47044ddc2ccde9610e518113
git-subtree-split: 51545597b41007d470695cf0239312f84bfb7144
Willy-JL 1 年之前
父节点
当前提交
2828e57a80
共有 100 个文件被更改,包括 8306 次插入0 次删除
  1. 12 0
      mp_flipper/.gitignore
  2. 2 0
      mp_flipper/.gitsubtree
  3. 170 0
      mp_flipper/CHANGELOG.md
  4. 22 0
      mp_flipper/LICENSE.txt
  5. 33 0
      mp_flipper/Makefile
  6. 63 0
      mp_flipper/README.md
  7. 63 0
      mp_flipper/application.fam
  8. 二进制
      mp_flipper/assets/file-browser.png
  9. 二进制
      mp_flipper/assets/qflipper.png
  10. 二进制
      mp_flipper/assets/tic-tac-toe.png
  11. 38 0
      mp_flipper/docs/CHANGELOG.md
  12. 15 0
      mp_flipper/docs/README.md
  13. 二进制
      mp_flipper/docs/file-browser.png
  14. 846 0
      mp_flipper/docs/pages/assets/adc_circuit.svg
  15. 二进制
      mp_flipper/docs/pages/assets/favicon.png
  16. 1024 0
      mp_flipper/docs/pages/assets/gpio_interrupt_circuit.svg
  17. 二进制
      mp_flipper/docs/pages/assets/logo.png
  18. 772 0
      mp_flipper/docs/pages/assets/pwm_circuit.svg
  19. 5 0
      mp_flipper/docs/pages/assets/pwm_signal.txt
  20. 二进制
      mp_flipper/docs/pages/assets/repl.gif
  21. 233 0
      mp_flipper/docs/pages/assets/repl.yml
  22. 2 0
      mp_flipper/docs/pages/changelog.rst
  23. 67 0
      mp_flipper/docs/pages/conf.py
  24. 75 0
      mp_flipper/docs/pages/examples.rst
  25. 71 0
      mp_flipper/docs/pages/features.md
  26. 54 0
      mp_flipper/docs/pages/index.rst
  27. 1 0
      mp_flipper/docs/pages/license.rst
  28. 51 0
      mp_flipper/docs/pages/quickstart.rst
  29. 501 0
      mp_flipper/docs/pages/reference.rst
  30. 23 0
      mp_flipper/docs/pages/roadmap.md
  31. 二进制
      mp_flipper/docs/tic-tac-toe.png
  32. 二进制
      mp_flipper/docs/welcome.png
  33. 9 0
      mp_flipper/examples/dict_test.py
  34. 20 0
      mp_flipper/examples/flipperzero_adc_test.py
  35. 37 0
      mp_flipper/examples/flipperzero_canvas_test.py
  36. 9 0
      mp_flipper/examples/flipperzero_dialog_message_test.py
  37. 16 0
      mp_flipper/examples/flipperzero_draw_on_input_test.py
  38. 14 0
      mp_flipper/examples/flipperzero_gpio_input_test.py
  39. 34 0
      mp_flipper/examples/flipperzero_gpio_interrupt_test.py
  40. 12 0
      mp_flipper/examples/flipperzero_gpio_test.py
  41. 10 0
      mp_flipper/examples/flipperzero_infrared_test.py
  42. 11 0
      mp_flipper/examples/flipperzero_input_test.py
  43. 32 0
      mp_flipper/examples/flipperzero_light_test.py
  44. 8 0
      mp_flipper/examples/flipperzero_pwm_test.py
  45. 27 0
      mp_flipper/examples/flipperzero_speaker_note_test.py
  46. 27 0
      mp_flipper/examples/flipperzero_speaker_test.py
  47. 8 0
      mp_flipper/examples/flipperzero_vibro_test.py
  48. 3 0
      mp_flipper/examples/gc_test.py
  49. 14 0
      mp_flipper/examples/greeter_test.py
  50. 1 0
      mp_flipper/examples/hello_test.py
  51. 1 0
      mp_flipper/examples/import_error_test.py
  52. 5 0
      mp_flipper/examples/import_test.py
  53. 97 0
      mp_flipper/examples/infrared_signal_viewer.py
  54. 15 0
      mp_flipper/examples/logger.py
  55. 5 0
      mp_flipper/examples/open.py
  56. 1 0
      mp_flipper/examples/overflow_test.py
  57. 1 0
      mp_flipper/examples/raise_test.py
  58. 7 0
      mp_flipper/examples/rand_test.py
  59. 3 0
      mp_flipper/examples/sleep_test.py
  60. 80 0
      mp_flipper/examples/tic_tac_toe.py
  61. 4 0
      mp_flipper/examples/try_except_test.py
  62. 18 0
      mp_flipper/examples/uart.py
  63. 14 0
      mp_flipper/flipper.json
  64. 6 0
      mp_flipper/flipperzero/README.md
  65. 30 0
      mp_flipper/flipperzero/_adc.py
  66. 242 0
      mp_flipper/flipperzero/_canvas.py
  67. 64 0
      mp_flipper/flipperzero/_dialog.py
  68. 257 0
      mp_flipper/flipperzero/_gpio.py
  69. 52 0
      mp_flipper/flipperzero/_infrared.py
  70. 101 0
      mp_flipper/flipperzero/_input.py
  71. 95 0
      mp_flipper/flipperzero/_light.py
  72. 39 0
      mp_flipper/flipperzero/_pwm.py
  73. 854 0
      mp_flipper/flipperzero/_speaker.py
  74. 166 0
      mp_flipper/flipperzero/_uart.py
  75. 10 0
      mp_flipper/flipperzero/_vibro.py
  76. 369 0
      mp_flipper/flipperzero/io.py
  77. 170 0
      mp_flipper/flipperzero/logging.py
  78. 二进制
      mp_flipper/icon.png
  79. 二进制
      mp_flipper/images/ButtonCenter_7x7.png
  80. 二进制
      mp_flipper/images/Pin_back_arrow_10x8.png
  81. 二进制
      mp_flipper/images/qrcode.png
  82. 二进制
      mp_flipper/images/splash.png
  83. 49 0
      mp_flipper/lib/micropython-port/mp_flipper_context.h
  84. 59 0
      mp_flipper/lib/micropython-port/mp_flipper_file_helper.c
  85. 7 0
      mp_flipper/lib/micropython-port/mp_flipper_file_helper.h
  86. 77 0
      mp_flipper/lib/micropython-port/mp_flipper_file_reader.c
  87. 89 0
      mp_flipper/lib/micropython-port/mp_flipper_fileio.c
  88. 33 0
      mp_flipper/lib/micropython-port/mp_flipper_halport.c
  89. 50 0
      mp_flipper/lib/micropython-port/mp_flipper_logging.c
  90. 47 0
      mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_adc.c
  91. 108 0
      mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_canvas.c
  92. 101 0
      mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_dialog.c
  93. 175 0
      mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_gpio.c
  94. 119 0
      mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_infrared.c
  95. 40 0
      mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_light.c
  96. 79 0
      mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_pwm.c
  97. 35 0
      mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_speaker.c
  98. 81 0
      mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_uart.c
  99. 7 0
      mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_vibro.c
  100. 9 0
      mp_flipper/lib/micropython-port/mp_flipper_modrandom.c

+ 12 - 0
mp_flipper/.gitignore

@@ -0,0 +1,12 @@
+/dist/
+/venv/
+/node_modules/
+/flipperzero/__init__.py
+/fssdk
+.vscode
+.clang-format
+.clangd
+.editorconfig
+.env
+.ufbt
+__pycache__

+ 2 - 0
mp_flipper/.gitsubtree

@@ -0,0 +1,2 @@
+https://github.com/xMasterX/all-the-plugins dev non_catalog_apps/mp_flipper 73d394b22988de08bc984a161819f87bca90c7c5
+https://github.com/ofabel/mp-flipper master /

+ 170 - 0
mp_flipper/CHANGELOG.md

@@ -0,0 +1,170 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [Unreleased]
+
+## [1.5.0] - 2024-10-06
+
+### Added
+
+* Support for basic file system operations using the `io` module:
+  * Read and write files.
+  * Open in text or binary mode.
+* Simple `logging` module:
+  * Log levels according to the Flipper Zero API: trace, debug, info, warn, error.
+  * Only the root logger is supported, so no `getLogger` function.
+  * Logs directly to the log output, so no output in the REPL.
+* Redirect output of `print` statements:
+  * To `stdout` when a script is invoked by `py` command from the CLI.
+  * To the log buffer, if a script is invoked from the UI.
+* UART support for the `flipperzero` module.
+
+### Changed
+
+* The `py` command waits until the script terminates.
+
+### Fixed
+
+* [#3](https://github.com/ofabel/mp-flipper/issues/3): Proper `CR` and `LF` handling in the REPL.
+
+## [1.4.0] - 2024-09-29
+
+### Added
+
+* Allow passing the path to the script to execute as a CLI argument.
+* Open a REPL from the CLI interface by using the `py` command:
+  * The `py` command is only available while the app is running.
+  * You cannot run a Python script and use the REPL at the same time.
+  * You can also start a Python script with the `py` command while the app is idle.
+
+### Changed
+
+* MicroPython update to version 1.23.0.
+
+## [1.3.0] - 2024-09-08
+
+### Added
+
+* Simple ADC support for the `flipperzero` module:
+  * Read raw value.
+  * Read voltage.
+* Simple PWM support for the `flipperzero` module:
+  * Start a signal.
+  * Stop a signal.
+  * Check the status.
+* Infrared support for the `flipperzero` module:
+  * Receive a signal.
+  * Transmit a signal.
+  * Check the status.
+* Reset used GPIO pins upon script termination.
+* Improved GPIO related functions to prevent user errors.
+* Published [Python package on PyPI](https://pypi.org/project/flipperzero/) for code completion support.
+
+### Changed
+
+* The GPIO init function `flipperzero.gpio_init_pin` returns a boolean value.
+
+## [1.2.0] - 2024-09-05
+
+### Added
+
+* Constants for all musical notes from C0 up to B8.
+* Constants for minimum and maximum speaker volumes.
+* Simple GPIO support for the `flipperzero` module:
+  * Initialize a pin.
+  * Read from a pin.
+  * Write to a pin.
+  * Handle interrupts.
+
+### Fixed
+
+* Message box alignment parameters `h` and `v` are now correctly evaluated.
+
+## [1.1.0] - 2024-08-28
+
+### Added
+
+* Display splash screen upon application start.
+* API documentation on [GitHub pages](https://ofabel.github.io/mp-flipper/).
+
+## [1.0.0] - 2024-08-22
+
+### Added
+
+* First stable release on the [application catalog](https://github.com/flipperdevices/flipper-application-catalog).
+
+### Changed
+
+* Application ID is now `upython`
+
+## [0.5.0-beta.1] - 2024-08-04
+
+### Added
+
+* Message dialog support.
+* Update to the latest 0.104.0 firmware.
+
+### Removed
+
+* Disabled various Python builtins to shrink binary size.
+
+## [0.4.0-beta.1] - 2024-04-14
+
+### Added
+
+* [Library](https://github.com/ofabel/mp-flipper/tree/lib) to include in the [firmware repository](https://github.com/ofabel/flipperzero-firmware).
+* All generated files from the build prozess are now [part of the repository](https://github.com/ofabel/mp-flipper/tree/lib-release).
+* Enabled split heap support for MicroPython:
+  * The runtime can allocate and free heap memory.
+  * Allows to start the Python process with small heap.
+* Enabled scheduler support (required for interrupt handling).
+* Enabled support for module `__init__` functions.
+* Stabilized `flipperzero` module API:
+  * Canvas support has now a proper implementation.
+  * Interrupts from buttons are supported.
+
+## [0.3.0-alpha.1] - 2024-04-04
+
+### Added
+
+* Floating point support
+* Extend `flipperzero` module with support for:
+  * Speaker, set volume and frequency
+  * Canvas, very wacky implementation
+
+## [0.2.0-alpha.1] - 2024-04-03
+
+### Added
+
+* Support for external imports
+* Python `time` module support
+* Python `random` module support
+* Basic `flipperzero` module with support for:
+  * Vibration
+  * LED
+  * Backlight
+* Some test Python scripts
+
+## [0.1.0-alpha.1] - 2024-04-01
+
+### Added
+
+* Basic build setup
+* Minimal working example
+
+[Unreleased]: https://github.com/ofabel/mp-flipper/compare/v1.5.0...dev
+[1.5.0]: https://github.com/ofabel/mp-flipper/compare/v1.4.0...v1.5.0
+[1.4.0]: https://github.com/ofabel/mp-flipper/compare/v1.3.0...v1.4.0
+[1.3.0]: https://github.com/ofabel/mp-flipper/compare/v1.2.0...v1.3.0
+[1.2.0]: https://github.com/ofabel/mp-flipper/compare/v1.1.0...v1.2.0
+[1.1.0]: https://github.com/ofabel/mp-flipper/compare/v1.0.0...v1.1.0
+[1.0.0]: https://github.com/ofabel/mp-flipper/compare/v0.5.0-beta.1...v1.0.0
+[0.5.0-beta.1]: https://github.com/ofabel/mp-flipper/compare/v0.4.0-beta.1...v0.5.0-beta.1
+[0.4.0-beta.1]: https://github.com/ofabel/mp-flipper/compare/v0.3.0-alpha.1...v0.4.0-beta.1
+[0.3.0-alpha.1]: https://github.com/ofabel/mp-flipper/compare/v0.2.0-alpha.1...v0.3.0-alpha.1
+[0.2.0-alpha.1]: https://github.com/ofabel/mp-flipper/compare/v0.1.0-alpha.1...v0.2.0-alpha.1
+[0.1.0-alpha.1]: https://github.com/ofabel/mp-flipper/releases/tag/v0.1.0-alpha.1

+ 22 - 0
mp_flipper/LICENSE.txt

@@ -0,0 +1,22 @@
+MIT License
+===========
+
+Copyright (c) 2024 Oliver Fabel
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 33 - 0
mp_flipper/Makefile

@@ -0,0 +1,33 @@
+.PHONY: update
+update:
+	git submodule update --remote lib/micropython && git add lib/micropython
+
+.PHONY: build-fap
+build-fap: update
+	ufbt build
+
+.PHONY: launch
+launch: build-fap
+	ufbt launch
+
+.PHONY: clean
+clean:
+	ufbt -c
+
+.PHONY: build-pages
+build-pages:
+	rm -rf ./dist/pages ./flipperzero/__init__.py
+	cat ./flipperzero/_*.py > ./flipperzero/__init__.py
+	source venv/bin/activate && sphinx-build docs/pages dist/pages
+
+.PHONY: publish-pages
+publish-pages: build-pages
+	./publish.sh pages
+
+.PHONY: build-python
+build-python:
+	source venv/bin/activate && hatch build
+
+.PHONY: publish-python
+publish-python: build-python
+	source venv/bin/activate && hatch publish dist/python/*

+ 63 - 0
mp_flipper/README.md

@@ -0,0 +1,63 @@
+![License](https://img.shields.io/github/license/ofabel/mp-flipper)
+![Version](https://img.shields.io/github/v/tag/ofabel/mp-flipper)
+![](https://img.shields.io/github/issues/ofabel/mp-flipper)
+
+# MicroPython Flipper Zero
+
+Allows you to use the power of Python natively on your Flipper Zero.
+The application is available on the official [Flipper Lab](https://lab.flipper.net/apps/upython).
+For details on how to programm your Flipper with Python, check out the [documentation](https://ofabel.github.io/mp-flipper/) on GitHub pages.
+
+![MicroPython REPL](./docs/pages/assets/repl.gif)
+
+## Disclaimer
+
+This FAP version requires about 80 kB from SRAM to start (needed for the Python runtime and compiler).
+Due to memory fragmentation it's possible, that the application crashes when you start it.
+If this happens, just try again (the crash doesn't harm your device).
+
+> [!IMPORTANT]
+> This problem is already addressed to the firmware developers in [this issue](https://github.com/flipperdevices/flipperzero-firmware/issues/3927).
+> Nevertheless, running the uPython application from the SD card is still a heavy task for the Flipper.
+
+_I'm thinking about publishing a fork of the original firmware with uPython bundled as a core service._
+_This would mitigate all the memory problems._
+_The SD card version would still be there and maintained, but possibly with a limited set of features._
+
+## Development
+
+This section is only relevant, if you want to build the FAP on your own.
+
+### Branches
+
+This branch contains the [FAP](https://developer.flipper.net/flipperzero/doxygen/apps_on_sd_card.html) version.
+The results of the preceding research phase is still available in the [poc](https://github.com/ofabel/mp-flipper/tree/poc) branch.
+The [lib](https://github.com/ofabel/mp-flipper/tree/lib) branch of this repository contains just the [MicroPython](https://github.com/micropython/micropython) library.
+The progress of further research on what can be achieved when moving functionality to the firmware can be found in the [fork of the original firmware](https://github.com/ofabel/flipperzero-firmware/tree/ofa/micropython).
+
+### Requirements
+
+* [Git](https://git-scm.com/)
+* [Make](https://www.gnu.org/software/make/)
+* [uFBT](https://pypi.org/project/ufbt/) available in your `PATH` (or you have to adjust the [Makefile](./Makefile))
+* [Flipper Zero](https://flipperzero.one/)
+
+### Setup
+
+```bash
+git clone --recurse-submodules git@github.com:ofabel/mp-flipper.git
+```
+
+### Build
+
+Just open a terminal and run the Makefile targets:
+
+```bash
+make build
+```
+
+You can also build an launch the application on the attached Flipper Zero device in one command:
+
+```bash
+make launch
+```

+ 63 - 0
mp_flipper/application.fam

@@ -0,0 +1,63 @@
+App(
+    appid="upython",
+    name="uPython",
+    apptype=FlipperAppType.EXTERNAL,
+    entry_point="upython",
+    stack_size=4 * 1024,
+    fap_category="Tools",
+    fap_version="1.5",
+    fap_description="Compile and execute MicroPython scripts",
+    fap_icon="icon.png",
+    fap_icon_assets="images",
+    fap_author="Oliver Fabel",
+    fap_file_assets="examples",
+    fap_weburl="https://github.com/ofabel/mp-flipper",
+    sources=[
+        "*.c*",
+        "!./lib/micropython",
+        "!./lib/micropython-port",
+        "!./flipperzero",
+        "!./node_modules",
+        "!./examples",
+        "!./assets",
+        "!./docs",
+        "!./venv",
+        "!./dist",
+    ],
+    fap_private_libs=[
+        Lib(
+            name="micropython",
+            cflags=[
+                "-Wno-error",
+                "-w",
+                #
+                # required for floating point support
+                #
+                "-mcpu=cortex-m4",
+                "-mfloat-abi=hard",
+                "-mfpu=fpv4-sp-d16",
+                "-mthumb",
+                "-fsingle-precision-constant",
+                "-fno-math-errno",
+            ],
+            cincludes=["."],
+        ),
+        Lib(
+            name="micropython-port",
+            cflags=[
+                "-Wno-error",
+                "-w",
+                #
+                # required for floating point support
+                #
+                "-mcpu=cortex-m4",
+                "-mfloat-abi=hard",
+                "-mfpu=fpv4-sp-d16",
+                "-mthumb",
+                "-fsingle-precision-constant",
+                "-fno-math-errno",
+            ],
+            cincludes=["."],
+        ),
+    ],
+)

二进制
mp_flipper/assets/file-browser.png


二进制
mp_flipper/assets/qflipper.png


二进制
mp_flipper/assets/tic-tac-toe.png


+ 38 - 0
mp_flipper/docs/CHANGELOG.md

@@ -0,0 +1,38 @@
+## 1.5
+
+* Added **io** module for basic file system operations.
+* Added **logging** module to allow level based log messages.
+* Rework of the **print** function: output redirection, based on script invocation.
+* Added UART support: connect, read and write.
+* Fixed the line feed handling in the REPL.
+
+## 1.4
+
+* Added interactive Python shell (aka REPL) as a CLI command.
+* Allow passing a path to a script to execute as a CLI argument.
+* Updated MicroPython to version 1.23.0.
+
+## 1.3
+
+* Added simple ADC support: read value and voltage.
+* Added simple PWM support: start, stop, check status.
+* Added infrared support: receive and transmit a signal, check status.
+* Added success indicator to GPIO init function.
+* Reset used GPIO pins upon script termination.
+* Improved GPIO related functions to prevent user errors.
+* Published Python package on PyPI for code completion support.
+
+## 1.2
+
+* Added simple GPIO support: initialize, read, write, interrupts.
+* Added constants for musical note frequencies from C0 up to B8.
+* Some minor fixes in the dialog functions.
+
+## 1.1
+
+* Display splash screen upon application start.
+* API documentation available on GitHub pages.
+
+## 1.0
+
+* Initial stable release.

+ 15 - 0
mp_flipper/docs/README.md

@@ -0,0 +1,15 @@
+# MicroPython
+
+MicroPython is a version of the popular Python programming language, especially tailored for the hardware requirements of small microcontrollers, like the Flipper Zero.
+Instead of supporting the full standard library, only a subset is implemented, carefully selected with the requirements of a microcontroller environment in mind.
+But this should not limit your creativity and ability to create great applications at all.
+
+## API
+
+Visit the repository website on GitHub or scan the QR code on the welcome screen.
+
+## Disclaimer
+
+Running MicroPython is a heavy task for the Flipper.
+This does sometimes lead to an _Out of Memory_ error.
+**This doesn't harm your Flipper.**

二进制
mp_flipper/docs/file-browser.png


+ 846 - 0
mp_flipper/docs/pages/assets/adc_circuit.svg

@@ -0,0 +1,846 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="52.2393mm" height="33.1893mm"
+ viewBox="0 0 617 392"
+ xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  version="1.2" baseProfile="tiny">
+<title>Flipper Zero</title>
+<desc>Generated with Qt</desc>
+<defs>
+</defs>
+<g fill="none" stroke="black" stroke-width="1" fill-rule="evenodd" stroke-linecap="square" stroke-linejoin="bevel" >
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="1.44" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M199.8,50.4 L217.8,50.4"/>
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="1.44" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M208.8,50.4 L208.8,43.2"/>
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.72" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<circle cx="201.6" cy="57.6" r="1.8"/>
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.72" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<circle cx="216" cy="57.6" r="1.8"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="223.2,57.6 217.8,57.6 " />
+<polyline fill="none" vector-effect="none" points="194.4,57.6 199.8,57.6 " />
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M122.4,129.6 L122.4,115.2 L259.2,115.2 L259.2,129.6 L122.4,129.6"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M128.999,120.149 L128.999,117.451 L130.201,117.451 L130.201,120.149 L128.999,120.149"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M136.199,120.149 L136.199,117.451 L137.401,117.451 L137.401,120.149 L136.199,120.149"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M143.399,120.149 L143.399,117.451 L144.601,117.451 L144.601,120.149 L143.399,120.149"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M150.599,120.149 L150.599,117.451 L151.801,117.451 L151.801,120.149 L150.599,120.149"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M157.799,120.149 L157.799,117.451 L159.001,117.451 L159.001,120.149 L157.799,120.149"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M164.999,120.149 L164.999,117.451 L166.201,117.451 L166.201,120.149 L164.999,120.149"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M172.199,120.149 L172.199,117.451 L173.401,117.451 L173.401,120.149 L172.199,120.149"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M179.399,120.149 L179.399,117.451 L180.601,117.451 L180.601,120.149 L179.399,120.149"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M186.599,120.149 L186.599,117.451 L187.801,117.451 L187.801,120.149 L186.599,120.149"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M193.799,120.149 L193.799,117.451 L195.001,117.451 L195.001,120.149 L193.799,120.149"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M200.999,120.149 L200.999,117.451 L202.201,117.451 L202.201,120.149 L200.999,120.149"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M208.199,120.149 L208.199,117.451 L209.401,117.451 L209.401,120.149 L208.199,120.149"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M215.399,120.149 L215.399,117.451 L216.601,117.451 L216.601,120.149 L215.399,120.149"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M222.599,120.149 L222.599,117.451 L223.801,117.451 L223.801,120.149 L222.599,120.149"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M229.799,120.149 L229.799,117.451 L231.001,117.451 L231.001,120.149 L229.799,120.149"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M236.999,120.149 L236.999,117.451 L238.201,117.451 L238.201,120.149 L236.999,120.149"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M244.199,120.149 L244.199,117.451 L245.401,117.451 L245.401,120.149 L244.199,120.149"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M251.399,120.149 L251.399,117.451 L252.601,117.451 L252.601,120.149 L251.399,120.149"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="216,108 216,118.8 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,421.061,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,421.061,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >13</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,421.061,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="151.2,108 151.2,118.8 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,150.915,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,150.915,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >4</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,150.915,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="158.4,108 158.4,118.8 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,180.931,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,180.931,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >5</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,180.931,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="172.8,108 172.8,118.8 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,240.964,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,240.964,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >7</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,240.964,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="180,108 180,118.8 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,270.98,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,270.98,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >8</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,270.98,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="165.6,108 165.6,118.8 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,210.947,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,210.947,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >6</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,210.947,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="194.4,108 194.4,118.8 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,331.012,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,331.012,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >10</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,331.012,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="244.8,108 244.8,118.8 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,541.126,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,541.126,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >17</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,541.126,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="230.4,108 230.4,118.8 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,481.093,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,481.093,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >15</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,481.093,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="187.2,108 187.2,118.8 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,300.996,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,300.996,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >9</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,300.996,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="223.2,108 223.2,118.8 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,451.077,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,451.077,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >14</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,451.077,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="201.6,108 201.6,118.8 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,361.028,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,361.028,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >11</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,361.028,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="136.8,108 136.8,118.8 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,90.8824,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,90.8824,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >2</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,90.8824,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="237.6,108 237.6,118.8 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,511.109,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,511.109,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >16</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,511.109,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="208.8,108 208.8,118.8 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,391.045,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,391.045,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >12</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,391.045,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="252,108 252,118.8 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,571.142,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,571.142,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >18</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,571.142,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="144,108 144,118.8 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,120.899,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,120.899,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >3</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,120.899,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="129.6,108 129.6,118.8 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,60.8662,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,60.8662,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >1</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.46196,2.46196,0,60.8662,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.72" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M254.88,87.9307 L249.12,87.9307 L249.12,70.4693 L254.88,70.4693 L254.88,87.9307"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="252,93.6 252,87.9307 " />
+<polyline fill="none" vector-effect="none" points="252,64.8 252,70.4693 " />
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.72" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M190.08,87.9307 L184.32,87.9307 L184.32,70.4693 L190.08,70.4693 L190.08,87.9307"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="187.2,93.6 187.2,87.9307 " />
+<polyline fill="none" vector-effect="none" points="187.2,64.8 187.2,70.4693 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(2.50135,0,0,2.50135,391.045,30.077)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(2.50135,0,0,2.50135,391.045,30.077)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.46875" y="-3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >S1</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(2.50135,0,0,2.50135,391.045,30.077)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.47656" y="-12" width="8.95313" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.50135,2.50135,0,30.85,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.50135,2.50135,0,30.85,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-3.35938" y="-3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >J1</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.50135,2.50135,0,30.85,360.255)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-3.36719" y="-12" width="6.73438" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.50135,2.50135,0,559.135,216.556)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.50135,2.50135,0,559.135,216.556)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="0" y="-3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >R2</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.50135,2.50135,0,559.135,216.556)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="0" y="-12" width="9.53125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.50135,2.50135,0,583.148,216.556)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.50135,2.50135,0,583.148,216.556)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="0" y="9" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >3.3k</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.50135,2.50135,0,583.148,216.556)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="0" y="0" width="16.5313" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.50135,2.50135,0,288.989,216.556)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.50135,2.50135,0,288.989,216.556)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="0" y="-3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >R1</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.50135,2.50135,0,288.989,216.556)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="0" y="-12" width="9.53125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.50135,2.50135,0,313.002,216.556)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.50135,2.50135,0,313.002,216.556)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="0" y="9" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >10k</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.50135,2.50135,0,313.002,216.556)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="0" y="0" width="14.3906" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="187.2,64.8 187.2,57.6 " />
+<polyline fill="none" vector-effect="none" points="194.4,57.6 187.2,57.6 " />
+<polyline fill="none" vector-effect="none" points="252,108 252,93.6 " />
+<polyline fill="none" vector-effect="none" points="252,64.8 252,57.6 " />
+<polyline fill="none" vector-effect="none" points="230.4,57.6 252,57.6 " />
+<polyline fill="none" vector-effect="none" points="223.2,57.6 230.4,57.6 " />
+<polyline fill="none" vector-effect="none" points="230.4,100.8 230.4,57.6 " />
+<polyline fill="none" vector-effect="none" points="230.4,108 230.4,100.8 " />
+<polyline fill="none" vector-effect="none" points="187.2,93.6 187.2,108 " />
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="none" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<circle cx="230.4" cy="57.6" r="1.70079"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16892,4.16892,0,571.142,300.223)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16892,4.16892,0,571.142,300.223)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="0" y="-2" font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+ >GND</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16892,4.16892,0,571.142,300.223)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+<rect x="0" y="-7" width="8.17188" height="7"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="none" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16892,4.16892,0,481.093,300.223)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16892,4.16892,0,481.093,300.223)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="0" y="-2" font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+ >C1</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16892,4.16892,0,481.093,300.223)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+<rect x="0" y="-7" width="5.78125" height="7"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="none" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16892,4.16892,0,300.996,300.223)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16892,4.16892,0,300.996,300.223)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="0" y="-2" font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+ >3V3</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16892,4.16892,0,300.996,300.223)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+<rect x="0" y="-7" width="8.17188" height="7"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="none" transform="matrix(4.16892,0,0,4.16892,-479.426,-150.02)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(1,0,0,1,0,0)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+</g>
+</svg>

二进制
mp_flipper/docs/pages/assets/favicon.png


+ 1024 - 0
mp_flipper/docs/pages/assets/gpio_interrupt_circuit.svg

@@ -0,0 +1,1024 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="52.2393mm" height="26.0773mm"
+ viewBox="0 0 617 308"
+ xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  version="1.2" baseProfile="tiny">
+<title>Flipper Zero</title>
+<desc>Generated with Qt</desc>
+<defs>
+</defs>
+<g fill="none" stroke="black" stroke-width="1" fill-rule="evenodd" stroke-linecap="square" stroke-linejoin="bevel" >
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.72" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M254.88,95.1307 L249.12,95.1307 L249.12,77.6693 L254.88,77.6693 L254.88,95.1307"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="252,100.8 252,95.1307 " />
+<polyline fill="none" vector-effect="none" points="252,72 252,77.6693 " />
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M122.4,136.8 L122.4,122.4 L259.2,122.4 L259.2,136.8 L122.4,136.8"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M128.999,127.349 L128.999,124.651 L130.201,124.651 L130.201,127.349 L128.999,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M136.199,127.349 L136.199,124.651 L137.401,124.651 L137.401,127.349 L136.199,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M143.399,127.349 L143.399,124.651 L144.601,124.651 L144.601,127.349 L143.399,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M150.599,127.349 L150.599,124.651 L151.801,124.651 L151.801,127.349 L150.599,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M157.799,127.349 L157.799,124.651 L159.001,124.651 L159.001,127.349 L157.799,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M164.999,127.349 L164.999,124.651 L166.201,124.651 L166.201,127.349 L164.999,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M172.199,127.349 L172.199,124.651 L173.401,124.651 L173.401,127.349 L172.199,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M179.399,127.349 L179.399,124.651 L180.601,124.651 L180.601,127.349 L179.399,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M186.599,127.349 L186.599,124.651 L187.801,124.651 L187.801,127.349 L186.599,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M193.799,127.349 L193.799,124.651 L195.001,124.651 L195.001,127.349 L193.799,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M200.999,127.349 L200.999,124.651 L202.201,124.651 L202.201,127.349 L200.999,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M208.199,127.349 L208.199,124.651 L209.401,124.651 L209.401,127.349 L208.199,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M215.399,127.349 L215.399,124.651 L216.601,124.651 L216.601,127.349 L215.399,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M222.599,127.349 L222.599,124.651 L223.801,124.651 L223.801,127.349 L222.599,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M229.799,127.349 L229.799,124.651 L231.001,124.651 L231.001,127.349 L229.799,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M236.999,127.349 L236.999,124.651 L238.201,124.651 L238.201,127.349 L236.999,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M244.199,127.349 L244.199,124.651 L245.401,124.651 L245.401,127.349 L244.199,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M251.399,127.349 L251.399,124.651 L252.601,124.651 L252.601,127.349 L251.399,127.349"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="158.4,115.2 158.4,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,181.138,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,181.138,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >5</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,181.138,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="136.8,115.2 136.8,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,91.2351,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,91.2351,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >2</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,91.2351,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="129.6,115.2 129.6,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,61.2676,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,61.2676,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >1</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,61.2676,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="244.8,115.2 244.8,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,540.749,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,540.749,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >17</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,540.749,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="230.4,115.2 230.4,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,480.814,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,480.814,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >15</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,480.814,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="201.6,115.2 201.6,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,360.943,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,360.943,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >11</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,360.943,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="252,115.2 252,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,570.716,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,570.716,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >18</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,570.716,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="208.8,115.2 208.8,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,390.911,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,390.911,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >12</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,390.911,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="180,115.2 180,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,271.041,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,271.041,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >8</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,271.041,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="237.6,115.2 237.6,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,510.781,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,510.781,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >16</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,510.781,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="187.2,115.2 187.2,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,301.008,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,301.008,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >9</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,301.008,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="216,115.2 216,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,420.878,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,420.878,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >13</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,420.878,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="172.8,115.2 172.8,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,241.073,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,241.073,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >7</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,241.073,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="165.6,115.2 165.6,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,211.105,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,211.105,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >6</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,211.105,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="151.2,115.2 151.2,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,151.17,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,151.17,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >4</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,151.17,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="194.4,115.2 194.4,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,330.976,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,330.976,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >10</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,330.976,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="144,115.2 144,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,121.203,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,121.203,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >3</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,121.203,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="223.2,115.2 223.2,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,450.846,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,450.846,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >14</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45797,2.45797,0,450.846,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="1.44" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M230.4,95.4 L230.4,77.4"/>
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="1.44" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M230.4,86.4 L223.2,86.4"/>
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.72" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<circle cx="237.6" cy="93.6" r="1.8"/>
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.72" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<circle cx="237.6" cy="79.2" r="1.8"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="237.6,72 237.6,77.4 " />
+<polyline fill="none" vector-effect="none" points="237.6,100.8 237.6,95.4 " />
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.72" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M182.88,95.1307 L177.12,95.1307 L177.12,77.6693 L182.88,77.6693 L182.88,95.1307"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="180,100.8 180,95.1307 " />
+<polyline fill="none" vector-effect="none" points="180,72 180,77.6693 " />
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="1.44" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M208.8,95.4 L208.8,77.4"/>
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="1.44" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M208.8,86.4 L201.6,86.4"/>
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.72" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<circle cx="216" cy="93.6" r="1.8"/>
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.72" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<circle cx="216" cy="79.2" r="1.8"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="216,72 216,77.4 " />
+<polyline fill="none" vector-effect="none" points="216,100.8 216,95.4 " />
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.72" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M133.2,90 L136.8,82.8 L140.4,90 L133.2,90"/>
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.72" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M140.4,82.8 L133.2,82.8"/>
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M136.8,90 L136.8,82.8"/>
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.432" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M141.66,87.84 L145.62,83.88"/>
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.432" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M140.4,85.5 L144.36,81.54"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.432" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M145.62,83.88 L144.54,86.4 L143.1,84.96 L145.62,83.88"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.432" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M144.36,81.54 L143.28,84.06 L141.84,82.62 L144.36,81.54"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="136.8,79.2 136.8,82.8 " />
+<polyline fill="none" vector-effect="none" points="136.8,93.6 136.8,90 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,558.729,133.733)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,558.729,133.733)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="0" y="-3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >R2</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,558.729,133.733)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="0" y="-12" width="9.53125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,582.703,133.733)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,582.703,133.733)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="0" y="9" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >220</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,582.703,133.733)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="0" y="0" width="13.6875" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,31.3,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,31.3,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-3.35938" y="-3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >J1</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,31.3,277.2)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-3.36719" y="-12" width="6.73438" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,450.846,97.3946)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,450.846,97.3946)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.46875" y="-3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >S2</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,450.846,97.3946)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.47656" y="-12" width="8.95313" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,259.054,133.733)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,259.054,133.733)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="0" y="-3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >R1</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,259.054,133.733)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="0" y="-12" width="9.53125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,283.028,133.733)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,283.028,133.733)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="0" y="9" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >1k</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,283.028,133.733)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="0" y="0" width="9.82813" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,360.943,97.3946)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,360.943,97.3946)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.46875" y="-3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >S1</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,360.943,97.3946)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.47656" y="-12" width="8.95313" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,71.1781,97.3946)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,71.1781,97.3946)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="0" y="-3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >LED1</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.4973,2.4973,0,71.1781,97.3946)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="0" y="-12" width="19.0156" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="230.4,115.2 230.4,108 " />
+<polyline fill="none" vector-effect="none" points="230.4,108 216,108 " />
+<polyline fill="none" vector-effect="none" points="216,100.8 216,108 " />
+<polyline fill="none" vector-effect="none" points="216,72 216,64.8 " />
+<polyline fill="none" vector-effect="none" points="252,72 252,64.8 " />
+<polyline fill="none" vector-effect="none" points="237.6,64.8 252,64.8 " />
+<polyline fill="none" vector-effect="none" points="237.6,72 237.6,64.8 " />
+<polyline fill="none" vector-effect="none" points="237.6,64.8 216,64.8 " />
+<polyline fill="none" vector-effect="none" points="180,64.8 136.8,64.8 " />
+<polyline fill="none" vector-effect="none" points="136.8,79.2 136.8,64.8 " />
+<polyline fill="none" vector-effect="none" points="180,72 180,64.8 " />
+<polyline fill="none" vector-effect="none" points="180,100.8 180,115.2 " />
+<polyline fill="none" vector-effect="none" points="237.6,100.8 237.6,115.2 " />
+<polyline fill="none" vector-effect="none" points="136.8,93.6 136.8,115.2 " />
+<polyline fill="none" vector-effect="none" points="252,115.2 252,100.8 " />
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="none" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<circle cx="237.6" cy="64.8" r="1.70079"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16216,4.16216,0,480.814,217.265)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16216,4.16216,0,480.814,217.265)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="0" y="-2" font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+ >C1</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16216,4.16216,0,480.814,217.265)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+<rect x="0" y="-7" width="5.78125" height="7"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="none" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16216,4.16216,0,271.041,217.265)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16216,4.16216,0,271.041,217.265)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="0" y="-2" font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+ >GND</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16216,4.16216,0,271.041,217.265)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+<rect x="0" y="-7" width="8.17188" height="7"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="none" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16216,4.16216,0,510.781,217.265)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16216,4.16216,0,510.781,217.265)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="0" y="-2" font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+ >C0</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16216,4.16216,0,510.781,217.265)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+<rect x="0" y="-7" width="5.78125" height="7"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="none" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16216,4.16216,0,91.2351,217.265)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16216,4.16216,0,91.2351,217.265)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="0" y="-2" font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+ >A7</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16216,4.16216,0,91.2351,217.265)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+<rect x="0" y="-7" width="5.78125" height="7"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="none" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16216,4.16216,0,570.716,217.265)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16216,4.16216,0,570.716,217.265)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="0" y="-2" font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+ >GND</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16216,4.16216,0,570.716,217.265)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+<rect x="0" y="-7" width="8.17188" height="7"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="none" transform="matrix(4.16216,0,0,4.16216,-478.149,-262.216)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(1,0,0,1,0,0)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+</g>
+</svg>

二进制
mp_flipper/docs/pages/assets/logo.png


+ 772 - 0
mp_flipper/docs/pages/assets/pwm_circuit.svg

@@ -0,0 +1,772 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="51.1387mm" height="25.7387mm"
+ viewBox="0 0 604 304"
+ xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  version="1.2" baseProfile="tiny">
+<title>Flipper Zero</title>
+<desc>Generated with Qt</desc>
+<defs>
+</defs>
+<g fill="none" stroke="black" stroke-width="1" fill-rule="evenodd" stroke-linecap="square" stroke-linejoin="bevel" >
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.72" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M182.88,95.1307 L177.12,95.1307 L177.12,77.6693 L182.88,77.6693 L182.88,95.1307"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="180,100.8 180,95.1307 " />
+<polyline fill="none" vector-effect="none" points="180,72 180,77.6693 " />
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.72" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M133.2,90 L136.8,82.8 L140.4,90 L133.2,90"/>
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.72" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M140.4,82.8 L133.2,82.8"/>
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M136.8,90 L136.8,82.8"/>
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.432" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M141.66,87.84 L145.62,83.88"/>
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.432" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M140.4,85.5 L144.36,81.54"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.432" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M145.62,83.88 L144.54,86.4 L143.1,84.96 L145.62,83.88"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.432" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M144.36,81.54 L143.28,84.06 L141.84,82.62 L144.36,81.54"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="136.8,93.6 136.8,90 " />
+<polyline fill="none" vector-effect="none" points="136.8,79.2 136.8,82.8 " />
+</g>
+
+<g fill="#fafafa" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M122.4,136.8 L122.4,122.4 L259.2,122.4 L259.2,136.8 L122.4,136.8"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M128.999,127.349 L128.999,124.651 L130.201,124.651 L130.201,127.349 L128.999,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M136.199,127.349 L136.199,124.651 L137.401,124.651 L137.401,127.349 L136.199,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M143.399,127.349 L143.399,124.651 L144.601,124.651 L144.601,127.349 L143.399,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M150.599,127.349 L150.599,124.651 L151.801,124.651 L151.801,127.349 L150.599,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M157.799,127.349 L157.799,124.651 L159.001,124.651 L159.001,127.349 L157.799,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M164.999,127.349 L164.999,124.651 L166.201,124.651 L166.201,127.349 L164.999,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M172.199,127.349 L172.199,124.651 L173.401,124.651 L173.401,127.349 L172.199,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M179.399,127.349 L179.399,124.651 L180.601,124.651 L180.601,127.349 L179.399,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M186.599,127.349 L186.599,124.651 L187.801,124.651 L187.801,127.349 L186.599,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M193.799,127.349 L193.799,124.651 L195.001,124.651 L195.001,127.349 L193.799,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M200.999,127.349 L200.999,124.651 L202.201,124.651 L202.201,127.349 L200.999,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M208.199,127.349 L208.199,124.651 L209.401,124.651 L209.401,127.349 L208.199,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M215.399,127.349 L215.399,124.651 L216.601,124.651 L216.601,127.349 L215.399,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M222.599,127.349 L222.599,124.651 L223.801,124.651 L223.801,127.349 L222.599,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M229.799,127.349 L229.799,124.651 L231.001,124.651 L231.001,127.349 L229.799,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M236.999,127.349 L236.999,124.651 L238.201,124.651 L238.201,127.349 L236.999,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M244.199,127.349 L244.199,124.651 L245.401,124.651 L245.401,127.349 L244.199,127.349"/>
+</g>
+
+<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="0.708661" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<path vector-effect="none" fill-rule="evenodd" d="M251.399,127.349 L251.399,124.651 L252.601,124.651 L252.601,127.349 L251.399,127.349"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="244.8,115.2 244.8,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,540.619,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,540.619,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >17</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,540.619,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="187.2,115.2 187.2,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,300.751,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,300.751,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >9</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,300.751,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="144,115.2 144,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,120.849,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,120.849,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >3</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,120.849,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="223.2,115.2 223.2,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,450.668,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,450.668,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >14</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,450.668,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="165.6,115.2 165.6,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,210.8,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,210.8,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >6</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,210.8,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="216,115.2 216,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,420.685,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,420.685,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >13</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,420.685,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="158.4,115.2 158.4,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,180.816,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,180.816,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >5</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,180.816,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="230.4,115.2 230.4,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,480.652,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,480.652,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >15</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,480.652,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="136.8,115.2 136.8,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,90.8658,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,90.8658,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >2</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,90.8658,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="237.6,115.2 237.6,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,510.636,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,510.636,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >16</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,510.636,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="151.2,115.2 151.2,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,150.833,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,150.833,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >4</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,150.833,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="180,115.2 180,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,270.767,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,270.767,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >8</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,270.767,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="194.4,115.2 194.4,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,330.734,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,330.734,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >10</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,330.734,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="252,115.2 252,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,570.603,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,570.603,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >18</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,570.603,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="208.8,115.2 208.8,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,390.701,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,390.701,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >12</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,390.701,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="201.6,115.2 201.6,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,360.718,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,360.718,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-9.125" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >11</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,360.718,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-9.125" y="-6" width="9.125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="129.6,115.2 129.6,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,60.8822,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,60.8822,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >1</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,60.8822,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="172.8,115.2 172.8,126 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,240.784,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,240.784,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-4.5625" y="3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >7</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.45928,2.45928,0,240.784,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-4.5625" y="-6" width="4.5625" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.49863,2.49863,0,258.774,129.64)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.49863,2.49863,0,258.774,129.64)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="0" y="-3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >R1</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.49863,2.49863,0,258.774,129.64)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="0" y="-12" width="9.53125" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.49863,2.49863,0,282.761,129.64)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.49863,2.49863,0,282.761,129.64)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="0" y="9" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >1k</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.49863,2.49863,0,282.761,129.64)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="0" y="0" width="9.82813" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.49863,2.49863,0,70.798,93.2822)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.49863,2.49863,0,70.798,93.2822)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="0" y="-3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >LED1</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.49863,2.49863,0,70.798,93.2822)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="0" y="-12" width="19.0156" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.49863,2.49863,0,30.8986,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.49863,2.49863,0,30.8986,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="-3.35938" y="-3" font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+ >J1</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-2.49863,2.49863,0,30.8986,273.184)"
+font-family="Noto Sans" font-size="8" font-weight="400" font-style="normal" 
+>
+<rect x="-3.36719" y="-12" width="6.73438" height="12"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+<polyline fill="none" vector-effect="none" points="136.8,93.6 136.8,115.2 " />
+<polyline fill="none" vector-effect="none" points="180,100.8 180,115.2 " />
+<polyline fill="none" vector-effect="none" points="136.8,79.2 136.8,64.8 " />
+<polyline fill="none" vector-effect="none" points="180,72 180,64.8 " />
+<polyline fill="none" vector-effect="none" points="180,64.8 136.8,64.8 " />
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16438,4.16438,0,90.8658,213.216)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16438,4.16438,0,90.8658,213.216)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="0" y="-2" font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+ >A7</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16438,4.16438,0,90.8658,213.216)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+<rect x="0" y="-7" width="5.78125" height="7"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16438,4.16438,0,270.767,213.216)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16438,4.16438,0,270.767,213.216)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="0" y="-2" font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+ >GND</text>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="0" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0,-4.16438,4.16438,0,270.767,213.216)"
+font-family="Noto Sans Mono" font-size="4" font-weight="400" font-style="normal" 
+>
+<rect x="0" y="-7" width="8.17188" height="7"/>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="0.45" stroke-linecap="round" stroke-linejoin="round" transform="matrix(4.16438,0,0,4.16438,-478.822,-266.521)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+
+<g fill="none" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(1,0,0,1,0,0)"
+font-family="Noto Sans" font-size="41.6667" font-weight="400" font-style="normal" 
+>
+</g>
+</g>
+</svg>

+ 5 - 0
mp_flipper/docs/pages/assets/pwm_signal.txt

@@ -0,0 +1,5 @@
+────┐       ┌──┐    ┌────┐      ┌──┐              ┌────┐      ┌ ⋅ ⋅ ⋅
+    │       │  │    │    │      │  │              │    │      │
+    └───────┘  └────┘    └──────┘  └──────────────┘    └──────┘
+
+ 40 |  70   |20| 40 | 40 |  60  |20|     140      | 40 |  60  | ⋅ ⋅ ⋅

二进制
mp_flipper/docs/pages/assets/repl.gif


+ 233 - 0
mp_flipper/docs/pages/assets/repl.yml

@@ -0,0 +1,233 @@
+# The configurations that used for the recording, feel free to edit them
+config:
+
+  # Specify a command to be executed
+  # like `/bin/bash -l`, `ls`, or any other commands
+  # the default is bash for Linux
+  # or powershell.exe for Windows
+  command: bash -l
+  
+  # Specify the current working directory path
+  # the default is the current working directory path
+  cwd: ~/dev/scripts
+  
+  # Export additional ENV variables
+  env:
+    recording: true
+  
+  # Explicitly set the number of columns
+  # or use `auto` to take the current
+  # number of columns of your shell
+  cols: 80
+  
+  # Explicitly set the number of rows
+  # or use `auto` to take the current
+  # number of rows of your shell
+  rows: 17
+  
+  # Amount of times to repeat GIF
+  # If value is -1, play once
+  # If value is 0, loop indefinitely
+  # If value is a positive number, loop n times
+  repeat: 0
+  
+  # Quality
+  # 1 - 100
+  quality: 100
+  
+  # Delay between frames in ms
+  # If the value is `auto` use the actual recording delays
+  frameDelay: auto
+  
+  # Maximum delay between frames in ms
+  # Ignored if the `frameDelay` isn't set to `auto`
+  # Set to `auto` to prevent limiting the max idle time
+  maxIdleTime: 2000
+  
+  # The surrounding frame box
+  # The `type` can be null, window, floating, or solid`
+  # To hide the title use the value null
+  # Don't forget to add a backgroundColor style with a null as type
+  frameBox:
+    type: floating
+    title: null
+    style:
+      border: 0px black solid
+      # boxShadow: none
+      # margin: 0px
+  
+  # Add a watermark image to the rendered gif
+  # You need to specify an absolute path for
+  # the image on your machine or a URL, and you can also
+  # add your own CSS styles
+  watermark:
+    imagePath: null
+    style:
+      position: absolute
+      right: 15px
+      bottom: 15px
+      width: 100px
+      opacity: 0.9
+  
+  # Cursor style can be one of
+  # `block`, `underline`, or `bar`
+  cursorStyle: block
+  
+  # Font family
+  # You can use any font that is installed on your machine
+  # in CSS-like syntax
+  fontFamily: "Monaco, Lucida Console, Ubuntu Mono, Monospace"
+  
+  # The size of the font
+  fontSize: 12
+  
+  # The height of lines
+  lineHeight: 1
+  
+  # The spacing between letters
+  letterSpacing: 0
+  
+  # Theme
+  theme:
+    background: "transparent"
+    foreground: "#afafaf"
+    cursor: "#c7c7c7"
+    black: "#232628"
+    red: "#fc4384"
+    green: "#b3e33b"
+    yellow: "#ffa727"
+    blue: "#75dff2"
+    magenta: "#ae89fe"
+    cyan: "#708387"
+    white: "#d5d5d0"
+    brightBlack: "#626566"
+    brightRed: "#ff7fac"
+    brightGreen: "#c8ed71"
+    brightYellow: "#ebdf86"
+    brightBlue: "#75dff2"
+    brightMagenta: "#ae89fe"
+    brightCyan: "#b1c6ca"
+    brightWhite: "#f9f9f4"
+  
+# Records, feel free to edit them
+records:
+  - delay: 0
+    content: "\r"
+  - delay: 35
+    content: "\r\r\n              _.-------.._                    -,\r\r\n          .-\"```\"--..,,_/ /`-,               -,  \\ \r\r\n       .:\"          /:/  /'\\  \\     ,_...,  `. |  |\r\r\n      /       ,----/:/  /`\\ _\\~`_-\"`     _;\r\r\n     '      / /`\"\"\"'\\ \\ \\.~`_-'      ,-\"'/ \r\r\n    |      | |  0    | | .-'      ,/`  /\r\r\n   |    ,..\\ \\     ,.-\"`       ,/`    /\r\r\n  ;    :    `/`\"\"\\`           ,/--==,/-----,\r\r\n  |    `-...|        -.___-Z:_______J...---;\r\r\n  :         `                           _-'\r\r\n _L_  _     ___  ___  ___  ___  ____--\"`___  _     ___\r\r\n| __|| |   |_ _|| _ \\| _ \\| __|| _ \\   / __|| |   |_ _|\r\r\n| _| | |__  | | |  _/|  _/| _| |   /  | (__ | |__  | |\r\r\n|_|  |____||___||_|  |_|  |___||_|_\\   \\___||____||___|\r\r\n\r\r\nWelcome to Flipper Zero Command Line Interface!\r\r\nRead the manual: https://docs.flipper.net/development/cli\r\r\nRun `help` or `?` to list available commands\r\r\n\r\r\nFirmware version: 1.0.1 1.0.1 (fe424061 built on 10-09-2024)\r\r\n\r\r\n>: "
+  - delay: 1259
+    content: p
+  - delay: 105
+    content: 'y'
+  - delay: 1112
+    content: "\r\r\nMicroPython (v1.23.0, 2024-09-27) on Flipper Zero\r\r\nQuit: Ctrl+D | Heap: 4220 bytes | Stack: 2048 bytes\r\r\n      To do a reboot, press Left+Back for 5 seconds.\r\r\nDocs: https://ofabel.github.io/mp-flipper\r\r\n"
+  - delay: 5
+    content: "\e[2K\r>>> "
+  - delay: 2144
+    content: p
+  - delay: 195
+    content: r
+  - delay: 194
+    content: i
+  - delay: 53
+    content: 'n'
+  - delay: 55
+    content: t
+  - delay: 80
+    content: (
+  - delay: 80
+    content: ''''
+  - delay: 100
+    content: H
+  - delay: 100
+    content: e
+  - delay: 134
+    content: l
+  - delay: 149
+    content: l
+  - delay: 254
+    content: o
+  - delay: 107
+    content: ' '
+  - delay: 250
+    content: f
+  - delay: 161
+    content: r
+  - delay: 120
+    content: o
+  - delay: 112
+    content: m
+  - delay: 53
+    content: ' '
+  - delay: 30
+    content: R
+  - delay: 104
+    content: E
+  - delay: 247
+    content: P
+  - delay: 256
+    content: L
+  - delay: 250
+    content: ''''
+  - delay: 250
+    content: )
+  - delay: 541
+    content: "\r\r\nHello from REPL\r\n\e[2K\r>>> "
+  - delay: 3126
+    content: i
+  - delay: 82
+    content: m
+  - delay: 300
+    content: p
+  - delay: 300
+    content: "\e[2K\r>>> import "
+  - delay: 500
+    content: f
+  - delay: 82
+    content: l
+  - delay: 317
+    content: "\e[2K\r>>> import flipperzero"
+  - delay: 300
+    content: ' '
+  - delay: 144
+    content: a
+  - delay: 75
+    content: s
+  - delay: 300
+    content: ' '
+  - delay: 208
+    content: f
+  - delay: 121
+    content: '0'
+  - delay: 451
+    content: "\r\r\n\e[2K\r>>> "
+  - delay: 845
+    content: f
+  - delay: 75
+    content: '0'
+  - delay: 248
+    content: .
+  - delay: 307
+    content: i
+  - delay: 53
+    content: 'n'
+  - delay: 459
+    content: "\e[2K\r>>> f0.infrared_"
+  - delay: 1072
+    content: "\r\ninfrared_is_busy                infrared_receive\r\ninfrared_transmit\r\n\e[2K\r>>> f0.infrared_"
+  - delay: 1169
+    content: r
+  - delay: 61
+    content: e
+  - delay: 984
+    content: "\e[2K\r>>> f0.infrared_receive"
+  - delay: 814
+    content: (
+  - delay: 172
+    content: )
+  - delay: 1262
+    content: "\r\r\n"
+  - delay: 2086
+    content: "[9086, 4417, 636, 522, 611, 522, 610, 524, 608, 525, 608, 1635, 606, 527, 607, 527, 607, 527, 606, 527, 607, 528, 606, 527, 607, 528, 605, 1636, 606, 528, 606, 528, 606, 528, 606, 1636, 606, 528, 606, 528, 606, 1636, 605, 528, 606, 528, 606, 1636, 606, 528, 606, 528, 606, 1636, 605, 1636, 606, 528, 605, 1636, 606, 1636, 605, 528, 606, 1636, 605, 46392, 9057, 2206, 606, 95997, 9083, 2205, 607]\r\n\e[2K\r>>> "
+  - delay: 3424
+    content: "\e[2K\r>>> "

+ 2 - 0
mp_flipper/docs/pages/changelog.rst

@@ -0,0 +1,2 @@
+.. include:: ../../CHANGELOG.md
+  :parser: myst_parser.sphinx_

+ 67 - 0
mp_flipper/docs/pages/conf.py

@@ -0,0 +1,67 @@
+import datetime
+import pathlib
+import sys
+
+base = pathlib.Path(__file__).parent.parent.parent
+root = base.__str__()
+flipperzero = base.joinpath("flipperzero").__str__()
+
+sys.path.append(root)
+sys.path.append(flipperzero)
+
+
+def copy_dict(source, target):
+    for key, value in source.__dict__.items():
+        target.__dict__[key] = value
+
+
+import flipperzero.logging
+import logging
+
+copy_dict(flipperzero.logging, logging)
+
+import flipperzero.io
+import io
+
+copy_dict(flipperzero.io, io)
+
+now = datetime.datetime.now()
+
+project = "uPython"
+copyright = str(now.year) + ", Oliver Fabel"
+author = "Oliver Fabel"
+release = "1.5.0"
+version = "1.5"
+language = "en"
+
+extensions = ["sphinx.ext.autodoc", "myst_parser"]
+source_suffix = {".rst": "restructuredtext", ".md": "markdown"}
+
+templates_path = ["templates"]
+exclude_patterns = []
+include_patterns = ["**"]
+
+html_theme = "alabaster"
+html_theme_options = {
+    "show_powered_by": False,
+    "extra_nav_links": {
+        "Source Code": "https://www.github.com/ofabel/mp-flipper",
+        "Bugtracker": "https://www.github.com/ofabel/mp-flipper/issues",
+        "Releases": "https://lab.flipper.net/apps/upython",
+    },
+}
+html_scaled_image_link = False
+html_copy_source = False
+html_show_copyright = False
+html_show_sphinx = False
+html_static_path = ["static"]
+html_logo = "assets/logo.png"
+html_favicon = "assets/favicon.png"
+
+autodoc_default_options = {
+    "member-order": "bysource",
+}
+
+add_module_names = True
+
+maximum_signature_line_length = 50

+ 75 - 0
mp_flipper/docs/pages/examples.rst

@@ -0,0 +1,75 @@
+Examples
+========
+
+This page contains a few examples.
+See more on `GitHub <https://github.com/ofabel/mp-flipper/tree/master/examples>`_.
+
+Speaker
+-------
+
+.. literalinclude:: ../../examples/flipperzero_speaker_test.py
+   :language: python
+
+For details, see the :ref:`reference-speaker` section on the :doc:`reference` page.
+
+Input
+-----
+
+.. literalinclude:: ../../examples/flipperzero_draw_on_input_test.py
+   :language: python
+
+For details, see the :ref:`reference-input` section on the :doc:`reference` page.
+
+Interrupts
+----------
+
+.. literalinclude:: ../../examples/flipperzero_gpio_interrupt_test.py
+   :language: python
+
+This example drives an external LED upon interrupts: A rising edge on ``C0`` sets the pin ``A7`` to high, a rising edge on ``C1`` sets the pin ``A7`` to low.
+The following schematic circuit diagram shows the hardware setup for this example:
+
+.. figure:: ./assets/gpio_interrupt_circuit.svg
+   :width: 90%
+
+   Hardware setup for the GPIO interrupt example.
+
+For details, see the :ref:`reference-gpio` section on the :doc:`reference` page.
+
+ADC
+---
+
+.. literalinclude:: ../../examples/flipperzero_adc_test.py
+   :language: python
+
+This example uses a voltage divider with the 3.3 V source from pin 9. The switch ``S1`` changes the input voltage on ``C1`` between 0 and about 0.8 V.
+
+.. figure:: ./assets/adc_circuit.svg
+   :width: 90%
+
+   Hardware setup for the ADC example.
+
+For details, see the :ref:`reference-adc` section on the :doc:`reference` page.
+
+PWM
+---
+
+.. literalinclude:: ../../examples/flipperzero_pwm_test.py
+   :language: python
+
+This example drives an LED connected to pin ``A7`` and ``GND`` using a PWM signal with two different frequency and duty cycle settings.
+
+.. figure:: ./assets/pwm_circuit.svg
+   :width: 90%
+
+   Hardware setup for the PWM example.
+
+For details, see the :ref:`reference-pwm` section on the :doc:`reference` page.
+
+Infrared
+--------
+
+.. literalinclude:: ../../examples/flipperzero_infrared_test.py
+   :language: python
+
+For details, see the :ref:`reference-infrared` section on the :doc:`reference` page.

+ 71 - 0
mp_flipper/docs/pages/features.md

@@ -0,0 +1,71 @@
+# Features
+
+Adding Python support to the Flipper Zero platform was only possible by rigorously sorting unnecessary language features.
+So here is a detailed list of all supported and unsupported Python language features.
+
+## Supported
+
+The following features are enabled and supported by the interpreter:
+
+* Garbage collector is enabled.
+* Finaliser calls in the garbage collector (e.g. `__del__`).
+* The `__file__` constant.
+* Import of external files from the SD card.
+* Read and write files from and to the SD card.
+* The `time` module.
+* The `random` module.
+* The `logging` module.
+* The `io` module.
+* The `float` data type.
+* The `%` string formatting operator.
+* The `.format` string formatting function.
+* Support for [decorator](https://docs.python.org/3/glossary.html#term-decorator) functions.
+* The `setattr` function.
+* The `filter` function.
+* The `reversed` function.
+* The `min` and `max` function.
+* Module-level `__init__` imports.
+* Support for a [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop).
+
+## Unsupported
+
+The following features are disabled and _not_ supported by the interpreter:
+
+* The `__doc__` constants.
+* Source code line numbers in exceptions.
+* The `cmath` module.
+* The `complex` data type.
+* Support for multiple inheritance.
+* Module-level `__getattr__` support according to [PEP 562](https://peps.python.org/pep-0562/).
+* Support for the descriptors `__get__`, `__set__` and  `__delete__`.
+* Coroutines with `async` and `await` functions.
+* The `:=` assign expression.
+* Non-standard `.pend_throw()` method for generators.
+* Support for `bytes.hex` and `bytes.fromhex`.
+* Support for unicode characters.
+* The string functions `.center`, `.count`, `.partition`, `.rpartition` and `.splitlines`.
+* The `bytearray` data type.
+* The `memoryview` data type.
+* The `slice` object.
+* The `frozenset` object.
+* The `property` decorator.
+* The `next` function with a second argument.
+* All special methods for user classes (e.g. `__imul__`).
+* The `enumerate` function.
+* The `compile` function.
+* Support for `eval`, `exec` and `execfile` functions.
+* The `NotImplemented` special constant.
+* The `input` function.
+* The `pow` function with 3 integer arguments.
+* The `help` function.
+* The `micropython` module.
+* The `array` module.
+* The `collections` module.
+* The `struct` module.
+* The `gc` module.
+* The `sys` module.
+* The `select` module.
+* The `json` module.
+
+This list of unsupported features is not set in stone. 
+If you miss support for one particular feature, feel free to [open an issue](https://github.com/ofabel/mp-flipper/issues) or make a pull request.

+ 54 - 0
mp_flipper/docs/pages/index.rst

@@ -0,0 +1,54 @@
+.. toctree::
+   :hidden:
+   :maxdepth: 2
+
+   quickstart
+   reference
+   features
+   examples
+   roadmap
+   Changelog </changelog>
+   License </license>
+
+MicroPython on Flipper Zero
+===========================
+
+.. image:: https://img.shields.io/github/license/ofabel/mp-flipper
+   :alt: License
+
+.. image:: https://img.shields.io/github/v/tag/ofabel/mp-flipper
+   :alt: Version
+
+.. image:: https://img.shields.io/github/issues/ofabel/mp-flipper
+   :alt: Issues
+
+A `MicroPython <https://github.com/micropython/micropython>`_ port for the famous `Flipper Zero <https://flipperzero.one/>`_.
+No need to learn C: Use your favourite programming language to create apps, games and scripts.
+
+.. image:: ./assets/repl.gif
+   :align: center
+
+Features
+--------
+
+* Support for basic language constructs like functions, classes, loops, ...
+* Access the Flipper's hardware: buttons, speaker, LED, GPIO, ADC, PWM ...
+* No custom firmware is required, so no risk to brick your Flipper.
+
+A complete list can be found in the :doc:`features </features>` section.
+
+How to Start
+------------
+
+1. Install the application from the `Flipper Lab <https://lab.flipper.net/apps/upython>`_ on your Flipper device.
+2. Write some Python code or use one of the provided `examples <https://github.com/ofabel/mp-flipper/tree/master/examples>`_.
+3. Use the `qFlipper <https://flipperzero.one/update>`_ application to upload the code to your Flipper's SD card.
+4. Start the **uPython** application on your Flipper to execute your Python script.
+
+Checkout the :doc:`reference </reference>` section for an in-depth API documentation.
+
+License
+-------
+
+The uPython application is published under the :doc:`MIT </license>` license.
+

+ 1 - 0
mp_flipper/docs/pages/license.rst

@@ -0,0 +1 @@
+.. include:: ../../LICENSE.txt

+ 51 - 0
mp_flipper/docs/pages/quickstart.rst

@@ -0,0 +1,51 @@
+Quickstart
+==========
+
+This page provides some details on how to start and use the application.
+
+Basics
+------
+
+How to install the application and run a script:
+
+1. Install the application from the `Flipper Lab <https://lab.flipper.net/apps/upython>`_ on your Flipper device.
+2. Write some Python code or use one of the provided `examples <https://github.com/ofabel/mp-flipper/tree/master/examples>`_.
+3. Use the `qFlipper <https://flipperzero.one/update>`_ application to upload the code to your Flipper's SD card.
+4. Start the **uPython** application on your Flipper to run your Python script.
+
+Instead of running a script, you could also use the interactive MicroPython shell from the terminal.
+Visit the `Flipper documentation <https://docs.flipper.net/development/cli>`_ for details about the CLI in general.
+
+.. hint::
+
+   Looking for a more efficient solution to copy your files or start a CLI session?
+   Try the `Flipper Zero Script SDK <https://github.com/ofabel/fssdk>`_ helper.
+
+In case your Flipper is not responding or a script doesn't behave as expected and won't finish: `do a reboot <https://docs.flipper.net/basics/reboot>`_ by pressing and holding **Left** and **Back** for 5 seconds.
+
+Usage
+-----
+
+Due to the occasional crashes upon application start, the application is especially designed to run in the background while working on the CLI from the computer.
+
+You can use the CLI to start the application:
+
+.. code-block:: shell
+
+   loader open /ext/apps/Tools/upython.fap
+
+You can also use the CLI to start the application and run a Python script:
+
+.. code-block:: shell
+
+   loader open /ext/apps/Tools/upython.fap /ext/scripts/tic_tac_toe.py
+
+Once the application is up an running, it registers the **py** command in the Flipper's CLI command registry.
+You can use this command to access the interactive MicroPython shell or start a script by passing the path as an argument:
+
+.. code-block:: shell
+
+   py /ext/scripts/tic_tac_toe.py
+
+Be aware, that the **py** command is only available as long as the application is running on the Flipper.
+Furthermore, you can only run one script at the time.

+ 501 - 0
mp_flipper/docs/pages/reference.rst

@@ -0,0 +1,501 @@
+Reference
+=========
+
+This page contains the API documentation of the ``flipperzero`` module and some built-in functions.
+The module is also available as a Python package on `PyPI <https://pypi.org/project/flipperzero/>`_.
+Install it in your local development environment if you need code completion support inside your IDE.
+
+Vibration
+---------
+
+Control the vibration motor of your Flipper.
+
+.. autofunction:: flipperzero.vibro_set
+
+.. _reference-light:
+
+Light
+-----
+
+Control the RGB LED and display backlight of your Flipper.
+
+Constants
+~~~~~~~~~
+
+.. autodata:: flipperzero.LIGHT_RED
+.. autodata:: flipperzero.LIGHT_GREEN
+.. autodata:: flipperzero.LIGHT_BLUE
+.. autodata:: flipperzero.LIGHT_BACKLIGHT
+
+Functions
+~~~~~~~~~
+
+.. autofunction:: flipperzero.light_set
+.. autofunction:: flipperzero.light_blink_start
+.. autofunction:: flipperzero.light_blink_set_color
+.. autofunction:: flipperzero.light_blink_stop
+
+.. _reference-speaker:
+
+Speaker
+-------
+
+Full control over the built-in speaker module.
+
+Musical Notes
+~~~~~~~~~~~~~
+
+..
+   for octave in range(9):
+      for name in ['C', 'CS', 'D', 'DS', 'E', 'F', 'FS', 'G', 'GS', 'A', 'AS', 'B']:
+         print(f'.. autodata:: flipperzero.SPEAKER_NOTE_{name}{octave}')
+
+.. autodata:: flipperzero.SPEAKER_NOTE_C0
+.. autodata:: flipperzero.SPEAKER_NOTE_CS0
+.. autodata:: flipperzero.SPEAKER_NOTE_D0
+.. autodata:: flipperzero.SPEAKER_NOTE_DS0
+.. autodata:: flipperzero.SPEAKER_NOTE_E0
+.. autodata:: flipperzero.SPEAKER_NOTE_F0
+.. autodata:: flipperzero.SPEAKER_NOTE_FS0
+.. autodata:: flipperzero.SPEAKER_NOTE_G0
+.. autodata:: flipperzero.SPEAKER_NOTE_GS0
+.. autodata:: flipperzero.SPEAKER_NOTE_A0
+.. autodata:: flipperzero.SPEAKER_NOTE_AS0
+.. autodata:: flipperzero.SPEAKER_NOTE_B0
+.. autodata:: flipperzero.SPEAKER_NOTE_C1
+.. autodata:: flipperzero.SPEAKER_NOTE_CS1
+.. autodata:: flipperzero.SPEAKER_NOTE_D1
+.. autodata:: flipperzero.SPEAKER_NOTE_DS1
+.. autodata:: flipperzero.SPEAKER_NOTE_E1
+.. autodata:: flipperzero.SPEAKER_NOTE_F1
+.. autodata:: flipperzero.SPEAKER_NOTE_FS1
+.. autodata:: flipperzero.SPEAKER_NOTE_G1
+.. autodata:: flipperzero.SPEAKER_NOTE_GS1
+.. autodata:: flipperzero.SPEAKER_NOTE_A1
+.. autodata:: flipperzero.SPEAKER_NOTE_AS1
+.. autodata:: flipperzero.SPEAKER_NOTE_B1
+.. autodata:: flipperzero.SPEAKER_NOTE_C2
+.. autodata:: flipperzero.SPEAKER_NOTE_CS2
+.. autodata:: flipperzero.SPEAKER_NOTE_D2
+.. autodata:: flipperzero.SPEAKER_NOTE_DS2
+.. autodata:: flipperzero.SPEAKER_NOTE_E2
+.. autodata:: flipperzero.SPEAKER_NOTE_F2
+.. autodata:: flipperzero.SPEAKER_NOTE_FS2
+.. autodata:: flipperzero.SPEAKER_NOTE_G2
+.. autodata:: flipperzero.SPEAKER_NOTE_GS2
+.. autodata:: flipperzero.SPEAKER_NOTE_A2
+.. autodata:: flipperzero.SPEAKER_NOTE_AS2
+.. autodata:: flipperzero.SPEAKER_NOTE_B2
+.. autodata:: flipperzero.SPEAKER_NOTE_C3
+.. autodata:: flipperzero.SPEAKER_NOTE_CS3
+.. autodata:: flipperzero.SPEAKER_NOTE_D3
+.. autodata:: flipperzero.SPEAKER_NOTE_DS3
+.. autodata:: flipperzero.SPEAKER_NOTE_E3
+.. autodata:: flipperzero.SPEAKER_NOTE_F3
+.. autodata:: flipperzero.SPEAKER_NOTE_FS3
+.. autodata:: flipperzero.SPEAKER_NOTE_G3
+.. autodata:: flipperzero.SPEAKER_NOTE_GS3
+.. autodata:: flipperzero.SPEAKER_NOTE_A3
+.. autodata:: flipperzero.SPEAKER_NOTE_AS3
+.. autodata:: flipperzero.SPEAKER_NOTE_B3
+.. autodata:: flipperzero.SPEAKER_NOTE_C4
+.. autodata:: flipperzero.SPEAKER_NOTE_CS4
+.. autodata:: flipperzero.SPEAKER_NOTE_D4
+.. autodata:: flipperzero.SPEAKER_NOTE_DS4
+.. autodata:: flipperzero.SPEAKER_NOTE_E4
+.. autodata:: flipperzero.SPEAKER_NOTE_F4
+.. autodata:: flipperzero.SPEAKER_NOTE_FS4
+.. autodata:: flipperzero.SPEAKER_NOTE_G4
+.. autodata:: flipperzero.SPEAKER_NOTE_GS4
+.. autodata:: flipperzero.SPEAKER_NOTE_A4
+.. autodata:: flipperzero.SPEAKER_NOTE_AS4
+.. autodata:: flipperzero.SPEAKER_NOTE_B4
+.. autodata:: flipperzero.SPEAKER_NOTE_C5
+.. autodata:: flipperzero.SPEAKER_NOTE_CS5
+.. autodata:: flipperzero.SPEAKER_NOTE_D5
+.. autodata:: flipperzero.SPEAKER_NOTE_DS5
+.. autodata:: flipperzero.SPEAKER_NOTE_E5
+.. autodata:: flipperzero.SPEAKER_NOTE_F5
+.. autodata:: flipperzero.SPEAKER_NOTE_FS5
+.. autodata:: flipperzero.SPEAKER_NOTE_G5
+.. autodata:: flipperzero.SPEAKER_NOTE_GS5
+.. autodata:: flipperzero.SPEAKER_NOTE_A5
+.. autodata:: flipperzero.SPEAKER_NOTE_AS5
+.. autodata:: flipperzero.SPEAKER_NOTE_B5
+.. autodata:: flipperzero.SPEAKER_NOTE_C6
+.. autodata:: flipperzero.SPEAKER_NOTE_CS6
+.. autodata:: flipperzero.SPEAKER_NOTE_D6
+.. autodata:: flipperzero.SPEAKER_NOTE_DS6
+.. autodata:: flipperzero.SPEAKER_NOTE_E6
+.. autodata:: flipperzero.SPEAKER_NOTE_F6
+.. autodata:: flipperzero.SPEAKER_NOTE_FS6
+.. autodata:: flipperzero.SPEAKER_NOTE_G6
+.. autodata:: flipperzero.SPEAKER_NOTE_GS6
+.. autodata:: flipperzero.SPEAKER_NOTE_A6
+.. autodata:: flipperzero.SPEAKER_NOTE_AS6
+.. autodata:: flipperzero.SPEAKER_NOTE_B6
+.. autodata:: flipperzero.SPEAKER_NOTE_C7
+.. autodata:: flipperzero.SPEAKER_NOTE_CS7
+.. autodata:: flipperzero.SPEAKER_NOTE_D7
+.. autodata:: flipperzero.SPEAKER_NOTE_DS7
+.. autodata:: flipperzero.SPEAKER_NOTE_E7
+.. autodata:: flipperzero.SPEAKER_NOTE_F7
+.. autodata:: flipperzero.SPEAKER_NOTE_FS7
+.. autodata:: flipperzero.SPEAKER_NOTE_G7
+.. autodata:: flipperzero.SPEAKER_NOTE_GS7
+.. autodata:: flipperzero.SPEAKER_NOTE_A7
+.. autodata:: flipperzero.SPEAKER_NOTE_AS7
+.. autodata:: flipperzero.SPEAKER_NOTE_B7
+.. autodata:: flipperzero.SPEAKER_NOTE_C8
+.. autodata:: flipperzero.SPEAKER_NOTE_CS8
+.. autodata:: flipperzero.SPEAKER_NOTE_D8
+.. autodata:: flipperzero.SPEAKER_NOTE_DS8
+.. autodata:: flipperzero.SPEAKER_NOTE_E8
+.. autodata:: flipperzero.SPEAKER_NOTE_F8
+.. autodata:: flipperzero.SPEAKER_NOTE_FS8
+.. autodata:: flipperzero.SPEAKER_NOTE_G8
+.. autodata:: flipperzero.SPEAKER_NOTE_GS8
+.. autodata:: flipperzero.SPEAKER_NOTE_A8
+.. autodata:: flipperzero.SPEAKER_NOTE_AS8
+.. autodata:: flipperzero.SPEAKER_NOTE_B8
+
+Volume
+~~~~~~
+
+.. autodata:: flipperzero.SPEAKER_VOLUME_MIN
+.. autodata:: flipperzero.SPEAKER_VOLUME_MAX
+
+Functions
+~~~~~~~~~
+
+.. autofunction:: flipperzero.speaker_start
+.. autofunction:: flipperzero.speaker_set_volume
+.. autofunction:: flipperzero.speaker_stop
+
+.. _reference-input:
+
+Input
+-----
+
+Make your application interactive with full control over the Flipper's hardware buttons.
+
+Buttons
+~~~~~~~
+
+.. autodata:: flipperzero.INPUT_BUTTON_UP
+.. autodata:: flipperzero.INPUT_BUTTON_DOWN
+.. autodata:: flipperzero.INPUT_BUTTON_RIGHT
+.. autodata:: flipperzero.INPUT_BUTTON_LEFT
+.. autodata:: flipperzero.INPUT_BUTTON_OK
+.. autodata:: flipperzero.INPUT_BUTTON_BACK
+
+Events
+~~~~~~
+
+.. autodata:: flipperzero.INPUT_TYPE_PRESS
+.. autodata:: flipperzero.INPUT_TYPE_RELEASE
+.. autodata:: flipperzero.INPUT_TYPE_SHORT
+.. autodata:: flipperzero.INPUT_TYPE_LONG
+.. autodata:: flipperzero.INPUT_TYPE_REPEAT
+
+Functions
+~~~~~~~~~
+
+.. autodecorator:: flipperzero.on_input
+
+.. _reference-canvas:
+
+Canvas
+------
+
+Write text and draw dots and shapes on the the display.
+
+Basics
+~~~~~~
+
+.. autofunction:: flipperzero.canvas_update
+.. autofunction:: flipperzero.canvas_clear
+.. autofunction:: flipperzero.canvas_width
+.. autofunction:: flipperzero.canvas_height
+
+Colors
+~~~~~~
+
+.. autodata:: flipperzero.COLOR_BLACK
+.. autodata:: flipperzero.COLOR_WHITE
+.. autofunction:: flipperzero.canvas_set_color
+
+Alignment
+~~~~~~~~~
+
+.. autodata:: flipperzero.ALIGN_BEGIN
+.. autodata:: flipperzero.ALIGN_END
+.. autodata:: flipperzero.ALIGN_CENTER
+.. autofunction:: flipperzero.canvas_set_text_align
+
+Text
+~~~~
+
+.. autodata:: flipperzero.FONT_PRIMARY
+.. autodata:: flipperzero.FONT_SECONDARY
+.. autofunction:: flipperzero.canvas_set_font
+.. autofunction:: flipperzero.canvas_set_text
+
+Shapes
+~~~~~~
+
+.. autofunction:: flipperzero.canvas_draw_dot
+.. autofunction:: flipperzero.canvas_draw_box
+.. autofunction:: flipperzero.canvas_draw_frame
+.. autofunction:: flipperzero.canvas_draw_line
+.. autofunction:: flipperzero.canvas_draw_circle
+.. autofunction:: flipperzero.canvas_draw_disc
+
+Dialog
+------
+
+Display message dialogs on the display for user infos and confirm actions.
+
+.. autofunction:: flipperzero.dialog_message_set_header
+.. autofunction:: flipperzero.dialog_message_set_text
+.. autofunction:: flipperzero.dialog_message_set_button
+.. autofunction:: flipperzero.dialog_message_show
+
+.. _reference-gpio:
+
+GPIO
+----
+
+Access to the GPIO pins of your Flipper.
+
+Pins
+~~~~
+
+.. autodata:: flipperzero.GPIO_PIN_PC0
+.. autodata:: flipperzero.GPIO_PIN_PC1
+.. autodata:: flipperzero.GPIO_PIN_PC3
+.. autodata:: flipperzero.GPIO_PIN_PB2
+.. autodata:: flipperzero.GPIO_PIN_PB3
+.. autodata:: flipperzero.GPIO_PIN_PA4
+.. autodata:: flipperzero.GPIO_PIN_PA6
+.. autodata:: flipperzero.GPIO_PIN_PA7
+
+Modes
+~~~~~
+
+.. autodata:: flipperzero.GPIO_MODE_INPUT
+.. autodata:: flipperzero.GPIO_MODE_OUTPUT_PUSH_PULL
+.. autodata:: flipperzero.GPIO_MODE_OUTPUT_OPEN_DRAIN
+.. autodata:: flipperzero.GPIO_MODE_ANALOG
+.. autodata:: flipperzero.GPIO_MODE_INTERRUPT_RISE
+.. autodata:: flipperzero.GPIO_MODE_INTERRUPT_FALL
+
+Pull 
+~~~~
+
+.. autodata:: flipperzero.GPIO_PULL_NO
+.. autodata:: flipperzero.GPIO_PULL_UP
+.. autodata:: flipperzero.GPIO_PULL_DOWN
+
+Speed
+~~~~~
+
+.. autodata:: flipperzero.GPIO_SPEED_LOW
+.. autodata:: flipperzero.GPIO_SPEED_MEDIUM
+.. autodata:: flipperzero.GPIO_SPEED_HIGH
+.. autodata:: flipperzero.GPIO_SPEED_VERY_HIGH
+
+Functions
+~~~~~~~~~
+
+.. autofunction:: flipperzero.gpio_init_pin
+.. autofunction:: flipperzero.gpio_deinit_pin
+.. autofunction:: flipperzero.gpio_set_pin
+.. autofunction:: flipperzero.gpio_get_pin
+.. autodecorator:: flipperzero.on_gpio
+
+.. _reference-adc:
+
+ADC
+---
+
+Read analog values from selected GPIO pins:
+
+* :const:`flipperzero.GPIO_PIN_PC0`
+* :const:`flipperzero.GPIO_PIN_PC1`
+* :const:`flipperzero.GPIO_PIN_PC3`
+* :const:`flipperzero.GPIO_PIN_PA4`
+* :const:`flipperzero.GPIO_PIN_PA6`
+* :const:`flipperzero.GPIO_PIN_PA7`
+
+The corresponding pin must be initialized in the analog mode:
+
+.. code-block::
+
+   import flipperzero as f0
+
+   f0.gpio_init_pin(f0.GPIO_PIN_PC0, f0.GPIO_MODE_ANALOG)
+
+This configures the pin as ADC input with the following settings:
+
+* Reference voltage is set to 2.048 V.
+* Clock speed is at 64 MHz in synchronous mode.
+* Oversample rate is set to 64.
+
+`This default configuration is best for relatively high impedance circuits with slowly or or not changing signals.`
+
+Functions
+~~~~~~~~~
+
+.. autofunction:: flipperzero.adc_read_pin_value
+.. autofunction:: flipperzero.adc_read_pin_voltage
+
+.. _reference-pwm:
+
+PWM
+---
+
+Output a PWM signal on selected GPIO pins:
+
+* :const:`flipperzero.GPIO_PIN_PA4`
+* :const:`flipperzero.GPIO_PIN_PA7`
+
+Functions
+~~~~~~~~~
+
+.. autofunction:: flipperzero.pwm_start
+.. autofunction:: flipperzero.pwm_stop
+.. autofunction:: flipperzero.pwm_is_running
+
+.. _reference-infrared:
+
+Infrared
+--------
+
+Send and receive infrared signals.
+
+Signal Format
+~~~~~~~~~~~~~
+
+The format to represent infrared signals uses a simple list of integers.
+Each value represents the duration between two signal edges in microseconds.
+Since this is a digital signal, there are only two levels: `high` and `low`.
+The timing list always starts with a `high` level.
+
+.. literalinclude:: ./assets/pwm_signal.txt
+   :language: text
+
+.. hint::
+
+   This is equal to the raw signal format of the `IR file <https://developer.flipper.net/flipperzero/doxygen/infrared_file_format.html>`_ specification.
+
+Functions
+~~~~~~~~~
+
+.. autofunction:: flipperzero.infrared_receive
+.. autofunction:: flipperzero.infrared_transmit
+.. autofunction:: flipperzero.infrared_is_busy
+
+UART
+----
+
+Connect to UART enabled devices.
+
+Modes
+~~~~~
+
+.. autodata:: flipperzero.UART_MODE_LPUART
+.. autodata:: flipperzero.UART_MODE_USART
+
+Functions
+~~~~~~~~~
+
+.. autofunction:: flipperzero.uart_open
+
+Classes
+~~~~~~~
+
+.. autoclass:: flipperzero.UART
+   :members: read, readline, readlines, write, flush, close, __enter__, __exit__, __del__
+
+Logging
+-------
+
+Log messages to the Flipper's own logging backend.
+Check out the `Flipper Zero docs <https://docs.flipper.net/development/cli#_yZ2E>`_ on how to reveal them in the CLI.
+Be aware, that you can't change Flipper's global log level from within your script.
+Change the `corresponding settings <https://docs.flipper.net/basics/settings#d5TAt>`_ instead or use the **log** command in the CLI with the desired log level as the first argument.
+
+Levels
+~~~~~~
+
+.. autodata:: logging.TRACE
+.. autodata:: logging.DEBUG
+.. autodata:: logging.INFO
+.. autodata:: logging.WARN
+.. autodata:: logging.ERROR
+.. autodata:: logging.NONE
+.. autodata:: logging.level
+
+Functions
+~~~~~~~~~
+
+.. autofunction:: logging.setLevel
+.. autofunction:: logging.getEffectiveLevel
+.. autofunction:: logging.trace
+.. autofunction:: logging.debug
+.. autofunction:: logging.info
+.. autofunction:: logging.warn
+.. autofunction:: logging.error
+.. autofunction:: logging.log
+
+I/O
+---
+
+Read and write files on the SD card.
+
+Constants
+~~~~~~~~~
+
+.. autodata:: io.SEEK_SET
+.. autodata:: io.SEEK_CUR
+.. autodata:: io.SEEK_END
+
+Functions
+~~~~~~~~~
+
+.. autofunction:: io.open
+
+Classes
+~~~~~~~
+
+.. autoclass:: io.BinaryFileIO
+   :members: name, read, readline, readlines, readable, writable, write, flush, seek, tell, close, __enter__, __exit__, __del__
+
+.. autoclass:: io.TextFileIO
+   :members: name, read, readline, readlines, readable, writable, write, flush, seek, tell, close, __enter__, __exit__, __del__
+
+Built-In
+--------
+
+The functions in this section are `not` part of the ``flipperzero`` module.
+They're members of the global namespace instead.
+
+.. py:function:: print(*objects, sep=' ', end='\n') -> None
+
+   The standard Python `print <https://docs.python.org/3/library/functions.html#print>`_ function.
+   Where the output of this function will be redirected depends on how the script is invoked:
+
+      * When invoked from the UI, the output will be sent to the Flipper's log buffer.
+        Check out the `Flipper Zero docs <https://docs.flipper.net/development/cli#_yZ2E>`_ on how to view them in the CLI interface.
+      * In the REPL, the output will be sent to the standard output buffer.
+      * When invoked by the **py** command, the output will be sent to the standard output buffer.
+
+   :param objects: The objects to print (mostly a single string).
+   :param sep: The separator to use between the objects.
+   :param end: The line terminator character to use.
+
+   .. versionadded:: 1.0.0
+   .. versionchanged:: 1.5.0
+
+      Output redirection, based on script invocation.

+ 23 - 0
mp_flipper/docs/pages/roadmap.md

@@ -0,0 +1,23 @@
+# Roadmap
+
+This is the roadmap for the application.
+Here you can see what to expect from future releases.
+
+## Next Release
+
+* Improved `print` function.
+* UART
+* I2C
+* Maybe USB HID
+
+## Planned
+
+* I2C
+* NFC
+* UART
+* USB HID
+* Subghz
+* Bluetooth
+* ...
+
+Feel free to [file an issue](https://www.github.com/ofabel/mp-flipper/issues), if you miss any particular feature.

二进制
mp_flipper/docs/tic-tac-toe.png


二进制
mp_flipper/docs/welcome.png


+ 9 - 0
mp_flipper/examples/dict_test.py

@@ -0,0 +1,9 @@
+d = dict()
+
+for i in range(1, 100):
+    d[str(i)] = i
+
+for k, v in d.items():
+    val = "{0} = {1}".format(k, v)
+
+    print(val)

+ 20 - 0
mp_flipper/examples/flipperzero_adc_test.py

@@ -0,0 +1,20 @@
+import flipperzero as f0
+import time
+
+f0.gpio_init_pin(f0.GPIO_PIN_PC1, f0.GPIO_MODE_ANALOG)
+
+for _ in range(1, 1000):
+    raw_value = f0.adc_read_pin_value(f0.GPIO_PIN_PC1)
+    raw_voltage = f0.adc_read_pin_voltage(f0.GPIO_PIN_PC1)
+
+    value = "{value} #".format(value=raw_value)
+    voltage = "{value} mV".format(value=raw_voltage)
+
+    f0.canvas_clear()
+
+    f0.canvas_set_text(10, 32, value)
+    f0.canvas_set_text(70, 32, voltage)
+
+    f0.canvas_update()
+
+    time.sleep_ms(10)

+ 37 - 0
mp_flipper/examples/flipperzero_canvas_test.py

@@ -0,0 +1,37 @@
+import time
+import flipperzero
+
+color = False
+
+
+def draw_action():
+    print("on draw")
+
+    global color
+
+    for x in range(0, 128):
+        color = not color
+
+        for y in range(0, 64):
+            flipperzero.canvas_set_color(
+                flipperzero.CANVAS_BLACK if color else flipperzero.CANVAS_WHITE
+            )
+            flipperzero.canvas_draw_dot(x, y)
+
+            color = not color
+
+    color = not color
+
+    flipperzero.canvas_set_text(64, 32, "Test")
+
+    flipperzero.canvas_update()
+
+
+print("start")
+
+draw_action()
+
+for _ in range(1, 5):
+    time.sleep(1)
+
+    draw_action()

+ 9 - 0
mp_flipper/examples/flipperzero_dialog_message_test.py

@@ -0,0 +1,9 @@
+import flipperzero as f0
+
+f0.dialog_message_set_header("Important", 64, 12)
+f0.dialog_message_set_text("Shutdown?", 64, 24)
+f0.dialog_message_set_button("Yes", f0.INPUT_BUTTON_LEFT)
+f0.dialog_message_set_button("No", f0.INPUT_BUTTON_RIGHT)
+
+while f0.dialog_message_show() is not f0.INPUT_BUTTON_LEFT:
+    pass

+ 16 - 0
mp_flipper/examples/flipperzero_draw_on_input_test.py

@@ -0,0 +1,16 @@
+import time
+import flipperzero
+
+
+@flipperzero.on_input
+def on_input(button, type):
+    flipperzero.canvas_clear()
+    flipperzero.canvas_set_color(flipperzero.CANVAS_BLACK)
+    flipperzero.canvas_set_text(
+        64, 32, "{button} - {type}".format(button=button, type=type)
+    )
+    flipperzero.canvas_update()
+
+
+for _ in range(1, 1000):
+    time.sleep_ms(10)

+ 14 - 0
mp_flipper/examples/flipperzero_gpio_input_test.py

@@ -0,0 +1,14 @@
+import flipperzero as f0
+import time
+
+f0.gpio_init_pin(f0.GPIO_PIN_PA7, f0.GPIO_MODE_OUTPUT_PUSH_PULL)
+f0.gpio_init_pin(
+    f0.GPIO_PIN_PC1, f0.GPIO_MODE_INPUT, f0.GPIO_PULL_UP, f0.GPIO_SPEED_HIGH
+)
+
+for _ in range(0, 15):
+    state = f0.gpio_get_pin(f0.GPIO_PIN_PC1)
+
+    f0.gpio_set_pin(f0.GPIO_PIN_PA7, state)
+
+    time.sleep(1)

+ 34 - 0
mp_flipper/examples/flipperzero_gpio_interrupt_test.py

@@ -0,0 +1,34 @@
+import flipperzero as f0
+import time
+
+# init pins
+f0.gpio_init_pin(f0.GPIO_PIN_PA7, f0.GPIO_MODE_OUTPUT_PUSH_PULL)
+f0.gpio_init_pin(
+    f0.GPIO_PIN_PC0,
+    f0.GPIO_MODE_INTERRUPT_RISE,
+    f0.GPIO_PULL_UP,
+    f0.GPIO_SPEED_VERY_HIGH,
+)
+f0.gpio_init_pin(
+    f0.GPIO_PIN_PC1,
+    f0.GPIO_MODE_INTERRUPT_RISE,
+    f0.GPIO_PULL_UP,
+    f0.GPIO_SPEED_VERY_HIGH,
+)
+
+
+@f0.on_gpio
+def on_gpio(pin):
+    if pin == f0.GPIO_PIN_PC0:
+        f0.gpio_set_pin(f0.GPIO_PIN_PA7, True)
+    if pin == f0.GPIO_PIN_PC1:
+        f0.gpio_set_pin(f0.GPIO_PIN_PA7, False)
+
+
+for _ in range(1, 1500):
+    time.sleep_ms(10)
+
+# reset pins
+f0.gpio_init_pin(f0.GPIO_PIN_PA7, f0.GPIO_MODE_ANALOG)
+f0.gpio_init_pin(f0.GPIO_PIN_PC0, f0.GPIO_MODE_ANALOG)
+f0.gpio_init_pin(f0.GPIO_PIN_PC1, f0.GPIO_MODE_ANALOG)

+ 12 - 0
mp_flipper/examples/flipperzero_gpio_test.py

@@ -0,0 +1,12 @@
+import flipperzero as f0
+import time
+
+f0.gpio_init_pin(f0.GPIO_PIN_PA7, f0.GPIO_MODE_OUTPUT_PUSH_PULL)
+
+f0.gpio_set_pin(f0.GPIO_PIN_PA7, True)
+time.sleep(1)
+f0.gpio_set_pin(f0.GPIO_PIN_PA7, False)
+time.sleep(1)
+f0.gpio_set_pin(f0.GPIO_PIN_PA7, True)
+time.sleep(1)
+f0.gpio_set_pin(f0.GPIO_PIN_PA7, False)

+ 10 - 0
mp_flipper/examples/flipperzero_infrared_test.py

@@ -0,0 +1,10 @@
+import flipperzero as f0
+import time
+
+# receive IR signal
+signal = f0.infrared_receive()
+
+time.sleep(3)
+
+# transmit received signal
+f0.infrared_transmit(signal)

+ 11 - 0
mp_flipper/examples/flipperzero_input_test.py

@@ -0,0 +1,11 @@
+import time
+import flipperzero
+
+
+@flipperzero.on_input
+def on_input(button, type):
+    print("{button} - {type}".format(button=button, type=type))
+
+
+for _ in range(1, 1000):
+    time.sleep_ms(10)

+ 32 - 0
mp_flipper/examples/flipperzero_light_test.py

@@ -0,0 +1,32 @@
+import time
+import flipperzero
+
+is_red = True
+
+for i in range(0, 25, 1):
+    brightness = i * 10
+    is_red = not is_red
+
+    flipperzero.light_set(flipperzero.LIGHT_RED, brightness if is_red else 0)
+    flipperzero.light_set(flipperzero.LIGHT_GREEN, brightness if not is_red else 0)
+    flipperzero.light_set(flipperzero.LIGHT_BLUE, 0)
+    flipperzero.light_set(flipperzero.LIGHT_BACKLIGHT, brightness)
+
+    time.sleep_ms(200)
+
+flipperzero.light_set(flipperzero.LIGHT_RED, 0)
+flipperzero.light_set(flipperzero.LIGHT_GREEN, 0)
+flipperzero.light_set(flipperzero.LIGHT_BLUE, 0)
+flipperzero.light_set(flipperzero.LIGHT_BACKLIGHT, 0)
+
+time.sleep_ms(500)
+
+flipperzero.light_blink_start(flipperzero.LIGHT_RED, 200, 100, 200)
+
+time.sleep(1)
+
+flipperzero.light_blink_set_color(flipperzero.LIGHT_BLUE)
+
+time.sleep(1)
+
+flipperzero.light_blink_stop()

+ 8 - 0
mp_flipper/examples/flipperzero_pwm_test.py

@@ -0,0 +1,8 @@
+import flipperzero as f0
+import time
+
+f0.pwm_start(f0.GPIO_PIN_PA7, 4, 50)
+time.sleep(3)
+
+f0.pwm_start(f0.GPIO_PIN_PA7, 1, 25)
+time.sleep(3)

+ 27 - 0
mp_flipper/examples/flipperzero_speaker_note_test.py

@@ -0,0 +1,27 @@
+import time
+import flipperzero as f0
+
+
+def play_frequency(frequency: float):
+    volume = 0.8
+
+    f0.speaker_start(frequency, volume)
+
+    for _ in range(0, 150):
+        volume *= 0.9945679
+
+        f0.speaker_set_volume(volume)
+
+        time.sleep_ms(1)
+
+    f0.speaker_stop()
+
+
+play_frequency(f0.SPEAKER_NOTE_C5)
+play_frequency(f0.SPEAKER_NOTE_D5)
+play_frequency(f0.SPEAKER_NOTE_E5)
+play_frequency(f0.SPEAKER_NOTE_F5)
+play_frequency(f0.SPEAKER_NOTE_G5)
+play_frequency(f0.SPEAKER_NOTE_A5)
+play_frequency(f0.SPEAKER_NOTE_B5)
+play_frequency(f0.SPEAKER_NOTE_C6)

+ 27 - 0
mp_flipper/examples/flipperzero_speaker_test.py

@@ -0,0 +1,27 @@
+import time
+import flipperzero
+
+
+def play_frequency(frequency: float):
+    volume = 0.8
+
+    flipperzero.speaker_start(frequency, volume)
+
+    for _ in range(0, 150):
+        volume *= 0.9945679
+
+        flipperzero.speaker_set_volume(volume)
+
+        time.sleep_ms(1)
+
+    flipperzero.speaker_stop()
+
+
+play_frequency(100.0)
+play_frequency(200.0)
+play_frequency(300.0)
+play_frequency(500.0)
+play_frequency(800.0)
+play_frequency(1300.0)
+play_frequency(2100.0)
+play_frequency(3400.0)

+ 8 - 0
mp_flipper/examples/flipperzero_vibro_test.py

@@ -0,0 +1,8 @@
+import time
+import flipperzero
+
+flipperzero.vibro_set(True)
+
+time.sleep_ms(125)
+
+flipperzero.vibro_set(False)

+ 3 - 0
mp_flipper/examples/gc_test.py

@@ -0,0 +1,3 @@
+import gc
+
+gc.collect()

+ 14 - 0
mp_flipper/examples/greeter_test.py

@@ -0,0 +1,14 @@
+class Greeter:
+    def __init__(self, name: str):
+        self._name = name
+
+    def __str__(self):
+        return "hello {name}!".format(name=self._name)
+
+    def __repr__(self):
+        return self.__str__()
+
+
+world_greeter = Greeter("world")
+
+print(world_greeter)

+ 1 - 0
mp_flipper/examples/hello_test.py

@@ -0,0 +1 @@
+print("hello world!")

+ 1 - 0
mp_flipper/examples/import_error_test.py

@@ -0,0 +1 @@
+import unknown_module

+ 5 - 0
mp_flipper/examples/import_test.py

@@ -0,0 +1,5 @@
+from greeter_test import Greeter
+
+buddy = Greeter("buddy")
+
+print(buddy)

+ 97 - 0
mp_flipper/examples/infrared_signal_viewer.py

@@ -0,0 +1,97 @@
+import flipperzero as f0
+import time
+
+terminate = False
+index = 0
+signal = []
+
+
+def draw_signal():
+    global index
+    global signal
+
+    f0.canvas_clear()
+
+    level = False if index % 2 else True
+    x = 0
+    y_low = 32
+    y_high = 40
+    y_level = y_low
+
+    for i in range(100):
+        i += index
+
+        if i > len(signal):
+            break
+
+        duration = int(signal[i] / 100)
+
+        if level:
+            f0.canvas_draw_line(x, y_low, x, y_high)
+            y_level = y_high
+        else:
+            f0.canvas_draw_line(x, y_high, x, y_low)
+            y_level = y_low
+
+        f0.canvas_draw_line(x, y_level, x + duration, y_level)
+
+        x += duration
+
+        level = not level
+
+        if x > f0.canvas_width():
+            break
+
+    f0.canvas_update()
+
+
+@f0.on_input
+def on_input(button, type):
+    global terminate
+    global index
+    global signal
+
+    # terminate upon back button press
+    if button == f0.INPUT_BUTTON_BACK and type == f0.INPUT_TYPE_SHORT:
+        terminate = True
+
+        return
+
+    # transmit signal upon ok button
+    if button == f0.INPUT_BUTTON_OK and type == f0.INPUT_TYPE_SHORT and len(signal) > 0:
+        f0.infrared_transmit(signal)
+
+        return
+
+    # scroll left upon button left
+    if button == f0.INPUT_BUTTON_LEFT and type == f0.INPUT_TYPE_SHORT:
+        index -= 1 if index > 0 else 0
+
+        draw_signal()
+
+        return
+
+    # scroll right upon button left
+    if button == f0.INPUT_BUTTON_RIGHT and type == f0.INPUT_TYPE_SHORT:
+        index += 1
+        index %= len(signal)
+
+        draw_signal()
+
+        return
+
+
+f0.canvas_set_text(10, 32, "Waiting for IR signal ...")
+f0.canvas_update()
+
+# receive signal
+signal = f0.infrared_receive()
+
+if len(signal):
+    draw_signal()
+else:
+    terminate = True
+
+# wait upon termination
+while not terminate:
+    time.sleep_ms(1)

+ 15 - 0
mp_flipper/examples/logger.py

@@ -0,0 +1,15 @@
+import logging
+
+logging.setLevel(logging.TRACE)
+
+logging.trace("trace")
+logging.debug("debug %d %s", 123, "message")
+logging.info("info %d %s", 123, "message")
+logging.warn("warn %d %s", 123, "message")
+logging.error("error %d %s", 123, "message")
+
+logging.log(logging.TRACE, "level: %d", logging.TRACE)
+logging.log(logging.DEBUG, "level: %d", logging.DEBUG)
+logging.log(logging.INFO, "level: %d", logging.INFO)
+logging.log(logging.WARN, "level: %d", logging.WARN)
+logging.log(logging.ERROR, "level: %d", logging.ERROR)

+ 5 - 0
mp_flipper/examples/open.py

@@ -0,0 +1,5 @@
+fp = open("/ext/spam.txt", "w")
+
+fp.write("Some spam")
+
+fp.close()

+ 1 - 0
mp_flipper/examples/overflow_test.py

@@ -0,0 +1 @@
+data = list(range(0, 9999))

+ 1 - 0
mp_flipper/examples/raise_test.py

@@ -0,0 +1 @@
+raise Exception("something went wrong")

+ 7 - 0
mp_flipper/examples/rand_test.py

@@ -0,0 +1,7 @@
+from random import randint, seed
+
+seed(None)
+
+for limit in range(2, 100):
+    value = randint(2, limit)
+    print(value)

+ 3 - 0
mp_flipper/examples/sleep_test.py

@@ -0,0 +1,3 @@
+import time
+
+time.sleep(5)

+ 80 - 0
mp_flipper/examples/tic_tac_toe.py

@@ -0,0 +1,80 @@
+import time
+import flipperzero as f0
+
+
+def init_grid():
+    return [
+        [" ", " ", " "],
+        [" ", " ", " "],
+        [" ", " ", " "],
+    ]
+
+
+m_exit = False
+
+m_grid = init_grid()
+
+m_x = 1
+m_y = 1
+
+m_is_cross = True
+
+
+@f0.on_input
+def input_handler(button, type):
+    global m_exit
+    global m_grid
+    global m_x
+    global m_y
+    global m_is_cross
+
+    if button == f0.INPUT_BUTTON_BACK and type == f0.INPUT_TYPE_LONG:
+        m_exit = True
+    elif button == f0.INPUT_BUTTON_BACK and type == f0.INPUT_TYPE_SHORT:
+        m_grid = init_grid()
+    elif button == f0.INPUT_BUTTON_LEFT and type == f0.INPUT_TYPE_SHORT:
+        m_x = (m_x - 1) % 3
+    elif button == f0.INPUT_BUTTON_RIGHT and type == f0.INPUT_TYPE_SHORT:
+        m_x = (m_x + 1) % 3
+    elif button == f0.INPUT_BUTTON_UP and type == f0.INPUT_TYPE_SHORT:
+        m_y = (m_y - 1) % 3
+    elif button == f0.INPUT_BUTTON_DOWN and type == f0.INPUT_TYPE_SHORT:
+        m_y = (m_y + 1) % 3
+    elif button == f0.INPUT_BUTTON_OK and type == f0.INPUT_TYPE_SHORT:
+        m_grid[m_x][m_y] = "X" if m_is_cross else "O"
+        m_is_cross = not m_is_cross
+
+
+def draw_grid():
+    global m_grid
+    global m_x
+    global m_y
+
+    f0.canvas_clear()
+    f0.canvas_draw_frame(2, 2, 60, 60)
+
+    f0.canvas_draw_line(22, 2, 22, 62)
+    f0.canvas_draw_line(42, 2, 42, 62)
+
+    f0.canvas_draw_line(2, 22, 62, 22)
+    f0.canvas_draw_line(2, 42, 62, 42)
+
+    px = m_x * 20 + 4
+    py = m_y * 20 + 4
+
+    f0.canvas_draw_frame(px, py, 16, 16)
+
+    f0.canvas_set_text_align(f0.ALIGN_CENTER, f0.ALIGN_CENTER)
+
+    for x in range(0, 3):
+        for y in range(0, 3):
+            px = x * 20 + 10 + 2
+            py = y * 20 + 10 + 2
+            f0.canvas_set_text(px, py, m_grid[x][y])
+
+    f0.canvas_update()
+
+
+while not m_exit:
+    draw_grid()
+    time.sleep_ms(25)

+ 4 - 0
mp_flipper/examples/try_except_test.py

@@ -0,0 +1,4 @@
+try:
+    raise Exception("something went wrong")
+except Exception as e:
+    print(e)

+ 18 - 0
mp_flipper/examples/uart.py

@@ -0,0 +1,18 @@
+import time
+import flipperzero as f0
+
+
+def read_from_uart():
+    with f0.uart_open(f0.UART_MODE_USART, 115200) as uart:
+        for _ in range(1000):
+            raw_data = uart.read()
+
+            if len(raw_data) > 0:
+                data = raw_data.decode()
+
+                print(data)
+
+            time.sleep_ms(10)
+
+
+read_from_uart()

+ 14 - 0
mp_flipper/flipper.json

@@ -0,0 +1,14 @@
+{
+    "source": "examples",
+    "target": "/ext/examples",
+    "orphans": "ignore",
+    "include": [
+        "*.py"
+    ],
+    "exclude": [
+        "**.git**"
+    ],
+    "run": [
+        "loader open /ext/apps/Tools/upython.fap"
+    ]
+}

+ 6 - 0
mp_flipper/flipperzero/README.md

@@ -0,0 +1,6 @@
+# MicroPython on Flipper Zero
+
+This package contains the type definitions for the `flipperzero` [MicroPython](https://micropython.org/) module.
+Check out the [website](https://ofabel.github.io/mp-flipper/) for details.
+
+Get the interpreter application on [Flipper Lab](https://lab.flipper.net/apps/upython) to write and run your own Python files.

+ 30 - 0
mp_flipper/flipperzero/_adc.py

@@ -0,0 +1,30 @@
+def adc_read_pin_value(pin: int) -> int:
+    """
+    Read the raw value from the ADC channel.
+
+    :param pin: The pin to read (e.g. :const:`GPIO_PIN_PC1`).
+    :returns: The raw value between 0 and 4095.
+
+    .. versionadded:: 1.3.0
+
+    .. hint::
+
+        Don't forget to initialize the pin first.
+    """
+    pass
+
+
+def adc_read_pin_voltage(pin: int) -> float:
+    """
+    Read the voltage from the ADC channel.
+
+    :param pin: The pin to read (e.g. :const:`GPIO_PIN_PC1`).
+    :returns: The voltage between 0 - 2.048 V with a precision of ~0.1%.
+
+    .. versionadded:: 1.3.0
+
+    .. hint::
+
+        Don't forget to initialize the pin first.
+    """
+    pass

+ 242 - 0
mp_flipper/flipperzero/_canvas.py

@@ -0,0 +1,242 @@
+def canvas_update() -> None:
+    """
+    Updates the display buffer with your drawings from the canvas.
+
+    .. versionadded:: 1.0.0
+
+    .. note::
+
+        Your drawings will only appear on the display after this function call.
+    """
+    pass
+
+
+def canvas_clear() -> None:
+    """
+    Clear the whole canvas. This does not affect the current display buffer.
+    You need to call :func:`canvas_update` to reveal your changes.
+
+    .. versionadded:: 1.0.0
+    """
+    pass
+
+
+def canvas_width() -> int:
+    """
+    Get the canvas width in pixels.
+
+    .. versionadded:: 1.0.0
+
+    :returns: The canvas width.
+    """
+    pass
+
+
+def canvas_height() -> int:
+    """
+    Get the canvas height in pixels.
+
+    .. versionadded:: 1.0.0
+
+    :returns: The canvas height.
+    """
+    pass
+
+
+COLOR_BLACK: int
+"""
+Constant value for the color `black`.
+
+.. versionadded:: 1.0.0
+"""
+
+COLOR_WHITE: int
+"""
+Constant value for the color `white`.
+
+.. versionadded:: 1.0.0
+"""
+
+
+def canvas_set_color(color: int) -> None:
+    """
+    Set the color to use when drawing or writing on the canvas.
+
+    .. versionadded:: 1.0.0
+
+    :param color: The color to use.
+    """
+    pass
+
+
+ALIGN_BEGIN: int
+"""
+Align element at `begin` (horizontal or vertical, depends on the context).
+
+.. versionadded:: 1.0.0
+"""
+
+ALIGN_END: int
+"""
+Align element at `end` (horizontal or vertical, depends on the context).
+
+.. versionadded:: 1.0.0
+"""
+
+ALIGN_CENTER: int
+"""
+Align element at `center` (horizontal or vertical, depends on the context).
+
+.. versionadded:: 1.0.0
+"""
+
+
+def canvas_set_text_align(x: int, y: int) -> None:
+    """
+    Define how the text should be aligned in relation to the ``x`` and ``y`` coordinates
+    when writing on the canvas, using the :func:`canvas_set_text` function.
+
+    :param x: The horizontal alignment.
+    :param y: The vertical alignment.
+
+    .. versionadded:: 1.0.0
+    """
+    pass
+
+
+FONT_PRIMARY: int
+"""
+Constant value for the primary font.
+
+.. versionadded:: 1.0.0
+"""
+
+FONT_SECONDARY: int
+"""
+Constant value for the secondary font.
+
+.. versionadded:: 1.0.0
+"""
+
+
+def canvas_set_font(font: int) -> None:
+    """
+    Change the font to use when writing on the canvas using the :func:`canvas_set_text` function.
+
+    :param font: The font to use.
+
+    .. versionadded:: 1.0.0
+    """
+    pass
+
+
+def canvas_set_text(x: int, y: int, text: str) -> None:
+    """
+    Write text on the canvas at the position of ``x`` and ``y`` by using the currently active color, font and alignment settings.
+
+    :param x: The horizontal position.
+    :param y: The vertical position.
+    :param text: The text to write.
+
+    .. versionadded:: 1.0.0
+
+    .. code-block::
+
+        import flipperzero as f0
+
+        f0.canvas_set_color(f0.COLOR_BLACK)
+        f0.canvas_set_text_align(f0.ALIGN_CENTER, f0.ALIGN_BEGIN)
+        f0.canvas_set_text(64, 32, 'Hello World!')
+        f0.canvas_update()
+
+    .. seealso::
+
+        * :func:`canvas_set_color` to change the canvas color.
+        * :func:`canvas_set_text_align` to change the alignment settings.
+        * :func:`canvas_set_font` to change the current font.
+    """
+    pass
+
+
+def canvas_draw_dot(x: int, y: int) -> None:
+    """
+    Draw a dot on the canvas by using the currently active color settings.
+
+    :param x: The horizontal position.
+    :param y: The vertical position.
+
+    .. versionadded:: 1.0.0
+    """
+    pass
+
+
+def canvas_draw_box(x: int, y: int, w: int, h: int, r: int) -> None:
+    """
+    Draw a box on the canvas. The fill color is defined by the currently active color settings.
+    Set the corner radius to zero to draw a rectangle without rounded corners.
+
+    :param x: The horizontal position.
+    :param y: The vertical position.
+    :param w: The width of the box.
+    :param h: The height of the box.
+    :param r: The corner radius to use.
+
+    .. versionadded:: 1.0.0
+    """
+    pass
+
+
+def canvas_draw_frame(x: int, y: int, w: int, h: int, r: int) -> None:
+    """
+    Draw a frame on the canvas. The border color is defined by the currently active color settings.
+    Set the corner radius to zero to draw a rectangle without rounded corners.
+
+    :param x: The horizontal position.
+    :param y: The vertical position.
+    :param w: The width of the box.
+    :param h: The height of the box.
+    :param r: The corner radius to use.
+
+    .. versionadded:: 1.0.0
+    """
+    pass
+
+
+def canvas_draw_line(x0: int, y0: int, x1: int, y1: int) -> None:
+    """
+    Draw a line on the canvas. The color is defined by the currently active color settings.
+
+    :param x0: The horizontal start position.
+    :param y0: The vertical start position.
+    :param x1: The horizontal end position.
+    :param y1: The vertical end sposition.
+
+    .. versionadded:: 1.0.0
+    """
+    pass
+
+
+def canvas_draw_circle(x: int, y: int, r: int) -> None:
+    """
+    Draw a circle on the canvas. The border color is defined by the currently active color settings.
+
+    :param x: The horizontal position.
+    :param y: The vertical position.
+    :param r: The radius to use.
+
+    .. versionadded:: 1.0.0
+    """
+    pass
+
+
+def canvas_draw_disc(x: int, y: int, r: int) -> None:
+    """
+    Draw a disc on the canvas. The fill color is defined by the currently active color settings.
+
+    :param x: The horizontal position.
+    :param y: The vertical position.
+    :param r: The radius to use.
+
+    .. versionadded:: 1.0.0
+    """
+    pass

+ 64 - 0
mp_flipper/flipperzero/_dialog.py

@@ -0,0 +1,64 @@
+def dialog_message_set_header(text: str, x: int, y: int, h: int, v: int) -> None:
+    """
+    Set a header text on the dialog box.
+
+    :param text: The text to set.
+    :param x: The x coordinates to use.
+    :param y: The y coordinates to use.
+    :param h: The horizontal alignment.
+    :param v: The vertical alignment.
+
+    .. versionadded:: 1.0.0
+    """
+    pass
+
+
+def dialog_message_set_text(text: str, x: int, y: int, h: int, v: int) -> None:
+    """
+    Set a text on the dialog box.
+
+    :param text: The text to set.
+    :param x: The x coordinates to use.
+    :param y: The y coordinates to use.
+    :param h: The horizontal alignment.
+    :param v: The vertical alignment.
+
+    .. versionadded:: 1.0.0
+    """
+    pass
+
+
+def dialog_message_set_button(text: str, button: int) -> None:
+    """
+    Set the text of a dialog box button.
+
+    :param text: The text to set.
+    :param button: The button to use (e.g. :const:`INPUT_BUTTON_UP`).
+
+    .. versionadded:: 1.0.0
+    """
+    pass
+
+
+def dialog_message_show() -> int:
+    """
+    Display the dialog box with the configured settings.
+    This function is blocking.
+
+    :returns: The button code, used to close the dialog (e.g. :const:`INPUT_BUTTON_OK`)
+
+    .. versionadded:: 1.0.0
+
+    .. code-block::
+
+        import flipperzero as f0
+
+        f0.dialog_message_set_header('Important',64, 12)
+        f0.dialog_message_set_text('It this awesome?', 64, 24)
+        f0.dialog_message_set_button('Yes', f0.INPUT_BUTTON_LEFT)
+        f0.dialog_message_set_button('No', f0.INPUT_BUTTON_RIGHT)
+
+        while f0.dialog_message_show() is not f0.INPUT_BUTTON_LEFT:
+            pass
+    """
+    pass

+ 257 - 0
mp_flipper/flipperzero/_gpio.py

@@ -0,0 +1,257 @@
+from typing import Callable
+
+GPIO_PIN_PC0: int
+"""
+Constant identifier for GPIO pin PC0.
+
+* This pin can be used as ADC input.
+    
+.. versionadded:: 1.2.0
+"""
+
+GPIO_PIN_PC1: int
+"""
+Constant identifier for GPIO pin PC1.
+
+* This pin can be used as ADC input.
+    
+.. versionadded:: 1.2.0
+"""
+
+GPIO_PIN_PC3: int
+"""
+Constant identifier for GPIO pin PC3.
+
+* This pin can be used as ADC input.
+    
+.. versionadded:: 1.2.0
+"""
+
+GPIO_PIN_PB2: int
+"""
+Constant identifier for GPIO pin PB2.
+    
+.. versionadded:: 1.2.0
+"""
+
+GPIO_PIN_PB3: int
+"""
+Constant identifier for GPIO pin PB3.
+    
+.. versionadded:: 1.2.0
+"""
+
+GPIO_PIN_PA4: int
+"""
+Constant identifier for GPIO pin PA4.
+
+* This pin can be used as ADC input.
+* This pin can be used as PWM output.
+    
+.. versionadded:: 1.2.0
+"""
+
+GPIO_PIN_PA6: int
+"""
+Constant identifier for GPIO pin PA6.
+
+* This pin can be used as ADC input.
+    
+.. versionadded:: 1.2.0
+"""
+
+GPIO_PIN_PA7: int
+"""
+Constant identifier for GPIO pin PA7.
+
+* This pin can be used as ADC input.
+* This pin can be used as PWM output.
+* This pin can be used to transmit an infrared signal with an IR LED.
+
+    
+.. versionadded:: 1.2.0
+"""
+
+GPIO_MODE_INPUT: int
+"""
+Constant configuration value for the GPIO input mode.
+    
+.. versionadded:: 1.2.0
+"""
+
+GPIO_MODE_OUTPUT_PUSH_PULL: int
+"""
+Constant configuration value for the GPIO output as push-pull mode.
+    
+.. versionadded:: 1.2.0
+"""
+
+GPIO_MODE_OUTPUT_OPEN_DRAIN: int
+"""
+Constant configuration value for the GPIO output as open-drain mode.
+    
+.. versionadded:: 1.2.0
+"""
+
+GPIO_MODE_ANALOG: int
+"""
+Constant configuration value for the GPIO analog mode.
+    
+.. versionadded:: 1.2.0
+"""
+
+GPIO_MODE_INTERRUPT_RISE: int
+"""
+Constant configuration value for the GPIO interrupt on rising edges mode.
+    
+.. versionadded:: 1.2.0
+"""
+
+GPIO_MODE_INTERRUPT_FALL: int
+"""
+Constant configuration value for the GPIO interrupt on falling edges mode.
+    
+.. versionadded:: 1.2.0
+"""
+
+GPIO_PULL_NO: int
+"""
+Constant configuration value for the GPIO internal pull resistor disabled.
+    
+.. versionadded:: 1.2.0
+"""
+
+GPIO_PULL_UP: int
+"""
+Constant configuration value for the GPIO internal pull-up resistor enabled.
+    
+.. versionadded:: 1.2.0
+"""
+
+GPIO_PULL_DOWN: int
+"""
+Constant configuration value for the GPIO internal pull-down resistor enabled.
+    
+.. versionadded:: 1.2.0
+"""
+
+GPIO_SPEED_LOW: int
+"""
+Constant configuration value for the GPIO in low speed.
+    
+.. versionadded:: 1.2.0
+"""
+
+GPIO_SPEED_MEDIUM: int
+"""
+Constant configuration value for the GPIO in medium speed.
+    
+.. versionadded:: 1.2.0
+"""
+
+GPIO_SPEED_HIGH: int
+"""
+Constant configuration value for the GPIO in high speed.
+    
+.. versionadded:: 1.2.0
+"""
+
+GPIO_SPEED_VERY_HIGH: int
+"""
+Constant configuration value for the GPIO in very high speed.
+    
+.. versionadded:: 1.2.0
+"""
+
+
+def gpio_init_pin(pin: int, mode: int, pull: int = None, speed: int = None) -> bool:
+    """
+    Initialize a GPIO pin.
+
+    :param pin: The pin to initialize (e.g. :const:`GPIO_PIN_PA4`).
+    :param mode: The mode to use (e.g. :const:`GPIO_MODE_INPUT`).
+    :param pull: The pull resistor to use. Default is :const:`GPIO_PULL_NO`.
+    :param speed: The speed to use. Default is :const:`GPIO_SPEED_LOW`.
+    :returns: :const:`True` on success, :const:`False` otherwise.
+
+    .. versionadded:: 1.2.0
+    .. versionchanged:: 1.3.0
+       The return value changed from ``None`` to ``bool``.
+
+    .. hint::
+
+        The interrupt modes :const:`GPIO_MODE_INTERRUPT_RISE` and :const:`GPIO_MODE_INTERRUPT_FALL` can be combined using bitwise OR.
+        This allows you to handle rising `and` falling edges.
+    """
+    pass
+
+
+def gpio_deinit_pin(pin: int) -> None:
+    """
+    Deinitialize a GPIO pin.
+
+    :param pin: The pin to deinitialize (e.g. :const:`GPIO_PIN_PA4`).
+
+    .. versionadded:: 1.3.0
+
+    .. note::
+
+        It's not strictly necessary to deinitialize your GPIO pins upon script termination, this is already covered by the interpreter.
+    """
+    pass
+
+
+def gpio_set_pin(pin: int, state: bool) -> None:
+    """
+    Set the state of an output pin.
+
+    :param pin: The pin to set (e.g. :const:`GPIO_PIN_PA4`).
+    :param state: The state to set.
+
+    .. versionadded:: 1.2.0
+
+    .. hint::
+
+        Don't forget to initialize the pin first.
+    """
+    pass
+
+
+def gpio_get_pin(pin: int) -> bool:
+    """
+    Read the state of an input pin.
+
+    :param pin: The pin to read (e.g. :const:`GPIO_PIN_PA4`).
+    :returns: :const:`True` if the pin is high, :const:`False` on a low signal.
+
+    .. versionadded:: 1.2.0
+
+    .. hint::
+
+        Don't forget to initialize the pin first.
+    """
+    pass
+
+
+def on_gpio() -> Callable[[int], None]:
+    """
+    Decorate a function to be used as GPIO interrupt handler. The decorated function will be invoked upon a GPIO interrupt.
+
+    .. versionadded:: 1.0.0
+
+    .. code-block::
+
+        import flipperzero as f0
+
+        f0.gpio_init_pin(f0.GPIO_PIN_PC0, f0.GPIO_MODE_INTERRUPT_RISE, f0.GPIO_PULL_UP)
+
+        @f0.on_gpio
+        def interrupt_handler(pin):
+            if pin == f0.GPIO_PIN_PC0:
+                ...
+
+    .. warning::
+
+        You can only decorate one function per application.
+    """
+    pass

+ 52 - 0
mp_flipper/flipperzero/_infrared.py

@@ -0,0 +1,52 @@
+from typing import List
+
+
+def infrared_receive(timeout: int = 1000000) -> List[int]:
+    """
+    Receive an infrared signal. This is a blocking method.
+    The method blocks until a timeout occurs or the internal
+    signal buffer (capacity is 1024 timings) is filled.
+
+    :param timeout: The timeout to use in microseconds.
+    :returns: A list of timings in microseconds, starting with high.
+
+    .. versionadded:: 1.3.0
+    """
+    pass
+
+
+def infrared_transmit(
+    signal: List[int],
+    repeat: int = 1,
+    use_external_pin: bool = False,
+    frequency: int = 38000,
+    duty: float = 0.33,
+) -> bool:
+    """
+    Transmit an infrared signal. This is a blocking method.
+    The method blocks until the whole signal is sent.
+    The signal list has the same format as the return value
+    of :func:`infrared_receive`. Hence you can directly re-send
+    a received signal without any further processing.
+
+    :param signal: The signal to use.
+    :param repeat: How many times the signal should be sent.
+    :param use_external_pin: :const:`True` to use an external IR LED on GPIO pin :const:`flipperzero.GPIO_PIN_PA7`.
+    :param frequency: The frequency to use for the PWM signal.
+    :param duty: The duty cycle to use for the PWM signal.
+    :returns: :const:`True` on success, :const:`False` otherwise.
+
+    .. versionadded:: 1.3.0
+    """
+    pass
+
+
+def infrared_is_busy() -> bool:
+    """
+    Check if the infrared subsystem is busy.
+
+    :returns: :const:`True` if occupied, :const:`False` otherwise.
+
+    .. versionadded:: 1.3.0
+    """
+    pass

+ 101 - 0
mp_flipper/flipperzero/_input.py

@@ -0,0 +1,101 @@
+from typing import Callable
+
+INPUT_BUTTON_UP: int
+"""
+Constant value for the `up` button.
+
+.. versionadded:: 1.0.0
+"""
+
+INPUT_BUTTON_DOWN: int
+"""
+Constant value for the `down` button.
+
+.. versionadded:: 1.0.0
+"""
+
+INPUT_BUTTON_RIGHT: int
+"""
+Constant value for the `right` button.
+
+.. versionadded:: 1.0.0
+"""
+
+INPUT_BUTTON_LEFT: int
+"""
+Constant value for the `left` button.
+
+.. versionadded:: 1.0.0
+"""
+
+INPUT_BUTTON_OK: int
+"""
+Constant value for the `ok` button.
+
+.. versionadded:: 1.0.0
+"""
+
+INPUT_BUTTON_BACK: int
+"""
+Constant value for the `back` button.
+
+.. versionadded:: 1.0.0
+"""
+
+INPUT_TYPE_PRESS: int
+"""
+Constant value for the `press` event of a button.
+
+.. versionadded:: 1.0.0
+"""
+
+INPUT_TYPE_RELEASE: int
+"""
+Constant value for the `release` event of a button.
+
+.. versionadded:: 1.0.0
+"""
+
+INPUT_TYPE_SHORT: int
+"""
+Constant value for the `short` press event of a button.
+
+.. versionadded:: 1.0.0
+"""
+
+INPUT_TYPE_LONG: int
+"""
+Constant value for the `long` press event of a button.
+
+.. versionadded:: 1.0.0
+"""
+
+INPUT_TYPE_REPEAT: int
+"""
+Constant value for the `repeat` press event of a button.
+
+.. versionadded:: 1.0.0
+"""
+
+
+def on_input() -> Callable[[int, int], None]:
+    """
+    Decorate a function to be used as input handler. The decorated function will be invoked upon interaction with one of the buttons on the Flipper.
+
+    .. versionadded:: 1.0.0
+
+    .. code-block::
+
+        import flipperzero as f0
+
+        @f0.on_input
+        def input_handler(button, type):
+            if button == f0.INPUT_BUTTON_BACK:
+                if type == f0.INPUT_TYPE_LONG:
+                    ...
+
+    .. warning::
+
+        You can only decorate one function per application.
+    """
+    pass

+ 95 - 0
mp_flipper/flipperzero/_light.py

@@ -0,0 +1,95 @@
+from typing import Callable
+
+LIGHT_RED: int
+"""
+Constant value for the red LED light.
+
+.. versionadded:: 1.0.0
+"""
+
+LIGHT_GREEN: int
+"""
+Constant value for the green LED light.
+
+.. versionadded:: 1.0.0
+"""
+
+LIGHT_BLUE: int
+"""
+Constant value for the blue LED light.
+
+.. versionadded:: 1.0.0
+"""
+
+LIGHT_BACKLIGHT: int
+"""
+Constant value for the display backlight.
+
+.. versionadded:: 1.0.0
+"""
+
+
+def light_set(light: int, brightness: int) -> None:
+    """
+    Control the RGB LED on your Flipper. You can also set the brightness of multiple channels at once using bitwise operations.
+    The ``brightness`` parameter accepts values from 0 (light off) to 255 (very bright).
+
+    :param light: The RGB channels to set.
+    :param brightness: The brightness to use.
+
+    .. versionadded:: 1.0.0
+
+    .. code-block::
+
+        import flipperzero as f0
+
+        f0.light_set(f0.LIGHT_RED | f0.LIGHT_GREEN, 250)
+
+    .. tip::
+
+        You can use  up to seven colors using `additive mixing <https://en.wikipedia.org/wiki/Additive_color>`_.
+    """
+    pass
+
+
+def light_blink_start(light: int, brightness: int, on_time: int, period: int) -> None:
+    """
+    Let the RGB LED blink. You can define the total duration of a blink period and the duration, the LED is active during a blink period.
+    Hence, ``on_time`` must be smaller than ``period``. This is a non-blocking operation. The LED will continue to blink until you call :func:`light_blink_stop`.
+
+    :param light: The RGB channels to set.
+    :param brightness: The brightness to use.
+    :param on_time: The LED's active duration in milliseconds.
+    :param period: Total duration of a blink period in milliseconds.
+
+    .. versionadded:: 1.0.0
+
+    .. code-block::
+
+        import flipperzero as f0
+
+        f0.light_blink_start(f0.LIGHT_RED, 150, 100, 200)
+    """
+    pass
+
+
+def light_blink_set_color(light: int) -> None:
+    """
+    Change the RGB LED's color while blinking. This is a non-blocking operation.
+    Be aware, that you must start the blinking procedure first by using the :func:`light_blink_start` function.
+    Call the :func:`light_blink_stop` function to stop the blinking LED.
+
+    :param light: The RGB channels to set.
+
+    .. versionadded:: 1.0.0
+    """
+    pass
+
+
+def light_blink_stop() -> None:
+    """
+    Stop the blinking LED.
+
+    .. versionadded:: 1.0.0
+    """
+    pass

+ 39 - 0
mp_flipper/flipperzero/_pwm.py

@@ -0,0 +1,39 @@
+def pwm_start(pin: int, frequency: int, duty: int) -> bool:
+    """
+    Start or change the PWM signal on the corresponding GPIO pin.
+
+    :param pin: The pin to read (e.g. :const:`GPIO_PIN_PA7`).
+    :param frequency: The frequency to set in Hz.
+    :param duty: The duty cycle per period in percent.
+    :returns: :const:`True` on success, :const:`False` otherwise.
+
+    .. versionadded:: 1.3.0
+
+    .. warning::
+
+        You don't have to initialize the pin first.
+    """
+    pass
+
+
+def pwm_stop(pin: int) -> None:
+    """
+    Stop the PWM signal on the corresponding GPIO pin.
+
+    :param pin: The pin to use (e.g. :const:`GPIO_PIN_PA7`).
+
+    .. versionadded:: 1.3.0
+    """
+    pass
+
+
+def pwm_is_running(pin: int) -> bool:
+    """
+    Check if the corresponding GPIO pin has a PWM signal output.
+
+    :param pin: The pin to check (e.g. :const:`GPIO_PIN_PA7`).
+    :returns: :const:`True` on success, :const:`False` otherwise.
+
+    .. versionadded:: 1.3.0
+    """
+    pass

+ 854 - 0
mp_flipper/flipperzero/_speaker.py

@@ -0,0 +1,854 @@
+"""
+Python script for notes generation
+
+# coding: utf-8
+# Python script for notes generation
+
+from typing import List
+
+note_names: List = ['C', 'CS', 'D', 'DS', 'E', 'F', 'FS', 'G', 'GS', 'A', 'AS', 'B']
+
+for octave in range(9):
+    for name in note_names:
+        print("SPEAKER_NOTE_%s%s: float" % (name, octave))
+        print('\'\'\'')
+        print('The musical note %s\\ :sub:`0` as frequency in `Hz`.\n' % (name if len(name) == 1 else (name[0]+'#')))
+        print('.. versionadded:: 1.2.0')
+        print('\'\'\'\n')
+"""
+
+SPEAKER_NOTE_C0: float
+"""
+The musical note C\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_CS0: float
+"""
+The musical note C#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_D0: float
+"""
+The musical note D\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_DS0: float
+"""
+The musical note D#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_E0: float
+"""
+The musical note E\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_F0: float
+"""
+The musical note F\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_FS0: float
+"""
+The musical note F#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_G0: float
+"""
+The musical note G\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_GS0: float
+"""
+The musical note G#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_A0: float
+"""
+The musical note A\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_AS0: float
+"""
+The musical note A#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_B0: float
+"""
+The musical note B\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_C1: float
+"""
+The musical note C\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_CS1: float
+"""
+The musical note C#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_D1: float
+"""
+The musical note D\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_DS1: float
+"""
+The musical note D#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_E1: float
+"""
+The musical note E\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_F1: float
+"""
+The musical note F\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_FS1: float
+"""
+The musical note F#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_G1: float
+"""
+The musical note G\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_GS1: float
+"""
+The musical note G#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_A1: float
+"""
+The musical note A\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_AS1: float
+"""
+The musical note A#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_B1: float
+"""
+The musical note B\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_C2: float
+"""
+The musical note C\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_CS2: float
+"""
+The musical note C#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_D2: float
+"""
+The musical note D\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_DS2: float
+"""
+The musical note D#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_E2: float
+"""
+The musical note E\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_F2: float
+"""
+The musical note F\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_FS2: float
+"""
+The musical note F#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_G2: float
+"""
+The musical note G\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_GS2: float
+"""
+The musical note G#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_A2: float
+"""
+The musical note A\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_AS2: float
+"""
+The musical note A#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_B2: float
+"""
+The musical note B\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_C3: float
+"""
+The musical note C\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_CS3: float
+"""
+The musical note C#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_D3: float
+"""
+The musical note D\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_DS3: float
+"""
+The musical note D#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_E3: float
+"""
+The musical note E\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_F3: float
+"""
+The musical note F\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_FS3: float
+"""
+The musical note F#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_G3: float
+"""
+The musical note G\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_GS3: float
+"""
+The musical note G#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_A3: float
+"""
+The musical note A\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_AS3: float
+"""
+The musical note A#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_B3: float
+"""
+The musical note B\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_C4: float
+"""
+The musical note C\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_CS4: float
+"""
+The musical note C#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_D4: float
+"""
+The musical note D\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_DS4: float
+"""
+The musical note D#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_E4: float
+"""
+The musical note E\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_F4: float
+"""
+The musical note F\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_FS4: float
+"""
+The musical note F#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_G4: float
+"""
+The musical note G\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_GS4: float
+"""
+The musical note G#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_A4: float
+"""
+The musical note A\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_AS4: float
+"""
+The musical note A#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_B4: float
+"""
+The musical note B\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_C5: float
+"""
+The musical note C\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_CS5: float
+"""
+The musical note C#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_D5: float
+"""
+The musical note D\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_DS5: float
+"""
+The musical note D#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_E5: float
+"""
+The musical note E\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_F5: float
+"""
+The musical note F\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_FS5: float
+"""
+The musical note F#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_G5: float
+"""
+The musical note G\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_GS5: float
+"""
+The musical note G#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_A5: float
+"""
+The musical note A\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_AS5: float
+"""
+The musical note A#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_B5: float
+"""
+The musical note B\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_C6: float
+"""
+The musical note C\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_CS6: float
+"""
+The musical note C#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_D6: float
+"""
+The musical note D\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_DS6: float
+"""
+The musical note D#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_E6: float
+"""
+The musical note E\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_F6: float
+"""
+The musical note F\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_FS6: float
+"""
+The musical note F#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_G6: float
+"""
+The musical note G\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_GS6: float
+"""
+The musical note G#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_A6: float
+"""
+The musical note A\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_AS6: float
+"""
+The musical note A#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_B6: float
+"""
+The musical note B\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_C7: float
+"""
+The musical note C\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_CS7: float
+"""
+The musical note C#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_D7: float
+"""
+The musical note D\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_DS7: float
+"""
+The musical note D#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_E7: float
+"""
+The musical note E\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_F7: float
+"""
+The musical note F\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_FS7: float
+"""
+The musical note F#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_G7: float
+"""
+The musical note G\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_GS7: float
+"""
+The musical note G#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_A7: float
+"""
+The musical note A\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_AS7: float
+"""
+The musical note A#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_B7: float
+"""
+The musical note B\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_C8: float
+"""
+The musical note C\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_CS8: float
+"""
+The musical note C#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_D8: float
+"""
+The musical note D\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_DS8: float
+"""
+The musical note D#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_E8: float
+"""
+The musical note E\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_F8: float
+"""
+The musical note F\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_FS8: float
+"""
+The musical note F#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_G8: float
+"""
+The musical note G\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_GS8: float
+"""
+The musical note G#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_A8: float
+"""
+The musical note A\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_AS8: float
+"""
+The musical note A#\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_NOTE_B8: float
+"""
+The musical note B\ :sub:`0` as frequency in `Hz`.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_VOLUME_MIN: float
+"""
+The minimal volume value.
+
+.. versionadded:: 1.2.0
+"""
+
+SPEAKER_VOLUME_MAX: float
+"""
+The maximum volume value.
+
+.. versionadded:: 1.2.0
+"""
+
+
+def speaker_start(frequency: float, volume: float) -> bool:
+    """
+    Output a steady tone of a defined frequency and volume on the Flipper's speaker.
+    This is a non-blocking operation. The tone will continue until you call :func:`speaker_stop`.
+    The ``volume`` parameter accepts values from :py:const:`SPEAKER_VOLUME_MIN` (silent) up to :py:const:`SPEAKER_VOLUME_MAX` (very loud).
+
+    :param frequency: The frequency to play in `Hz <https://en.wikipedia.org/wiki/Hertz>`_.
+    :param volume: The volume to use.
+    :returns: :const:`True` if the speaker was acquired.
+
+    .. versionadded:: 1.0.0
+
+    .. code-block::
+
+        import flipperzero as f0
+
+        f0.speaker_start(50.0, 0.8)
+    """
+    pass
+
+
+def speaker_set_volume(volume: float) -> bool:
+    """
+    Set the speaker's volume while playing a tone. This is a non-blocking operation.
+    The tone will continue until you call :func:`speaker_stop`.
+    The ``volume`` parameter accepts values from 0.0 (silent) up to 1.0 (very loud).
+
+    :param volume: The volume to use.
+    :returns: :const:`True` if the speaker was acquired.
+
+    .. versionadded:: 1.0.0
+
+    This function can be used to play `nice` sounds:
+
+    .. code-block::
+
+        import time
+        import flipperzero as f0
+
+        volume = 0.8
+
+        f0.speaker_start(100.0, volume)
+
+        for _ in range(0, 150):
+            volume *= 0.9945679
+
+            f0.speaker_set_volume(volume)
+
+            time.sleep_ms(1)
+
+        f0.speaker_stop()
+    """
+    pass
+
+
+def speaker_stop() -> bool:
+    """
+    Stop the speaker output.
+
+    :returns: :const:`True` if the speaker was successfully released.
+
+    .. versionadded:: 1.0.0
+    """
+    pass

+ 166 - 0
mp_flipper/flipperzero/_uart.py

@@ -0,0 +1,166 @@
+from typing import List
+
+UART_MODE_LPUART: int
+"""
+Constant value for the low power UART mode.
+
+.. versionadded:: 1.5.0
+"""
+
+UART_MODE_USART: int
+"""
+Constant value for the USART mode.
+
+.. versionadded:: 1.5.0
+"""
+
+
+class UART:
+    """
+    This represents an UART connection.
+    The class has no :const:`__init__` method, use :func:`uart_open` to start an UART connection and receive an instance.
+
+    .. versionadded:: 1.5.0
+
+    An :class:`UART` instance is iterable:
+
+    .. code-block::
+
+        import flipperzero as f0
+
+        with f0.open(f0.UART_MODE_USART, 115200) as uart:
+            lines = [line for line in uart]
+
+    An :class:`UART` instance can be used with a `context manager <https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers>`_:
+
+    .. code-block::
+
+        import flipperzero as f0
+
+        with f0.open(f0.UART_MODE_USART, 115200) as uart:
+            ...
+
+    .. hint::
+
+        The read and write methods are non-blocking in terms of data availability.
+        They don't block code execution upon data is available.
+        Just an empty result will be returned.
+    """
+
+    def read(self, size: int = -1) -> bytes:
+        """
+        Read from the connection.
+        The method will read up to ``size`` bytes and return them.
+        If ``size`` is not specified, all available data will be returned.
+        The method will return zero bytes, if no data is available.
+
+        :param size: The maximum number of bytes to read.
+        :returns: Up to ``size`` bytes.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def readline(self, size: int = -1) -> bytes:
+        """
+        Read and return one line from the connection.
+        If ``size`` is specified, at most ``size`` bytes will be read.
+        The line terminator is always ``b'\\n'``.
+
+        :param size: The maximum number of bytes to read.
+        :returns: Up to ``size`` bytes.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def readlines(self) -> List[bytes]:
+        """
+        Read and return a list of lines from the connection.
+        The line terminator is always ``b'\\n'``.
+
+        :returns: A list of bytes.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def write(self, data: bytes) -> int:
+        """
+        Write the given bytes to the connection stream.
+        The number of written bytes will be returned.
+        This can be less than the length of the provided data.
+        Be aware, that the data is not sent synchronously.
+        Call :meth:`flush` if you have to wait for the data to be sent.
+
+        :param data: The data to transmit.
+        :returns: The number of bytes sent.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def flush(self) -> None:
+        """
+        Flush the transmission buffer to the underlying UART connection.
+        This method blocks until all data is sent.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def close(self) -> None:
+        """
+        Close the UART connection.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def __enter__(self) -> "UART":
+        """
+        This method is invoked, when the instance enters a runtime context.
+
+        :returns: The :class:`UART` connection.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def __exit__(self, *args, **kwargs) -> None:
+        """
+        This method is invoked, when the instance leavs a runtime context.
+        This basically calls :meth:`close` on the instance.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def __del__(self) -> None:
+        """
+        This method is invoked, when the garbage collector removes the object.
+        This basically calls :meth:`close` on the instance.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+
+def uart_open(mode: int, baud_rate: int) -> UART:
+    """
+    Open a connection to an UART enabled device by using the specified mode and baud rate.
+
+    :param mode: The mode to use, either :const:`UART_MODE_LPUART` or :const:`UART_MODE_USART`.
+    :param baud_rate: The baud rate to use.
+    :returns: A :class:`UART` object on success, :const:`None` otherwise.
+
+    .. versionadded:: 1.5.0
+
+    .. code-block::
+
+        import flipperzero as f0
+
+        with f0.uart_open(f0.UART_MODE_USART, 115200) as uart:
+            ...
+    """
+    pass

+ 10 - 0
mp_flipper/flipperzero/_vibro.py

@@ -0,0 +1,10 @@
+def vibro_set(state: bool) -> bool:
+    """
+    Turn vibration on or off. This is a non-blocking operation. The vibration motor will continue to run until you stop it.
+
+    :param state: :const:`True` to turn on vibration.
+    :returns: :const:`True` if vibration is on.
+
+    .. versionadded:: 1.0.0
+    """
+    pass

+ 369 - 0
mp_flipper/flipperzero/io.py

@@ -0,0 +1,369 @@
+import typing
+import io
+
+_open = io.open
+
+SEEK_SET: int = 0
+"""
+Set the pointer position relative to the beginning of the stream.
+
+.. versionadded:: 1.5.0
+"""
+
+SEEK_CUR: int = 1
+"""
+Set the pointer position relative to the current position.
+
+.. versionadded:: 1.5.0
+"""
+
+SEEK_END: int = 2
+"""
+Set the pointer position relative to the end of the stream.
+
+.. versionadded:: 1.5.0
+"""
+
+
+class BinaryFileIO:
+    """
+    Represents a file, opened in binary mode.
+
+    .. versionadded:: 1.5.0
+    """
+
+    name: str
+    """
+    The name of the file.
+
+    .. versionadded:: 1.5.0
+    """
+
+    readable: bool
+    """
+    Read-only attribute, indicating if the file is readable.
+    
+    .. versionadded:: 1.5.0
+    """
+
+    writable: bool
+    """
+    Read-only attribute, indicating if the file is writable.
+    
+    .. versionadded:: 1.5.0
+    """
+
+    def read(self, size: int = -1) -> bytes:
+        """
+        Read from the file.
+        The method will read up to ``size`` bytes and return them.
+        If ``size`` is not specified, all content up to EOF will be returned.
+        If the internal pointer is already at EOF, an empty byte string ``b''`` will be returned.
+
+        :param size: The maximum number of bytes to read.
+        :returns: Up to ``size`` bytes.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def readline(self, size: int = -1) -> bytes:
+        """
+        Read and return one line from the file.
+        If ``size`` is specified, at most ``size`` bytes will be read.
+        The line terminator is defined as ``b'\\n'``.
+        The new line character is included in the return value.
+        If the internal pointer is at EOF, an empty byte string ``b''`` will be returned.
+
+        :param size: The maximum number of bytes to read.
+        :returns: Up to ``size`` bytes.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def readlines(self) -> typing.List[bytes]:
+        """
+        Read and return a list of lines from the file.
+        The line terminator is defined as ``b'\\n'``.
+        The new line character is included in the return value.
+        If the internal pointer is at EOF, an empty list will be returned.
+
+        :returns: A list of bytes.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def write(self, data: bytes) -> int:
+        """
+        Write the given bytes to the file.
+        The number of written bytes will be returned.
+        This can be less than the length of the provided data.
+
+        :param data: The data to write.
+        :returns: The number of bytes written.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def flush(self) -> None:
+        """
+        Write the contents of the file buffer to the file on the SD card.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def seek(self, offset: int, whence: int = SEEK_SET) -> int:
+        """
+        Set the pointer position by the given ``offset``, relative to the position indicated by ``whence``.
+        The new absolute position will be returned.
+
+        :param offset: The offset to use.
+        :param whence: How to interpret the offset (e.g. :const:`SEEK_SET`).
+        :returns: The new absolute position.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def tell(self) -> int:
+        """
+        Get the current pointer position.
+
+        :returns: The absolute position of the pointer.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def close(self) -> None:
+        """
+        Close the file handle.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def __enter__(self) -> "BinaryFileIO":
+        """
+        This method is invoked, when the instance enters a runtime context.
+
+        :returns: The :class:`BinaryFileIO` instance.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def __exit__(self, *args, **kwargs) -> None:
+        """
+        This method is invoked, when the instance leavs a runtime context.
+        This basically calls :meth:`close` on the instance.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def __del__(self) -> None:
+        """
+        This method is invoked, when the garbage collector removes the object.
+        This basically calls :meth:`close` on the instance.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+
+class TextFileIO:
+    """
+    Represents a file, opened in text mode.
+
+    .. versionadded:: 1.5.0
+    """
+
+    name: str
+    """
+    The name of the file.
+
+    .. versionadded:: 1.5.0
+    """
+
+    readable: bool
+    """
+    Read-only attribute, indicating if the file is readable.
+    
+    .. versionadded:: 1.5.0
+    """
+
+    writable: bool
+    """
+    Read-only attribute, indicating if the file is writable.
+    
+    .. versionadded:: 1.5.0
+    """
+
+    def read(self, size: int = -1) -> str:
+        """
+        Read from the file.
+        The method will read up to ``size`` characters and return them.
+        If ``size`` is not specified, all content up to EOF will be returned.
+        If the internal pointer is already at EOF, an empty string will be returned.
+
+        :param size: The maximum number of characters to read.
+        :returns: Up to ``size`` characters.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def readline(self, size: int = -1) -> str:
+        """
+        Read and return one line from the file.
+        If ``size`` is specified, at most ``size`` characters will be read.
+        The line terminator is defined as ``'\\n'``.
+        The new line character is included in the return value.
+        If the internal pointer is at EOF, an empty string will be returned.
+
+        :param size: The maximum number of characters to read.
+        :returns: Up to ``size`` characters.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def readlines(self) -> typing.List[str]:
+        """
+        Read and return a list of lines from the file.
+        The line terminator is defined as ``'\\n'``.
+        The new line character is included in the return value.
+        If the internal pointer is at EOF, an empty list will be returned.
+
+        :returns: A list of strings.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def write(self, data: str) -> int:
+        """
+        Write the given string to the file.
+        The number of written characters will be returned.
+        This can be less than the length of the provided data.
+
+        :param data: The data to write.
+        :returns: The number of characters written.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def flush(self) -> None:
+        """
+        Write the contents of the file buffer to the file on the SD card.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def seek(self, offset: int, whence: int = SEEK_SET) -> int:
+        """
+        Set the pointer position by the given ``offset``, relative to the position indicated by ``whence``.
+        The new absolute position will be returned.
+
+        :param offset: The offset to use.
+        :param whence: How to interpret the offset (e.g. :const:`SEEK_SET`).
+        :returns: The new absolute position.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def tell(self) -> int:
+        """
+        Get the current pointer position.
+
+        :returns: The absolute position of the pointer.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def close(self) -> None:
+        """
+        Close the file handle.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def __enter__(self) -> "TextFileIO":
+        """
+        This method is invoked, when the instance enters a runtime context.
+
+        :returns: The :class:`BinaryFileIO` instance.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def __exit__(self, *args, **kwargs) -> None:
+        """
+        This method is invoked, when the instance leavs a runtime context.
+        This basically calls :meth:`close` on the instance.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+    def __del__(self) -> None:
+        """
+        This method is invoked, when the garbage collector removes the object.
+        This basically calls :meth:`close` on the instance.
+
+        .. versionadded:: 1.5.0
+        """
+        pass
+
+
+def open(path: str, mode: str, *args, **kwargs) -> BinaryFileIO | TextFileIO:
+    """
+    Open a file on the file system with the specified mode.
+    The file path must always be absolute, beginning with ``/ext``.
+    The following modifiers are available:
+
+    .. list-table::
+        :header-rows: 1
+        :width: 90%
+
+        * - Character
+          - Description
+        * - ``'r'``
+          - Open for reading.
+            This is the default.
+            Will fail, if the file not exists.
+        * - ``'w'``
+          - Open for writing, truncating an existing file first.
+        * - ``'b'``
+          - Open the file in binary mode.
+            The return value will be a :class:`BinaryFileIO` instance.
+        * - ``'t'``
+          - Open the in text mode.
+            This is the default.
+            The return value will be a :class:`TextFileIO` instance.
+        * - ``'+'``
+          - Open for reading and writing.
+            Will create the file, if it not exists.
+            The pointer will be placed at the end of the file.
+
+    The modifiers can be combined, e.g. ``'rb+'`` would open a file for reading and writing in binary mode.
+
+    :param path: The path to the file to open.
+    :param mode: How the file should be opened.
+    :param args: Is ignored at the moment.
+    :param kwargs: Is ignored at the moment.
+
+    .. versionadded:: 1.5.0
+    """
+    return io._open(path, mode, *args, **kwargs)

+ 170 - 0
mp_flipper/flipperzero/logging.py

@@ -0,0 +1,170 @@
+import typing
+
+TRACE: int = 6
+"""
+Constant value for the `trace` log level.
+
+.. versionadded:: 1.5.0
+"""
+
+DEBUG: int = 5
+"""
+Constant value for the `debug` log level.
+
+.. versionadded:: 1.5.0
+"""
+
+INFO: int = 4
+"""
+Constant value for the `info` log level.
+
+.. versionadded:: 1.5.0
+"""
+
+WARN: int = 3
+"""
+Constant value for the `warn` log level.
+
+.. versionadded:: 1.5.0
+"""
+
+ERROR: int = 2
+"""
+Constant value for the `error` log level.
+
+.. versionadded:: 1.5.0
+"""
+
+NONE: int = 1
+"""
+Constant value for logging disabled.
+
+.. versionadded:: 1.5.0
+"""
+
+level: int
+"""
+The threshold log level, as set by the :func:`setLevel` function.
+The initial value is set to the :const:`INFO` level.
+
+.. versionadded:: 1.5.0
+
+.. hint::
+
+    Don't change the value of this variable, use :func:`setLevel` instead.
+"""
+
+
+def setLevel(level: int) -> None:
+    """
+    Set the current log level of the application.
+
+    :param level: The log level to set (e.g. :const:`INFO`).
+
+    .. versionadded:: 1.5.0
+
+    .. hint::
+
+        This doesn't change the Flipper's effective log level settings.
+        Check out the Flipper's `documentation <https://docs.flipper.net/basics/settings#d5TAt>`_ for details on this topic.
+    """
+    pass
+
+
+def getEffectiveLevel() -> int:
+    """
+    Get the effective log level from the Flipper's settings.
+
+    :returns: The effective log level.
+
+    .. versionadded:: 1.5.0
+    """
+    pass
+
+
+def trace(message: str, *args) -> None:
+    """
+    Log a message with level :const:`TRACE`.
+    The ``message`` argument can be a format string with ``%`` placeholders.
+    No % formatting operation is performed when ``args`` is empty.
+
+    :param message: The message to log.
+    :param args: Values for the % formatting.
+
+    .. versionadded:: 1.5.0
+
+    .. code-block::
+
+        import logging
+
+        value = 42
+
+        logging.trace('value is %d', value)
+    """
+    pass
+
+
+def debug(message: str, *args) -> None:
+    """
+    Log a message with level :const:`DEBUG`.
+    See :func:`trace` for details on the usage.
+
+    :param message: The message to log.
+    :param args: Values for the % formatting.
+
+    .. versionadded:: 1.5.0
+    """
+    pass
+
+
+def info(message: str, *args) -> None:
+    """
+    Log a message with level :const:`INFO`.
+    See :func:`trace` for details on the usage.
+
+    :param message: The message to log.
+    :param args: Values for the % formatting.
+
+    .. versionadded:: 1.5.0
+    """
+    pass
+
+
+def warn(message: str, *args) -> None:
+    """
+    Log a message with level :const:`WARN`.
+    See :func:`trace` for details on the usage.
+
+    :param message: The message to log.
+    :param args: Values for the % formatting.
+
+    .. versionadded:: 1.5.0
+    """
+    pass
+
+
+def error(message: str, *args) -> None:
+    """
+    Log a message with level :const:`ERROR`.
+    See :func:`trace` for details on the usage.
+
+    :param message: The message to log.
+    :param args: Values for the % formatting.
+
+    .. versionadded:: 1.5.0
+    """
+    pass
+
+
+def log(level: int, message: str, *args) -> None:
+    """
+    Log a message with the given log level.
+    See :func:`trace` for details on the usage.
+
+    :param level: The log level to use (e.g. :const:`INFO`).
+    :param message: The message to log.
+    :param args: Values for the % formatting.
+
+    .. versionadded:: 1.5.0
+    """
+    pass

二进制
mp_flipper/icon.png


二进制
mp_flipper/images/ButtonCenter_7x7.png


二进制
mp_flipper/images/Pin_back_arrow_10x8.png


二进制
mp_flipper/images/qrcode.png


二进制
mp_flipper/images/splash.png


+ 49 - 0
mp_flipper/lib/micropython-port/mp_flipper_context.h

@@ -0,0 +1,49 @@
+#include <stdint.h>
+
+#include <furi.h>
+#include <gui/gui.h>
+#include <dialogs/dialogs.h>
+#include <storage/storage.h>
+
+#include <mp_flipper_modflipperzero.h>
+
+#define MP_FLIPPER_GPIO_PIN_OFF     (1 << 15)
+#define MP_FLIPPER_GPIO_PIN_BLOCKED (1 << 7)
+#define MP_FLIPPER_GPIO_PIN_PWM     ((MP_FLIPPER_GPIO_PIN_BLOCKED) | (1 << 8))
+
+typedef uint16_t mp_flipper_gpio_pin_t;
+
+#define MP_FLIPPER_INFRARED_RX_BUFFER_SIZE (1024)
+
+typedef struct {
+    uint16_t size;
+    uint32_t* buffer;
+    uint16_t pointer;
+    bool running;
+} mp_flipper_infrared_rx_t;
+
+typedef struct {
+    size_t size;
+    void* signal;
+    size_t index;
+    uint32_t repeat;
+    bool level;
+    mp_flipper_infrared_signal_tx_provider provider;
+} mp_flipper_infrared_tx_t;
+
+typedef struct {
+    Gui* gui;
+    ViewPort* view_port;
+    Canvas* canvas;
+    FuriPubSub* input_event_queue;
+    FuriPubSubSubscription* input_event;
+    DialogMessage* dialog_message;
+    const char* dialog_message_button_left;
+    const char* dialog_message_button_center;
+    const char* dialog_message_button_right;
+    Storage* storage;
+    FuriHalAdcHandle* adc_handle;
+    mp_flipper_gpio_pin_t* gpio_pins;
+    mp_flipper_infrared_rx_t* infrared_rx;
+    mp_flipper_infrared_tx_t* infrared_tx;
+} mp_flipper_context_t;

+ 59 - 0
mp_flipper/lib/micropython-port/mp_flipper_file_helper.c

@@ -0,0 +1,59 @@
+#include <mp_flipper_runtime.h>
+#include <mp_flipper_halport.h>
+
+#include <furi.h>
+#include <storage/storage.h>
+
+#include <py/mperrno.h>
+
+#include "mp_flipper_context.h"
+
+mp_flipper_import_stat_t mp_flipper_try_resolve_filesystem_path(FuriString* path) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    const char* path_str = furi_string_get_cstr(path);
+    FuriString* _path = furi_string_alloc_printf("%s", path_str);
+
+    mp_flipper_import_stat_t stat = MP_FLIPPER_IMPORT_STAT_FILE;
+
+    FS_Error error;
+    FileInfo info;
+
+    do {
+        // make path absolute
+        if(!furi_string_start_with_str(_path, "/")) {
+            furi_string_printf(_path, "%s/%s", mp_flipper_root_module_path, path_str);
+        }
+
+        // check if file or folder exists
+        error = storage_common_stat(ctx->storage, furi_string_get_cstr(_path), &info);
+        if(error == FSE_OK) {
+            break;
+        }
+
+        // check for existing python file
+        furi_string_cat_str(_path, ".py");
+
+        error = storage_common_stat(ctx->storage, furi_string_get_cstr(_path), &info);
+        if(error == FSE_OK) {
+            break;
+        }
+    } while(false);
+
+    // file or folder missing
+    if(error == FSE_NOT_EXIST) {
+        stat = MP_FLIPPER_IMPORT_STAT_NO_EXIST;
+    }
+    // abort on error
+    else if(error != FSE_OK) {
+        mp_flipper_raise_os_error_with_filename(MP_ENOENT, furi_string_get_cstr(path));
+    }
+    // path points to directory
+    else if((info.flags & FSF_DIRECTORY) == FSF_DIRECTORY) {
+        stat = MP_FLIPPER_IMPORT_STAT_DIR;
+    }
+
+    furi_string_move(path, _path);
+
+    return stat;
+}

+ 7 - 0
mp_flipper/lib/micropython-port/mp_flipper_file_helper.h

@@ -0,0 +1,7 @@
+#pragma once
+
+#include <mp_flipper_halport.h>
+
+#include <furi.h>
+
+mp_flipper_import_stat_t mp_flipper_try_resolve_filesystem_path(FuriString* path);

+ 77 - 0
mp_flipper/lib/micropython-port/mp_flipper_file_reader.c

@@ -0,0 +1,77 @@
+#include <stdio.h>
+
+#include <furi.h>
+#include <storage/storage.h>
+
+#include <py/mperrno.h>
+
+#include <mp_flipper_runtime.h>
+#include <mp_flipper_file_reader.h>
+
+#include "mp_flipper_context.h"
+#include "mp_flipper_file_helper.h"
+
+typedef struct {
+    size_t pointer;
+    FuriString* content;
+    size_t size;
+} FileDescriptor;
+
+inline void* mp_flipper_file_reader_context_alloc(const char* filename) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    FuriString* path = furi_string_alloc_printf("%s", filename);
+    File* file = storage_file_alloc(ctx->storage);
+    FileDescriptor* fd = NULL;
+
+    do {
+        if(mp_flipper_try_resolve_filesystem_path(path) == MP_FLIPPER_IMPORT_STAT_NO_EXIST) {
+            mp_flipper_raise_os_error_with_filename(MP_ENOENT, filename);
+
+            break;
+        }
+
+        if(!storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) {
+            mp_flipper_raise_os_error_with_filename(MP_ENOENT, filename);
+
+            break;
+        }
+
+        fd = malloc(sizeof(FileDescriptor));
+
+        fd->pointer = 0;
+        fd->content = furi_string_alloc();
+        fd->size = storage_file_size(file);
+
+        char character = '\0';
+
+        for(size_t i = 0; i < fd->size; i++) {
+            storage_file_read(file, &character, 1);
+
+            furi_string_push_back(fd->content, character);
+        }
+    } while(false);
+
+    storage_file_free(file);
+    furi_string_free(path);
+
+    return fd;
+}
+
+inline uint32_t mp_flipper_file_reader_read(void* data) {
+    FileDescriptor* fd = data;
+
+    if(fd->pointer >= fd->size) {
+        return MP_FLIPPER_FILE_READER_EOF;
+    }
+
+    return furi_string_get_char(fd->content, fd->pointer++);
+}
+
+void mp_flipper_file_reader_close(void* data) {
+    FileDescriptor* fd = data;
+
+    furi_string_free(fd->content);
+
+    free(data);
+}

+ 89 - 0
mp_flipper/lib/micropython-port/mp_flipper_fileio.c

@@ -0,0 +1,89 @@
+#include <furi.h>
+#include <storage/storage.h>
+
+#include <mp_flipper_fileio.h>
+#include <mp_flipper_runtime.h>
+
+#include "mp_flipper_context.h"
+#include "mp_flipper_file_helper.h"
+
+uint8_t MP_FLIPPER_FILE_ACCESS_MODE_READ = FSAM_READ;
+uint8_t MP_FLIPPER_FILE_ACCESS_MODE_WRITE = FSAM_WRITE;
+
+uint8_t MP_FLIPPER_FILE_OPEN_MODE_OPEN_EXIST = FSOM_OPEN_EXISTING;
+uint8_t MP_FLIPPER_FILE_OPEN_MODE_OPEN_ALWAYS = FSOM_OPEN_ALWAYS;
+uint8_t MP_FLIPPER_FILE_OPEN_MODE_OPEN_APPEND = FSOM_OPEN_APPEND;
+uint8_t MP_FLIPPER_FILE_OPEN_MODE_CREATE_NEW = FSOM_CREATE_NEW;
+uint8_t MP_FLIPPER_FILE_OPEN_MODE_CREATE_ALWAYS = FSOM_CREATE_ALWAYS;
+
+inline void* mp_flipper_file_open(const char* name, uint8_t access_mode, uint8_t open_mode) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    File* file = storage_file_alloc(ctx->storage);
+    FuriString* path = furi_string_alloc_set_str(name);
+
+    do {
+        if(mp_flipper_try_resolve_filesystem_path(path) == MP_FLIPPER_IMPORT_STAT_NO_EXIST) {
+            break;
+        }
+
+        if(!storage_file_open(file, furi_string_get_cstr(path), access_mode, open_mode)) {
+            break;
+        }
+    } while(false);
+
+    if(!storage_file_is_open(file)) {
+        storage_file_close(file);
+        storage_file_free(file);
+
+        return NULL;
+    }
+
+    return file;
+}
+
+inline bool mp_flipper_file_close(void* handle) {
+    File* file = handle;
+
+    bool success = storage_file_is_open(file) && storage_file_close(file);
+
+    storage_file_free(file);
+
+    return success;
+}
+
+inline size_t mp_flipper_file_seek(void* handle, uint32_t offset) {
+    return storage_file_seek(handle, offset, true);
+}
+
+inline size_t mp_flipper_file_tell(void* handle) {
+    return storage_file_tell(handle);
+}
+
+inline size_t mp_flipper_file_size(void* handle) {
+    return storage_file_size(handle);
+}
+
+inline bool mp_flipper_file_sync(void* handle) {
+    return storage_file_sync(handle);
+}
+
+inline bool mp_flipper_file_eof(void* handle) {
+    return storage_file_eof(handle);
+}
+
+inline size_t mp_flipper_file_read(void* handle, void* buffer, size_t size, int* errcode) {
+    File* file = handle;
+
+    *errcode = 0; // TODO handle error
+
+    return storage_file_read(file, buffer, size);
+}
+
+inline size_t mp_flipper_file_write(void* handle, const void* buffer, size_t size, int* errcode) {
+    File* file = handle;
+
+    *errcode = 0; // TODO handle error
+
+    return storage_file_write(file, buffer, size);
+}

+ 33 - 0
mp_flipper/lib/micropython-port/mp_flipper_halport.c

@@ -0,0 +1,33 @@
+#include <stdio.h>
+
+#include <furi.h>
+#include <storage/storage.h>
+
+#include <mp_flipper_runtime.h>
+#include <mp_flipper_halport.h>
+
+#include "mp_flipper_file_helper.h"
+
+inline void mp_flipper_stdout_tx_str(const char* str) {
+    furi_thread_stdout_write(str, strlen(str));
+}
+
+inline void mp_flipper_stdout_tx_strn_cooked(const char* str, size_t len) {
+    furi_thread_stdout_write(str, len);
+}
+
+inline mp_flipper_import_stat_t mp_flipper_import_stat(const char* path) {
+    FuriString* file_path = furi_string_alloc_printf("%s", path);
+
+    mp_flipper_import_stat_t stat = mp_flipper_try_resolve_filesystem_path(file_path);
+
+    stat = furi_string_end_with_str(file_path, path) ? stat : MP_FLIPPER_IMPORT_STAT_NO_EXIST;
+
+    furi_string_free(file_path);
+
+    return stat;
+}
+
+inline size_t mp_flipper_gc_get_max_new_split(void) {
+    return memmgr_heap_get_max_free_block();
+}

+ 50 - 0
mp_flipper/lib/micropython-port/mp_flipper_logging.c

@@ -0,0 +1,50 @@
+#include <furi.h>
+
+#include <mp_flipper_logging.h>
+#include <mp_flipper_runtime.h>
+
+#include "mp_flipper_context.h"
+
+static inline FuriLogLevel decode_log_level(uint8_t level) {
+    switch(level) {
+    case MP_FLIPPER_LOG_LEVEL_TRACE:
+        return FuriLogLevelTrace;
+    case MP_FLIPPER_LOG_LEVEL_DEBUG:
+        return FuriLogLevelDebug;
+    case MP_FLIPPER_LOG_LEVEL_INFO:
+        return FuriLogLevelInfo;
+    case MP_FLIPPER_LOG_LEVEL_WARN:
+        return FuriLogLevelWarn;
+    case MP_FLIPPER_LOG_LEVEL_ERROR:
+        return FuriLogLevelError;
+    case MP_FLIPPER_LOG_LEVEL_NONE:
+        return FuriLogLevelNone;
+    default:
+        return FuriLogLevelNone;
+    }
+}
+
+inline uint8_t mp_flipper_log_get_effective_level() {
+    switch(furi_log_get_level()) {
+    case FuriLogLevelTrace:
+        return MP_FLIPPER_LOG_LEVEL_TRACE;
+    case FuriLogLevelDebug:
+        return MP_FLIPPER_LOG_LEVEL_DEBUG;
+    case FuriLogLevelInfo:
+        return MP_FLIPPER_LOG_LEVEL_INFO;
+    case FuriLogLevelWarn:
+        return MP_FLIPPER_LOG_LEVEL_WARN;
+    case FuriLogLevelError:
+        return MP_FLIPPER_LOG_LEVEL_ERROR;
+    case FuriLogLevelNone:
+        return MP_FLIPPER_LOG_LEVEL_NONE;
+    default:
+        return MP_FLIPPER_LOG_LEVEL_NONE;
+    }
+}
+
+inline void mp_flipper_log(uint8_t raw_level, const char* message) {
+    FuriLogLevel level = decode_log_level(raw_level);
+
+    furi_log_print_format(level, "uPython", message);
+}

+ 47 - 0
mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_adc.c

@@ -0,0 +1,47 @@
+#include <furi_hal.h>
+
+#include <mp_flipper_modflipperzero.h>
+#include <mp_flipper_runtime.h>
+
+#include "mp_flipper_context.h"
+
+inline static FuriHalAdcChannel decode_pin_to_adc_channel(uint8_t pin) {
+    switch(pin) {
+    case MP_FLIPPER_GPIO_PIN_PC0:
+        return FuriHalAdcChannel1;
+    case MP_FLIPPER_GPIO_PIN_PC1:
+        return FuriHalAdcChannel2;
+    case MP_FLIPPER_GPIO_PIN_PC3:
+        return FuriHalAdcChannel4;
+    case MP_FLIPPER_GPIO_PIN_PA4:
+        return FuriHalAdcChannel9;
+    case MP_FLIPPER_GPIO_PIN_PA6:
+        return FuriHalAdcChannel11;
+    case MP_FLIPPER_GPIO_PIN_PA7:
+        return FuriHalAdcChannel12;
+    default:
+        return FuriHalAdcChannelNone;
+    }
+}
+
+inline uint16_t mp_flipper_adc_read_pin(uint8_t raw_pin) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    FuriHalAdcChannel channel = decode_pin_to_adc_channel(raw_pin);
+
+    if(channel == FuriHalAdcChannelNone) {
+        return 0;
+    }
+
+    if(ctx->gpio_pins[raw_pin] != MP_FLIPPER_GPIO_MODE_ANALOG) {
+        return 0;
+    }
+
+    return ctx->adc_handle ? furi_hal_adc_read(ctx->adc_handle, channel) : 0;
+}
+
+inline float mp_flipper_adc_convert_to_voltage(uint16_t value) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    return ctx->adc_handle ? furi_hal_adc_convert_to_voltage(ctx->adc_handle, value) : 0.0f;
+}

+ 108 - 0
mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_canvas.c

@@ -0,0 +1,108 @@
+#include <gui/gui.h>
+#include <gui/elements.h>
+
+#include <mp_flipper_modflipperzero.h>
+#include <mp_flipper_runtime.h>
+
+#include "mp_flipper_context.h"
+
+static Align text_align_x = AlignLeft;
+static Align text_align_y = AlignTop;
+
+inline uint8_t mp_flipper_canvas_width() {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    return canvas_width(ctx->canvas);
+}
+
+inline uint8_t mp_flipper_canvas_height() {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    return canvas_height(ctx->canvas);
+}
+
+inline uint8_t mp_flipper_canvas_text_width(const char* text) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    return canvas_string_width(ctx->canvas, text);
+}
+
+inline uint8_t mp_flipper_canvas_text_height() {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    return canvas_current_font_height(ctx->canvas);
+}
+
+inline void mp_flipper_canvas_draw_dot(uint8_t x, uint8_t y) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    canvas_draw_dot(ctx->canvas, x, y);
+}
+
+inline void mp_flipper_canvas_draw_box(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t r) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    canvas_draw_rbox(ctx->canvas, x, y, w, h, r);
+}
+
+inline void mp_flipper_canvas_draw_frame(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t r) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    canvas_draw_rframe(ctx->canvas, x, y, w, h, r);
+}
+
+inline void mp_flipper_canvas_draw_line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    canvas_draw_line(ctx->canvas, x0, y0, x1, y1);
+}
+
+inline void mp_flipper_canvas_draw_circle(uint8_t x, uint8_t y, uint8_t r) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    canvas_draw_circle(ctx->canvas, x, y, r);
+}
+
+inline void mp_flipper_canvas_draw_disc(uint8_t x, uint8_t y, uint8_t r) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    canvas_draw_disc(ctx->canvas, x, y, r);
+}
+
+inline void mp_flipper_canvas_set_font(uint8_t font) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    canvas_set_font(ctx->canvas, font == MP_FLIPPER_FONT_PRIMARY ? FontPrimary : FontSecondary);
+}
+
+inline void mp_flipper_canvas_set_color(uint8_t color) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    canvas_set_color(ctx->canvas, color == MP_FLIPPER_COLOR_BLACK ? ColorBlack : ColorWhite);
+}
+
+inline void mp_flipper_canvas_set_text(uint8_t x, uint8_t y, const char* text) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    canvas_draw_str_aligned(ctx->canvas, x, y, text_align_x, text_align_y, text);
+}
+
+inline void mp_flipper_canvas_set_text_align(uint8_t x, uint8_t y) {
+    Align align_x = x == MP_FLIPPER_ALIGN_BEGIN ? AlignLeft : AlignRight;
+    Align align_y = y == MP_FLIPPER_ALIGN_BEGIN ? AlignTop : AlignBottom;
+
+    text_align_x = x == MP_FLIPPER_ALIGN_CENTER ? AlignCenter : align_x;
+    text_align_y = y == MP_FLIPPER_ALIGN_CENTER ? AlignCenter : align_y;
+}
+
+inline void mp_flipper_canvas_update() {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    canvas_commit(ctx->canvas);
+}
+
+inline void mp_flipper_canvas_clear() {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    canvas_clear(ctx->canvas);
+}

+ 101 - 0
mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_dialog.c

@@ -0,0 +1,101 @@
+#include <dialogs/dialogs.h>
+
+#include <mp_flipper_modflipperzero.h>
+#include <mp_flipper_runtime.h>
+
+#include "mp_flipper_context.h"
+
+void mp_flipper_dialog_message_set_text(
+    const char* text,
+    uint8_t x,
+    uint8_t y,
+    uint8_t h,
+    uint8_t v) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    Align align_x = h == MP_FLIPPER_ALIGN_BEGIN ? AlignLeft : AlignRight;
+    Align align_y = v == MP_FLIPPER_ALIGN_BEGIN ? AlignTop : AlignBottom;
+
+    align_x = h == MP_FLIPPER_ALIGN_CENTER ? AlignCenter : align_x;
+    align_y = v == MP_FLIPPER_ALIGN_CENTER ? AlignCenter : align_y;
+
+    dialog_message_set_text(ctx->dialog_message, text, x, y, align_x, align_y);
+}
+
+void mp_flipper_dialog_message_set_header(
+    const char* text,
+    uint8_t x,
+    uint8_t y,
+    uint8_t h,
+    uint8_t v) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    Align align_x = h == MP_FLIPPER_ALIGN_BEGIN ? AlignLeft : AlignRight;
+    Align align_y = v == MP_FLIPPER_ALIGN_BEGIN ? AlignTop : AlignBottom;
+
+    align_x = h == MP_FLIPPER_ALIGN_CENTER ? AlignCenter : align_x;
+    align_y = v == MP_FLIPPER_ALIGN_CENTER ? AlignCenter : align_y;
+
+    dialog_message_set_header(ctx->dialog_message, text, x, y, align_x, align_y);
+}
+
+void mp_flipper_dialog_message_set_button(const char* text, uint8_t button) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    // left button
+    if(button == MP_FLIPPER_INPUT_BUTTON_LEFT) {
+        ctx->dialog_message_button_left = text;
+    }
+    // center button
+    else if(button == MP_FLIPPER_INPUT_BUTTON_OK) {
+        ctx->dialog_message_button_center = text;
+    }
+    // right button
+    else if(button == MP_FLIPPER_INPUT_BUTTON_RIGHT) {
+        ctx->dialog_message_button_right = text;
+    }
+
+    dialog_message_set_buttons(
+        ctx->dialog_message,
+        ctx->dialog_message_button_left,
+        ctx->dialog_message_button_center,
+        ctx->dialog_message_button_right);
+}
+
+uint8_t mp_flipper_dialog_message_show() {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    gui_direct_draw_release(ctx->gui);
+
+    DialogsApp* dialog = furi_record_open(RECORD_DIALOGS);
+
+    uint8_t button = dialog_message_show(dialog, ctx->dialog_message);
+
+    furi_record_close(RECORD_DIALOGS);
+
+    ctx->canvas = gui_direct_draw_acquire(ctx->gui);
+
+    switch(button) {
+    case DialogMessageButtonLeft:
+        return MP_FLIPPER_INPUT_BUTTON_LEFT;
+    case DialogMessageButtonCenter:
+        return MP_FLIPPER_INPUT_BUTTON_OK;
+    case DialogMessageButtonRight:
+        return MP_FLIPPER_INPUT_BUTTON_RIGHT;
+    case DialogMessageButtonBack:
+    default:
+        return MP_FLIPPER_INPUT_BUTTON_BACK;
+    }
+}
+
+void mp_flipper_dialog_message_clear() {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    dialog_message_free(ctx->dialog_message);
+
+    ctx->dialog_message = dialog_message_alloc();
+
+    ctx->dialog_message_button_left = NULL;
+    ctx->dialog_message_button_center = NULL;
+    ctx->dialog_message_button_right = NULL;
+}

+ 175 - 0
mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_gpio.c

@@ -0,0 +1,175 @@
+#include <furi_hal.h>
+
+#include <mp_flipper_modflipperzero.h>
+#include <mp_flipper_runtime.h>
+
+#include "mp_flipper_context.h"
+
+#define NO_VALUE (-1)
+
+static const GpioPin* decode_pin(uint8_t pin) {
+    switch(pin) {
+    case MP_FLIPPER_GPIO_PIN_PC0:
+        return &gpio_ext_pc0;
+    case MP_FLIPPER_GPIO_PIN_PC1:
+        return &gpio_ext_pc1;
+    case MP_FLIPPER_GPIO_PIN_PC3:
+        return &gpio_ext_pc3;
+    case MP_FLIPPER_GPIO_PIN_PB2:
+        return &gpio_ext_pb2;
+    case MP_FLIPPER_GPIO_PIN_PB3:
+        return &gpio_ext_pb3;
+    case MP_FLIPPER_GPIO_PIN_PA4:
+        return &gpio_ext_pa4;
+    case MP_FLIPPER_GPIO_PIN_PA6:
+        return &gpio_ext_pa6;
+    case MP_FLIPPER_GPIO_PIN_PA7:
+        return &gpio_ext_pa7;
+    default:
+        return NULL;
+    }
+}
+
+static inline const GpioMode decode_mode(uint8_t mode) {
+    switch(mode) {
+    case MP_FLIPPER_GPIO_MODE_INPUT:
+        return GpioModeInput;
+    case MP_FLIPPER_GPIO_MODE_OUTPUT_PUSH_PULL:
+        return GpioModeOutputPushPull;
+    case MP_FLIPPER_GPIO_MODE_OUTPUT_OPEN_DRAIN:
+        return GpioModeOutputOpenDrain;
+    case MP_FLIPPER_GPIO_MODE_ANALOG:
+        return GpioModeAnalog;
+    case MP_FLIPPER_GPIO_MODE_INTERRUPT_FALL:
+        return GpioModeInterruptFall;
+    case MP_FLIPPER_GPIO_MODE_INTERRUPT_RISE:
+        return GpioModeInterruptRise;
+    }
+
+    if((mode & MP_FLIPPER_GPIO_MODE_INTERRUPT_FALL) &&
+       (mode & MP_FLIPPER_GPIO_MODE_INTERRUPT_RISE)) {
+        return GpioModeInterruptRiseFall;
+    }
+
+    return NO_VALUE;
+}
+
+static inline const GpioPull decode_pull(uint8_t pull) {
+    switch(pull) {
+    case MP_FLIPPER_GPIO_PULL_NO:
+        return GpioPullNo;
+    case MP_FLIPPER_GPIO_PULL_UP:
+        return GpioPullUp;
+    case MP_FLIPPER_GPIO_PULL_DOWN:
+        return GpioPullDown;
+    default:
+        return NO_VALUE;
+    }
+}
+
+static inline const GpioSpeed decode_speed(uint8_t speed) {
+    switch(speed) {
+    case MP_FLIPPER_GPIO_SPEED_LOW:
+        return GpioSpeedLow;
+    case MP_FLIPPER_GPIO_SPEED_MEDIUM:
+        return GpioSpeedMedium;
+    case MP_FLIPPER_GPIO_SPEED_HIGH:
+        return GpioSpeedHigh;
+    case MP_FLIPPER_GPIO_SPEED_VERY_HIGH:
+        return GpioSpeedVeryHigh;
+    default:
+        return NO_VALUE;
+    }
+}
+
+inline bool mp_flipper_gpio_init_pin(
+    uint8_t raw_pin,
+    uint8_t raw_mode,
+    uint8_t raw_pull,
+    uint8_t raw_speed) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    const GpioPin* pin = decode_pin(raw_pin);
+    const GpioMode mode = decode_mode(raw_mode);
+    const GpioPull pull = decode_pull(raw_pull);
+    const GpioSpeed speed = decode_speed(raw_speed);
+
+    if(pin == NULL || mode == NO_VALUE || pull == NO_VALUE || speed == NO_VALUE) {
+        return false;
+    }
+
+    if(ctx->gpio_pins[raw_pin] & MP_FLIPPER_GPIO_PIN_BLOCKED) {
+        return false;
+    }
+
+    furi_hal_gpio_init(pin, mode, pull, speed);
+
+    if(raw_mode & (MP_FLIPPER_GPIO_MODE_INTERRUPT_FALL | MP_FLIPPER_GPIO_MODE_INTERRUPT_RISE)) {
+        furi_hal_gpio_add_int_callback(pin, mp_flipper_on_gpio, (void*)raw_pin);
+        furi_hal_gpio_enable_int_callback(pin);
+    } else {
+        furi_hal_gpio_disable_int_callback(pin);
+        furi_hal_gpio_remove_int_callback(pin);
+    }
+
+    if(raw_mode == MP_FLIPPER_GPIO_MODE_ANALOG) {
+        ctx->adc_handle = furi_hal_adc_acquire();
+
+        furi_hal_adc_configure(ctx->adc_handle);
+    }
+
+    ctx->gpio_pins[raw_pin] = raw_mode;
+
+    return true;
+}
+
+inline void mp_flipper_gpio_deinit_pin(uint8_t raw_pin) {
+    const mp_flipper_context_t* ctx = mp_flipper_context;
+
+    const GpioPin* pin = decode_pin(raw_pin);
+
+    if(pin == NULL) {
+        return;
+    }
+
+    if(ctx->gpio_pins[raw_pin] & (MP_FLIPPER_GPIO_PIN_BLOCKED | MP_FLIPPER_GPIO_PIN_OFF)) {
+        return;
+    }
+
+    furi_hal_gpio_disable_int_callback(pin);
+    furi_hal_gpio_remove_int_callback(pin);
+    furi_hal_gpio_init_simple(pin, GpioModeAnalog);
+
+    ctx->gpio_pins[raw_pin] = MP_FLIPPER_GPIO_PIN_OFF;
+}
+
+inline void mp_flipper_gpio_set_pin(uint8_t raw_pin, bool state) {
+    const mp_flipper_context_t* ctx = mp_flipper_context;
+
+    const GpioPin* pin = decode_pin(raw_pin);
+
+    if(pin == NULL) {
+        return;
+    }
+
+    if(ctx->gpio_pins[raw_pin] == MP_FLIPPER_GPIO_MODE_OUTPUT_PUSH_PULL ||
+       ctx->gpio_pins[raw_pin] == MP_FLIPPER_GPIO_MODE_OUTPUT_OPEN_DRAIN) {
+        furi_hal_gpio_write(pin, state);
+    }
+}
+
+inline bool mp_flipper_gpio_get_pin(uint8_t raw_pin) {
+    const mp_flipper_context_t* ctx = mp_flipper_context;
+
+    const GpioPin* pin = decode_pin(raw_pin);
+
+    if(pin == NULL) {
+        return false;
+    }
+
+    if(ctx->gpio_pins[raw_pin] == MP_FLIPPER_GPIO_MODE_INPUT) {
+        return furi_hal_gpio_read(pin);
+    } else {
+        return false;
+    }
+}

+ 119 - 0
mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_infrared.c

@@ -0,0 +1,119 @@
+#include <furi_hal.h>
+#include <furi_hal_infrared.h>
+
+#include <mp_flipper_modflipperzero.h>
+#include <mp_flipper_runtime.h>
+
+#include "mp_flipper_context.h"
+
+inline static void on_rx(void* ctx, bool level, uint32_t duration) {
+    mp_flipper_infrared_rx_t* session = ctx;
+
+    if(session->pointer == 0 && !level) {
+        return;
+    }
+
+    if(session->pointer < session->size) {
+        session->buffer[session->pointer] = duration;
+
+        session->pointer++;
+    } else {
+        session->running = false;
+    }
+}
+
+inline static void on_rx_timeout(void* ctx) {
+    mp_flipper_infrared_rx_t* session = ctx;
+
+    session->running = false;
+}
+
+inline static FuriHalInfraredTxGetDataState on_tx(void* ctx, uint32_t* duration, bool* level) {
+    mp_flipper_infrared_tx_t* session = ctx;
+
+    *duration = session->provider(session->signal, session->index);
+    *level = session->level;
+
+    session->index++;
+
+    if(session->index >= session->size) {
+        session->index = 0;
+        session->repeat--;
+    }
+
+    session->level = !session->level;
+
+    return session->repeat > 0 ? FuriHalInfraredTxGetDataStateOk :
+                                 FuriHalInfraredTxGetDataStateLastDone;
+}
+
+inline uint32_t* mp_flipper_infrared_receive(uint32_t timeout, size_t* length) {
+    const mp_flipper_context_t* ctx = mp_flipper_context;
+
+    mp_flipper_infrared_rx_t* session = ctx->infrared_rx;
+
+    if(!furi_hal_infrared_is_busy()) {
+        session->pointer = 0;
+        session->running = true;
+
+        furi_hal_infrared_async_rx_set_capture_isr_callback(on_rx, session);
+        furi_hal_infrared_async_rx_set_timeout_isr_callback(on_rx_timeout, session);
+
+        furi_hal_infrared_async_rx_start();
+
+        furi_hal_infrared_async_rx_set_timeout(timeout);
+
+        while(session->running) {
+            furi_delay_tick(10);
+        }
+
+        furi_hal_infrared_async_rx_stop();
+
+        *length = session->pointer;
+    } else {
+        *length = 0;
+    }
+
+    return session->buffer;
+}
+
+inline bool mp_flipper_infrared_transmit(
+    void* signal,
+    size_t length,
+    mp_flipper_infrared_signal_tx_provider callback,
+    uint32_t repeat,
+    uint32_t frequency,
+    float duty,
+    bool use_external_pin) {
+    if(furi_hal_infrared_is_busy() || length == 0) {
+        return false;
+    }
+
+    const mp_flipper_context_t* ctx = mp_flipper_context;
+
+    mp_flipper_infrared_tx_t* session = ctx->infrared_tx;
+
+    session->index = 0;
+    session->level = true;
+    session->provider = callback;
+    session->signal = signal;
+    session->repeat = repeat;
+    session->size = length;
+
+    const FuriHalInfraredTxPin output = use_external_pin ? FuriHalInfraredTxPinExtPA7 :
+                                                           FuriHalInfraredTxPinInternal;
+
+    furi_hal_infrared_set_tx_output(output);
+
+    furi_hal_infrared_async_tx_set_data_isr_callback(on_tx, session);
+
+    furi_hal_infrared_async_tx_start(frequency, duty);
+
+    furi_hal_infrared_async_tx_wait_termination();
+
+    return true;
+}
+
+inline bool mp_flipper_infrared_is_busy() {
+    return furi_hal_infrared_is_busy();
+}

+ 40 - 0
mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_light.c

@@ -0,0 +1,40 @@
+#include <furi_hal.h>
+
+#include <mp_flipper_modflipperzero.h>
+
+static Light decode_light(uint8_t value) {
+    Light light = 0;
+
+    light += value & MP_FLIPPER_LED_RED ? LightRed : 0;
+    light += value & MP_FLIPPER_LED_GREEN ? LightGreen : 0;
+    light += value & MP_FLIPPER_LED_BLUE ? LightBlue : 0;
+    light += value & MP_FLIPPER_LED_BACKLIGHT ? LightBacklight : 0;
+
+    return light;
+}
+
+inline void mp_flipper_light_set(uint8_t raw_light, uint8_t brightness) {
+    Light light = decode_light(raw_light);
+
+    furi_hal_light_set(light, brightness);
+}
+
+inline void mp_flipper_light_blink_start(
+    uint8_t raw_light,
+    uint8_t brightness,
+    uint16_t on_time,
+    uint16_t period) {
+    Light light = decode_light(raw_light);
+
+    furi_hal_light_blink_start(light, brightness, on_time, period);
+}
+
+inline void mp_flipper_light_blink_set_color(uint8_t raw_light) {
+    Light light = decode_light(raw_light);
+
+    furi_hal_light_blink_set_color(light);
+}
+
+inline void mp_flipper_light_blink_stop() {
+    furi_hal_light_blink_stop();
+}

+ 79 - 0
mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_pwm.c

@@ -0,0 +1,79 @@
+#include <furi_hal.h>
+#include <furi_hal_pwm.h>
+
+#include <mp_flipper_modflipperzero.h>
+#include <mp_flipper_runtime.h>
+
+#include "mp_flipper_context.h"
+
+#define NO_VALUE (-1)
+
+inline static const FuriHalPwmOutputId decode_pin_to_output_id(uint8_t pin) {
+    switch(pin) {
+    case MP_FLIPPER_GPIO_PIN_PA4:
+        return FuriHalPwmOutputIdLptim2PA4;
+    case MP_FLIPPER_GPIO_PIN_PA7:
+        return FuriHalPwmOutputIdTim1PA7;
+    default:
+        return NO_VALUE;
+    }
+}
+
+inline bool mp_flipper_pwm_start(uint8_t raw_pin, uint32_t frequency, uint8_t duty) {
+    const mp_flipper_context_t* ctx = mp_flipper_context;
+
+    FuriHalPwmOutputId channel = decode_pin_to_output_id(raw_pin);
+
+    if(channel == NO_VALUE) {
+        return false;
+    }
+
+    if(ctx->gpio_pins[raw_pin] != MP_FLIPPER_GPIO_PIN_OFF &&
+       ctx->gpio_pins[raw_pin] != MP_FLIPPER_GPIO_PIN_PWM) {
+        return false;
+    }
+
+    if(ctx->gpio_pins[raw_pin] == MP_FLIPPER_GPIO_PIN_OFF) {
+        furi_hal_pwm_start(channel, frequency, duty);
+    } else {
+        furi_hal_pwm_set_params(channel, frequency, duty);
+    }
+
+    ctx->gpio_pins[raw_pin] = MP_FLIPPER_GPIO_PIN_PWM;
+
+    return true;
+}
+
+inline void mp_flipper_pwm_stop(uint8_t raw_pin) {
+    const mp_flipper_context_t* ctx = mp_flipper_context;
+
+    FuriHalPwmOutputId channel = decode_pin_to_output_id(raw_pin);
+
+    if(channel == NO_VALUE) {
+        return;
+    }
+
+    if(ctx->gpio_pins[raw_pin] != MP_FLIPPER_GPIO_PIN_PWM) {
+        return;
+    }
+
+    furi_hal_pwm_stop(channel);
+
+    ctx->gpio_pins[raw_pin] = MP_FLIPPER_GPIO_PIN_OFF;
+}
+
+inline bool mp_flipper_pwm_is_running(uint8_t raw_pin) {
+    const mp_flipper_context_t* ctx = mp_flipper_context;
+
+    FuriHalPwmOutputId channel = decode_pin_to_output_id(raw_pin);
+
+    if(channel == NO_VALUE) {
+        return false;
+    }
+
+    if(ctx->gpio_pins[raw_pin] != MP_FLIPPER_GPIO_PIN_PWM) {
+        return false;
+    }
+
+    return furi_hal_pwm_is_running(channel);
+}

+ 35 - 0
mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_speaker.c

@@ -0,0 +1,35 @@
+#include <furi_hal.h>
+
+#include <mp_flipper_modflipperzero.h>
+
+inline bool mp_flipper_speaker_start(float frequency, float volume) {
+    if(furi_hal_speaker_acquire(100)) {
+        furi_hal_speaker_start(frequency, volume);
+
+        return true;
+    }
+
+    return false;
+}
+
+inline bool mp_flipper_speaker_set_volume(float volume) {
+    if(furi_hal_speaker_is_mine()) {
+        furi_hal_speaker_set_volume(volume);
+
+        return true;
+    }
+
+    return false;
+}
+
+inline bool mp_flipper_speaker_stop() {
+    if(furi_hal_speaker_is_mine()) {
+        furi_hal_speaker_stop();
+
+        furi_hal_speaker_release();
+
+        return true;
+    }
+
+    return false;
+}

+ 81 - 0
mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_uart.c

@@ -0,0 +1,81 @@
+#include <furi.h>
+#include <furi_hal.h>
+
+#include <mp_flipper_modflipperzero.h>
+
+typedef struct {
+    FuriHalSerialHandle* handle;
+    FuriStreamBuffer* rx_buffer;
+} mp_flipper_uart_ctx_t;
+
+static void on_uart_rx(FuriHalSerialHandle* handle, FuriHalSerialRxEvent event, void* context) {
+    FuriStreamBuffer* buffer = context;
+
+    if(event & FuriHalSerialRxEventData) {
+        uint8_t data = furi_hal_serial_async_rx(handle);
+        furi_stream_buffer_send(buffer, &data, 1, 0);
+    }
+}
+
+inline void* mp_flipper_uart_open(uint8_t raw_mode, uint32_t baud_rate) {
+    FuriHalSerialId mode = raw_mode == MP_FLIPPER_UART_MODE_USART ? FuriHalSerialIdUsart :
+                                                                    FuriHalSerialIdLpuart;
+
+    if(furi_hal_serial_control_is_busy(mode)) {
+        return NULL;
+    }
+
+    mp_flipper_uart_ctx_t* ctx = malloc(sizeof(mp_flipper_uart_ctx_t));
+
+    ctx->handle = furi_hal_serial_control_acquire(mode);
+    ctx->rx_buffer = furi_stream_buffer_alloc(512, 1);
+
+    furi_hal_serial_init(ctx->handle, baud_rate);
+
+    furi_hal_serial_async_rx_start(ctx->handle, on_uart_rx, ctx->rx_buffer, false);
+
+    return ctx;
+}
+
+inline bool mp_flipper_uart_close(void* handle) {
+    mp_flipper_uart_ctx_t* ctx = handle;
+
+    furi_hal_serial_deinit(ctx->handle);
+    furi_hal_serial_control_release(ctx->handle);
+
+    furi_stream_buffer_free(ctx->rx_buffer);
+
+    free(ctx);
+
+    return true;
+}
+
+inline bool mp_flipper_uart_sync(void* handle) {
+    mp_flipper_uart_ctx_t* ctx = handle;
+
+    furi_hal_serial_tx_wait_complete(ctx->handle);
+}
+
+inline size_t mp_flipper_uart_read(void* handle, void* buffer, size_t size, int* errcode) {
+    mp_flipper_uart_ctx_t* ctx = handle;
+
+    size_t read = 0;
+    size_t total = 0;
+    size_t left = size;
+
+    do {
+        read = furi_stream_buffer_receive(ctx->rx_buffer, &buffer[read], left, 0);
+        total += read;
+        left -= read;
+    } while(read > 0);
+
+    return total;
+}
+
+inline size_t mp_flipper_uart_write(void* handle, const void* buffer, size_t size, int* errcode) {
+    mp_flipper_uart_ctx_t* ctx = handle;
+
+    furi_hal_serial_tx(ctx->handle, buffer, size);
+
+    return size;
+}

+ 7 - 0
mp_flipper/lib/micropython-port/mp_flipper_modflipperzero_vibro.c

@@ -0,0 +1,7 @@
+#include <furi_hal.h>
+
+#include <mp_flipper_modflipperzero.h>
+
+inline void mp_flipper_vibro(bool state) {
+    furi_hal_vibro_on(state);
+}

+ 9 - 0
mp_flipper/lib/micropython-port/mp_flipper_modrandom.c

@@ -0,0 +1,9 @@
+#include <furi_hal.h>
+
+#include <mp_flipper_modrandom.h>
+
+inline uint32_t mp_flipper_seed_init() {
+    furi_hal_random_init();
+
+    return furi_hal_random_get();
+}

部分文件因为文件数量过多而无法显示