Merge branch 'dev-8.0' into master
branch 'dev-8.0' is now obsolete, use 'master' instead previous master is available under release/v7
This commit is contained in:
commit
c067ac51cc
35
.git-blame-ignore-revs
Normal file
35
.git-blame-ignore-revs
Normal file
@ -0,0 +1,35 @@
|
||||
# tools/gen-cpydiff.py: Fix formatting of doc strings for new Black.
|
||||
0f78c36c5aa458a954eed39a46942209107a553e
|
||||
|
||||
# tests/run-tests.py: Reformat with Black.
|
||||
2a38d7103672580882fb621a5b76e8d26805d593
|
||||
|
||||
# all: Update Python code to conform to latest black formatting.
|
||||
06659077a81b85882254cf0953c33b27614e018e
|
||||
|
||||
# tools/uncrustify: Enable more opts to remove space between func and '('.
|
||||
77ed6f69ac35c1663a5633a8ee1d8a2446542204
|
||||
|
||||
# tools/codeformat.py: Include extmod/{btstack,nimble} in code formatting.
|
||||
026fda605e03113d6e753290d65fed774418bc53
|
||||
|
||||
# all: Format code to add space after C++-style comment start.
|
||||
84fa3312cfa7d2237d4b56952f2cd6e3591210c4
|
||||
|
||||
# tests: Format all Python code with black, except tests in basics subdir.
|
||||
3dc324d3f1312e40d3a8ed87e7244966bb756f26
|
||||
|
||||
# all: Remove spaces inside and around parenthesis.
|
||||
1a3e386c67e03a79eb768cb6e9f6777e002d6660
|
||||
|
||||
# all: Remove spaces between nested paren and inside function arg paren.
|
||||
feb25775851ba0c04b8d1013716f442258879d9c
|
||||
|
||||
# all: Reformat C and Python source code with tools/codeformat.py.
|
||||
69661f3343bedf86e514337cff63d96cc42f8859
|
||||
|
||||
# stm32/usbdev: Convert files to unix line endings.
|
||||
abde0fa2267f9062b28c3c015d7662a550125cc6
|
||||
|
||||
# all: Remove trailing spaces, per coding conventions.
|
||||
761e4c7ff62896c7d8f8c3dfc3cc98a4cc4f2f6f
|
||||
24
.github/workflows/ports_javascript.yml
vendored
Normal file
24
.github/workflows/ports_javascript.yml
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
name: javascript port
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/*.yml'
|
||||
- 'tools/**'
|
||||
- 'py/**'
|
||||
- 'extmod/**'
|
||||
- 'lib/**'
|
||||
- 'ports/javascript/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_javascript_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_javascript_build
|
||||
- name: Run tests
|
||||
run: source tools/ci.sh && ci_javascript_run_tests
|
||||
15
.github/workflows/unix_port.yml
vendored
15
.github/workflows/unix_port.yml
vendored
@ -2,9 +2,9 @@ name: Build lv_micropython unix port
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, lvgl_javascript ]
|
||||
branches: [ master, lvgl_javascript, dev-8.0 ]
|
||||
pull_request:
|
||||
branches: [ master, lvgl_javascript ]
|
||||
branches: [ master, lvgl_javascript, dev-8.0 ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@ -23,12 +23,7 @@ jobs:
|
||||
- name: Build mpy-cross
|
||||
run: make -j $(nproc) -C mpy-cross
|
||||
- name: Build the unix port
|
||||
run: make -j $(nproc) -C ports/unix
|
||||
- name: Run advanced_demo
|
||||
run: >
|
||||
echo "import gc,utime;
|
||||
utime.sleep(5);
|
||||
gc.collect();
|
||||
utime.sleep(5)" |
|
||||
ports/unix/micropython -i lib/lv_bindings/examples/advanced_demo.py
|
||||
run: make -j $(nproc) -C ports/unix VARIANT=dev DEBUG=1
|
||||
- name: Run tests
|
||||
run: lib/lv_bindings/tests/run.sh
|
||||
|
||||
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@ -9,6 +9,9 @@
|
||||
*.dis
|
||||
*.exe
|
||||
|
||||
lextab.py
|
||||
yacctab.py
|
||||
|
||||
# Packages
|
||||
############
|
||||
|
||||
@ -55,5 +58,6 @@ ports/javascript/node_modules
|
||||
######################
|
||||
genrst/
|
||||
|
||||
lextab.py
|
||||
yacctab.py
|
||||
# MacOS desktop metadata files
|
||||
######################
|
||||
.DS_Store
|
||||
|
||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -7,7 +7,7 @@
|
||||
url = https://github.com/atgreen/libffi
|
||||
[submodule "lib/lwip"]
|
||||
path = lib/lwip
|
||||
url = https://git.savannah.gnu.org/r/lwip.git
|
||||
url = https://github.com/lwip-tcpip/lwip.git
|
||||
[submodule "lib/berkeley-db-1.xx"]
|
||||
path = lib/berkeley-db-1.xx
|
||||
url = https://github.com/pfalcon/berkeley-db-1.xx
|
||||
|
||||
65
LICENSE
65
LICENSE
@ -19,3 +19,68 @@ 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.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Unless specified otherwise (see below), the above license and copyright applies
|
||||
to all files in this repository.
|
||||
|
||||
Individual files may include additional copyright holders.
|
||||
|
||||
The various ports of MicroPython may include third-party software that is
|
||||
licensed under different terms. These licenses are summarised in the tree
|
||||
below, please refer to these files and directories for further license and
|
||||
copyright information. Note that (L)GPL-licensed code listed below is only
|
||||
used during the build process and is not part of the compiled source code.
|
||||
|
||||
/ (MIT)
|
||||
/drivers
|
||||
/cc3000 (BSD-3-clause)
|
||||
/cc3100 (BSD-3-clause)
|
||||
/wiznet5k (BSD-3-clause)
|
||||
/extmod
|
||||
/crypto-algorithms (NONE)
|
||||
/re15 (BSD-3-clause)
|
||||
/uzlib (Zlib)
|
||||
/lib
|
||||
/asf4 (Apache-2.0)
|
||||
/axtls (BSD-3-clause)
|
||||
/config
|
||||
/scripts
|
||||
/config (GPL-2.0-or-later)
|
||||
/Rules.mak (GPL-2.0)
|
||||
/berkeley-db-1xx (BSD-4-clause)
|
||||
/btstack (See btstack/LICENSE)
|
||||
/cmsis (BSD-3-clause)
|
||||
/libhydrogen (ISC)
|
||||
/littlefs (BSD-3-clause)
|
||||
/lwip (BSD-3-clause)
|
||||
/mynewt-nimble (Apache-2.0)
|
||||
/nrfx (BSD-3-clause)
|
||||
/nxp_driver (BSD-3-Clause)
|
||||
/oofatfs (BSD-1-clause)
|
||||
/pico-sdk (BSD-3-clause)
|
||||
/stm32lib (BSD-3-clause)
|
||||
/tinytest (BSD-3-clause)
|
||||
/tinyusb (MIT)
|
||||
/logo (uses OFL-1.1)
|
||||
/ports
|
||||
/cc3200
|
||||
/hal (BSD-3-clause)
|
||||
/simplelink (BSD-3-clause)
|
||||
/FreeRTOS (GPL-2.0 with FreeRTOS exception)
|
||||
/stm32
|
||||
/usbd*.c (MCD-ST Liberty SW License Agreement V2)
|
||||
/stm32_it.* (MIT + BSD-3-clause)
|
||||
/system_stm32*.c (MIT + BSD-3-clause)
|
||||
/boards
|
||||
/startup_stm32*.s (BSD-3-clause)
|
||||
/*/stm32*.h (BSD-3-clause)
|
||||
/usbdev (MCD-ST Liberty SW License Agreement V2)
|
||||
/usbhost (MCD-ST Liberty SW License Agreement V2)
|
||||
/teensy
|
||||
/core (PJRC.COM)
|
||||
/zephyr
|
||||
/src (Apache-2.0)
|
||||
/tools
|
||||
/dfu.py (LGPL-3.0-only)
|
||||
|
||||
@ -66,7 +66,7 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'MicroPython'
|
||||
copyright = '2014-2021, Damien P. George, Paul Sokolovsky, and contributors'
|
||||
copyright = '- The MicroPython Documentation is Copyright © 2014-2021, Damien P. George, Paul Sokolovsky, and contributors'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
@ -74,7 +74,7 @@ copyright = '2014-2021, Damien P. George, Paul Sokolovsky, and contributors'
|
||||
#
|
||||
# We don't follow "The short X.Y version" vs "The full version, including alpha/beta/rc tags"
|
||||
# breakdown, so use the same version identifier for both to avoid confusion.
|
||||
version = release = '1.14'
|
||||
version = release = '1.16'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
@ -18,7 +18,11 @@ If however you're targeting obscure or proprietary systems it may make
|
||||
more sense to keep this external to the main MicroPython repository.
|
||||
|
||||
This chapter describes how to compile such external modules into the
|
||||
MicroPython executable or firmware image.
|
||||
MicroPython executable or firmware image. Both Make and CMake build
|
||||
tools are supported, and when writing an external module it's a good idea to
|
||||
add the build files for both of these tools so the module can be used on all
|
||||
ports. But when compiling a particular port you will only need to use one
|
||||
method of building, either Make or CMake.
|
||||
|
||||
An alternative approach is to use :ref:`natmod` which allows writing custom C
|
||||
code that is placed in a .mpy file, which can be imported dynamically in to
|
||||
@ -53,6 +57,30 @@ A MicroPython user C module is a directory with the following files:
|
||||
for header files), these should be added to ``CFLAGS_USERMOD`` for C code
|
||||
and to ``CXXFLAGS_USERMOD`` for C++ code.
|
||||
|
||||
* ``micropython.cmake`` contains the CMake configuration for this module.
|
||||
|
||||
In ``micropython.cmake``, you may use ``${CMAKE_CURRENT_LIST_DIR}`` as the path to
|
||||
the current module.
|
||||
|
||||
Your ``micropython.cmake`` should define an ``INTERFACE`` library and associate
|
||||
your source files, compile definitions and include directories with it.
|
||||
The library should then be linked to the ``usermod`` target.
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
add_library(usermod_cexample INTERFACE)
|
||||
|
||||
target_sources(usermod_cexample INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/examplemodule.c
|
||||
)
|
||||
|
||||
target_include_directories(usermod_cexample INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(usermod INTERFACE usermod_cexample)
|
||||
|
||||
|
||||
See below for full usage example.
|
||||
|
||||
|
||||
@ -63,16 +91,18 @@ This simple module named ``cexample`` provides a single function
|
||||
``cexample.add_ints(a, b)`` which adds the two integer args together and returns
|
||||
the result. It can be found in the MicroPython source tree
|
||||
`in the examples directory <https://github.com/micropython/micropython/tree/master/examples/usercmodule/cexample>`_
|
||||
and has a source file and a Makefile fragment with content as descibed above::
|
||||
and has a source file and a Makefile fragment with content as described above::
|
||||
|
||||
micropython/
|
||||
└──examples/
|
||||
└──usercmodule/
|
||||
└──cexample/
|
||||
├── examplemodule.c
|
||||
└── micropython.mk
|
||||
├── micropython.mk
|
||||
└── micropython.cmake
|
||||
|
||||
Refer to the comments in these 2 files for additional explanation.
|
||||
|
||||
Refer to the comments in these files for additional explanation.
|
||||
Next to the ``cexample`` module there's also ``cppexample`` which
|
||||
works in the same way but shows one way of mixing C and C++ code
|
||||
in MicroPython.
|
||||
@ -85,78 +115,140 @@ To build such a module, compile MicroPython (see `getting started
|
||||
<https://github.com/micropython/micropython/wiki/Getting-Started>`_),
|
||||
applying 2 modifications:
|
||||
|
||||
- an extra ``make`` flag named ``USER_C_MODULES`` set to the directory
|
||||
containing all modules you want included (not to the module itself).
|
||||
1. Set the build-time flag ``USER_C_MODULES`` to point to the modules
|
||||
you want to include. For ports that use Make this variable should be a
|
||||
directory which is searched automatically for modules. For ports that
|
||||
use CMake this variable should be a file which includes the modules to
|
||||
build. See below for details.
|
||||
|
||||
2. Enable the modules by setting the corresponding C preprocessor macro to
|
||||
1. This is only needed if the modules you are building are not
|
||||
automatically enabled.
|
||||
|
||||
For building the example modules which come with MicroPython,
|
||||
set ``USER_C_MODULES`` to the ``examples/usercmodule`` directory.
|
||||
For your own projects it's more convenient to keep custom code out of
|
||||
the main source tree so a typical project directory structure will look
|
||||
like this::
|
||||
set ``USER_C_MODULES`` to the ``examples/usercmodule`` directory for Make,
|
||||
or to ``examples/usercmodule/micropython.cmake`` for CMake.
|
||||
|
||||
my_project/
|
||||
├── modules/
|
||||
│ └──example1/
|
||||
│ ├──example1.c
|
||||
│ └──micropython.mk
|
||||
│ └──example2/
|
||||
│ ├──example2.c
|
||||
│ └──micropython.mk
|
||||
└── micropython/
|
||||
├──ports/
|
||||
... ├──stm32/
|
||||
...
|
||||
|
||||
|
||||
with ``USER_C_MODULES`` set to the ``my_project/modules`` directory.
|
||||
|
||||
- all modules found in this directory will be compiled, but only those
|
||||
which are explicitly enabled will be availabe for importing. Enabling a
|
||||
module is done by setting the preprocessor define from its module
|
||||
registration to 1. For example if the source code defines the module with
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
MP_REGISTER_MODULE(MP_QSTR_cexample, example_user_cmodule, MODULE_CEXAMPLE_ENABLED);
|
||||
|
||||
|
||||
then ``MODULE_CEXAMPLE_ENABLED`` has to be set to 1 to make the module available.
|
||||
This can be done by adding ``CFLAGS_EXTRA=-DMODULE_CEXAMPLE_ENABLED=1`` to
|
||||
the ``make`` command, or editing ``mpconfigport.h`` or ``mpconfigboard.h``
|
||||
to add
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#define MODULE_CEXAMPLE_ENABLED (1)
|
||||
|
||||
|
||||
Note that the exact method depends on the port as they have different
|
||||
structures. If not done correctly it will compile but importing will
|
||||
fail to find the module.
|
||||
|
||||
To sum up, here's how the ``cexample`` module from the ``examples/usercmodule``
|
||||
directory can be built for the unix port:
|
||||
For example, here's how the to build the unix port with the example modules:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cd micropython/ports/unix
|
||||
make USER_C_MODULES=../../examples/usercmodule CFLAGS_EXTRA=-DMODULE_CEXAMPLE_ENABLED=1 all
|
||||
make USER_C_MODULES=../../examples/usercmodule
|
||||
|
||||
The build output will show the modules found::
|
||||
You may need to run ``make clean`` once at the start when including new
|
||||
user modules in the build. The build output will show the modules found::
|
||||
|
||||
...
|
||||
Including User C Module from ../../examples/usercmodule/cexample
|
||||
Including User C Module from ../../examples/usercmodule/cppexample
|
||||
...
|
||||
|
||||
For a CMake-based port such as rp2, this will look a little different (note
|
||||
that CMake is actually invoked by ``make``):
|
||||
|
||||
Or for your own project with a directory structure as shown above,
|
||||
including both modules and building the stm32 port for example:
|
||||
.. code-block:: bash
|
||||
|
||||
cd micropython/ports/rp2
|
||||
make USER_C_MODULES=../../examples/usercmodule/micropython.cmake
|
||||
|
||||
Again, you may need to run ``make clean`` first for CMake to pick up the
|
||||
user modules. The CMake build output lists the modules by name::
|
||||
|
||||
...
|
||||
Including User C Module(s) from ../../examples/usercmodule/micropython.cmake
|
||||
Found User C Module(s): usermod_cexample, usermod_cppexample
|
||||
...
|
||||
|
||||
The contents of the top-level ``micropython.cmake`` can be used to control which
|
||||
modules are enabled.
|
||||
|
||||
For your own projects it's more convenient to keep custom code out of the main
|
||||
MicroPython source tree, so a typical project directory structure will look
|
||||
like this::
|
||||
|
||||
my_project/
|
||||
├── modules/
|
||||
│ ├── example1/
|
||||
│ │ ├── example1.c
|
||||
│ │ ├── micropython.mk
|
||||
│ │ └── micropython.cmake
|
||||
│ ├── example2/
|
||||
│ │ ├── example2.c
|
||||
│ │ ├── micropython.mk
|
||||
│ │ └── micropython.cmake
|
||||
│ └── micropython.cmake
|
||||
└── micropython/
|
||||
├──ports/
|
||||
... ├──stm32/
|
||||
...
|
||||
|
||||
When building with Make set ``USER_C_MODULES`` to the ``my_project/modules``
|
||||
directory. For example, building the stm32 port:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cd my_project/micropython/ports/stm32
|
||||
make USER_C_MODULES=../../../modules \
|
||||
CFLAGS_EXTRA="-DMODULE_EXAMPLE1_ENABLED=1 -DMODULE_EXAMPLE2_ENABLED=1" all
|
||||
make USER_C_MODULES=../../../modules
|
||||
|
||||
When building with CMake the top level ``micropython.cmake`` -- found directly
|
||||
in the ``my_project/modules`` directory -- should ``include`` all of the modules
|
||||
you want to have available:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/example1/micropython.cmake)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/example2/micropython.cmake)
|
||||
|
||||
Then build with:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cd my_project/micropython/ports/esp32
|
||||
make USER_C_MODULES=../../../../modules/micropython.cmake
|
||||
|
||||
Note that the esp32 port needs the extra ``..`` for relative paths due to the
|
||||
location of its main ``CMakeLists.txt`` file. You can also specify absolute
|
||||
paths to ``USER_C_MODULES``.
|
||||
|
||||
All modules specified by the ``USER_C_MODULES`` variable (either found in this
|
||||
directory when using Make, or added via ``include`` when using CMake) will be
|
||||
compiled, but only those which are enabled will be available for importing.
|
||||
User modules are usually enabled by default (this is decided by the developer
|
||||
of the module), in which case there is nothing more to do than set ``USER_C_MODULES``
|
||||
as described above.
|
||||
|
||||
If a module is not enabled by default then the corresponding C preprocessor macro
|
||||
must be enabled. This macro name can be found by searching for the ``MP_REGISTER_MODULE``
|
||||
line in the module's source code (it usually appears at the end of the main source file).
|
||||
The third argument to ``MP_REGISTER_MODULE`` is the macro name, and this must be set
|
||||
to 1 using ``CFLAGS_EXTRA`` to make the module available. If the third argument is just
|
||||
the number 1 then the module is enabled by default.
|
||||
|
||||
For example, the ``examples/usercmodule/cexample`` module is enabled by default so
|
||||
has the following line in its source code:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
MP_REGISTER_MODULE(MP_QSTR_cexample, example_user_cmodule, 1);
|
||||
|
||||
Alternatively, to make this module disabled by default but selectable through
|
||||
a preprocessor configuration option, it would be:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
MP_REGISTER_MODULE(MP_QSTR_cexample, example_user_cmodule, MODULE_CEXAMPLE_ENABLED);
|
||||
|
||||
In this case the module is enabled by adding ``CFLAGS_EXTRA=-DMODULE_CEXAMPLE_ENABLED=1``
|
||||
to the ``make`` command, or editing ``mpconfigport.h`` or ``mpconfigboard.h`` to add
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#define MODULE_CEXAMPLE_ENABLED (1)
|
||||
|
||||
Note that the exact method depends on the port as they have different
|
||||
structures. If not done correctly it will compile but importing will
|
||||
fail to find the module.
|
||||
|
||||
|
||||
Module usage in MicroPython
|
||||
|
||||
@ -21,6 +21,11 @@ of Git for your operating system to follow through the rest of the steps.
|
||||
Learn about the basic git commands in this `Git Handbook <https://guides.github.com/introduction/git-handbook/>`_
|
||||
or any other sources on the internet.
|
||||
|
||||
.. note::
|
||||
A .git-blame-ignore-revs file is included which avoids the output of git blame getting cluttered
|
||||
by commits which are only for formatting code but have no functional changes. See `git blame documentation
|
||||
<https://git-scm.com/docs/git-blame#Documentation/git-blame.txt---ignore-revltrevgt>`_ on how to use this.
|
||||
|
||||
Get the code
|
||||
------------
|
||||
|
||||
@ -273,7 +278,7 @@ To run a selection of tests on a board/device connected over USB use:
|
||||
.. code-block:: bash
|
||||
|
||||
$ cd tests
|
||||
$ ./run-tests --target minimal --device /dev/ttyACM0
|
||||
$ ./run-tests.py --target minimal --device /dev/ttyACM0
|
||||
|
||||
See also :ref:`writingtests`.
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ Writing tests
|
||||
=============
|
||||
|
||||
Tests in MicroPython are located at the path ``tests/``. The following is a listing of
|
||||
key directories and the run-tests runner script:
|
||||
key directories and the run-tests.py runner script:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@ -13,7 +13,7 @@ key directories and the run-tests runner script:
|
||||
├── extmod
|
||||
├── float
|
||||
├── micropython
|
||||
├── run-tests
|
||||
├── run-tests.py
|
||||
...
|
||||
|
||||
There are subfolders maintained to categorize the tests. Add a test by creating a new file in one of the
|
||||
@ -54,17 +54,17 @@ The other way to run tests, which is useful when running on targets other than t
|
||||
.. code-block:: bash
|
||||
|
||||
$ cd tests
|
||||
$ ./run-tests
|
||||
$ ./run-tests.py
|
||||
|
||||
Then to run on a board:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ ./run-tests --target minimal --device /dev/ttyACM0
|
||||
$ ./run-tests.py --target minimal --device /dev/ttyACM0
|
||||
|
||||
And to run only a certain set of tests (eg a directory):
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ ./run-tests -d basics
|
||||
$ ./run-tests float/builtin*.py
|
||||
$ ./run-tests.py -d basics
|
||||
$ ./run-tests.py float/builtin*.py
|
||||
|
||||
@ -58,7 +58,7 @@ The :mod:`esp32` module::
|
||||
import esp32
|
||||
|
||||
esp32.hall_sensor() # read the internal hall sensor
|
||||
esp32.raw_temperature() # read the internal temperature of the MCU, in Farenheit
|
||||
esp32.raw_temperature() # read the internal temperature of the MCU, in Fahrenheit
|
||||
esp32.ULP() # access to the Ultra-Low-Power Co-processor
|
||||
|
||||
Note that the temperature sensor in the ESP32 will typically read higher than
|
||||
@ -102,6 +102,14 @@ Once the network is established the :mod:`socket <usocket>` module can be used
|
||||
to create and use TCP/UDP sockets as usual, and the ``urequests`` module for
|
||||
convenient HTTP requests.
|
||||
|
||||
After a call to ``wlan.connect()``, the device will by default retry to connect
|
||||
**forever**, even when the authentication failed or no AP is in range.
|
||||
``wlan.status()`` will return ``network.STAT_CONNECTING`` in this state until a
|
||||
connection succeeds or the interface gets disabled. This can be changed by
|
||||
calling ``wlan.config(reconnects=n)``, where n are the number of desired reconnect
|
||||
attempts (0 means it won't retry, -1 will restore the default behaviour of trying
|
||||
to reconnect forever).
|
||||
|
||||
Delay and timing
|
||||
----------------
|
||||
|
||||
@ -171,6 +179,37 @@ Notes:
|
||||
* The pull value of some pins can be set to ``Pin.PULL_HOLD`` to reduce power
|
||||
consumption during deepsleep.
|
||||
|
||||
There's a higher-level abstraction :ref:`machine.Signal <machine.Signal>`
|
||||
which can be used to invert a pin. Useful for illuminating active-low LEDs
|
||||
using ``on()`` or ``value(1)``.
|
||||
|
||||
UART (serial bus)
|
||||
-----------------
|
||||
|
||||
See :ref:`machine.UART <machine.UART>`. ::
|
||||
|
||||
from machine import UART
|
||||
|
||||
uart1 = UART(1, baudrate=9600, tx=33, rx=32)
|
||||
uart1.write('hello') # write 5 bytes
|
||||
uart1.read(5) # read up to 5 bytes
|
||||
|
||||
The ESP32 has three hardware UARTs: UART0, UART1 and UART2.
|
||||
They each have default GPIO assigned to them, however depending on your
|
||||
ESP32 variant and board, these pins may conflict with embedded flash,
|
||||
onboard PSRAM or peripherals.
|
||||
|
||||
Any GPIO can be used for hardware UARTs using the GPIO matrix, so to avoid
|
||||
conflicts simply provide ``tx`` and ``rx`` pins when constructing. The default
|
||||
pins listed below.
|
||||
|
||||
===== ===== ===== =====
|
||||
\ UART0 UART1 UART2
|
||||
===== ===== ===== =====
|
||||
tx 1 10 17
|
||||
rx 3 9 16
|
||||
===== ===== ===== =====
|
||||
|
||||
PWM (pulse width modulation)
|
||||
----------------------------
|
||||
|
||||
@ -302,6 +341,7 @@ has the same methods as software SPI above::
|
||||
|
||||
from machine import Pin, SPI
|
||||
|
||||
hspi = SPI(1, 10000000)
|
||||
hspi = SPI(1, 10000000, sck=Pin(14), mosi=Pin(13), miso=Pin(12))
|
||||
vspi = SPI(2, baudrate=80000000, polarity=0, phase=0, bits=8, firstbit=0, sck=Pin(18), mosi=Pin(23), miso=Pin(19))
|
||||
|
||||
@ -356,6 +396,17 @@ See :ref:`machine.RTC <machine.RTC>` ::
|
||||
rtc.datetime((2017, 8, 23, 1, 12, 48, 0, 0)) # set a specific date and time
|
||||
rtc.datetime() # get date and time
|
||||
|
||||
WDT (Watchdog timer)
|
||||
--------------------
|
||||
|
||||
See :ref:`machine.WDT <machine.WDT>`. ::
|
||||
|
||||
from machine import WDT
|
||||
|
||||
# enable the WDT with a timeout of 5s (1s is the minimum)
|
||||
wdt = WDT(timeout=5000)
|
||||
wdt.feed()
|
||||
|
||||
Deep-sleep mode
|
||||
---------------
|
||||
|
||||
@ -385,6 +436,21 @@ Notes:
|
||||
|
||||
p1 = Pin(4, Pin.OUT, None)
|
||||
|
||||
SD card
|
||||
-------
|
||||
|
||||
See :ref:`machine.SDCard <machine.SDCard>`. ::
|
||||
|
||||
import machine, uos
|
||||
|
||||
# Slot 2 uses pins sck=18, cs=5, miso=19, mosi=23
|
||||
sd = machine.SDCard(slot=2)
|
||||
uos.mount(sd, "/sd") # mount
|
||||
|
||||
uos.listdir('/sd') # list directory contents
|
||||
|
||||
uos.umount('/sd') # eject
|
||||
|
||||
RMT
|
||||
---
|
||||
|
||||
@ -429,10 +495,10 @@ Be sure to put a 4.7k pull-up resistor on the data line. Note that
|
||||
the ``convert_temp()`` method must be called each time you want to
|
||||
sample the temperature.
|
||||
|
||||
NeoPixel driver
|
||||
---------------
|
||||
NeoPixel and APA106 driver
|
||||
--------------------------
|
||||
|
||||
Use the ``neopixel`` module::
|
||||
Use the ``neopixel`` and ``apa106`` modules::
|
||||
|
||||
from machine import Pin
|
||||
from neopixel import NeoPixel
|
||||
@ -443,6 +509,13 @@ Use the ``neopixel`` module::
|
||||
np.write() # write data to all pixels
|
||||
r, g, b = np[0] # get first pixel colour
|
||||
|
||||
|
||||
The APA106 driver extends NeoPixel, but internally uses a different colour order::
|
||||
|
||||
from apa106 import APA106
|
||||
ap = APA106(pin, 8)
|
||||
r, g, b = ap[0]
|
||||
|
||||
For low-level driving of a NeoPixel::
|
||||
|
||||
import esp
|
||||
@ -454,6 +527,7 @@ For low-level driving of a NeoPixel::
|
||||
400kHz) devices by passing ``timing=0`` when constructing the
|
||||
``NeoPixel`` object.
|
||||
|
||||
APA102 (DotStar) uses a different driver as it has an additional clock pin.
|
||||
|
||||
Capacitive touch
|
||||
----------------
|
||||
|
||||
@ -125,6 +125,16 @@ will overflow every 7:45h. If a long-term working RTC time is required then
|
||||
``time()`` or ``localtime()`` must be called at least once within 7 hours.
|
||||
MicroPython will then handle the overflow.
|
||||
|
||||
Simultaneous operation of STA_IF and AP_IF
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Simultaneous operation of STA_IF and AP_IF interfaces is supported.
|
||||
|
||||
However, due to restrictions of the hardware, there may be performance
|
||||
issues in the AP_IF, if the STA_IF is not connected and searching.
|
||||
An application should manage these interfaces and for example
|
||||
deactivate the STA_IF in environments where only the AP_IF is used.
|
||||
|
||||
Sockets and WiFi buffers overflow
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@ -153,25 +163,26 @@ SSL/TLS limitations
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
ESP8266 uses `axTLS <http://axtls.sourceforge.net/>`_ library, which is one
|
||||
of the smallest TLS libraries with the compatible licensing. However, it
|
||||
of the smallest TLS libraries with compatible licensing. However, it
|
||||
also has some known issues/limitations:
|
||||
|
||||
1. No support for Diffie-Hellman (DH) key exchange and Elliptic-curve
|
||||
cryptography (ECC). This means it can't work with sites which force
|
||||
the use of these features (it works ok with classic RSA certificates).
|
||||
cryptography (ECC). This means it can't work with sites which require
|
||||
the use of these features (it works ok with the typical sites that use
|
||||
RSA certificates).
|
||||
2. Half-duplex communication nature. axTLS uses a single buffer for both
|
||||
sending and receiving, which leads to considerable memory saving and
|
||||
works well with protocols like HTTP. But there may be problems with
|
||||
protocols which don't follow classic request-response model.
|
||||
|
||||
Besides axTLS own limitations, the configuration used for MicroPython is
|
||||
Besides axTLS's own limitations, the configuration used for MicroPython is
|
||||
highly optimized for code size, which leads to additional limitations
|
||||
(these may be lifted in the future):
|
||||
|
||||
3. Optimized RSA algorithms are not enabled, which may lead to slow
|
||||
SSL handshakes.
|
||||
4. Stored sessions are not supported (may allow faster repeated connections
|
||||
to the same site in some circumstances).
|
||||
4. Session Reuse is not enabled, which means every connection must undergo
|
||||
the full, expensive SSL handshake.
|
||||
|
||||
Besides axTLS specific limitations described above, there's another generic
|
||||
limitation with usage of TLS on the low-memory devices:
|
||||
@ -185,13 +196,16 @@ limitation with usage of TLS on the low-memory devices:
|
||||
accessing various REST APIs, which usually require much smaller messages.
|
||||
The buffers size is on the order of 5KB, and is adjusted from time to
|
||||
time, taking as a reference being able to access https://google.com .
|
||||
The smaller buffer hower means that some sites can't be accessed using
|
||||
it, and it's not possible to stream large amounts of data.
|
||||
The smaller buffer however means that some sites can't be accessed using
|
||||
it, and it's not possible to stream large amounts of data. axTLS does
|
||||
have support for TLS's Max Fragment Size extension, but no HTTPS website
|
||||
does, so use of the extension is really only effective for local
|
||||
communication with other devices.
|
||||
|
||||
There are also some not implemented features specifically in MicroPython's
|
||||
``ussl`` module based on axTLS:
|
||||
|
||||
6. Certificates are not validated (this may make connections susceptible
|
||||
6. Certificates are not validated (this makes connections susceptible
|
||||
to man-in-the-middle attacks).
|
||||
7. There is no support for client certificates (scheduled to be fixed in
|
||||
1.9.4 release).
|
||||
|
||||
@ -58,7 +58,7 @@ The :mod:`network` module::
|
||||
wlan.scan() # scan for access points
|
||||
wlan.isconnected() # check if the station is connected to an AP
|
||||
wlan.connect('essid', 'password') # connect to an AP
|
||||
wlan.config('mac') # get the interface's MAC adddress
|
||||
wlan.config('mac') # get the interface's MAC address
|
||||
wlan.ifconfig() # get the interface's IP/netmask/gw/DNS addresses
|
||||
|
||||
ap = network.WLAN(network.AP_IF) # create access-point interface
|
||||
@ -138,6 +138,10 @@ Also note that Pin(16) is a special pin (used for wakeup from deepsleep
|
||||
mode) and may be not available for use with higher-level classes like
|
||||
``Neopixel``.
|
||||
|
||||
There's a higher-level abstraction :ref:`machine.Signal <machine.Signal>`
|
||||
which can be used to invert a pin. Useful for illuminating active-low LEDs
|
||||
using ``on()`` or ``value(1)``.
|
||||
|
||||
UART (serial bus)
|
||||
-----------------
|
||||
|
||||
@ -293,6 +297,17 @@ See :ref:`machine.RTC <machine.RTC>` ::
|
||||
(using a custom handler), `RTC.init()` and `RTC.deinit()` are
|
||||
currently not supported.
|
||||
|
||||
WDT (Watchdog timer)
|
||||
--------------------
|
||||
|
||||
See :ref:`machine.WDT <machine.WDT>`. ::
|
||||
|
||||
from machine import WDT
|
||||
|
||||
# enable the WDT
|
||||
wdt = WDT()
|
||||
wdt.feed()
|
||||
|
||||
Deep-sleep mode
|
||||
---------------
|
||||
|
||||
@ -409,6 +424,20 @@ The DHT driver is implemented in software and works on all pins::
|
||||
d.temperature() # eg. 23.6 (°C)
|
||||
d.humidity() # eg. 41.3 (% RH)
|
||||
|
||||
SSD1306 driver
|
||||
--------------
|
||||
|
||||
Driver for SSD1306 monochrome OLED displays. See tutorial :ref:`ssd1306`. ::
|
||||
|
||||
from machine import Pin, I2C
|
||||
import ssd1306
|
||||
|
||||
i2c = I2C(scl=Pin(5), sda=Pin(4), freq=100000)
|
||||
display = ssd1306.SSD1306_I2C(128, 64, i2c)
|
||||
|
||||
display.text('Hello World', 0, 0, 1)
|
||||
display.show()
|
||||
|
||||
WebREPL (web browser interactive prompt)
|
||||
----------------------------------------
|
||||
|
||||
|
||||
@ -31,4 +31,5 @@ to `<https://www.python.org>`__.
|
||||
neopixel.rst
|
||||
apa102.rst
|
||||
dht.rst
|
||||
ssd1306.rst
|
||||
nextsteps.rst
|
||||
|
||||
@ -75,6 +75,10 @@ the DTR and RTS pins wired in a special way then deploying the firmware should
|
||||
be easy as all steps can be done automatically. Boards that have such features
|
||||
include the Adafruit Feather HUZZAH and NodeMCU boards.
|
||||
|
||||
If you do not have such a board, you need keep GPIO0 pulled to ground and reset
|
||||
the device by pulling the reset pin to ground and releasing it again to enter
|
||||
programming mode.
|
||||
|
||||
For best results it is recommended to first erase the entire flash of your
|
||||
device before putting on new MicroPython firmware.
|
||||
|
||||
@ -113,6 +117,10 @@ the firmware (note the ``-fm dio`` option)::
|
||||
If the above commands run without error then MicroPython should be installed on
|
||||
your board!
|
||||
|
||||
If you pulled GPIO0 manually to ground to enter programming mode, release it
|
||||
now and reset the device by again pulling the reset pin to ground for a short
|
||||
duration.
|
||||
|
||||
Serial prompt
|
||||
-------------
|
||||
|
||||
|
||||
93
docs/esp8266/tutorial/ssd1306.rst
Normal file
93
docs/esp8266/tutorial/ssd1306.rst
Normal file
@ -0,0 +1,93 @@
|
||||
.. _ssd1306:
|
||||
|
||||
Using a SSD1306 OLED display
|
||||
============================
|
||||
|
||||
The SSD1306 OLED display uses either a SPI or I2C interface and comes in a variety of
|
||||
sizes (128x64, 128x32, 72x40, 64x48) and colours (white, yellow, blue, yellow + blue).
|
||||
|
||||
Hardware SPI interface::
|
||||
|
||||
from machine import Pin, SPI
|
||||
import ssd1306
|
||||
|
||||
hspi = SPI(1) # sck=14 (scl), mosi=13 (sda), miso=12 (unused)
|
||||
|
||||
dc = Pin(4) # data/command
|
||||
rst = Pin(5) # reset
|
||||
cs = Pin(15) # chip select, some modules do not have a pin for this
|
||||
|
||||
display = ssd1306.SSD1306_SPI(128, 64, hspi, dc, rst, cs)
|
||||
|
||||
Software SPI interface::
|
||||
|
||||
from machine import Pin, SoftSPI
|
||||
import ssd1306
|
||||
|
||||
spi = SoftSPI(baudrate=500000, polarity=1, phase=0, sck=Pin(14), mosi=Pin(13), miso=Pin(12))
|
||||
|
||||
dc = Pin(4) # data/command
|
||||
rst = Pin(5) # reset
|
||||
cs = Pin(15) # chip select, some modules do not have a pin for this
|
||||
|
||||
display = ssd1306.SSD1306_SPI(128, 64, spi, dc, rst, cs)
|
||||
|
||||
I2C interface::
|
||||
|
||||
from machine import Pin, I2C
|
||||
import ssd1306
|
||||
|
||||
# using default address 0x3C
|
||||
i2c = I2C(sda=Pin(4), scl=Pin(5))
|
||||
display = ssd1306.SSD1306_I2C(128, 64, i2c)
|
||||
|
||||
Print Hello World on the first line::
|
||||
|
||||
display.text('Hello, World!', 0, 0, 1)
|
||||
display.show()
|
||||
|
||||
Basic functions::
|
||||
|
||||
display.poweroff() # power off the display, pixels persist in memory
|
||||
display.poweron() # power on the display, pixels redrawn
|
||||
display.contrast(0) # dim
|
||||
display.contrast(255) # bright
|
||||
display.invert(1) # display inverted
|
||||
display.invert(0) # display normal
|
||||
display.rotate(True) # rotate 180 degrees
|
||||
display.rotate(False) # rotate 0 degrees
|
||||
display.show() # write the contents of the FrameBuffer to display memory
|
||||
|
||||
Subclassing FrameBuffer provides support for graphics primitives::
|
||||
|
||||
display.fill(0) # fill entire screen with colour=0
|
||||
display.pixel(0, 10) # get pixel at x=0, y=10
|
||||
display.pixel(0, 10, 1) # set pixel at x=0, y=10 to colour=1
|
||||
display.hline(0, 8, 4, 1) # draw horizontal line x=0, y=8, width=4, colour=1
|
||||
display.vline(0, 8, 4, 1) # draw vertical line x=0, y=8, height=4, colour=1
|
||||
display.line(0, 0, 127, 63, 1) # draw a line from 0,0 to 127,63
|
||||
display.rect(10, 10, 107, 43, 1) # draw a rectangle outline 10,10 to 107,43, colour=1
|
||||
display.fill_rect(10, 10, 107, 43, 1) # draw a solid rectangle 10,10 to 107,43, colour=1
|
||||
display.text('Hello World', 0, 0, 1) # draw some text at x=0, y=0, colour=1
|
||||
display.scroll(20, 0) # scroll 20 pixels to the right
|
||||
|
||||
# draw another FrameBuffer on top of the current one at the given coordinates
|
||||
import framebuf
|
||||
fbuf = framebuf.FrameBuffer(bytearray(8 * 8 * 1), 8, 8, framebuf.MONO_VLSB)
|
||||
fbuf.line(0, 0, 7, 7, 1)
|
||||
display.blit(fbuf, 10, 10, 0) # draw on top at x=10, y=10, key=0
|
||||
display.show()
|
||||
|
||||
Draw the MicroPython logo and print some text::
|
||||
|
||||
display.fill(0)
|
||||
display.fill_rect(0, 0, 32, 32, 1)
|
||||
display.fill_rect(2, 2, 28, 28, 0)
|
||||
display.vline(9, 8, 22, 1)
|
||||
display.vline(16, 2, 22, 1)
|
||||
display.vline(23, 8, 22, 1)
|
||||
display.fill_rect(26, 24, 2, 4, 1)
|
||||
display.text('MicroPython', 40, 0, 1)
|
||||
display.text('SSD1306', 40, 12, 1)
|
||||
display.text('OLED 128x64', 40, 24, 1)
|
||||
display.show()
|
||||
@ -11,5 +11,6 @@ MicroPython documentation and references
|
||||
pyboard/quickref.rst
|
||||
esp8266/quickref.rst
|
||||
esp32/quickref.rst
|
||||
rp2/quickref.rst
|
||||
wipy/quickref.rst
|
||||
unix/quickref.rst
|
||||
|
||||
@ -176,10 +176,6 @@ Exceptions
|
||||
|
||||
.. exception:: OSError
|
||||
|
||||
|see_cpython| `python:OSError`. MicroPython doesn't implement ``errno``
|
||||
attribute, instead use the standard way to access exception arguments:
|
||||
``exc.args[0]``.
|
||||
|
||||
.. exception:: RuntimeError
|
||||
|
||||
.. exception:: StopIteration
|
||||
|
||||
@ -150,28 +150,24 @@ used to transmit or receive many other types of digital signals::
|
||||
from machine import Pin
|
||||
|
||||
r = esp32.RMT(0, pin=Pin(18), clock_div=8)
|
||||
r # RMT(channel=0, pin=18, source_freq=80000000, clock_div=8)
|
||||
r # RMT(channel=0, pin=18, source_freq=80000000, clock_div=8, idle_level=0)
|
||||
|
||||
# To use carrier frequency
|
||||
r = esp32.RMT(0, pin=Pin(18), clock_div=8, carrier_freq=38000)
|
||||
r # RMT(channel=0, pin=18, source_freq=80000000, clock_div=8, carrier_freq=38000, carrier_duty_percent=50)
|
||||
# To apply a carrier frequency to the high output
|
||||
r = esp32.RMT(0, pin=Pin(18), clock_div=8, tx_carrier=(38000, 50, 1))
|
||||
|
||||
# The channel resolution is 100ns (1/(source_freq/clock_div)).
|
||||
r.write_pulses((1, 20, 2, 40), start=0) # Send 0 for 100ns, 1 for 2000ns, 0 for 200ns, 1 for 4000ns
|
||||
r.write_pulses((1, 20, 2, 40), 0) # Send 0 for 100ns, 1 for 2000ns, 0 for 200ns, 1 for 4000ns
|
||||
|
||||
The input to the RMT module is an 80MHz clock (in the future it may be able to
|
||||
configure the input clock but, for now, it's fixed). ``clock_div`` *divides*
|
||||
the clock input which determines the resolution of the RMT channel. The
|
||||
numbers specificed in ``write_pulses`` are multiplied by the resolution to
|
||||
numbers specified in ``write_pulses`` are multiplied by the resolution to
|
||||
define the pulses.
|
||||
|
||||
``clock_div`` is an 8-bit divider (0-255) and each pulse can be defined by
|
||||
multiplying the resolution by a 15-bit (0-32,768) number. There are eight
|
||||
channels (0-7) and each can have a different clock divider.
|
||||
|
||||
To enable the carrier frequency feature of the esp32 hardware, specify the
|
||||
``carrier_freq`` as something like 38000, a typical IR carrier frequency.
|
||||
|
||||
So, in the example above, the 80MHz clock is divided by 8. Thus the
|
||||
resolution is (1/(80Mhz/8)) 100ns. Since the ``start`` level is 0 and toggles
|
||||
with each number, the bitstream is ``0101`` with durations of [100ns, 2000ns,
|
||||
@ -186,16 +182,21 @@ For more details see Espressif's `ESP-IDF RMT documentation.
|
||||
*beta feature* and the interface may change in the future.
|
||||
|
||||
|
||||
.. class:: RMT(channel, *, pin=None, clock_div=8, carrier_freq=0, carrier_duty_percent=50)
|
||||
.. class:: RMT(channel, *, pin=None, clock_div=8, idle_level=False, tx_carrier=None)
|
||||
|
||||
This class provides access to one of the eight RMT channels. *channel* is
|
||||
required and identifies which RMT channel (0-7) will be configured. *pin*,
|
||||
also required, configures which Pin is bound to the RMT channel. *clock_div*
|
||||
is an 8-bit clock divider that divides the source clock (80MHz) to the RMT
|
||||
channel allowing the resolution to be specified. *carrier_freq* is used to
|
||||
enable the carrier feature and specify its frequency, default value is ``0``
|
||||
(not enabled). To enable, specify a positive integer. *carrier_duty_percent*
|
||||
defaults to 50.
|
||||
channel allowing the resolution to be specified. *idle_level* specifies
|
||||
what level the output will be when no transmission is in progress and can
|
||||
be any value that converts to a boolean, with ``True`` representing high
|
||||
voltage and ``False`` representing low.
|
||||
|
||||
To enable the transmission carrier feature, *tx_carrier* should be a tuple
|
||||
of three positive integers: carrier frequency, duty percent (``0`` to
|
||||
``100``) and the output level to apply the carrier to (a boolean as per
|
||||
*idle_level*).
|
||||
|
||||
.. method:: RMT.source_freq()
|
||||
|
||||
@ -207,39 +208,47 @@ For more details see Espressif's `ESP-IDF RMT documentation.
|
||||
Return the clock divider. Note that the channel resolution is
|
||||
``1 / (source_freq / clock_div)``.
|
||||
|
||||
.. method:: RMT.wait_done(timeout=0)
|
||||
.. method:: RMT.wait_done(*, timeout=0)
|
||||
|
||||
Returns ``True`` if the channel is currently transmitting a stream of pulses
|
||||
started with a call to `RMT.write_pulses`.
|
||||
|
||||
If *timeout* (defined in ticks of ``source_freq / clock_div``) is specified
|
||||
the method will wait for *timeout* or until transmission is complete,
|
||||
returning ``False`` if the channel continues to transmit. If looping is
|
||||
enabled with `RMT.loop` and a stream has started, then this method will
|
||||
always (wait and) return ``False``.
|
||||
Returns ``True`` if the channel is idle or ``False`` if a sequence of
|
||||
pulses started with `RMT.write_pulses` is being transmitted. If the
|
||||
*timeout* keyword argument is given then block for up to this many
|
||||
milliseconds for transmission to complete.
|
||||
|
||||
.. method:: RMT.loop(enable_loop)
|
||||
|
||||
Configure looping on the channel. *enable_loop* is bool, set to ``True`` to
|
||||
enable looping on the *next* call to `RMT.write_pulses`. If called with
|
||||
``False`` while a looping stream is currently being transmitted then the
|
||||
current set of pulses will be completed before transmission stops.
|
||||
``False`` while a looping sequence is currently being transmitted then the
|
||||
current loop iteration will be completed and then transmission will stop.
|
||||
|
||||
.. method:: RMT.write_pulses(pulses, start)
|
||||
.. method:: RMT.write_pulses(duration, data=True)
|
||||
|
||||
Begin sending *pulses*, a list or tuple defining the stream of pulses. The
|
||||
length of each pulse is defined by a number to be multiplied by the channel
|
||||
resolution ``(1 / (source_freq / clock_div))``. *start* defines whether the
|
||||
stream starts at 0 or 1.
|
||||
Begin transmitting a sequence. There are three ways to specify this:
|
||||
|
||||
If transmission of a stream is currently in progress then this method will
|
||||
block until transmission of that stream has ended before beginning sending
|
||||
*pulses*.
|
||||
**Mode 1:** *duration* is a list or tuple of durations. The optional *data*
|
||||
argument specifies the initial output level. The output level will toggle
|
||||
after each duration.
|
||||
|
||||
If looping is enabled with `RMT.loop`, the stream of pulses will be repeated
|
||||
indefinitely. Further calls to `RMT.write_pulses` will end the previous
|
||||
stream - blocking until the last set of pulses has been transmitted -
|
||||
before starting the next stream.
|
||||
**Mode 2:** *duration* is a positive integer and *data* is a list or tuple
|
||||
of output levels. *duration* specifies a fixed duration for each.
|
||||
|
||||
**Mode 3:** *duration* and *data* are lists or tuples of equal length,
|
||||
specifying individual durations and the output level for each.
|
||||
|
||||
Durations are in integer units of the channel resolution (as described
|
||||
above), between 1 and 32767 units. Output levels are any value that can
|
||||
be converted to a boolean, with ``True`` representing high voltage and
|
||||
``False`` representing low.
|
||||
|
||||
If transmission of an earlier sequence is in progress then this method will
|
||||
block until that transmission is complete before beginning the new sequence.
|
||||
|
||||
If looping has been enabled with `RMT.loop`, the sequence will be
|
||||
repeated indefinitely. Further calls to this method will block until the
|
||||
end of the current loop iteration before immediately beginning to loop the
|
||||
new sequence of pulses. Looping sequences longer than 126 pulses is not
|
||||
supported by the hardware.
|
||||
|
||||
|
||||
Ultra-Low-Power co-processor
|
||||
@ -269,3 +278,51 @@ Constants
|
||||
esp32.WAKEUP_ANY_HIGH
|
||||
|
||||
Selects the wake level for pins.
|
||||
|
||||
Non-Volatile Storage
|
||||
--------------------
|
||||
|
||||
This class gives access to the Non-Volatile storage managed by ESP-IDF. The NVS is partitioned
|
||||
into namespaces and each namespace contains typed key-value pairs. The keys are strings and the
|
||||
values may be various integer types, strings, and binary blobs. The driver currently only
|
||||
supports 32-bit signed integers and blobs.
|
||||
|
||||
.. warning::
|
||||
|
||||
Changes to NVS need to be committed to flash by calling the commit method. Failure
|
||||
to call commit results in changes being lost at the next reset.
|
||||
|
||||
.. class:: NVS(namespace)
|
||||
|
||||
Create an object providing access to a namespace (which is automatically created if not
|
||||
present).
|
||||
|
||||
.. method:: NVS.set_i32(key, value)
|
||||
|
||||
Sets a 32-bit signed integer value for the specified key. Remember to call *commit*!
|
||||
|
||||
.. method:: NVS.get_i32(key)
|
||||
|
||||
Returns the signed integer value for the specified key. Raises an OSError if the key does not
|
||||
exist or has a different type.
|
||||
|
||||
.. method:: NVS.set_blob(key, value)
|
||||
|
||||
Sets a binary blob value for the specified key. The value passed in must support the buffer
|
||||
protocol, e.g. bytes, bytearray, str. (Note that esp-idf distinguishes blobs and strings, this
|
||||
method always writes a blob even if a string is passed in as value.)
|
||||
Remember to call *commit*!
|
||||
|
||||
.. method:: NVS.get_blob(key, buffer)
|
||||
|
||||
Reads the value of the blob for the specified key into the buffer, which must be a bytearray.
|
||||
Returns the actual length read. Raises an OSError if the key does not exist, has a different
|
||||
type, or if the buffer is too small.
|
||||
|
||||
.. method:: NVS.erase_key(key)
|
||||
|
||||
Erases a key-value pair.
|
||||
|
||||
.. method:: NVS.commit()
|
||||
|
||||
Commits changes made by *set_xxx* methods to flash.
|
||||
|
||||
@ -165,3 +165,14 @@ The following libraries are specific to the ESP8266 and ESP32.
|
||||
|
||||
esp.rst
|
||||
esp32.rst
|
||||
|
||||
|
||||
Libraries specific to the RP2040
|
||||
--------------------------------
|
||||
|
||||
The following libraries are specific to the RP2040, as used in the Raspberry Pi Pico.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
rp2.rst
|
||||
|
||||
79
docs/library/machine.PWM.rst
Normal file
79
docs/library/machine.PWM.rst
Normal file
@ -0,0 +1,79 @@
|
||||
.. currentmodule:: machine
|
||||
.. _machine.PWM:
|
||||
|
||||
class PWM -- pulse width modulation
|
||||
===================================
|
||||
|
||||
This class provides pulse width modulation output.
|
||||
|
||||
Example usage::
|
||||
|
||||
from machine import PWM
|
||||
|
||||
pwm = PWM(pin) # create a PWM object on a pin
|
||||
pwm.duty_u16(32768) # set duty to 50%
|
||||
|
||||
# reinitialise with a period of 200us, duty of 5us
|
||||
pwm.init(freq=5000, duty_ns=5000)
|
||||
|
||||
pwm.duty_ns(3000) # set pulse width to 3us
|
||||
|
||||
pwm.deinit()
|
||||
|
||||
Constructors
|
||||
------------
|
||||
|
||||
.. class:: PWM(dest, \*, freq, duty_u16, duty_ns)
|
||||
|
||||
Construct and return a new PWM object using the following parameters:
|
||||
|
||||
- *dest* is the entity on which the PWM is output, which is usually a
|
||||
:ref:`machine.Pin <machine.Pin>` object, but a port may allow other values,
|
||||
like integers.
|
||||
- *freq* should be an integer which sets the frequency in Hz for the
|
||||
PWM cycle.
|
||||
- *duty_u16* sets the duty cycle as a ratio ``duty_u16 / 65535``.
|
||||
- *duty_ns* sets the pulse width in nanoseconds.
|
||||
|
||||
Setting *freq* may affect other PWM objects if the objects share the same
|
||||
underlying PWM generator (this is hardware specific).
|
||||
Only one of *duty_u16* and *duty_ns* should be specified at a time.
|
||||
|
||||
Methods
|
||||
-------
|
||||
|
||||
.. method:: PWM.init(\*, freq, duty_u16, duty_ns)
|
||||
|
||||
Modify settings for the PWM object. See the above constructor for details
|
||||
about the parameters.
|
||||
|
||||
.. method:: PWM.deinit()
|
||||
|
||||
Disable the PWM output.
|
||||
|
||||
.. method:: PWM.freq([value])
|
||||
|
||||
Get or set the current frequency of the PWM output.
|
||||
|
||||
With no arguments the frequency in Hz is returned.
|
||||
|
||||
With a single *value* argument the frequency is set to that value in Hz. The
|
||||
method may raise a ``ValueError`` if the frequency is outside the valid range.
|
||||
|
||||
.. method:: PWM.duty_u16([value])
|
||||
|
||||
Get or set the current duty cycle of the PWM output, as an unsigned 16-bit
|
||||
value in the range 0 to 65535 inclusive.
|
||||
|
||||
With no arguments the duty cycle is returned.
|
||||
|
||||
With a single *value* argument the duty cycle is set to that value, measured
|
||||
as the ratio ``value / 65535``.
|
||||
|
||||
.. method:: PWM.duty_ns([value])
|
||||
|
||||
Get or set the current pulse width of the PWM output, as a value in nanoseconds.
|
||||
|
||||
With no arguments the pulse width in nanoseconds is returned.
|
||||
|
||||
With a single *value* argument the pulse width is set to that value.
|
||||
@ -10,8 +10,8 @@ and time.
|
||||
Example usage::
|
||||
|
||||
rtc = machine.RTC()
|
||||
rtc.init((2014, 5, 1, 4, 13, 0, 0, 0))
|
||||
print(rtc.now())
|
||||
rtc.datetime((2020, 1, 21, 2, 10, 32, 36, 0))
|
||||
print(rtc.datetime())
|
||||
|
||||
|
||||
Constructors
|
||||
@ -24,6 +24,20 @@ Constructors
|
||||
Methods
|
||||
-------
|
||||
|
||||
.. method:: RTC.datetime([datetimetuple])
|
||||
|
||||
Get or set the date and time of the RTC.
|
||||
|
||||
With no arguments, this method returns an 8-tuple with the current
|
||||
date and time. With 1 argument (being an 8-tuple) it sets the date
|
||||
and time.
|
||||
|
||||
The 8-tuple has the following format:
|
||||
|
||||
(year, month, day, weekday, hours, minutes, seconds, subseconds)
|
||||
|
||||
The meaning of the ``subseconds`` field is hardware dependent.
|
||||
|
||||
.. method:: RTC.init(datetime)
|
||||
|
||||
Initialise the RTC. Datetime is a tuple of the form:
|
||||
|
||||
@ -9,7 +9,7 @@ the most flexible and heterogeneous kind of hardware in MCUs and SoCs,
|
||||
differently greatly from a model to a model. MicroPython's Timer class
|
||||
defines a baseline operation of executing a callback with a given period
|
||||
(or once after some delay), and allow specific boards to define more
|
||||
non-standard behavior (which thus won't be portable to other boards).
|
||||
non-standard behaviour (which thus won't be portable to other boards).
|
||||
|
||||
See discussion of :ref:`important constraints <machine_callbacks>` on
|
||||
Timer callbacks.
|
||||
|
||||
@ -16,7 +16,7 @@ the most flexible and heterogeneous kind of hardware in MCUs and SoCs,
|
||||
differently greatly from a model to a model. MicroPython's Timer class
|
||||
defines a baseline operation of executing a callback with a given period
|
||||
(or once after some delay), and allow specific boards to define more
|
||||
non-standard behavior (which thus won't be portable to other boards).
|
||||
non-standard behaviour (which thus won't be portable to other boards).
|
||||
|
||||
See discussion of :ref:`important constraints <machine_callbacks>` on
|
||||
Timer callbacks.
|
||||
@ -115,7 +115,7 @@ Methods
|
||||
|
||||
.. method:: timerchannel.irq(*, trigger, priority=1, handler=None)
|
||||
|
||||
The behavior of this callback is heavily dependent on the operating
|
||||
The behaviour of this callback is heavily dependent on the operating
|
||||
mode of the timer channel:
|
||||
|
||||
- If mode is ``TimerWiPy.PERIODIC`` the callback is executed periodically
|
||||
|
||||
@ -37,6 +37,14 @@ Reset related functions
|
||||
|
||||
Get the reset cause. See :ref:`constants <machine_constants>` for the possible return values.
|
||||
|
||||
.. function:: bootloader([value])
|
||||
|
||||
Reset the device and enter its bootloader. This is typically used to put the
|
||||
device into a state where it can be programmed with new firmware.
|
||||
|
||||
Some ports support passing in an optional *value* argument which can control
|
||||
which bootloader to enter, what to pass to it, or other things.
|
||||
|
||||
Interrupt related functions
|
||||
---------------------------
|
||||
|
||||
@ -56,9 +64,11 @@ Interrupt related functions
|
||||
Power related functions
|
||||
-----------------------
|
||||
|
||||
.. function:: freq()
|
||||
.. function:: freq([hz])
|
||||
|
||||
Returns CPU frequency in hertz.
|
||||
Returns the CPU frequency in hertz.
|
||||
|
||||
On some ports this can also be used to set the CPU frequency by passing in *hz*.
|
||||
|
||||
.. function:: idle()
|
||||
|
||||
@ -167,6 +177,7 @@ Classes
|
||||
machine.Pin.rst
|
||||
machine.Signal.rst
|
||||
machine.ADC.rst
|
||||
machine.PWM.rst
|
||||
machine.UART.rst
|
||||
machine.SPI.rst
|
||||
machine.I2C.rst
|
||||
|
||||
@ -25,7 +25,7 @@ For this example to work the CC3000 module must have the following connections:
|
||||
- VBEN connected to Y4
|
||||
- IRQ connected to Y3
|
||||
|
||||
It is possible to use other SPI busses and other pins for CS, VBEN and IRQ.
|
||||
It is possible to use other SPI buses and other pins for CS, VBEN and IRQ.
|
||||
|
||||
Constructors
|
||||
------------
|
||||
|
||||
@ -26,7 +26,7 @@ For this example to work the WIZnet5x00 module must have the following connectio
|
||||
- nSS connected to X5
|
||||
- nRESET connected to X4
|
||||
|
||||
It is possible to use other SPI busses and other pins for nSS and nRESET.
|
||||
It is possible to use other SPI buses and other pins for nSS and nRESET.
|
||||
|
||||
Constructors
|
||||
------------
|
||||
|
||||
@ -129,4 +129,5 @@ Methods
|
||||
authmode Authentication mode supported (enumeration, see module constants)
|
||||
password Access password (string)
|
||||
dhcp_hostname The DHCP hostname to use
|
||||
reconnects Number of reconnect attempts to make (integer, 0=none, -1=unlimited)
|
||||
============= ===========
|
||||
|
||||
@ -55,7 +55,7 @@ parameter should be `id`.
|
||||
Activate ("up") or deactivate ("down") the network interface, if
|
||||
a boolean argument is passed. Otherwise, query current state if
|
||||
no argument is provided. Most other methods require an active
|
||||
interface (behavior of calling them on inactive interface is
|
||||
interface (behaviour of calling them on inactive interface is
|
||||
undefined).
|
||||
|
||||
.. method:: AbstractNIC.connect([service_id, key=None, *, ...])
|
||||
|
||||
@ -30,7 +30,7 @@ Constructors
|
||||
the bus, if any). If extra arguments are given, the bus is initialised.
|
||||
See :meth:`CAN.init` for parameters of initialisation.
|
||||
|
||||
The physical pins of the CAN busses are:
|
||||
The physical pins of the CAN buses are:
|
||||
|
||||
- ``CAN(1)`` is on ``YA``: ``(RX, TX) = (Y3, Y4) = (PB8, PB9)``
|
||||
- ``CAN(2)`` is on ``YB``: ``(RX, TX) = (Y5, Y6) = (PB12, PB13)``
|
||||
|
||||
@ -64,7 +64,7 @@ Constructors
|
||||
the bus, if any). If extra arguments are given, the bus is initialised.
|
||||
See ``init`` for parameters of initialisation.
|
||||
|
||||
The physical pins of the I2C busses on Pyboards V1.0 and V1.1 are:
|
||||
The physical pins of the I2C buses on Pyboards V1.0 and V1.1 are:
|
||||
|
||||
- ``I2C(1)`` is on the X position: ``(SCL, SDA) = (X9, X10) = (PB6, PB7)``
|
||||
- ``I2C(2)`` is on the Y position: ``(SCL, SDA) = (Y9, Y10) = (PB10, PB11)``
|
||||
|
||||
@ -98,11 +98,11 @@ Class methods
|
||||
Methods
|
||||
-------
|
||||
|
||||
.. method:: Pin.init(mode, pull=Pin.PULL_NONE, af=-1)
|
||||
.. method:: Pin.init(mode, pull=Pin.PULL_NONE, \*, value=None, alt=-1)
|
||||
|
||||
Initialise the pin:
|
||||
|
||||
- ``mode`` can be one of:
|
||||
- *mode* can be one of:
|
||||
|
||||
- ``Pin.IN`` - configure the pin for input;
|
||||
- ``Pin.OUT_PP`` - configure the pin for output, with push-pull control;
|
||||
@ -111,14 +111,17 @@ Methods
|
||||
- ``Pin.AF_OD`` - configure the pin for alternate function, open-drain;
|
||||
- ``Pin.ANALOG`` - configure the pin for analog.
|
||||
|
||||
- ``pull`` can be one of:
|
||||
- *pull* can be one of:
|
||||
|
||||
- ``Pin.PULL_NONE`` - no pull up or down resistors;
|
||||
- ``Pin.PULL_UP`` - enable the pull-up resistor;
|
||||
- ``Pin.PULL_DOWN`` - enable the pull-down resistor.
|
||||
|
||||
- when mode is ``Pin.AF_PP`` or ``Pin.AF_OD``, then af can be the index or name
|
||||
of one of the alternate functions associated with a pin.
|
||||
- *value* if not None will set the port output value before enabling the pin.
|
||||
|
||||
- *alt* can be used when mode is ``Pin.AF_PP`` or ``Pin.AF_OD`` to set the
|
||||
index or name of one of the alternate functions associated with a pin.
|
||||
This arg was previously called *af* which can still be used if needed.
|
||||
|
||||
Returns: ``None``.
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@ Constructors
|
||||
the bus, if any). If extra arguments are given, the bus is initialised.
|
||||
See ``init`` for parameters of initialisation.
|
||||
|
||||
The physical pins of the SPI busses are:
|
||||
The physical pins of the SPI buses are:
|
||||
|
||||
- ``SPI(1)`` is on the X position: ``(NSS, SCK, MISO, MOSI) = (X5, X6, X7, X8) = (PA4, PA5, PA6, PA7)``
|
||||
- ``SPI(2)`` is on the Y position: ``(NSS, SCK, MISO, MOSI) = (Y5, Y6, Y7, Y8) = (PB12, PB13, PB14, PB15)``
|
||||
|
||||
@ -57,7 +57,7 @@ Constructors
|
||||
the bus, if any). If extra arguments are given, the bus is initialised.
|
||||
See ``init`` for parameters of initialisation.
|
||||
|
||||
The physical pins of the UART busses on Pyboard are:
|
||||
The physical pins of the UART buses on Pyboard are:
|
||||
|
||||
- ``UART(4)`` is on ``XA``: ``(TX, RX) = (X1, X2) = (PA0, PA1)``
|
||||
- ``UART(1)`` is on ``XB``: ``(TX, RX) = (X9, X10) = (PB6, PB7)``
|
||||
|
||||
@ -109,6 +109,16 @@ Methods
|
||||
|
||||
Return value: number of bytes sent.
|
||||
|
||||
.. method:: USB_VCP.irq(handler=None, trigger=IRQ_RX, hard=False)
|
||||
|
||||
Register *handler* to be called whenever an event specified by *trigger*
|
||||
occurs. The *handler* function must take exactly one argument, which will
|
||||
be the USB VCP object. Pass in ``None`` to disable the callback.
|
||||
|
||||
Valid values for *trigger* are:
|
||||
|
||||
- ``USB_VCP.IRQ_RX``: new data is available for reading from the USB VCP object.
|
||||
|
||||
|
||||
Constants
|
||||
---------
|
||||
@ -117,3 +127,7 @@ Constants
|
||||
USB_VCP.CTS
|
||||
|
||||
to select the flow control type.
|
||||
|
||||
.. data:: USB_VCP.IRQ_RX
|
||||
|
||||
IRQ trigger values for :meth:`USB_VCP.irq`.
|
||||
|
||||
@ -126,7 +126,7 @@ Power related functions
|
||||
- pclk2: frequency of the APB2 bus
|
||||
|
||||
If given any arguments then the function sets the frequency of the CPU,
|
||||
and the busses if additional arguments are given. Frequencies are given in
|
||||
and the buses if additional arguments are given. Frequencies are given in
|
||||
Hz. Eg freq(120000000) sets sysclk (the CPU frequency) to 120MHz. Note that
|
||||
not all values are supported and the largest supported frequency not greater
|
||||
than the given value will be selected.
|
||||
|
||||
36
docs/library/rp2.Flash.rst
Normal file
36
docs/library/rp2.Flash.rst
Normal file
@ -0,0 +1,36 @@
|
||||
.. currentmodule:: rp2
|
||||
.. _rp2.Flash:
|
||||
|
||||
class Flash -- access to built-in flash storage
|
||||
===============================================
|
||||
|
||||
This class gives access to the SPI flash memory.
|
||||
|
||||
In most cases, to store persistent data on the device, you'll want to use a
|
||||
higher-level abstraction, for example the filesystem via Python's standard file
|
||||
API, but this interface is useful to :ref:`customise the filesystem
|
||||
configuration <filesystem>` or implement a low-level storage system for your
|
||||
application.
|
||||
|
||||
|
||||
Constructors
|
||||
------------
|
||||
|
||||
.. class:: Flash()
|
||||
|
||||
Gets the singleton object for accessing the SPI flash memory.
|
||||
|
||||
|
||||
Methods
|
||||
-------
|
||||
|
||||
.. method:: Flash.readblocks(block_num, buf)
|
||||
Flash.readblocks(block_num, buf, offset)
|
||||
.. method:: Flash.writeblocks(block_num, buf)
|
||||
Flash.writeblocks(block_num, buf, offset)
|
||||
.. method:: Flash.ioctl(cmd, arg)
|
||||
|
||||
These methods implement the simple and extended
|
||||
:ref:`block protocol <block-device-interface>` defined by
|
||||
:class:`uos.AbstractBlockDev`.
|
||||
|
||||
94
docs/library/rp2.PIO.rst
Normal file
94
docs/library/rp2.PIO.rst
Normal file
@ -0,0 +1,94 @@
|
||||
.. currentmodule:: rp2
|
||||
.. _rp2.PIO:
|
||||
|
||||
class PIO -- advanced PIO usage
|
||||
===============================
|
||||
|
||||
The :class:`PIO` class gives access to an instance of the RP2040's PIO
|
||||
(programmable I/O) interface.
|
||||
|
||||
The preferred way to interact with PIO is using :class:`rp2.StateMachine`, the
|
||||
PIO class is for advanced use.
|
||||
|
||||
For assembling PIO programs, see :func:`rp2.asm_pio`.
|
||||
|
||||
|
||||
Constructors
|
||||
------------
|
||||
|
||||
.. class:: PIO(id)
|
||||
|
||||
Gets the PIO instance numbered *id*. The RP2040 has two PIO instances,
|
||||
numbered 0 and 1.
|
||||
|
||||
Raises a ``ValueError`` if any other argument is provided.
|
||||
|
||||
|
||||
Methods
|
||||
-------
|
||||
|
||||
.. method:: PIO.add_program(program)
|
||||
|
||||
Add the *program* to the instruction memory of this PIO instance.
|
||||
|
||||
The amount of memory available for programs on each PIO instance is
|
||||
limited. If there isn't enough space left in the PIO's program memory
|
||||
this method will raise ``OSError(ENOMEM)``.
|
||||
|
||||
.. method:: PIO.remove_program([program])
|
||||
|
||||
Remove *program* from the instruction memory of this PIO instance.
|
||||
|
||||
If no program is provided, it removes all programs.
|
||||
|
||||
It is not an error to remove a program which has already been removed.
|
||||
|
||||
.. method:: PIO.state_machine(id, [program, ...])
|
||||
|
||||
Gets the state machine numbered *id*. On the RP2040, each PIO instance has
|
||||
four state machines, numbered 0 to 3.
|
||||
|
||||
Optionally initialize it with a *program*: see `StateMachine.init`.
|
||||
|
||||
>>> rp2.PIO(1).state_machine(3)
|
||||
StateMachine(7)
|
||||
|
||||
.. method:: PIO.irq(handler=None, trigger=IRQ_SM0|IRQ_SM1|IRQ_SM2|IRQ_SM3, hard=False)
|
||||
|
||||
Returns the IRQ object for this PIO instance.
|
||||
|
||||
MicroPython only uses IRQ 0 on each PIO instance. IRQ 1 is not available.
|
||||
|
||||
Optionally configure it.
|
||||
|
||||
|
||||
Constants
|
||||
---------
|
||||
|
||||
.. data:: PIO.IN_LOW
|
||||
PIO.IN_HIGH
|
||||
PIO.OUT_LOW
|
||||
PIO.OUT_HIGH
|
||||
|
||||
These constants are used for the *out_init*, *set_init*, and *sideset_init*
|
||||
arguments to `asm_pio`.
|
||||
|
||||
.. data:: PIO.SHIFT_LEFT
|
||||
PIO.SHIFT_RIGHT
|
||||
|
||||
These constants are used for the *in_shiftdir* and *out_shiftdir* arguments
|
||||
to `asm_pio` or `StateMachine.init`.
|
||||
|
||||
.. data:: PIO.JOIN_NONE
|
||||
PIO.JOIN_TX
|
||||
PIO.JOIN_RX
|
||||
|
||||
These constants are used for the *fifo_join* argument to `asm_pio`.
|
||||
|
||||
.. data:: PIO.IRQ_SM0
|
||||
PIO.IRQ_SM1
|
||||
PIO.IRQ_SM2
|
||||
PIO.IRQ_SM3
|
||||
|
||||
These constants are used for the *trigger* argument to `PIO.irq`.
|
||||
|
||||
131
docs/library/rp2.StateMachine.rst
Normal file
131
docs/library/rp2.StateMachine.rst
Normal file
@ -0,0 +1,131 @@
|
||||
.. currentmodule:: rp2
|
||||
.. _rp2.StateMachine:
|
||||
|
||||
class StateMachine -- access to the RP2040's programmable I/O interface
|
||||
=======================================================================
|
||||
|
||||
The :class:`StateMachine` class gives access to the RP2040's PIO (programmable
|
||||
I/O) interface.
|
||||
|
||||
For assembling PIO programs, see :func:`rp2.asm_pio`.
|
||||
|
||||
|
||||
Constructors
|
||||
------------
|
||||
|
||||
.. class:: StateMachine(id, [program, ...])
|
||||
|
||||
Get the state machine numbered *id*. The RP2040 has two identical PIO
|
||||
instances, each with 4 state machines: so there are 8 state machines in
|
||||
total, numbered 0 to 7.
|
||||
|
||||
Optionally initialize it with the given program *program*: see
|
||||
`StateMachine.init`.
|
||||
|
||||
|
||||
Methods
|
||||
-------
|
||||
|
||||
.. method:: StateMachine.init(program, freq=-1, *, in_base=None, out_base=None, set_base=None, jmp_pin=None, sideset_base=None, in_shiftdir=None, out_shiftdir=None, push_thresh=None, pull_thresh=None)
|
||||
|
||||
Configure the state machine instance to run the given *program*.
|
||||
|
||||
The program is added to the instruction memory of this PIO instance. If the
|
||||
instruction memory already contains this program, then its offset is
|
||||
re-used so as to save on instruction memory.
|
||||
|
||||
- *freq* is the frequency in Hz to run the state machine at. Defaults to
|
||||
the system clock frequency.
|
||||
|
||||
The clock divider is computed as ``system clock frequency / freq``, so
|
||||
there can be slight rounding errors.
|
||||
|
||||
The minimum possible clock divider is one 65536th of the system clock: so
|
||||
at the default system clock frequency of 125MHz, the minimum value of
|
||||
*freq* is ``1908``. To run state machines at slower frequencies, you'll
|
||||
need to reduce the system clock speed with `machine.freq()`.
|
||||
- *in_base* is the first pin to use for ``in()`` instructions.
|
||||
- *out_base* is the first pin to use for ``out()`` instructions.
|
||||
- *set_base* is the first pin to use for ``set()`` instructions.
|
||||
- *jmp_pin* is the first pin to use for ``jmp(pin, ...)`` instructions.
|
||||
- *sideset_base* is the first pin to use for side-setting.
|
||||
- *in_shiftdir* is the direction the ISR will shift, either
|
||||
`PIO.SHIFT_LEFT` or `PIO.SHIFT_RIGHT`.
|
||||
- *out_shiftdir* is the direction the OSR will shift, either
|
||||
`PIO.SHIFT_LEFT` or `PIO.SHIFT_RIGHT`.
|
||||
- *push_thresh* is the threshold in bits before auto-push or conditional
|
||||
re-pushing is triggered.
|
||||
- *pull_thresh* is the threshold in bits before auto-push or conditional
|
||||
re-pushing is triggered.
|
||||
|
||||
.. method:: StateMachine.active([value])
|
||||
|
||||
Gets or sets whether the state machine is currently running.
|
||||
|
||||
>>> sm.active()
|
||||
True
|
||||
>>> sm.active(0)
|
||||
False
|
||||
|
||||
.. method:: StateMachine.restart()
|
||||
|
||||
Restarts the state machine and jumps to the beginning of the program.
|
||||
|
||||
This method clears the state machine's internal state using the RP2040's
|
||||
``SM_RESTART`` register. This includes:
|
||||
|
||||
- input and output shift counters
|
||||
- the contents of the input shift register
|
||||
- the delay counter
|
||||
- the waiting-on-IRQ state
|
||||
- a stalled instruction run using `StateMachine.exec()`
|
||||
|
||||
.. method:: StateMachine.exec(instr)
|
||||
|
||||
Execute a single PIO instruction. Uses `asm_pio_encode` to encode the
|
||||
instruction from the given string *instr*.
|
||||
|
||||
>>> sm.exec("set(0, 1)")
|
||||
|
||||
.. method:: StateMachine.get(buf=None, shift=0)
|
||||
|
||||
Pull a word from the state machine's RX FIFO.
|
||||
|
||||
If the FIFO is empty, it blocks until data arrives (i.e. the state machine
|
||||
pushes a word).
|
||||
|
||||
The value is shifted right by *shift* bits before returning, i.e. the
|
||||
return value is ``word >> shift``.
|
||||
|
||||
.. method:: StateMachine.put(value, shift=0)
|
||||
|
||||
Push a word onto the state machine's TX FIFO.
|
||||
|
||||
If the FIFO is full, it blocks until there is space (i.e. the state machine
|
||||
pulls a word).
|
||||
|
||||
The value is first shifted left by *shift* bits, i.e. the state machine
|
||||
receives ``value << shift``.
|
||||
|
||||
.. method:: StateMachine.rx_fifo()
|
||||
|
||||
Returns the number of words in the state machine's RX FIFO. A value of 0
|
||||
indicates the FIFO is empty.
|
||||
|
||||
Useful for checking if data is waiting to be read, before calling
|
||||
`StateMachine.get()`.
|
||||
|
||||
.. method:: StateMachine.tx_fifo()
|
||||
|
||||
Returns the number of words in the state machine's TX FIFO. A value of 0
|
||||
indicates the FIFO is empty.
|
||||
|
||||
Useful for checking if there is space to push another word using
|
||||
`StateMachine.put()`.
|
||||
|
||||
.. method:: StateMachine.irq(handler=None, trigger=0|1, hard=False)
|
||||
|
||||
Returns the IRQ object for the given StateMachine.
|
||||
|
||||
Optionally configure it.
|
||||
|
||||
83
docs/library/rp2.rst
Normal file
83
docs/library/rp2.rst
Normal file
@ -0,0 +1,83 @@
|
||||
.. currentmodule:: rp2
|
||||
|
||||
:mod:`rp2` --- functionality specific to the RP2040
|
||||
===================================================
|
||||
|
||||
.. module:: rp2
|
||||
:synopsis: functionality specific to the RP2
|
||||
|
||||
The ``rp2`` module contains functions and classes specific to the RP2040, as
|
||||
used in the Raspberry Pi Pico.
|
||||
|
||||
See the `RP2040 Python datasheet
|
||||
<https://datasheets.raspberrypi.org/pico/raspberry-pi-pico-python-sdk.pdf>`_
|
||||
for more information, and `pico-micropython-examples
|
||||
<https://github.com/raspberrypi/pico-micropython-examples/tree/master/pio>`_
|
||||
for example code.
|
||||
|
||||
|
||||
PIO related functions
|
||||
---------------------
|
||||
|
||||
The ``rp2`` module includes functions for assembling PIO programs.
|
||||
|
||||
For running PIO programs, see :class:`rp2.StateMachine`.
|
||||
|
||||
.. function:: asm_pio(*, out_init=None, set_init=None, sideset_init=None, in_shiftdir=0, out_shiftdir=0, autopush=False, autopull=False, push_thresh=32, pull_thresh=32, fifo_join=PIO.JOIN_NONE)
|
||||
|
||||
Assemble a PIO program.
|
||||
|
||||
The following parameters control the initial state of the GPIO pins, as one
|
||||
of `PIO.IN_LOW`, `PIO.IN_HIGH`, `PIO.OUT_LOW` or `PIO.OUT_HIGH`. If the
|
||||
program uses more than one pin, provide a tuple, e.g.
|
||||
``out_init=(PIO.OUT_LOW, PIO.OUT_LOW)``.
|
||||
|
||||
- *out_init* configures the pins used for ``out()`` instructions.
|
||||
- *set_init* configures the pins used for ``set()`` instructions. There can
|
||||
be at most 5.
|
||||
- *sideset_init* configures the pins used side-setting. There can be at
|
||||
most 5.
|
||||
|
||||
The following parameters are used by default, but can be overridden in
|
||||
`StateMachine.init()`:
|
||||
|
||||
- *in_shiftdir* is the default direction the ISR will shift, either
|
||||
`PIO.SHIFT_LEFT` or `PIO.SHIFT_RIGHT`.
|
||||
- *out_shiftdir* is the default direction the OSR will shift, either
|
||||
`PIO.SHIFT_LEFT` or `PIO.SHIFT_RIGHT`.
|
||||
- *push_thresh* is the threshold in bits before auto-push or conditional
|
||||
re-pushing is triggered.
|
||||
- *pull_thresh* is the threshold in bits before auto-push or conditional
|
||||
re-pushing is triggered.
|
||||
|
||||
The remaining parameters are:
|
||||
|
||||
- *autopush* configures whether auto-push is enabled.
|
||||
- *autopull* configures whether auto-pull is enabled.
|
||||
- *fifo_join* configures whether the 4-word TX and RX FIFOs should be
|
||||
combined into a single 8-word FIFO for one direction only. The options
|
||||
are `PIO.JOIN_NONE`, `PIO.JOIN_RX` and `PIO.JOIN_TX`.
|
||||
|
||||
.. function:: asm_pio_encode(instr, sideset_count)
|
||||
|
||||
Assemble a single PIO instruction. You usually want to use `asm_pio()`
|
||||
instead.
|
||||
|
||||
>>> rp2.asm_pio_encode("set(0, 1)", 0)
|
||||
57345
|
||||
|
||||
.. class:: PIOASMError
|
||||
|
||||
This exception is raised from `asm_pio()` or `asm_pio_encode()` if there is
|
||||
an error assembling a PIO program.
|
||||
|
||||
|
||||
Classes
|
||||
-------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
rp2.Flash.rst
|
||||
rp2.PIO.rst
|
||||
rp2.StateMachine.rst
|
||||
@ -40,6 +40,10 @@ Core functions
|
||||
|
||||
Returns the corresponding `Task` object.
|
||||
|
||||
.. function:: current_task()
|
||||
|
||||
Return the `Task` object associated with the currently running task.
|
||||
|
||||
.. function:: run(coro)
|
||||
|
||||
Create a new task from the given coroutine and run it until it completes.
|
||||
@ -121,6 +125,9 @@ class Event
|
||||
|
||||
Set the event. Any tasks waiting on the event will be scheduled to run.
|
||||
|
||||
Note: This must be called from within a task. It is not safe to call this
|
||||
from an IRQ, scheduler callback, or other thread. See `ThreadSafeFlag`.
|
||||
|
||||
.. method:: Event.clear()
|
||||
|
||||
Clear the event.
|
||||
@ -132,6 +139,29 @@ class Event
|
||||
|
||||
This is a coroutine.
|
||||
|
||||
class ThreadSafeFlag
|
||||
--------------------
|
||||
|
||||
.. class:: ThreadSafeFlag()
|
||||
|
||||
Create a new flag which can be used to synchronise a task with code running
|
||||
outside the asyncio loop, such as other threads, IRQs, or scheduler
|
||||
callbacks. Flags start in the cleared state.
|
||||
|
||||
.. method:: ThreadSafeFlag.set()
|
||||
|
||||
Set the flag. If there is a task waiting on the event, it will be scheduled
|
||||
to run.
|
||||
|
||||
.. method:: ThreadSafeFlag.wait()
|
||||
|
||||
Wait for the flag to be set. If the flag is already set then it returns
|
||||
immediately.
|
||||
|
||||
A flag may only be waited on by a single task at a time.
|
||||
|
||||
This is a coroutine.
|
||||
|
||||
class Lock
|
||||
----------
|
||||
|
||||
@ -210,6 +240,14 @@ TCP stream connections
|
||||
|
||||
This is a coroutine.
|
||||
|
||||
.. method:: Stream.readinto(buf)
|
||||
|
||||
Read up to n bytes into *buf* with n being equal to the length of *buf*.
|
||||
|
||||
Return the number of bytes read into *buf*.
|
||||
|
||||
This is a coroutine, and a MicroPython extension.
|
||||
|
||||
.. method:: Stream.readline()
|
||||
|
||||
Read a line and return it.
|
||||
|
||||
@ -245,7 +245,7 @@ Module contents
|
||||
|
||||
.. data:: VOID
|
||||
|
||||
``VOID`` is an alias for ``UINT8``, and is provided to conviniently define
|
||||
``VOID`` is an alias for ``UINT8``, and is provided to conveniently define
|
||||
C's void pointers: ``(uctypes.PTR, uctypes.VOID)``.
|
||||
|
||||
.. data:: PTR
|
||||
|
||||
@ -16,13 +16,13 @@ Constants
|
||||
|
||||
Error codes, based on ANSI C/POSIX standard. All error codes start with
|
||||
"E". As mentioned above, inventory of the codes depends on
|
||||
:term:`MicroPython port`. Errors are usually accessible as ``exc.args[0]``
|
||||
:term:`MicroPython port`. Errors are usually accessible as ``exc.errno``
|
||||
where ``exc`` is an instance of `OSError`. Usage example::
|
||||
|
||||
try:
|
||||
uos.mkdir("my_dir")
|
||||
except OSError as exc:
|
||||
if exc.args[0] == uerrno.EEXIST:
|
||||
if exc.errno == uerrno.EEXIST:
|
||||
print("Directory already exists")
|
||||
|
||||
.. data:: errorcode
|
||||
|
||||
@ -6,9 +6,11 @@
|
||||
|
||||
|see_cpython_module| :mod:`python:heapq`.
|
||||
|
||||
This module implements the heap queue algorithm.
|
||||
This module implements the
|
||||
`min heap queue algorithm <https://en.wikipedia.org/wiki/Heap_%28data_structure%29>`_.
|
||||
|
||||
A heap queue is simply a list that has its elements stored in a certain way.
|
||||
A heap queue is essentially a list that has its elements stored in such a way
|
||||
that the first item of the list is always the smallest.
|
||||
|
||||
Functions
|
||||
---------
|
||||
@ -19,8 +21,10 @@ Functions
|
||||
|
||||
.. function:: heappop(heap)
|
||||
|
||||
Pop the first item from the ``heap``, and return it. Raises IndexError if
|
||||
heap is empty.
|
||||
Pop the first item from the ``heap``, and return it. Raise ``IndexError`` if
|
||||
``heap`` is empty.
|
||||
|
||||
The returned item will be the smallest item in the ``heap``.
|
||||
|
||||
.. function:: heapify(x)
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ Conceptual hierarchy
|
||||
Conceptual hierarchy of stream base classes is simplified in MicroPython,
|
||||
as described in this section.
|
||||
|
||||
(Abstract) base stream classes, which serve as a foundation for behavior
|
||||
(Abstract) base stream classes, which serve as a foundation for behaviour
|
||||
of all the concrete classes, adhere to few dichotomies (pair-wise
|
||||
classifications) in CPython. In MicroPython, they are somewhat simplified
|
||||
and made implicit to achieve higher efficiencies and save resources.
|
||||
@ -41,15 +41,15 @@ more concise and efficient programs - something which is highly desirable
|
||||
for MicroPython. So, while MicroPython doesn't support buffered streams,
|
||||
it still provides for no-short-operations streams. Whether there will
|
||||
be short operations or not depends on each particular class' needs, but
|
||||
developers are strongly advised to favor no-short-operations behavior
|
||||
developers are strongly advised to favour no-short-operations behaviour
|
||||
for the reasons stated above. For example, MicroPython sockets are
|
||||
guaranteed to avoid short read/writes. Actually, at this time, there is
|
||||
no example of a short-operations stream class in the core, and one would
|
||||
be a port-specific class, where such a need is governed by hardware
|
||||
peculiarities.
|
||||
|
||||
The no-short-operations behavior gets tricky in case of non-blocking
|
||||
streams, blocking vs non-blocking behavior being another CPython dichotomy,
|
||||
The no-short-operations behaviour gets tricky in case of non-blocking
|
||||
streams, blocking vs non-blocking behaviour being another CPython dichotomy,
|
||||
fully supported by MicroPython. Non-blocking streams never wait for
|
||||
data either to arrive or be written - they read/write whatever possible,
|
||||
or signal lack of data (or ability to write data). Clearly, this conflicts
|
||||
|
||||
@ -87,11 +87,11 @@ Methods
|
||||
`callee-owned tuple`. This function provides an efficient, allocation-free
|
||||
way to poll on streams.
|
||||
|
||||
If *flags* is 1, one-shot behavior for events is employed: streams for
|
||||
If *flags* is 1, one-shot behaviour for events is employed: streams for
|
||||
which events happened will have their event masks automatically reset
|
||||
(equivalent to ``poll.modify(obj, 0)``), so new events for such a stream
|
||||
won't be processed until new mask is set with `poll.modify()`. This
|
||||
behavior is useful for asynchronous I/O schedulers.
|
||||
behaviour is useful for asynchronous I/O schedulers.
|
||||
|
||||
.. admonition:: Difference to CPython
|
||||
:class: attention
|
||||
|
||||
@ -222,7 +222,7 @@ Methods
|
||||
Unlike `send()`, this method will try to send all of data, by sending data
|
||||
chunk by chunk consecutively.
|
||||
|
||||
The behavior of this method on non-blocking sockets is undefined. Due to this,
|
||||
The behaviour of this method on non-blocking sockets is undefined. Due to this,
|
||||
on MicroPython, it's recommended to use `write()` method instead, which
|
||||
has the same "no short writes" policy for blocking sockets, and will return
|
||||
number of bytes sent on non-blocking sockets.
|
||||
|
||||
@ -13,16 +13,24 @@ facilities for network sockets, both client-side and server-side.
|
||||
Functions
|
||||
---------
|
||||
|
||||
.. function:: ussl.wrap_socket(sock, server_side=False, keyfile=None, certfile=None, cert_reqs=CERT_NONE, ca_certs=None)
|
||||
.. function:: ussl.wrap_socket(sock, server_side=False, keyfile=None, certfile=None, cert_reqs=CERT_NONE, ca_certs=None, do_handshake=True)
|
||||
|
||||
Takes a `stream` *sock* (usually usocket.socket instance of ``SOCK_STREAM`` type),
|
||||
and returns an instance of ssl.SSLSocket, which wraps the underlying stream in
|
||||
an SSL context. Returned object has the usual `stream` interface methods like
|
||||
``read()``, ``write()``, etc. In MicroPython, the returned object does not expose
|
||||
socket interface and methods like ``recv()``, ``send()``. In particular, a
|
||||
server-side SSL socket should be created from a normal socket returned from
|
||||
``read()``, ``write()``, etc.
|
||||
A server-side SSL socket should be created from a normal socket returned from
|
||||
:meth:`~usocket.socket.accept()` on a non-SSL listening server socket.
|
||||
|
||||
- *do_handshake* determines whether the handshake is done as part of the ``wrap_socket``
|
||||
or whether it is deferred to be done as part of the initial reads or writes
|
||||
(there is no ``do_handshake`` method as in CPython).
|
||||
For blocking sockets doing the handshake immediately is standard. For non-blocking
|
||||
sockets (i.e. when the *sock* passed into ``wrap_socket`` is in non-blocking mode)
|
||||
the handshake should generally be deferred because otherwise ``wrap_socket`` blocks
|
||||
until it completes. Note that in AXTLS the handshake can be deferred until the first
|
||||
read or write but it then blocks until completion.
|
||||
|
||||
Depending on the underlying module implementation in a particular
|
||||
:term:`MicroPython port`, some or all keyword arguments above may be not supported.
|
||||
|
||||
@ -31,6 +39,11 @@ Functions
|
||||
Some implementations of ``ussl`` module do NOT validate server certificates,
|
||||
which makes an SSL connection established prone to man-in-the-middle attacks.
|
||||
|
||||
CPython's ``wrap_socket`` returns an ``SSLSocket`` object which has methods typical
|
||||
for sockets, such as ``send``, ``recv``, etc. MicroPython's ``wrap_socket``
|
||||
returns an object more similar to CPython's ``SSLObject`` which does not have
|
||||
these socket methods.
|
||||
|
||||
Exceptions
|
||||
----------
|
||||
|
||||
|
||||
@ -173,7 +173,7 @@ Functions
|
||||
long sleep), then once you finally look again, it may seem to you that only 1 hour
|
||||
has passed. To avoid this mistake, just look at the clock regularly. Your application
|
||||
should do the same. "Too long sleep" metaphor also maps directly to application
|
||||
behavior: don't let your application run any single task for too long. Run tasks
|
||||
behaviour: don't let your application run any single task for too long. Run tasks
|
||||
in steps, and do time-keeping inbetween.
|
||||
|
||||
`ticks_diff()` is designed to accommodate various usage patterns, among them:
|
||||
|
||||
@ -93,7 +93,7 @@ on the pin for any changes, and the following will occur:
|
||||
running Python script.
|
||||
3. The microcontroller starts executing the special interrupt handler
|
||||
associated with the switch's external trigger. This interrupt handler
|
||||
get the function that you registered with ``sw.callback()`` and executes
|
||||
gets the function that you registered with ``sw.callback()`` and executes
|
||||
it.
|
||||
4. Your callback function is executed until it finishes, returning control
|
||||
to the switch interrupt handler.
|
||||
|
||||
@ -48,7 +48,9 @@ Running ``pyboard.py --help`` gives the following output:
|
||||
available
|
||||
--follow follow the output after running the scripts
|
||||
[default if no scripts given]
|
||||
-f, --filesystem perform a filesystem action
|
||||
-f, --filesystem perform a filesystem action: cp local :device | cp
|
||||
:device local | cat path | ls [path] | rm path | mkdir
|
||||
path | rmdir path
|
||||
|
||||
Running a command on the device
|
||||
-------------------------------
|
||||
|
||||
18
docs/rp2/general.rst
Normal file
18
docs/rp2/general.rst
Normal file
@ -0,0 +1,18 @@
|
||||
.. _rp2_general:
|
||||
|
||||
General information about the RP2xxx port
|
||||
=========================================
|
||||
|
||||
The rp2 port supports boards powered by the Raspberry Pi Foundation's RP2xxx
|
||||
family of microcontrollers, most notably the Raspberry Pi Pico that employs
|
||||
the RP2040.
|
||||
|
||||
Technical specifications and SoC datasheets
|
||||
-------------------------------------------
|
||||
|
||||
Datasheets!
|
||||
|
||||
Short summary of tech specs!
|
||||
|
||||
Description of general structure of the port (it's built on top of the APIs
|
||||
provided by the Raspberry Pi SDK).
|
||||
BIN
docs/rp2/img/rpipico.jpg
Normal file
BIN
docs/rp2/img/rpipico.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 87 KiB |
288
docs/rp2/quickref.rst
Normal file
288
docs/rp2/quickref.rst
Normal file
@ -0,0 +1,288 @@
|
||||
.. _rp2_quickref:
|
||||
|
||||
Quick reference for the RP2
|
||||
===========================
|
||||
|
||||
.. image:: img/rpipico.jpg
|
||||
:alt: Raspberry Pi Pico
|
||||
:width: 640px
|
||||
|
||||
The Raspberry Pi Pico Development Board (image attribution: Raspberry Pi Foundation).
|
||||
|
||||
Below is a quick reference for Raspberry Pi RP2xxx boards. If it is your first time
|
||||
working with this board it may be useful to get an overview of the microcontroller:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
general.rst
|
||||
tutorial/intro.rst
|
||||
|
||||
Installing MicroPython
|
||||
----------------------
|
||||
|
||||
See the corresponding section of tutorial: :ref:`rp2_intro`. It also includes
|
||||
a troubleshooting subsection.
|
||||
|
||||
General board control
|
||||
---------------------
|
||||
|
||||
The MicroPython REPL is on the USB serial port.
|
||||
Tab-completion is useful to find out what methods an object has.
|
||||
Paste mode (ctrl-E) is useful to paste a large slab of Python code into
|
||||
the REPL.
|
||||
|
||||
The :mod:`machine` module::
|
||||
|
||||
import machine
|
||||
|
||||
machine.freq() # get the current frequency of the CPU
|
||||
machine.freq(240000000) # set the CPU frequency to 240 MHz
|
||||
|
||||
The :mod:`rp2` module::
|
||||
|
||||
import rp2
|
||||
|
||||
Delay and timing
|
||||
----------------
|
||||
|
||||
Use the :mod:`time <utime>` module::
|
||||
|
||||
import time
|
||||
|
||||
time.sleep(1) # sleep for 1 second
|
||||
time.sleep_ms(500) # sleep for 500 milliseconds
|
||||
time.sleep_us(10) # sleep for 10 microseconds
|
||||
start = time.ticks_ms() # get millisecond counter
|
||||
delta = time.ticks_diff(time.ticks_ms(), start) # compute time difference
|
||||
|
||||
Timers
|
||||
------
|
||||
|
||||
How do they work?
|
||||
|
||||
.. _rp2_Pins_and_GPIO:
|
||||
|
||||
Pins and GPIO
|
||||
-------------
|
||||
|
||||
Use the :ref:`machine.Pin <machine.Pin>` class::
|
||||
|
||||
from machine import Pin
|
||||
|
||||
p0 = Pin(0, Pin.OUT) # create output pin on GPIO0
|
||||
p0.on() # set pin to "on" (high) level
|
||||
p0.off() # set pin to "off" (low) level
|
||||
p0.value(1) # set pin to on/high
|
||||
|
||||
p2 = Pin(2, Pin.IN) # create input pin on GPIO2
|
||||
print(p2.value()) # get value, 0 or 1
|
||||
|
||||
p4 = Pin(4, Pin.IN, Pin.PULL_UP) # enable internal pull-up resistor
|
||||
p5 = Pin(5, Pin.OUT, value=1) # set pin high on creation
|
||||
|
||||
UART (serial bus)
|
||||
-----------------
|
||||
|
||||
See :ref:`machine.UART <machine.UART>`. ::
|
||||
|
||||
from machine import UART
|
||||
|
||||
uart1 = UART(1, baudrate=9600, tx=33, rx=32)
|
||||
uart1.write('hello') # write 5 bytes
|
||||
uart1.read(5) # read up to 5 bytes
|
||||
|
||||
|
||||
PWM (pulse width modulation)
|
||||
----------------------------
|
||||
|
||||
How does PWM work on the RPi RP2xxx?
|
||||
|
||||
Use the ``machine.PWM`` class::
|
||||
|
||||
from machine import Pin, PWM
|
||||
|
||||
pwm0 = PWM(Pin(0)) # create PWM object from a pin
|
||||
pwm0.freq() # get current frequency
|
||||
pwm0.freq(1000) # set frequency
|
||||
pwm0.duty_u16() # get current duty cycle, range 0-65535
|
||||
pwm0.duty_u16(200) # set duty cycle, range 0-65535
|
||||
pwm0.deinit() # turn off PWM on the pin
|
||||
|
||||
ADC (analog to digital conversion)
|
||||
----------------------------------
|
||||
|
||||
How does the ADC module work?
|
||||
|
||||
Use the :ref:`machine.ADC <machine.ADC>` class::
|
||||
|
||||
from machine import ADC
|
||||
|
||||
adc = ADC(Pin(32)) # create ADC object on ADC pin
|
||||
adc.read_u16() # read value, 0-65535 across voltage range 0.0v - 3.3v
|
||||
|
||||
Software SPI bus
|
||||
----------------
|
||||
|
||||
Software SPI (using bit-banging) works on all pins, and is accessed via the
|
||||
:ref:`machine.SoftSPI <machine.SoftSPI>` class::
|
||||
|
||||
from machine import Pin, SoftSPI
|
||||
|
||||
# construct a SoftSPI bus on the given pins
|
||||
# polarity is the idle state of SCK
|
||||
# phase=0 means sample on the first edge of SCK, phase=1 means the second
|
||||
spi = SoftSPI(baudrate=100000, polarity=1, phase=0, sck=Pin(0), mosi=Pin(2), miso=Pin(4))
|
||||
|
||||
spi.init(baudrate=200000) # set the baudrate
|
||||
|
||||
spi.read(10) # read 10 bytes on MISO
|
||||
spi.read(10, 0xff) # read 10 bytes while outputting 0xff on MOSI
|
||||
|
||||
buf = bytearray(50) # create a buffer
|
||||
spi.readinto(buf) # read into the given buffer (reads 50 bytes in this case)
|
||||
spi.readinto(buf, 0xff) # read into the given buffer and output 0xff on MOSI
|
||||
|
||||
spi.write(b'12345') # write 5 bytes on MOSI
|
||||
|
||||
buf = bytearray(4) # create a buffer
|
||||
spi.write_readinto(b'1234', buf) # write to MOSI and read from MISO into the buffer
|
||||
spi.write_readinto(buf, buf) # write buf to MOSI and read MISO back into buf
|
||||
|
||||
.. Warning::
|
||||
Currently *all* of ``sck``, ``mosi`` and ``miso`` *must* be specified when
|
||||
initialising Software SPI.
|
||||
|
||||
Hardware SPI bus
|
||||
----------------
|
||||
|
||||
Hardware SPI is accessed via the :ref:`machine.SPI <machine.SPI>` class and
|
||||
has the same methods as software SPI above::
|
||||
|
||||
from machine import Pin, SPI
|
||||
|
||||
spi = SPI(1, 10000000)
|
||||
spi = SPI(1, 10000000, sck=Pin(14), mosi=Pin(13), miso=Pin(12))
|
||||
spi = SPI(2, baudrate=80000000, polarity=0, phase=0, bits=8, firstbit=0, sck=Pin(18), mosi=Pin(23), miso=Pin(19))
|
||||
|
||||
Software I2C bus
|
||||
----------------
|
||||
|
||||
Software I2C (using bit-banging) works on all output-capable pins, and is
|
||||
accessed via the :ref:`machine.SoftI2C <machine.SoftI2C>` class::
|
||||
|
||||
from machine import Pin, SoftI2C
|
||||
|
||||
i2c = SoftI2C(scl=Pin(5), sda=Pin(4), freq=100000)
|
||||
|
||||
i2c.scan() # scan for devices
|
||||
|
||||
i2c.readfrom(0x3a, 4) # read 4 bytes from device with address 0x3a
|
||||
i2c.writeto(0x3a, '12') # write '12' to device with address 0x3a
|
||||
|
||||
buf = bytearray(10) # create a buffer with 10 bytes
|
||||
i2c.writeto(0x3a, buf) # write the given buffer to the slave
|
||||
|
||||
Hardware I2C bus
|
||||
----------------
|
||||
|
||||
The driver is accessed via the :ref:`machine.I2C <machine.I2C>` class and
|
||||
has the same methods as software I2C above::
|
||||
|
||||
from machine import Pin, I2C
|
||||
|
||||
i2c = I2C(0)
|
||||
i2c = I2C(1, scl=Pin(5), sda=Pin(4), freq=400000)
|
||||
|
||||
Real time clock (RTC)
|
||||
---------------------
|
||||
|
||||
See :ref:`machine.RTC <machine.RTC>` ::
|
||||
|
||||
from machine import RTC
|
||||
|
||||
rtc = RTC()
|
||||
rtc.datetime((2017, 8, 23, 2, 12, 48, 0, 0)) # set a specific date and time
|
||||
rtc.datetime() # get date and time
|
||||
|
||||
WDT (Watchdog timer)
|
||||
--------------------
|
||||
|
||||
Is there a watchdog timer?
|
||||
|
||||
See :ref:`machine.WDT <machine.WDT>`. ::
|
||||
|
||||
from machine import WDT
|
||||
|
||||
# enable the WDT with a timeout of 5s (1s is the minimum)
|
||||
wdt = WDT(timeout=5000)
|
||||
wdt.feed()
|
||||
|
||||
Deep-sleep mode
|
||||
---------------
|
||||
|
||||
Is there deep-sleep support for the rp2?
|
||||
|
||||
The following code can be used to sleep, wake and check the reset cause::
|
||||
|
||||
import machine
|
||||
|
||||
# check if the device woke from a deep sleep
|
||||
if machine.reset_cause() == machine.DEEPSLEEP_RESET:
|
||||
print('woke from a deep sleep')
|
||||
|
||||
# put the device to sleep for 10 seconds
|
||||
machine.deepsleep(10000)
|
||||
|
||||
OneWire driver
|
||||
--------------
|
||||
|
||||
The OneWire driver is implemented in software and works on all pins::
|
||||
|
||||
from machine import Pin
|
||||
import onewire
|
||||
|
||||
ow = onewire.OneWire(Pin(12)) # create a OneWire bus on GPIO12
|
||||
ow.scan() # return a list of devices on the bus
|
||||
ow.reset() # reset the bus
|
||||
ow.readbyte() # read a byte
|
||||
ow.writebyte(0x12) # write a byte on the bus
|
||||
ow.write('123') # write bytes on the bus
|
||||
ow.select_rom(b'12345678') # select a specific device by its ROM code
|
||||
|
||||
There is a specific driver for DS18S20 and DS18B20 devices::
|
||||
|
||||
import time, ds18x20
|
||||
ds = ds18x20.DS18X20(ow)
|
||||
roms = ds.scan()
|
||||
ds.convert_temp()
|
||||
time.sleep_ms(750)
|
||||
for rom in roms:
|
||||
print(ds.read_temp(rom))
|
||||
|
||||
Be sure to put a 4.7k pull-up resistor on the data line. Note that
|
||||
the ``convert_temp()`` method must be called each time you want to
|
||||
sample the temperature.
|
||||
|
||||
NeoPixel and APA106 driver
|
||||
--------------------------
|
||||
|
||||
Use the ``neopixel`` and ``apa106`` modules::
|
||||
|
||||
from machine import Pin
|
||||
from neopixel import NeoPixel
|
||||
|
||||
pin = Pin(0, Pin.OUT) # set GPIO0 to output to drive NeoPixels
|
||||
np = NeoPixel(pin, 8) # create NeoPixel driver on GPIO0 for 8 pixels
|
||||
np[0] = (255, 255, 255) # set the first pixel to white
|
||||
np.write() # write data to all pixels
|
||||
r, g, b = np[0] # get first pixel colour
|
||||
|
||||
|
||||
The APA106 driver extends NeoPixel, but internally uses a different colour order::
|
||||
|
||||
from apa106 import APA106
|
||||
ap = APA106(pin, 8)
|
||||
r, g, b = ap[0]
|
||||
|
||||
APA102 (DotStar) uses a different driver as it has an additional clock pin.
|
||||
6
docs/rp2/tutorial/intro.rst
Normal file
6
docs/rp2/tutorial/intro.rst
Normal file
@ -0,0 +1,6 @@
|
||||
.. _rp2_intro:
|
||||
|
||||
Getting started with MicroPython on the RP2xxx
|
||||
==============================================
|
||||
|
||||
Let's get started!
|
||||
4
docs/templates/topindex.html
vendored
4
docs/templates/topindex.html
vendored
@ -58,6 +58,10 @@
|
||||
<a class="biglink" href="{{ pathto("esp32/quickref") }}">Quick reference for the ESP32</a><br/>
|
||||
<span class="linkdescr">pinout for ESP32-based boards, snippets of useful code, and a tutorial</span>
|
||||
</p>
|
||||
<p class="biglink">
|
||||
<a class="biglink" href="{{ pathto("rp2/quickref") }}">Quick reference for the Raspberry Pi RP2xxx</a><br/>
|
||||
<span class="linkdescr">pinout for rp2xxx-based boards, snippets of useful code, and a tutorial</span>
|
||||
</p>
|
||||
<p class="biglink">
|
||||
<a class="biglink" href="{{ pathto("wipy/quickref") }}">Quick reference for the WiPy/CC3200</a><br/>
|
||||
<span class="linkdescr">pinout for the WiPy/CC3200, snippets of useful code, and a tutorial</span>
|
||||
|
||||
@ -112,7 +112,7 @@ void cyw43_deinit(cyw43_t *self) {
|
||||
self->itf_state = 0;
|
||||
|
||||
// Disable async polling
|
||||
SDMMC1->MASK &= ~SDMMC_MASK_SDIOITIE;
|
||||
sdio_enable_irq(false);
|
||||
cyw43_poll = NULL;
|
||||
|
||||
#ifdef pyb_pin_WL_RFSW_VDD
|
||||
@ -164,7 +164,7 @@ STATIC int cyw43_ensure_up(cyw43_t *self) {
|
||||
cyw43_sleep = CYW43_SLEEP_MAX;
|
||||
cyw43_poll = cyw43_poll_func;
|
||||
#if USE_SDIOIT
|
||||
SDMMC1->MASK |= SDMMC_MASK_SDIOITIE;
|
||||
sdio_enable_irq(true);
|
||||
#else
|
||||
extern void extint_set(const pin_obj_t *pin, uint32_t mode);
|
||||
extint_set(pyb_pin_WL_HOST_WAKE, GPIO_MODE_IT_FALLING);
|
||||
@ -209,7 +209,7 @@ STATIC void cyw43_poll_func(void) {
|
||||
}
|
||||
|
||||
#if USE_SDIOIT
|
||||
SDMMC1->MASK |= SDMMC_MASK_SDIOITIE;
|
||||
sdio_enable_irq(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -227,10 +227,7 @@ int cyw43_cb_read_host_interrupt_pin(void *cb_data) {
|
||||
void cyw43_cb_ensure_awake(void *cb_data) {
|
||||
cyw43_sleep = CYW43_SLEEP_MAX;
|
||||
#if !USE_SDIOIT
|
||||
if (__HAL_RCC_SDMMC1_IS_CLK_DISABLED()) {
|
||||
__HAL_RCC_SDMMC1_CLK_ENABLE(); // enable SDIO peripheral
|
||||
sdio_enable_high_speed_4bit();
|
||||
}
|
||||
sdio_reenable();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -55,7 +55,7 @@ STATIC void cywbt_wait_cts_low(void) {
|
||||
}
|
||||
mp_hal_delay_ms(1);
|
||||
}
|
||||
mp_hal_pin_config_alt_static(pyb_pin_BT_CTS, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_USART6_CTS);
|
||||
mp_hal_pin_config_alt(pyb_pin_BT_CTS, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, AF_FN_UART, mp_bluetooth_hci_uart_obj.uart_id);
|
||||
}
|
||||
|
||||
STATIC int cywbt_hci_cmd_raw(size_t len, uint8_t *buf) {
|
||||
@ -149,10 +149,14 @@ STATIC int cywbt_download_firmware(const uint8_t *firmware) {
|
||||
}
|
||||
|
||||
// RF switch must select high path during BT patch boot
|
||||
#if MICROPY_HW_ENABLE_RF_SWITCH
|
||||
mp_hal_pin_config(pyb_pin_WL_GPIO_1, MP_HAL_PIN_MODE_INPUT, MP_HAL_PIN_PULL_UP, 0);
|
||||
#endif
|
||||
mp_hal_delay_ms(10); // give some time for CTS to go high
|
||||
cywbt_wait_cts_low();
|
||||
#if MICROPY_HW_ENABLE_RF_SWITCH
|
||||
mp_hal_pin_config(pyb_pin_WL_GPIO_1, MP_HAL_PIN_MODE_INPUT, MP_HAL_PIN_PULL_DOWN, 0); // Select chip antenna (could also select external)
|
||||
#endif
|
||||
|
||||
mp_bluetooth_hci_uart_set_baudrate(115200);
|
||||
cywbt_set_baudrate(3000000);
|
||||
@ -170,9 +174,11 @@ int mp_bluetooth_hci_controller_init(void) {
|
||||
mp_hal_pin_output(pyb_pin_BT_DEV_WAKE);
|
||||
mp_hal_pin_low(pyb_pin_BT_DEV_WAKE);
|
||||
|
||||
#if MICROPY_HW_ENABLE_RF_SWITCH
|
||||
// TODO don't select antenna if wifi is enabled
|
||||
mp_hal_pin_config(pyb_pin_WL_GPIO_4, MP_HAL_PIN_MODE_OUTPUT, MP_HAL_PIN_PULL_NONE, 0); // RF-switch power
|
||||
mp_hal_pin_high(pyb_pin_WL_GPIO_4); // Turn the RF-switch on
|
||||
#endif
|
||||
|
||||
uint8_t buf[256];
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@ SET_PAGE_ADDR = const(0x22)
|
||||
SET_DISP_START_LINE = const(0x40)
|
||||
SET_SEG_REMAP = const(0xA0)
|
||||
SET_MUX_RATIO = const(0xA8)
|
||||
SET_IREF_SELECT = const(0xAD)
|
||||
SET_COM_OUT_DIR = const(0xC0)
|
||||
SET_DISP_OFFSET = const(0xD3)
|
||||
SET_COM_PIN_CFG = const(0xDA)
|
||||
@ -37,12 +38,12 @@ class SSD1306(framebuf.FrameBuffer):
|
||||
|
||||
def init_display(self):
|
||||
for cmd in (
|
||||
SET_DISP | 0x00, # off
|
||||
SET_DISP, # display off
|
||||
# address setting
|
||||
SET_MEM_ADDR,
|
||||
0x00, # horizontal
|
||||
# resolution and layout
|
||||
SET_DISP_START_LINE | 0x00,
|
||||
SET_DISP_START_LINE, # start at line 0
|
||||
SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
|
||||
SET_MUX_RATIO,
|
||||
self.height - 1,
|
||||
@ -63,17 +64,19 @@ class SSD1306(framebuf.FrameBuffer):
|
||||
0xFF, # maximum
|
||||
SET_ENTIRE_ON, # output follows RAM contents
|
||||
SET_NORM_INV, # not inverted
|
||||
SET_IREF_SELECT,
|
||||
0x30, # enable internal IREF during display on
|
||||
# charge pump
|
||||
SET_CHARGE_PUMP,
|
||||
0x10 if self.external_vcc else 0x14,
|
||||
SET_DISP | 0x01,
|
||||
SET_DISP | 0x01, # display on
|
||||
): # on
|
||||
self.write_cmd(cmd)
|
||||
self.fill(0)
|
||||
self.show()
|
||||
|
||||
def poweroff(self):
|
||||
self.write_cmd(SET_DISP | 0x00)
|
||||
self.write_cmd(SET_DISP)
|
||||
|
||||
def poweron(self):
|
||||
self.write_cmd(SET_DISP | 0x01)
|
||||
@ -85,13 +88,18 @@ class SSD1306(framebuf.FrameBuffer):
|
||||
def invert(self, invert):
|
||||
self.write_cmd(SET_NORM_INV | (invert & 1))
|
||||
|
||||
def rotate(self, rotate):
|
||||
self.write_cmd(SET_COM_OUT_DIR | ((rotate & 1) << 3))
|
||||
self.write_cmd(SET_SEG_REMAP | (rotate & 1))
|
||||
|
||||
def show(self):
|
||||
x0 = 0
|
||||
x1 = self.width - 1
|
||||
if self.width == 64:
|
||||
# displays with width of 64 pixels are shifted by 32
|
||||
x0 += 32
|
||||
x1 += 32
|
||||
if self.width != 128:
|
||||
# narrow displays use centred columns
|
||||
col_offset = (128 - self.width) // 2
|
||||
x0 += col_offset
|
||||
x1 += col_offset
|
||||
self.write_cmd(SET_COL_ADDR)
|
||||
self.write_cmd(x0)
|
||||
self.write_cmd(x1)
|
||||
|
||||
@ -176,6 +176,7 @@ class SDCard:
|
||||
self.spi.readinto(self.tokenbuf, 0xFF)
|
||||
if self.tokenbuf[0] == _TOKEN_DATA:
|
||||
break
|
||||
time.sleep_ms(1)
|
||||
else:
|
||||
self.cs(1)
|
||||
raise OSError("timeout waiting for response")
|
||||
|
||||
@ -21,6 +21,9 @@ CWARN = -Wall -Werror
|
||||
CWARN += -Wpointer-arith -Wuninitialized
|
||||
CFLAGS = $(INC) $(CWARN) -std=gnu99 -DUNIX $(CFLAGS_MOD) $(COPT) $(CFLAGS_EXTRA)
|
||||
|
||||
# Some systems (eg MacOS) need -fno-common so that mp_state_ctx is placed in the BSS.
|
||||
CFLAGS += -fno-common
|
||||
|
||||
# Debugging/Optimization
|
||||
ifdef DEBUG
|
||||
CFLAGS += -g
|
||||
@ -133,7 +136,6 @@ SRC_C = $(addprefix ports/unix/,\
|
||||
gccollect.c \
|
||||
unix_mphal.c \
|
||||
input.c \
|
||||
file.c \
|
||||
modmachine.c \
|
||||
modos.c \
|
||||
moduselect.c \
|
||||
@ -146,6 +148,7 @@ SRC_C = $(addprefix ports/unix/,\
|
||||
LIB_SRC_C = $(addprefix lib/,\
|
||||
$(LIB_SRC_C_EXTRA) \
|
||||
utils/printf.c \
|
||||
utils/gchelper_generic.c \
|
||||
timeutils/timeutils.c \
|
||||
)
|
||||
|
||||
|
||||
@ -53,7 +53,7 @@ mp_obj_t execute_from_str(const char *str) {
|
||||
|
||||
int main() {
|
||||
// Initialized stack limit
|
||||
mp_stack_set_limit(40000 * (BYTES_PER_WORD / 4));
|
||||
mp_stack_set_limit(40000 * (sizeof(void *) / 4));
|
||||
// Initialize heap
|
||||
gc_init(heap, heap + sizeof(heap));
|
||||
// Initialize interpreter
|
||||
|
||||
@ -22,6 +22,7 @@ PIO_RX_PIN = Pin(3, Pin.IN, Pin.PULL_UP)
|
||||
autopush=True,
|
||||
push_thresh=8,
|
||||
in_shiftdir=rp2.PIO.SHIFT_RIGHT,
|
||||
fifo_join=PIO.JOIN_RX,
|
||||
)
|
||||
def uart_rx_mini():
|
||||
# fmt: off
|
||||
|
||||
@ -31,4 +31,7 @@ const mp_obj_module_t example_user_cmodule = {
|
||||
};
|
||||
|
||||
// Register the module to make it available in Python.
|
||||
MP_REGISTER_MODULE(MP_QSTR_cexample, example_user_cmodule, MODULE_CEXAMPLE_ENABLED);
|
||||
// Note: the "1" in the third argument means this module is always enabled.
|
||||
// This "1" can be optionally replaced with a macro like MODULE_CEXAMPLE_ENABLED
|
||||
// which can then be used to conditionally enable this module.
|
||||
MP_REGISTER_MODULE(MP_QSTR_cexample, example_user_cmodule, 1);
|
||||
|
||||
15
examples/usercmodule/cexample/micropython.cmake
Normal file
15
examples/usercmodule/cexample/micropython.cmake
Normal file
@ -0,0 +1,15 @@
|
||||
# Create an INTERFACE library for our C module.
|
||||
add_library(usermod_cexample INTERFACE)
|
||||
|
||||
# Add our source files to the lib
|
||||
target_sources(usermod_cexample INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/examplemodule.c
|
||||
)
|
||||
|
||||
# Add the current directory as an include directory.
|
||||
target_include_directories(usermod_cexample INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
)
|
||||
|
||||
# Link our INTERFACE library to the usermod target.
|
||||
target_link_libraries(usermod INTERFACE usermod_cexample)
|
||||
@ -22,4 +22,7 @@ const mp_obj_module_t cppexample_user_cmodule = {
|
||||
};
|
||||
|
||||
// Register the module to make it available in Python.
|
||||
MP_REGISTER_MODULE(MP_QSTR_cppexample, cppexample_user_cmodule, MODULE_CPPEXAMPLE_ENABLED);
|
||||
// Note: the "1" in the third argument means this module is always enabled.
|
||||
// This "1" can be optionally replaced with a macro like MODULE_CPPEXAMPLE_ENABLED
|
||||
// which can then be used to conditionally enable this module.
|
||||
MP_REGISTER_MODULE(MP_QSTR_cppexample, cppexample_user_cmodule, 1);
|
||||
|
||||
16
examples/usercmodule/cppexample/micropython.cmake
Normal file
16
examples/usercmodule/cppexample/micropython.cmake
Normal file
@ -0,0 +1,16 @@
|
||||
# Create an INTERFACE library for our CPP module.
|
||||
add_library(usermod_cppexample INTERFACE)
|
||||
|
||||
# Add our source files to the library.
|
||||
target_sources(usermod_cppexample INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/example.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/examplemodule.c
|
||||
)
|
||||
|
||||
# Add the current directory as an include directory.
|
||||
target_include_directories(usermod_cppexample INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
)
|
||||
|
||||
# Link our INTERFACE library to the usermod target.
|
||||
target_link_libraries(usermod INTERFACE usermod_cppexample)
|
||||
11
examples/usercmodule/micropython.cmake
Normal file
11
examples/usercmodule/micropython.cmake
Normal file
@ -0,0 +1,11 @@
|
||||
# This top-level micropython.cmake is responsible for listing
|
||||
# the individual modules we want to include.
|
||||
# Paths are absolute, and ${CMAKE_CURRENT_LIST_DIR} can be
|
||||
# used to prefix subdirectories.
|
||||
|
||||
# Add the C example.
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/cexample/micropython.cmake)
|
||||
|
||||
# Add the CPP example.
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/cppexample/micropython.cmake)
|
||||
|
||||
@ -11,6 +11,8 @@ EXTMOD_SRC_C += extmod/btstack/modbluetooth_btstack.c
|
||||
INC += -I$(TOP)/$(BTSTACK_EXTMOD_DIR)
|
||||
|
||||
CFLAGS_MOD += -DMICROPY_BLUETOOTH_BTSTACK=1
|
||||
CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS=1
|
||||
CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING=1
|
||||
|
||||
BTSTACK_DIR = $(TOP)/lib/btstack
|
||||
|
||||
@ -28,7 +30,6 @@ INC += -I$(BTSTACK_DIR)/3rd-party/yxml
|
||||
SRC_BTSTACK = \
|
||||
$(addprefix lib/btstack/src/, $(SRC_FILES)) \
|
||||
$(addprefix lib/btstack/src/ble/, $(filter-out %_tlv.c, $(SRC_BLE_FILES))) \
|
||||
lib/btstack/platform/embedded/btstack_run_loop_embedded.c
|
||||
|
||||
ifeq ($(MICROPY_BLUETOOTH_BTSTACK_USB),1)
|
||||
ifeq ($(MICROPY_BLUETOOTH_BTSTACK_H4),1)
|
||||
|
||||
@ -38,6 +38,11 @@
|
||||
|
||||
#include "mpbtstackport.h"
|
||||
|
||||
#define HCI_TRACE (0)
|
||||
#define COL_OFF "\033[0m"
|
||||
#define COL_GREEN "\033[0;32m"
|
||||
#define COL_BLUE "\033[0;34m"
|
||||
|
||||
// Implements a btstack btstack_uart_block_t on top of the mphciuart.h
|
||||
// interface to an HCI UART provided by the port.
|
||||
|
||||
@ -62,8 +67,7 @@ STATIC int btstack_uart_init(const btstack_uart_config_t *uart_config) {
|
||||
send_handler = NULL;
|
||||
|
||||
// Set up the UART peripheral, attach IRQ and power up the HCI controller.
|
||||
// We haven't been told the baud rate yet, so defer that until btstack_uart_set_baudrate.
|
||||
if (mp_bluetooth_hci_uart_init(MICROPY_HW_BLE_UART_ID, 0)) {
|
||||
if (mp_bluetooth_hci_uart_init(MICROPY_HW_BLE_UART_ID, MICROPY_HW_BLE_UART_BAUDRATE)) {
|
||||
init_success = false;
|
||||
return -1;
|
||||
}
|
||||
@ -82,6 +86,7 @@ STATIC int btstack_uart_open(void) {
|
||||
|
||||
STATIC int btstack_uart_close(void) {
|
||||
mp_bluetooth_hci_controller_deinit();
|
||||
mp_bluetooth_hci_uart_deinit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -114,6 +119,14 @@ STATIC void btstack_uart_receive_block(uint8_t *buf, uint16_t len) {
|
||||
}
|
||||
|
||||
STATIC void btstack_uart_send_block(const uint8_t *buf, uint16_t len) {
|
||||
#if HCI_TRACE
|
||||
printf(COL_GREEN "< [% 8d] %02x", (int)mp_hal_ticks_ms(), buf[0]);
|
||||
for (size_t i = 1; i < len; ++i) {
|
||||
printf(":%02x", buf[i]);
|
||||
}
|
||||
printf(COL_OFF "\n");
|
||||
#endif
|
||||
|
||||
mp_bluetooth_hci_uart_write(buf, len);
|
||||
send_done = true;
|
||||
}
|
||||
@ -165,6 +178,13 @@ void mp_bluetooth_btstack_hci_uart_process(void) {
|
||||
while (recv_idx < recv_len && (chr = mp_bluetooth_hci_uart_readchar()) >= 0) {
|
||||
recv_buf[recv_idx++] = chr;
|
||||
if (recv_idx == recv_len) {
|
||||
#if HCI_TRACE
|
||||
printf(COL_BLUE "> [% 8d] %02x", (int)mp_hal_ticks_ms(), recv_buf[0]);
|
||||
for (size_t i = 1; i < recv_len; ++i) {
|
||||
printf(":%02x", recv_buf[i]);
|
||||
}
|
||||
printf(COL_OFF "\n");
|
||||
#endif
|
||||
recv_idx = 0;
|
||||
recv_len = 0;
|
||||
if (recv_handler) {
|
||||
|
||||
@ -91,7 +91,7 @@ STATIC mp_obj_bluetooth_uuid_t create_mp_uuid(uint16_t uuid16, const uint8_t *uu
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
|
||||
// Notes on supporting background ops (e.g. an attempt to gatts_notify while
|
||||
// an existing notification is in progress):
|
||||
@ -218,7 +218,7 @@ STATIC mp_btstack_pending_op_t *btstack_enqueue_pending_operation(uint16_t op_ty
|
||||
return pending_op;
|
||||
}
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
|
||||
// Cleans up a pending op of the specified type for this conn_handle (and if specified, value_handle).
|
||||
// Used by MP_BLUETOOTH_BTSTACK_PENDING_WRITE and MP_BLUETOOTH_BTSTACK_PENDING_WRITE_NO_RESPONSE.
|
||||
@ -282,7 +282,7 @@ STATIC void btstack_packet_handler_att_server(uint8_t packet_type, uint16_t chan
|
||||
}
|
||||
}
|
||||
|
||||
#if MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS
|
||||
#if MICROPY_BLUETOOTH_USE_ZEPHYR_STATIC_ADDRESS
|
||||
// During startup, the controller (e.g. Zephyr) might give us a static address that we can use.
|
||||
STATIC uint8_t controller_static_addr[6] = {0};
|
||||
STATIC bool controller_static_addr_available = false;
|
||||
@ -349,13 +349,13 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t
|
||||
DEBUG_printf(" --> hci transport packet sent\n");
|
||||
} else if (event_type == HCI_EVENT_COMMAND_COMPLETE) {
|
||||
DEBUG_printf(" --> hci command complete\n");
|
||||
#if MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS
|
||||
#if MICROPY_BLUETOOTH_USE_ZEPHYR_STATIC_ADDRESS
|
||||
if (memcmp(packet, read_static_address_command_complete_prefix, sizeof(read_static_address_command_complete_prefix)) == 0) {
|
||||
DEBUG_printf(" --> static address available\n");
|
||||
reverse_48(&packet[7], controller_static_addr);
|
||||
controller_static_addr_available = true;
|
||||
}
|
||||
#endif // MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS
|
||||
#endif // MICROPY_BLUETOOTH_USE_ZEPHYR_STATIC_ADDRESS
|
||||
} else if (event_type == HCI_EVENT_COMMAND_STATUS) {
|
||||
DEBUG_printf(" --> hci command status\n");
|
||||
} else if (event_type == HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS) {
|
||||
@ -418,6 +418,8 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t
|
||||
uint8_t length = gap_event_advertising_report_get_data_length(packet);
|
||||
const uint8_t *data = gap_event_advertising_report_get_data(packet);
|
||||
mp_bluetooth_gap_on_scan_result(address_type, address, adv_event_type, rssi, data, length);
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
} else if (event_type == GATT_EVENT_QUERY_COMPLETE) {
|
||||
uint16_t conn_handle = gatt_event_query_complete_get_handle(packet);
|
||||
uint16_t status = gatt_event_query_complete_get_att_status(packet);
|
||||
@ -487,7 +489,7 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t
|
||||
// Note: Can't "del" the pending_op from IRQ context. Leave it for the GC.
|
||||
}
|
||||
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
} else {
|
||||
DEBUG_printf(" --> hci event type: unknown (0x%02x)\n", event_type);
|
||||
}
|
||||
@ -506,7 +508,7 @@ STATIC btstack_packet_callback_registration_t hci_event_callback_registration =
|
||||
.callback = &btstack_packet_handler_generic
|
||||
};
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
// For when the handler is being used for service discovery.
|
||||
STATIC void btstack_packet_handler_discover_services(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
|
||||
(void)channel;
|
||||
@ -541,7 +543,7 @@ STATIC void btstack_packet_handler_write_with_response(uint8_t packet_type, uint
|
||||
(void)size;
|
||||
btstack_packet_handler(packet_type, packet, MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE);
|
||||
}
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
|
||||
STATIC btstack_timer_source_t btstack_init_deinit_timeout;
|
||||
|
||||
@ -575,12 +577,12 @@ STATIC bool set_public_address(void) {
|
||||
}
|
||||
|
||||
STATIC void set_random_address(void) {
|
||||
#if MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS
|
||||
#if MICROPY_BLUETOOTH_USE_ZEPHYR_STATIC_ADDRESS
|
||||
if (controller_static_addr_available) {
|
||||
DEBUG_printf("set_random_address: Using static address supplied by controller.\n");
|
||||
gap_random_address_set(controller_static_addr);
|
||||
} else
|
||||
#endif // MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS
|
||||
#endif // MICROPY_BLUETOOTH_USE_ZEPHYR_STATIC_ADDRESS
|
||||
{
|
||||
bd_addr_t static_addr;
|
||||
|
||||
@ -635,7 +637,7 @@ int mp_bluetooth_init(void) {
|
||||
|
||||
btstack_memory_init();
|
||||
|
||||
#if MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS
|
||||
#if MICROPY_BLUETOOTH_USE_ZEPHYR_STATIC_ADDRESS
|
||||
controller_static_addr_available = false;
|
||||
#endif
|
||||
|
||||
@ -662,12 +664,12 @@ int mp_bluetooth_init(void) {
|
||||
sm_set_er(dummy_key);
|
||||
sm_set_ir(dummy_key);
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
gatt_client_init();
|
||||
|
||||
// We always require explicitly exchanging MTU with ble.gattc_exchange_mtu().
|
||||
gatt_client_mtu_enable_auto_negotiation(false);
|
||||
#endif
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
|
||||
// Register for HCI events.
|
||||
hci_add_event_handler(&hci_event_callback_registration);
|
||||
@ -719,10 +721,10 @@ int mp_bluetooth_init(void) {
|
||||
set_random_address();
|
||||
}
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
// Enable GATT_EVENT_NOTIFICATION/GATT_EVENT_INDICATION for all connections and handles.
|
||||
gatt_client_listen_for_characteristic_value_updates(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->notification, &btstack_packet_handler_generic, GATT_CLIENT_ANY_CONNECTION, NULL);
|
||||
#endif
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -737,10 +739,10 @@ void mp_bluetooth_deinit(void) {
|
||||
|
||||
mp_bluetooth_gap_advertise_stop();
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
// Remove our registration for notify/indicate.
|
||||
gatt_client_stop_listening_for_characteristic_value_updates(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->notification);
|
||||
#endif
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
|
||||
// Set a timer that will forcibly set the state to TIMEOUT, which will stop the loop below.
|
||||
btstack_run_loop_set_timer(&btstack_init_deinit_timeout, BTSTACK_INIT_DEINIT_TIMEOUT_MS);
|
||||
@ -847,6 +849,11 @@ int mp_bluetooth_gap_set_device_name(const uint8_t *buf, size_t len) {
|
||||
|
||||
int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, const uint8_t *adv_data, size_t adv_data_len, const uint8_t *sr_data, size_t sr_data_len) {
|
||||
DEBUG_printf("mp_bluetooth_gap_advertise_start\n");
|
||||
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return ERRNO_BLUETOOTH_NOT_ACTIVE;
|
||||
}
|
||||
|
||||
uint16_t adv_int_min = interval_us / 625;
|
||||
uint16_t adv_int_max = interval_us / 625;
|
||||
uint8_t adv_type = connectable ? 0 : 2;
|
||||
@ -883,6 +890,11 @@ int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, cons
|
||||
|
||||
void mp_bluetooth_gap_advertise_stop(void) {
|
||||
DEBUG_printf("mp_bluetooth_gap_advertise_stop\n");
|
||||
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return;
|
||||
}
|
||||
|
||||
gap_advertisements_enable(false);
|
||||
MP_STATE_PORT(bluetooth_btstack_root_pointers)->adv_data_alloc = 0;
|
||||
MP_STATE_PORT(bluetooth_btstack_root_pointers)->adv_data = NULL;
|
||||
@ -890,6 +902,11 @@ void mp_bluetooth_gap_advertise_stop(void) {
|
||||
|
||||
int mp_bluetooth_gatts_register_service_begin(bool append) {
|
||||
DEBUG_printf("mp_bluetooth_gatts_register_service_begin\n");
|
||||
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return ERRNO_BLUETOOTH_NOT_ACTIVE;
|
||||
}
|
||||
|
||||
if (!append) {
|
||||
// This will reset the DB.
|
||||
// Becase the DB is statically allocated, there's no problem with just re-initing it.
|
||||
@ -1062,16 +1079,27 @@ int mp_bluetooth_gatts_register_service_end(void) {
|
||||
|
||||
int mp_bluetooth_gatts_read(uint16_t value_handle, uint8_t **value, size_t *value_len) {
|
||||
DEBUG_printf("mp_bluetooth_gatts_read\n");
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return ERRNO_BLUETOOTH_NOT_ACTIVE;
|
||||
}
|
||||
return mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, value, value_len);
|
||||
}
|
||||
|
||||
int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t value_len) {
|
||||
DEBUG_printf("mp_bluetooth_gatts_write\n");
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return ERRNO_BLUETOOTH_NOT_ACTIVE;
|
||||
}
|
||||
return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, value, value_len);
|
||||
}
|
||||
|
||||
int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle) {
|
||||
DEBUG_printf("mp_bluetooth_gatts_notify\n");
|
||||
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return ERRNO_BLUETOOTH_NOT_ACTIVE;
|
||||
}
|
||||
|
||||
// Note: btstack doesn't appear to support sending a notification without a value, so include the stored value.
|
||||
uint8_t *data = NULL;
|
||||
size_t len = 0;
|
||||
@ -1082,6 +1110,10 @@ int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle) {
|
||||
int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t value_len) {
|
||||
DEBUG_printf("mp_bluetooth_gatts_notify_send\n");
|
||||
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return ERRNO_BLUETOOTH_NOT_ACTIVE;
|
||||
}
|
||||
|
||||
// Attempt to send immediately. If it succeeds, btstack will copy the buffer.
|
||||
MICROPY_PY_BLUETOOTH_ENTER
|
||||
int err = att_server_notify(conn_handle, value_handle, value, value_len);
|
||||
@ -1108,6 +1140,10 @@ int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle,
|
||||
int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) {
|
||||
DEBUG_printf("mp_bluetooth_gatts_indicate\n");
|
||||
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return ERRNO_BLUETOOTH_NOT_ACTIVE;
|
||||
}
|
||||
|
||||
uint8_t *data = NULL;
|
||||
size_t len = 0;
|
||||
mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, &data, &len);
|
||||
@ -1140,6 +1176,9 @@ int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) {
|
||||
|
||||
int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append) {
|
||||
DEBUG_printf("mp_bluetooth_gatts_set_buffer\n");
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return ERRNO_BLUETOOTH_NOT_ACTIVE;
|
||||
}
|
||||
return mp_bluetooth_gatts_db_resize(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, len, append);
|
||||
}
|
||||
|
||||
@ -1163,16 +1202,26 @@ int mp_bluetooth_set_preferred_mtu(uint16_t mtu) {
|
||||
|
||||
int mp_bluetooth_gap_disconnect(uint16_t conn_handle) {
|
||||
DEBUG_printf("mp_bluetooth_gap_disconnect\n");
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return ERRNO_BLUETOOTH_NOT_ACTIVE;
|
||||
}
|
||||
gap_disconnect(conn_handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING
|
||||
|
||||
int mp_bluetooth_gap_pair(uint16_t conn_handle) {
|
||||
DEBUG_printf("mp_bluetooth_gap_pair: conn_handle=%d\n", conn_handle);
|
||||
sm_request_pairing(conn_handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mp_bluetooth_gap_passkey(uint16_t conn_handle, uint8_t action, mp_int_t passkey) {
|
||||
DEBUG_printf("mp_bluetooth_gap_passkey: conn_handle=%d action=%d passkey=%d\n", conn_handle, action, (int)passkey);
|
||||
return MP_EOPNOTSUPP;
|
||||
}
|
||||
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
@ -1186,6 +1235,10 @@ STATIC void scan_duration_timeout_handler(btstack_timer_source_t *ds) {
|
||||
int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_t window_us, bool active_scan) {
|
||||
DEBUG_printf("mp_bluetooth_gap_scan_start\n");
|
||||
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return ERRNO_BLUETOOTH_NOT_ACTIVE;
|
||||
}
|
||||
|
||||
if (duration_ms > 0) {
|
||||
btstack_run_loop_set_timer(&scan_duration_timeout, duration_ms);
|
||||
btstack_run_loop_set_timer_handler(&scan_duration_timeout, scan_duration_timeout_handler);
|
||||
@ -1200,6 +1253,9 @@ int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_
|
||||
|
||||
int mp_bluetooth_gap_scan_stop(void) {
|
||||
DEBUG_printf("mp_bluetooth_gap_scan_stop\n");
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return ERRNO_BLUETOOTH_NOT_ACTIVE;
|
||||
}
|
||||
btstack_run_loop_remove_timer(&scan_duration_timeout);
|
||||
gap_stop_scan();
|
||||
mp_bluetooth_gap_on_scan_complete();
|
||||
@ -1225,8 +1281,17 @@ int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr,
|
||||
return btstack_error_to_errno(gap_connect(btstack_addr, addr_type));
|
||||
}
|
||||
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
|
||||
int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_obj_bluetooth_uuid_t *uuid) {
|
||||
DEBUG_printf("mp_bluetooth_gattc_discover_primary_services\n");
|
||||
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return ERRNO_BLUETOOTH_NOT_ACTIVE;
|
||||
}
|
||||
|
||||
uint8_t err;
|
||||
if (uuid) {
|
||||
if (uuid->type == MP_BLUETOOTH_UUID_TYPE_16) {
|
||||
@ -1247,6 +1312,11 @@ int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_
|
||||
|
||||
int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, const mp_obj_bluetooth_uuid_t *uuid) {
|
||||
DEBUG_printf("mp_bluetooth_gattc_discover_characteristics\n");
|
||||
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return ERRNO_BLUETOOTH_NOT_ACTIVE;
|
||||
}
|
||||
|
||||
gatt_client_service_t service = {
|
||||
// Only start/end handles needed for gatt_client_discover_characteristics_for_service.
|
||||
.start_group_handle = start_handle,
|
||||
@ -1274,6 +1344,11 @@ int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t s
|
||||
|
||||
int mp_bluetooth_gattc_discover_descriptors(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle) {
|
||||
DEBUG_printf("mp_bluetooth_gattc_discover_descriptors\n");
|
||||
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return ERRNO_BLUETOOTH_NOT_ACTIVE;
|
||||
}
|
||||
|
||||
gatt_client_characteristic_t characteristic = {
|
||||
// Only start/end handles needed for gatt_client_discover_characteristic_descriptors.
|
||||
.start_handle = start_handle,
|
||||
@ -1288,12 +1363,19 @@ int mp_bluetooth_gattc_discover_descriptors(uint16_t conn_handle, uint16_t start
|
||||
|
||||
int mp_bluetooth_gattc_read(uint16_t conn_handle, uint16_t value_handle) {
|
||||
DEBUG_printf("mp_bluetooth_gattc_read\n");
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return ERRNO_BLUETOOTH_NOT_ACTIVE;
|
||||
}
|
||||
return btstack_error_to_errno(gatt_client_read_value_of_characteristic_using_value_handle(&btstack_packet_handler_read, conn_handle, value_handle));
|
||||
}
|
||||
|
||||
int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t *value_len, unsigned int mode) {
|
||||
DEBUG_printf("mp_bluetooth_gattc_write\n");
|
||||
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return ERRNO_BLUETOOTH_NOT_ACTIVE;
|
||||
}
|
||||
|
||||
// We should be distinguishing between gatt_client_write_value_of_characteristic vs
|
||||
// gatt_client_write_characteristic_descriptor_using_descriptor_handle.
|
||||
// However both are implemented using send_gatt_write_attribute_value_request under the hood,
|
||||
@ -1339,6 +1421,35 @@ int mp_bluetooth_gattc_exchange_mtu(uint16_t conn_handle) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS
|
||||
|
||||
int mp_bluetooth_l2cap_listen(uint16_t psm, uint16_t mtu) {
|
||||
DEBUG_printf("mp_bluetooth_l2cap_listen: psm=%d, mtu=%d\n", psm, mtu);
|
||||
return MP_EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int mp_bluetooth_l2cap_connect(uint16_t conn_handle, uint16_t psm, uint16_t mtu) {
|
||||
DEBUG_printf("mp_bluetooth_l2cap_connect: conn_handle=%d, psm=%d, mtu=%d\n", conn_handle, psm, mtu);
|
||||
return MP_EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int mp_bluetooth_l2cap_disconnect(uint16_t conn_handle, uint16_t cid) {
|
||||
DEBUG_printf("mp_bluetooth_l2cap_disconnect: conn_handle=%d, cid=%d\n", conn_handle, cid);
|
||||
return MP_EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int mp_bluetooth_l2cap_send(uint16_t conn_handle, uint16_t cid, const uint8_t *buf, size_t len, bool *stalled) {
|
||||
DEBUG_printf("mp_bluetooth_l2cap_send: conn_handle=%d, cid=%d, len=%d\n", conn_handle, cid, (int)len);
|
||||
return MP_EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int mp_bluetooth_l2cap_recvinto(uint16_t conn_handle, uint16_t cid, uint8_t *buf, size_t *len) {
|
||||
DEBUG_printf("mp_bluetooth_l2cap_recvinto: conn_handle=%d, cid=%d, len=%d\n", conn_handle, cid, (int)*len);
|
||||
return MP_EOPNOTSUPP;
|
||||
}
|
||||
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS
|
||||
|
||||
#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK
|
||||
|
||||
@ -46,7 +46,7 @@ static void sha256_transform(CRYAL_SHA256_CTX *ctx, const BYTE data[])
|
||||
WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
|
||||
|
||||
for (i = 0, j = 0; i < 16; ++i, j += 4)
|
||||
m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
|
||||
m[i] = ((uint32_t)data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
|
||||
for ( ; i < 64; ++i)
|
||||
m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
|
||||
|
||||
|
||||
93
extmod/extmod.cmake
Normal file
93
extmod/extmod.cmake
Normal file
@ -0,0 +1,93 @@
|
||||
# CMake fragment for MicroPython extmod component
|
||||
|
||||
set(MICROPY_EXTMOD_DIR "${MICROPY_DIR}/extmod")
|
||||
set(MICROPY_OOFATFS_DIR "${MICROPY_DIR}/lib/oofatfs")
|
||||
|
||||
set(MICROPY_SOURCE_EXTMOD
|
||||
${MICROPY_DIR}/lib/embed/abort_.c
|
||||
${MICROPY_DIR}/lib/utils/printf.c
|
||||
${MICROPY_EXTMOD_DIR}/machine_i2c.c
|
||||
${MICROPY_EXTMOD_DIR}/machine_mem.c
|
||||
${MICROPY_EXTMOD_DIR}/machine_pulse.c
|
||||
${MICROPY_EXTMOD_DIR}/machine_signal.c
|
||||
${MICROPY_EXTMOD_DIR}/machine_spi.c
|
||||
${MICROPY_EXTMOD_DIR}/modbluetooth.c
|
||||
${MICROPY_EXTMOD_DIR}/modbtree.c
|
||||
${MICROPY_EXTMOD_DIR}/modframebuf.c
|
||||
${MICROPY_EXTMOD_DIR}/modonewire.c
|
||||
${MICROPY_EXTMOD_DIR}/moduasyncio.c
|
||||
${MICROPY_EXTMOD_DIR}/modubinascii.c
|
||||
${MICROPY_EXTMOD_DIR}/moducryptolib.c
|
||||
${MICROPY_EXTMOD_DIR}/moductypes.c
|
||||
${MICROPY_EXTMOD_DIR}/moduhashlib.c
|
||||
${MICROPY_EXTMOD_DIR}/moduheapq.c
|
||||
${MICROPY_EXTMOD_DIR}/modujson.c
|
||||
${MICROPY_EXTMOD_DIR}/modurandom.c
|
||||
${MICROPY_EXTMOD_DIR}/modure.c
|
||||
${MICROPY_EXTMOD_DIR}/moduselect.c
|
||||
${MICROPY_EXTMOD_DIR}/modussl_axtls.c
|
||||
${MICROPY_EXTMOD_DIR}/modussl_mbedtls.c
|
||||
${MICROPY_EXTMOD_DIR}/modutimeq.c
|
||||
${MICROPY_EXTMOD_DIR}/moduwebsocket.c
|
||||
${MICROPY_EXTMOD_DIR}/moduzlib.c
|
||||
${MICROPY_EXTMOD_DIR}/modwebrepl.c
|
||||
${MICROPY_EXTMOD_DIR}/uos_dupterm.c
|
||||
${MICROPY_EXTMOD_DIR}/utime_mphal.c
|
||||
${MICROPY_EXTMOD_DIR}/vfs.c
|
||||
${MICROPY_EXTMOD_DIR}/vfs_blockdev.c
|
||||
${MICROPY_EXTMOD_DIR}/vfs_fat.c
|
||||
${MICROPY_EXTMOD_DIR}/vfs_fat_diskio.c
|
||||
${MICROPY_EXTMOD_DIR}/vfs_fat_file.c
|
||||
${MICROPY_EXTMOD_DIR}/vfs_lfs.c
|
||||
${MICROPY_EXTMOD_DIR}/vfs_posix.c
|
||||
${MICROPY_EXTMOD_DIR}/vfs_posix_file.c
|
||||
${MICROPY_EXTMOD_DIR}/vfs_reader.c
|
||||
${MICROPY_EXTMOD_DIR}/virtpin.c
|
||||
${MICROPY_EXTMOD_DIR}/nimble/modbluetooth_nimble.c
|
||||
)
|
||||
|
||||
# Library for btree module and associated code
|
||||
|
||||
set(MICROPY_LIB_BERKELEY_DIR "${MICROPY_DIR}/lib/berkeley-db-1.xx")
|
||||
|
||||
if(EXISTS "${MICROPY_LIB_BERKELEY_DIR}/btree/bt_close.c")
|
||||
add_library(micropy_extmod_btree OBJECT
|
||||
${MICROPY_LIB_BERKELEY_DIR}/btree/bt_close.c
|
||||
${MICROPY_LIB_BERKELEY_DIR}/btree/bt_conv.c
|
||||
${MICROPY_LIB_BERKELEY_DIR}/btree/bt_debug.c
|
||||
${MICROPY_LIB_BERKELEY_DIR}/btree/bt_delete.c
|
||||
${MICROPY_LIB_BERKELEY_DIR}/btree/bt_get.c
|
||||
${MICROPY_LIB_BERKELEY_DIR}/btree/bt_open.c
|
||||
${MICROPY_LIB_BERKELEY_DIR}/btree/bt_overflow.c
|
||||
${MICROPY_LIB_BERKELEY_DIR}/btree/bt_page.c
|
||||
${MICROPY_LIB_BERKELEY_DIR}/btree/bt_put.c
|
||||
${MICROPY_LIB_BERKELEY_DIR}/btree/bt_search.c
|
||||
${MICROPY_LIB_BERKELEY_DIR}/btree/bt_seq.c
|
||||
${MICROPY_LIB_BERKELEY_DIR}/btree/bt_split.c
|
||||
${MICROPY_LIB_BERKELEY_DIR}/btree/bt_utils.c
|
||||
${MICROPY_LIB_BERKELEY_DIR}/mpool/mpool.c
|
||||
)
|
||||
|
||||
target_include_directories(micropy_extmod_btree PRIVATE
|
||||
${MICROPY_LIB_BERKELEY_DIR}/PORT/include
|
||||
)
|
||||
|
||||
target_compile_definitions(micropy_extmod_btree PRIVATE
|
||||
__DBINTERFACE_PRIVATE=1
|
||||
mpool_error=printf
|
||||
abort=abort_
|
||||
"virt_fd_t=void*"
|
||||
)
|
||||
|
||||
# The include directories and compile definitions below are needed to build
|
||||
# modbtree.c and should be added to the main MicroPython target.
|
||||
|
||||
list(APPEND MICROPY_INC_CORE
|
||||
"${MICROPY_LIB_BERKELEY_DIR}/PORT/include"
|
||||
)
|
||||
|
||||
list(APPEND MICROPY_DEF_CORE
|
||||
__DBINTERFACE_PRIVATE=1
|
||||
"virt_fd_t=void*"
|
||||
)
|
||||
endif()
|
||||
@ -619,6 +619,11 @@ STATIC mp_obj_t bluetooth_ble_gatts_register_services(mp_obj_t self_in, mp_obj_t
|
||||
}
|
||||
result->items[i] = MP_OBJ_FROM_PTR(service_handles);
|
||||
}
|
||||
|
||||
// Free temporary arrays.
|
||||
m_del(uint16_t *, handles, len);
|
||||
m_del(size_t, num_handles, len);
|
||||
|
||||
return MP_OBJ_FROM_PTR(result);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(bluetooth_ble_gatts_register_services_obj, bluetooth_ble_gatts_register_services);
|
||||
@ -761,7 +766,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gatts_set_buffer_obj, 3
|
||||
// Bluetooth object: GATTC (Central/Scanner role)
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
|
||||
STATIC mp_obj_t bluetooth_ble_gattc_discover_services(size_t n_args, const mp_obj_t *args) {
|
||||
mp_int_t conn_handle = mp_obj_get_int(args[1]);
|
||||
@ -830,7 +835,7 @@ STATIC mp_obj_t bluetooth_ble_gattc_exchange_mtu(mp_obj_t self_in, mp_obj_t conn
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(bluetooth_ble_gattc_exchange_mtu_obj, bluetooth_ble_gattc_exchange_mtu);
|
||||
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS
|
||||
|
||||
@ -921,15 +926,15 @@ STATIC const mp_rom_map_elem_t bluetooth_ble_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_gap_pair), MP_ROM_PTR(&bluetooth_ble_gap_pair_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_gap_passkey), MP_ROM_PTR(&bluetooth_ble_gap_passkey_obj) },
|
||||
#endif
|
||||
// GATT Server (i.e. peripheral/advertiser role)
|
||||
// GATT Server
|
||||
{ MP_ROM_QSTR(MP_QSTR_gatts_register_services), MP_ROM_PTR(&bluetooth_ble_gatts_register_services_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_gatts_read), MP_ROM_PTR(&bluetooth_ble_gatts_read_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_gatts_write), MP_ROM_PTR(&bluetooth_ble_gatts_write_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_gatts_notify), MP_ROM_PTR(&bluetooth_ble_gatts_notify_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_gatts_indicate), MP_ROM_PTR(&bluetooth_ble_gatts_indicate_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_gatts_set_buffer), MP_ROM_PTR(&bluetooth_ble_gatts_set_buffer_obj) },
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
// GATT Client (i.e. central/scanner role)
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
// GATT Client
|
||||
{ MP_ROM_QSTR(MP_QSTR_gattc_discover_services), MP_ROM_PTR(&bluetooth_ble_gattc_discover_services_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_gattc_discover_characteristics), MP_ROM_PTR(&bluetooth_ble_gattc_discover_characteristics_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_gattc_discover_descriptors), MP_ROM_PTR(&bluetooth_ble_gattc_discover_descriptors_obj) },
|
||||
@ -1067,6 +1072,8 @@ STATIC mp_obj_t bluetooth_ble_invoke_irq(mp_obj_t none_in) {
|
||||
} else if (event == MP_BLUETOOTH_IRQ_SCAN_DONE) {
|
||||
// No params required.
|
||||
data_tuple->len = 0;
|
||||
#endif
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
} else if (event == MP_BLUETOOTH_IRQ_GATTC_SERVICE_RESULT) {
|
||||
// conn_handle, start_handle, end_handle, uuid
|
||||
ringbuf_extract(&o->ringbuf, data_tuple, 3, 0, NULL, 0, &o->irq_data_uuid, NULL);
|
||||
@ -1085,7 +1092,7 @@ STATIC mp_obj_t bluetooth_ble_invoke_irq(mp_obj_t none_in) {
|
||||
} else if (event == MP_BLUETOOTH_IRQ_GATTC_READ_DONE || event == MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE) {
|
||||
// conn_handle, value_handle, status
|
||||
ringbuf_extract(&o->ringbuf, data_tuple, 3, 0, NULL, 0, NULL, NULL);
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
}
|
||||
|
||||
MICROPY_PY_BLUETOOTH_EXIT
|
||||
@ -1228,7 +1235,7 @@ void mp_bluetooth_gatts_on_mtu_exchanged(uint16_t conn_handle, uint16_t value) {
|
||||
}
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS
|
||||
mp_int_t mp_bluetooth_gattc_on_l2cap_accept(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu) {
|
||||
mp_int_t mp_bluetooth_on_l2cap_accept(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu) {
|
||||
mp_int_t args[] = {conn_handle, cid, psm, our_mtu, peer_mtu};
|
||||
mp_obj_t result = invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_ACCEPT, args, 5, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0);
|
||||
// Return non-zero from IRQ handler to fail the accept.
|
||||
@ -1237,22 +1244,22 @@ mp_int_t mp_bluetooth_gattc_on_l2cap_accept(uint16_t conn_handle, uint16_t cid,
|
||||
return ret;
|
||||
}
|
||||
|
||||
void mp_bluetooth_gattc_on_l2cap_connect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu) {
|
||||
void mp_bluetooth_on_l2cap_connect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu) {
|
||||
mp_int_t args[] = {conn_handle, cid, psm, our_mtu, peer_mtu};
|
||||
invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_CONNECT, args, 5, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0);
|
||||
}
|
||||
|
||||
void mp_bluetooth_gattc_on_l2cap_disconnect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t status) {
|
||||
void mp_bluetooth_on_l2cap_disconnect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t status) {
|
||||
mp_int_t args[] = {conn_handle, cid, psm, status};
|
||||
invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_DISCONNECT, args, 4, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0);
|
||||
}
|
||||
|
||||
void mp_bluetooth_gattc_on_l2cap_send_ready(uint16_t conn_handle, uint16_t cid, uint8_t status) {
|
||||
void mp_bluetooth_on_l2cap_send_ready(uint16_t conn_handle, uint16_t cid, uint8_t status) {
|
||||
mp_int_t args[] = {conn_handle, cid, status};
|
||||
invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_SEND_READY, args, 3, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0);
|
||||
}
|
||||
|
||||
void mp_bluetooth_gattc_on_l2cap_recv(uint16_t conn_handle, uint16_t cid) {
|
||||
void mp_bluetooth_on_l2cap_recv(uint16_t conn_handle, uint16_t cid) {
|
||||
mp_int_t args[] = {conn_handle, cid};
|
||||
invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_RECV, args, 2, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0);
|
||||
}
|
||||
@ -1267,7 +1274,9 @@ void mp_bluetooth_gap_on_scan_result(uint8_t addr_type, const uint8_t *addr, uin
|
||||
mp_int_t args[] = {addr_type, adv_type, rssi};
|
||||
invoke_irq_handler(MP_BLUETOOTH_IRQ_SCAN_RESULT, args, 1, 2, addr, NULL_UUID, &data, &data_len, 1);
|
||||
}
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
void mp_bluetooth_gattc_on_primary_service_result(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, mp_obj_bluetooth_uuid_t *service_uuid) {
|
||||
mp_int_t args[] = {conn_handle, start_handle, end_handle};
|
||||
invoke_irq_handler(MP_BLUETOOTH_IRQ_GATTC_SERVICE_RESULT, args, 3, 0, NULL_ADDR, service_uuid, NULL_DATA, NULL_DATA_LEN, 0);
|
||||
@ -1325,7 +1334,7 @@ void mp_bluetooth_gattc_on_read_write_status(uint8_t event, uint16_t conn_handle
|
||||
invoke_irq_handler(event, args, 3, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0);
|
||||
}
|
||||
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
|
||||
#else // !MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
|
||||
// Callbacks are called in interrupt context (i.e. can't allocate), so we need to push the data
|
||||
@ -1471,7 +1480,9 @@ void mp_bluetooth_gap_on_scan_result(uint8_t addr_type, const uint8_t *addr, uin
|
||||
}
|
||||
schedule_ringbuf(atomic_state);
|
||||
}
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
void mp_bluetooth_gattc_on_primary_service_result(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, mp_obj_bluetooth_uuid_t *service_uuid) {
|
||||
MICROPY_PY_BLUETOOTH_ENTER
|
||||
mp_obj_bluetooth_ble_t *o = MP_OBJ_TO_PTR(MP_STATE_VM(bluetooth));
|
||||
@ -1559,7 +1570,7 @@ void mp_bluetooth_gattc_on_read_write_status(uint8_t event, uint16_t conn_handle
|
||||
}
|
||||
schedule_ringbuf(atomic_state);
|
||||
}
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
|
||||
#endif // MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
|
||||
|
||||
|
||||
@ -43,6 +43,12 @@
|
||||
#define MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE (0)
|
||||
#endif
|
||||
|
||||
#ifndef MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
// Enable the client by default if we're enabling central mode. It's possible
|
||||
// to enable client without central though.
|
||||
#define MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT (MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE)
|
||||
#endif
|
||||
|
||||
#ifndef MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
|
||||
// This can be enabled if the BLE stack runs entirely in scheduler context
|
||||
// and therefore is able to call directly into the VM to run Python callbacks.
|
||||
@ -365,7 +371,9 @@ int mp_bluetooth_gap_scan_stop(void);
|
||||
|
||||
// Connect to a found peripheral.
|
||||
int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, int32_t duration_ms);
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
// Find all primary services on the connected peripheral.
|
||||
int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_obj_bluetooth_uuid_t *uuid);
|
||||
|
||||
@ -383,7 +391,7 @@ int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const
|
||||
|
||||
// Initiate MTU exchange for a specific connection using the preferred MTU.
|
||||
int mp_bluetooth_gattc_exchange_mtu(uint16_t conn_handle);
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS
|
||||
int mp_bluetooth_l2cap_listen(uint16_t psm, uint16_t mtu);
|
||||
@ -440,7 +448,9 @@ void mp_bluetooth_gap_on_scan_complete(void);
|
||||
|
||||
// Notify modbluetooth of a scan result.
|
||||
void mp_bluetooth_gap_on_scan_result(uint8_t addr_type, const uint8_t *addr, uint8_t adv_type, const int8_t rssi, const uint8_t *data, size_t data_len);
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
// Notify modbluetooth that a service was found (either by discover-all, or discover-by-uuid).
|
||||
void mp_bluetooth_gattc_on_primary_service_result(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, mp_obj_bluetooth_uuid_t *service_uuid);
|
||||
|
||||
@ -458,14 +468,14 @@ void mp_bluetooth_gattc_on_data_available(uint8_t event, uint16_t conn_handle, u
|
||||
|
||||
// Notify modbluetooth that a read or write operation has completed.
|
||||
void mp_bluetooth_gattc_on_read_write_status(uint8_t event, uint16_t conn_handle, uint16_t value_handle, uint16_t status);
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS
|
||||
mp_int_t mp_bluetooth_gattc_on_l2cap_accept(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu);
|
||||
void mp_bluetooth_gattc_on_l2cap_connect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu);
|
||||
void mp_bluetooth_gattc_on_l2cap_disconnect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t status);
|
||||
void mp_bluetooth_gattc_on_l2cap_send_ready(uint16_t conn_handle, uint16_t cid, uint8_t status);
|
||||
void mp_bluetooth_gattc_on_l2cap_recv(uint16_t conn_handle, uint16_t cid);
|
||||
mp_int_t mp_bluetooth_on_l2cap_accept(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu);
|
||||
void mp_bluetooth_on_l2cap_connect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu);
|
||||
void mp_bluetooth_on_l2cap_disconnect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t status);
|
||||
void mp_bluetooth_on_l2cap_send_ready(uint16_t conn_handle, uint16_t cid, uint8_t status);
|
||||
void mp_bluetooth_on_l2cap_recv(uint16_t conn_handle, uint16_t cid);
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS
|
||||
|
||||
// For stacks that don't manage attribute value data (currently all of them), helpers
|
||||
|
||||
@ -31,12 +31,22 @@
|
||||
|
||||
#if MICROPY_PY_UASYNCIO
|
||||
|
||||
// Used when task cannot be guaranteed to be non-NULL.
|
||||
#define TASK_PAIRHEAP(task) ((task) ? &(task)->pairheap : NULL)
|
||||
|
||||
#define TASK_STATE_RUNNING_NOT_WAITED_ON (mp_const_true)
|
||||
#define TASK_STATE_DONE_NOT_WAITED_ON (mp_const_none)
|
||||
#define TASK_STATE_DONE_WAS_WAITED_ON (mp_const_false)
|
||||
|
||||
#define TASK_IS_DONE(task) ( \
|
||||
(task)->state == TASK_STATE_DONE_NOT_WAITED_ON \
|
||||
|| (task)->state == TASK_STATE_DONE_WAS_WAITED_ON)
|
||||
|
||||
typedef struct _mp_obj_task_t {
|
||||
mp_pairheap_t pairheap;
|
||||
mp_obj_t coro;
|
||||
mp_obj_t data;
|
||||
mp_obj_t waiting;
|
||||
|
||||
mp_obj_t state;
|
||||
mp_obj_t ph_key;
|
||||
} mp_obj_task_t;
|
||||
|
||||
@ -103,7 +113,7 @@ STATIC mp_obj_t task_queue_push_sorted(size_t n_args, const mp_obj_t *args) {
|
||||
assert(mp_obj_is_small_int(args[2]));
|
||||
task->ph_key = args[2];
|
||||
}
|
||||
self->heap = (mp_obj_task_t *)mp_pairheap_push(task_lt, &self->heap->pairheap, &task->pairheap);
|
||||
self->heap = (mp_obj_task_t *)mp_pairheap_push(task_lt, TASK_PAIRHEAP(self->heap), TASK_PAIRHEAP(task));
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(task_queue_push_sorted_obj, 2, 3, task_queue_push_sorted);
|
||||
@ -146,9 +156,6 @@ STATIC const mp_obj_type_t task_queue_type = {
|
||||
/******************************************************************************/
|
||||
// Task class
|
||||
|
||||
// For efficiency, the task object is stored to the coro entry when the task is done.
|
||||
#define TASK_IS_DONE(task) ((task)->coro == MP_OBJ_FROM_PTR(task))
|
||||
|
||||
// This is the core uasyncio context with cur_task, _task_queue and CancelledError.
|
||||
STATIC mp_obj_t uasyncio_context = MP_OBJ_NULL;
|
||||
|
||||
@ -159,7 +166,7 @@ STATIC mp_obj_t task_make_new(const mp_obj_type_t *type, size_t n_args, size_t n
|
||||
mp_pairheap_init_node(task_lt, &self->pairheap);
|
||||
self->coro = args[0];
|
||||
self->data = mp_const_none;
|
||||
self->waiting = mp_const_none;
|
||||
self->state = TASK_STATE_RUNNING_NOT_WAITED_ON;
|
||||
self->ph_key = MP_OBJ_NEW_SMALL_INT(0);
|
||||
if (n_args == 2) {
|
||||
uasyncio_context = args[1];
|
||||
@ -218,24 +225,6 @@ STATIC mp_obj_t task_cancel(mp_obj_t self_in) {
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(task_cancel_obj, task_cancel);
|
||||
|
||||
STATIC mp_obj_t task_throw(mp_obj_t self_in, mp_obj_t value_in) {
|
||||
// This task raised an exception which was uncaught; handle that now.
|
||||
mp_obj_task_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
// Set the data because it was cleared by the main scheduling loop.
|
||||
self->data = value_in;
|
||||
if (self->waiting == mp_const_none) {
|
||||
// Nothing await'ed on the task so call the exception handler.
|
||||
mp_obj_t _exc_context = mp_obj_dict_get(uasyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR__exc_context));
|
||||
mp_obj_dict_store(_exc_context, MP_OBJ_NEW_QSTR(MP_QSTR_exception), value_in);
|
||||
mp_obj_dict_store(_exc_context, MP_OBJ_NEW_QSTR(MP_QSTR_future), self_in);
|
||||
mp_obj_t Loop = mp_obj_dict_get(uasyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR_Loop));
|
||||
mp_obj_t call_exception_handler = mp_load_attr(Loop, MP_QSTR_call_exception_handler);
|
||||
mp_call_function_1(call_exception_handler, _exc_context);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(task_throw_obj, task_throw);
|
||||
|
||||
STATIC void task_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
||||
mp_obj_task_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if (dest[0] == MP_OBJ_NULL) {
|
||||
@ -244,32 +233,24 @@ STATIC void task_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
||||
dest[0] = self->coro;
|
||||
} else if (attr == MP_QSTR_data) {
|
||||
dest[0] = self->data;
|
||||
} else if (attr == MP_QSTR_waiting) {
|
||||
if (self->waiting != mp_const_none && self->waiting != mp_const_false) {
|
||||
dest[0] = self->waiting;
|
||||
}
|
||||
} else if (attr == MP_QSTR_state) {
|
||||
dest[0] = self->state;
|
||||
} else if (attr == MP_QSTR_done) {
|
||||
dest[0] = MP_OBJ_FROM_PTR(&task_done_obj);
|
||||
dest[1] = self_in;
|
||||
} else if (attr == MP_QSTR_cancel) {
|
||||
dest[0] = MP_OBJ_FROM_PTR(&task_cancel_obj);
|
||||
dest[1] = self_in;
|
||||
} else if (attr == MP_QSTR_throw) {
|
||||
dest[0] = MP_OBJ_FROM_PTR(&task_throw_obj);
|
||||
dest[1] = self_in;
|
||||
} else if (attr == MP_QSTR_ph_key) {
|
||||
dest[0] = self->ph_key;
|
||||
}
|
||||
} else if (dest[1] != MP_OBJ_NULL) {
|
||||
// Store
|
||||
if (attr == MP_QSTR_coro) {
|
||||
self->coro = dest[1];
|
||||
dest[0] = MP_OBJ_NULL;
|
||||
} else if (attr == MP_QSTR_data) {
|
||||
if (attr == MP_QSTR_data) {
|
||||
self->data = dest[1];
|
||||
dest[0] = MP_OBJ_NULL;
|
||||
} else if (attr == MP_QSTR_waiting) {
|
||||
self->waiting = dest[1];
|
||||
} else if (attr == MP_QSTR_state) {
|
||||
self->state = dest[1];
|
||||
dest[0] = MP_OBJ_NULL;
|
||||
}
|
||||
}
|
||||
@ -278,15 +259,12 @@ STATIC void task_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
||||
STATIC mp_obj_t task_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) {
|
||||
(void)iter_buf;
|
||||
mp_obj_task_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if (self->waiting == mp_const_none) {
|
||||
// The is the first access of the "waiting" entry.
|
||||
if (TASK_IS_DONE(self)) {
|
||||
// Signal that the completed-task has been await'ed on.
|
||||
self->waiting = mp_const_false;
|
||||
} else {
|
||||
// Lazily allocate the waiting queue.
|
||||
self->waiting = task_queue_make_new(&task_queue_type, 0, 0, NULL);
|
||||
}
|
||||
self->state = TASK_STATE_DONE_WAS_WAITED_ON;
|
||||
} else if (self->state == TASK_STATE_RUNNING_NOT_WAITED_ON) {
|
||||
// Allocate the waiting queue.
|
||||
self->state = task_queue_make_new(&task_queue_type, 0, 0, NULL);
|
||||
}
|
||||
return self_in;
|
||||
}
|
||||
@ -299,7 +277,7 @@ STATIC mp_obj_t task_iternext(mp_obj_t self_in) {
|
||||
} else {
|
||||
// Put calling task on waiting queue.
|
||||
mp_obj_t cur_task = mp_obj_dict_get(uasyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR_cur_task));
|
||||
mp_obj_t args[2] = { self->waiting, cur_task };
|
||||
mp_obj_t args[2] = { self->state, cur_task };
|
||||
task_queue_push_sorted(2, args);
|
||||
// Set calling task's data to this task that it waits on, to double-link it.
|
||||
((mp_obj_task_t *)MP_OBJ_TO_PTR(cur_task))->data = self_in;
|
||||
|
||||
@ -34,38 +34,10 @@
|
||||
|
||||
#if MICROPY_PY_UCTYPES
|
||||
|
||||
/// \module uctypes - Access data structures in memory
|
||||
///
|
||||
/// The module allows to define layout of raw data structure (using terms
|
||||
/// of C language), and then access memory buffers using this definition.
|
||||
/// The module also provides convenience functions to access memory buffers
|
||||
/// contained in Python objects or wrap memory buffers in Python objects.
|
||||
/// \constant UINT8_1 - uint8_t value type
|
||||
|
||||
/// \class struct - C-like structure
|
||||
///
|
||||
/// Encapsulalation of in-memory data structure. This class doesn't define
|
||||
/// any methods, only attribute access (for structure fields) and
|
||||
/// indexing (for pointer and array fields).
|
||||
///
|
||||
/// Usage:
|
||||
///
|
||||
/// # Define layout of a structure with 2 fields
|
||||
/// # 0 and 4 are byte offsets of fields from the beginning of struct
|
||||
/// # they are logically ORed with field type
|
||||
/// FOO_STRUCT = {"a": 0 | uctypes.UINT32, "b": 4 | uctypes.UINT8}
|
||||
///
|
||||
/// # Example memory buffer to access (contained in bytes object)
|
||||
/// buf = b"\x64\0\0\0\0x14"
|
||||
///
|
||||
/// # Create structure object referring to address of
|
||||
/// # the data in the buffer above
|
||||
/// s = uctypes.struct(FOO_STRUCT, uctypes.addressof(buf))
|
||||
///
|
||||
/// # Access fields
|
||||
/// print(s.a, s.b)
|
||||
/// # Result:
|
||||
/// # 100, 20
|
||||
// The uctypes module allows defining the layout of a raw data structure (using
|
||||
// terms of the C language), and then access memory buffers using this definition.
|
||||
// The module also provides convenience functions to access memory buffers
|
||||
// contained in Python objects or wrap memory buffers in Python objects.
|
||||
|
||||
#define LAYOUT_LITTLE_ENDIAN (0)
|
||||
#define LAYOUT_BIG_ENDIAN (1)
|
||||
@ -75,6 +47,7 @@
|
||||
#define BITF_LEN_BITS 5
|
||||
#define BITF_OFF_BITS 5
|
||||
#define OFFSET_BITS 17
|
||||
#define LEN_BITS (OFFSET_BITS + BITF_OFF_BITS)
|
||||
#if VAL_TYPE_BITS + BITF_LEN_BITS + BITF_OFF_BITS + OFFSET_BITS != 31
|
||||
#error Invalid encoding field length
|
||||
#endif
|
||||
@ -191,7 +164,7 @@ STATIC mp_uint_t uctypes_struct_agg_size(mp_obj_tuple_t *t, int layout_type, mp_
|
||||
mp_uint_t item_s;
|
||||
if (t->len == 2) {
|
||||
// Elements of array are scalar
|
||||
item_s = GET_SCALAR_SIZE(val_type);
|
||||
item_s = uctypes_struct_scalar_size(val_type);
|
||||
if (item_s > *max_field_size) {
|
||||
*max_field_size = item_s;
|
||||
}
|
||||
@ -419,10 +392,8 @@ STATIC mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set
|
||||
mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(deref);
|
||||
mp_uint_t val_type = GET_TYPE(offset, VAL_TYPE_BITS);
|
||||
offset &= VALUE_MASK(VAL_TYPE_BITS);
|
||||
// printf("scalar type=%d offset=%x\n", val_type, offset);
|
||||
|
||||
if (val_type <= INT64 || val_type == FLOAT32 || val_type == FLOAT64) {
|
||||
// printf("size=%d\n", GET_SCALAR_SIZE(val_type));
|
||||
if (self->flags == LAYOUT_NATIVE) {
|
||||
if (set_val == MP_OBJ_NULL) {
|
||||
return get_aligned(val_type, self->addr + offset, 0);
|
||||
@ -439,9 +410,9 @@ STATIC mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set
|
||||
}
|
||||
}
|
||||
} else if (val_type >= BFUINT8 && val_type <= BFINT32) {
|
||||
uint bit_offset = (offset >> 17) & 31;
|
||||
uint bit_len = (offset >> 22) & 31;
|
||||
offset &= (1 << 17) - 1;
|
||||
uint bit_offset = (offset >> OFFSET_BITS) & 31;
|
||||
uint bit_len = (offset >> LEN_BITS) & 31;
|
||||
offset &= (1 << OFFSET_BITS) - 1;
|
||||
mp_uint_t val;
|
||||
if (self->flags == LAYOUT_NATIVE) {
|
||||
val = get_aligned_basic(val_type & 6, self->addr + offset);
|
||||
@ -489,7 +460,6 @@ STATIC mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set
|
||||
mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(sub->items[0]);
|
||||
mp_uint_t agg_type = GET_TYPE(offset, AGG_TYPE_BITS);
|
||||
offset &= VALUE_MASK(AGG_TYPE_BITS);
|
||||
// printf("agg type=%d offset=%x\n", agg_type, offset);
|
||||
|
||||
switch (agg_type) {
|
||||
case STRUCT: {
|
||||
@ -514,7 +484,6 @@ STATIC mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set
|
||||
o->desc = MP_OBJ_FROM_PTR(sub);
|
||||
o->addr = self->addr + offset;
|
||||
o->flags = self->flags;
|
||||
// printf("PTR/ARR base addr=%p\n", o->addr);
|
||||
return MP_OBJ_FROM_PTR(o);
|
||||
}
|
||||
}
|
||||
@ -572,7 +541,7 @@ STATIC mp_obj_t uctypes_struct_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_ob
|
||||
return value; // just !MP_OBJ_NULL
|
||||
}
|
||||
} else {
|
||||
byte *p = self->addr + GET_SCALAR_SIZE(val_type) * index;
|
||||
byte *p = self->addr + uctypes_struct_scalar_size(val_type) * index;
|
||||
if (value == MP_OBJ_SENTINEL) {
|
||||
return get_unaligned(val_type, p, self->flags);
|
||||
} else {
|
||||
@ -647,9 +616,8 @@ STATIC mp_int_t uctypes_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// \function addressof()
|
||||
/// Return address of object's data (applies to object providing buffer
|
||||
/// interface).
|
||||
// addressof()
|
||||
// Return address of object's data (applies to objects providing the buffer interface).
|
||||
STATIC mp_obj_t uctypes_struct_addressof(mp_obj_t buf) {
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ);
|
||||
@ -657,25 +625,20 @@ STATIC mp_obj_t uctypes_struct_addressof(mp_obj_t buf) {
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(uctypes_struct_addressof_obj, uctypes_struct_addressof);
|
||||
|
||||
/// \function bytearray_at()
|
||||
/// Capture memory at given address of given size as bytearray. Memory is
|
||||
/// captured by reference (and thus memory pointed by bytearray may change
|
||||
/// or become invalid at later time). Use bytes_at() to capture by value.
|
||||
// bytearray_at()
|
||||
// Capture memory at given address of given size as bytearray.
|
||||
STATIC mp_obj_t uctypes_struct_bytearray_at(mp_obj_t ptr, mp_obj_t size) {
|
||||
return mp_obj_new_bytearray_by_ref(mp_obj_int_get_truncated(size), (void *)(uintptr_t)mp_obj_int_get_truncated(ptr));
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(uctypes_struct_bytearray_at_obj, uctypes_struct_bytearray_at);
|
||||
|
||||
/// \function bytes_at()
|
||||
/// Capture memory at given address of given size as bytes. Memory is
|
||||
/// captured by value, i.e. copied. Use bytearray_at() to capture by reference
|
||||
/// ("zero copy").
|
||||
// bytes_at()
|
||||
// Capture memory at given address of given size as bytes.
|
||||
STATIC mp_obj_t uctypes_struct_bytes_at(mp_obj_t ptr, mp_obj_t size) {
|
||||
return mp_obj_new_bytes((void *)(uintptr_t)mp_obj_int_get_truncated(ptr), mp_obj_int_get_truncated(size));
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(uctypes_struct_bytes_at_obj, uctypes_struct_bytes_at);
|
||||
|
||||
|
||||
STATIC const mp_obj_type_t uctypes_struct_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_struct,
|
||||
@ -695,81 +658,63 @@ STATIC const mp_rom_map_elem_t mp_module_uctypes_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_bytes_at), MP_ROM_PTR(&uctypes_struct_bytes_at_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_bytearray_at), MP_ROM_PTR(&uctypes_struct_bytearray_at_obj) },
|
||||
|
||||
/// \moduleref uctypes
|
||||
|
||||
/// \constant NATIVE - Native structure layout - native endianness,
|
||||
/// platform-specific field alignment
|
||||
{ MP_ROM_QSTR(MP_QSTR_NATIVE), MP_ROM_INT(LAYOUT_NATIVE) },
|
||||
/// \constant LITTLE_ENDIAN - Little-endian structure layout, tightly packed
|
||||
/// (no alignment constraints)
|
||||
{ MP_ROM_QSTR(MP_QSTR_LITTLE_ENDIAN), MP_ROM_INT(LAYOUT_LITTLE_ENDIAN) },
|
||||
/// \constant BIG_ENDIAN - Big-endian structure layout, tightly packed
|
||||
/// (no alignment constraints)
|
||||
{ MP_ROM_QSTR(MP_QSTR_BIG_ENDIAN), MP_ROM_INT(LAYOUT_BIG_ENDIAN) },
|
||||
|
||||
/// \constant VOID - void value type, may be used only as pointer target type.
|
||||
{ MP_ROM_QSTR(MP_QSTR_VOID), MP_ROM_INT(TYPE2SMALLINT(UINT8, VAL_TYPE_BITS)) },
|
||||
|
||||
/// \constant UINT8 - uint8_t value type
|
||||
{ MP_ROM_QSTR(MP_QSTR_UINT8), MP_ROM_INT(TYPE2SMALLINT(UINT8, 4)) },
|
||||
/// \constant INT8 - int8_t value type
|
||||
{ MP_ROM_QSTR(MP_QSTR_INT8), MP_ROM_INT(TYPE2SMALLINT(INT8, 4)) },
|
||||
/// \constant UINT16 - uint16_t value type
|
||||
{ MP_ROM_QSTR(MP_QSTR_UINT16), MP_ROM_INT(TYPE2SMALLINT(UINT16, 4)) },
|
||||
/// \constant INT16 - int16_t value type
|
||||
{ MP_ROM_QSTR(MP_QSTR_INT16), MP_ROM_INT(TYPE2SMALLINT(INT16, 4)) },
|
||||
/// \constant UINT32 - uint32_t value type
|
||||
{ MP_ROM_QSTR(MP_QSTR_UINT32), MP_ROM_INT(TYPE2SMALLINT(UINT32, 4)) },
|
||||
/// \constant INT32 - int32_t value type
|
||||
{ MP_ROM_QSTR(MP_QSTR_INT32), MP_ROM_INT(TYPE2SMALLINT(INT32, 4)) },
|
||||
/// \constant UINT64 - uint64_t value type
|
||||
{ MP_ROM_QSTR(MP_QSTR_UINT64), MP_ROM_INT(TYPE2SMALLINT(UINT64, 4)) },
|
||||
/// \constant INT64 - int64_t value type
|
||||
{ MP_ROM_QSTR(MP_QSTR_INT64), MP_ROM_INT(TYPE2SMALLINT(INT64, 4)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_UINT8), MP_ROM_INT(TYPE2SMALLINT(UINT8, VAL_TYPE_BITS)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_INT8), MP_ROM_INT(TYPE2SMALLINT(INT8, VAL_TYPE_BITS)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_UINT16), MP_ROM_INT(TYPE2SMALLINT(UINT16, VAL_TYPE_BITS)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_INT16), MP_ROM_INT(TYPE2SMALLINT(INT16, VAL_TYPE_BITS)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_UINT32), MP_ROM_INT(TYPE2SMALLINT(UINT32, VAL_TYPE_BITS)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_INT32), MP_ROM_INT(TYPE2SMALLINT(INT32, VAL_TYPE_BITS)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_UINT64), MP_ROM_INT(TYPE2SMALLINT(UINT64, VAL_TYPE_BITS)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_INT64), MP_ROM_INT(TYPE2SMALLINT(INT64, VAL_TYPE_BITS)) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_BFUINT8), MP_ROM_INT(TYPE2SMALLINT(BFUINT8, 4)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_BFINT8), MP_ROM_INT(TYPE2SMALLINT(BFINT8, 4)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_BFUINT16), MP_ROM_INT(TYPE2SMALLINT(BFUINT16, 4)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_BFINT16), MP_ROM_INT(TYPE2SMALLINT(BFINT16, 4)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_BFUINT32), MP_ROM_INT(TYPE2SMALLINT(BFUINT32, 4)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_BFINT32), MP_ROM_INT(TYPE2SMALLINT(BFINT32, 4)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_BFUINT8), MP_ROM_INT(TYPE2SMALLINT(BFUINT8, VAL_TYPE_BITS)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_BFINT8), MP_ROM_INT(TYPE2SMALLINT(BFINT8, VAL_TYPE_BITS)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_BFUINT16), MP_ROM_INT(TYPE2SMALLINT(BFUINT16, VAL_TYPE_BITS)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_BFINT16), MP_ROM_INT(TYPE2SMALLINT(BFINT16, VAL_TYPE_BITS)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_BFUINT32), MP_ROM_INT(TYPE2SMALLINT(BFUINT32, VAL_TYPE_BITS)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_BFINT32), MP_ROM_INT(TYPE2SMALLINT(BFINT32, VAL_TYPE_BITS)) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_BF_POS), MP_ROM_INT(17) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_BF_LEN), MP_ROM_INT(22) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_BF_POS), MP_ROM_INT(OFFSET_BITS) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_BF_LEN), MP_ROM_INT(LEN_BITS) },
|
||||
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
{ MP_ROM_QSTR(MP_QSTR_FLOAT32), MP_ROM_INT(TYPE2SMALLINT(FLOAT32, 4)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_FLOAT64), MP_ROM_INT(TYPE2SMALLINT(FLOAT64, 4)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_FLOAT32), MP_ROM_INT(TYPE2SMALLINT(FLOAT32, VAL_TYPE_BITS)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_FLOAT64), MP_ROM_INT(TYPE2SMALLINT(FLOAT64, VAL_TYPE_BITS)) },
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_UCTYPES_NATIVE_C_TYPES
|
||||
// C native type aliases. These depend on GCC-compatible predefined
|
||||
// preprocessor macros.
|
||||
#if __SIZEOF_SHORT__ == 2
|
||||
{ MP_ROM_QSTR(MP_QSTR_SHORT), MP_ROM_INT(TYPE2SMALLINT(INT16, 4)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_USHORT), MP_ROM_INT(TYPE2SMALLINT(UINT16, 4)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_SHORT), MP_ROM_INT(TYPE2SMALLINT(INT16, VAL_TYPE_BITS)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_USHORT), MP_ROM_INT(TYPE2SMALLINT(UINT16, VAL_TYPE_BITS)) },
|
||||
#endif
|
||||
#if __SIZEOF_INT__ == 4
|
||||
{ MP_ROM_QSTR(MP_QSTR_INT), MP_ROM_INT(TYPE2SMALLINT(INT32, 4)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_UINT), MP_ROM_INT(TYPE2SMALLINT(UINT32, 4)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_INT), MP_ROM_INT(TYPE2SMALLINT(INT32, VAL_TYPE_BITS)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_UINT), MP_ROM_INT(TYPE2SMALLINT(UINT32, VAL_TYPE_BITS)) },
|
||||
#endif
|
||||
#if __SIZEOF_LONG__ == 4
|
||||
{ MP_ROM_QSTR(MP_QSTR_LONG), MP_ROM_INT(TYPE2SMALLINT(INT32, 4)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ULONG), MP_ROM_INT(TYPE2SMALLINT(UINT32, 4)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_LONG), MP_ROM_INT(TYPE2SMALLINT(INT32, VAL_TYPE_BITS)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ULONG), MP_ROM_INT(TYPE2SMALLINT(UINT32, VAL_TYPE_BITS)) },
|
||||
#elif __SIZEOF_LONG__ == 8
|
||||
{ MP_ROM_QSTR(MP_QSTR_LONG), MP_ROM_INT(TYPE2SMALLINT(INT64, 4)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ULONG), MP_ROM_INT(TYPE2SMALLINT(UINT64, 4)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_LONG), MP_ROM_INT(TYPE2SMALLINT(INT64, VAL_TYPE_BITS)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ULONG), MP_ROM_INT(TYPE2SMALLINT(UINT64, VAL_TYPE_BITS)) },
|
||||
#endif
|
||||
#if __SIZEOF_LONG_LONG__ == 8
|
||||
{ MP_ROM_QSTR(MP_QSTR_LONGLONG), MP_ROM_INT(TYPE2SMALLINT(INT64, 4)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ULONGLONG), MP_ROM_INT(TYPE2SMALLINT(UINT64, 4)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_LONGLONG), MP_ROM_INT(TYPE2SMALLINT(INT64, VAL_TYPE_BITS)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ULONGLONG), MP_ROM_INT(TYPE2SMALLINT(UINT64, VAL_TYPE_BITS)) },
|
||||
#endif
|
||||
#endif // MICROPY_PY_UCTYPES_NATIVE_C_TYPES
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_PTR), MP_ROM_INT(TYPE2SMALLINT(PTR, AGG_TYPE_BITS)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ARRAY), MP_ROM_INT(TYPE2SMALLINT(ARRAY, AGG_TYPE_BITS)) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_uctypes_globals, mp_module_uctypes_globals_table);
|
||||
|
||||
const mp_obj_module_t mp_module_uctypes = {
|
||||
|
||||
@ -60,9 +60,16 @@
|
||||
|
||||
typedef struct _mp_obj_hash_t {
|
||||
mp_obj_base_t base;
|
||||
char state[0];
|
||||
bool final; // if set, update and digest raise an exception
|
||||
uintptr_t state[0]; // must be aligned to a machine word
|
||||
} mp_obj_hash_t;
|
||||
|
||||
static void uhashlib_ensure_not_final(mp_obj_hash_t *self) {
|
||||
if (self->final) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("hash is final"));
|
||||
}
|
||||
}
|
||||
|
||||
#if MICROPY_PY_UHASHLIB_SHA256
|
||||
STATIC mp_obj_t uhashlib_sha256_update(mp_obj_t self_in, mp_obj_t arg);
|
||||
|
||||
@ -78,6 +85,7 @@ STATIC mp_obj_t uhashlib_sha256_make_new(const mp_obj_type_t *type, size_t n_arg
|
||||
mp_arg_check_num(n_args, n_kw, 0, 1, false);
|
||||
mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(mbedtls_sha256_context));
|
||||
o->base.type = type;
|
||||
o->final = false;
|
||||
mbedtls_sha256_init((mbedtls_sha256_context *)&o->state);
|
||||
mbedtls_sha256_starts_ret((mbedtls_sha256_context *)&o->state, 0);
|
||||
if (n_args == 1) {
|
||||
@ -88,6 +96,7 @@ STATIC mp_obj_t uhashlib_sha256_make_new(const mp_obj_type_t *type, size_t n_arg
|
||||
|
||||
STATIC mp_obj_t uhashlib_sha256_update(mp_obj_t self_in, mp_obj_t arg) {
|
||||
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
uhashlib_ensure_not_final(self);
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ);
|
||||
mbedtls_sha256_update_ret((mbedtls_sha256_context *)&self->state, bufinfo.buf, bufinfo.len);
|
||||
@ -96,6 +105,8 @@ STATIC mp_obj_t uhashlib_sha256_update(mp_obj_t self_in, mp_obj_t arg) {
|
||||
|
||||
STATIC mp_obj_t uhashlib_sha256_digest(mp_obj_t self_in) {
|
||||
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
uhashlib_ensure_not_final(self);
|
||||
self->final = true;
|
||||
vstr_t vstr;
|
||||
vstr_init_len(&vstr, 32);
|
||||
mbedtls_sha256_finish_ret((mbedtls_sha256_context *)&self->state, (unsigned char *)vstr.buf);
|
||||
@ -110,6 +121,7 @@ STATIC mp_obj_t uhashlib_sha256_make_new(const mp_obj_type_t *type, size_t n_arg
|
||||
mp_arg_check_num(n_args, n_kw, 0, 1, false);
|
||||
mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(CRYAL_SHA256_CTX));
|
||||
o->base.type = type;
|
||||
o->final = false;
|
||||
sha256_init((CRYAL_SHA256_CTX *)o->state);
|
||||
if (n_args == 1) {
|
||||
uhashlib_sha256_update(MP_OBJ_FROM_PTR(o), args[0]);
|
||||
@ -119,6 +131,7 @@ STATIC mp_obj_t uhashlib_sha256_make_new(const mp_obj_type_t *type, size_t n_arg
|
||||
|
||||
STATIC mp_obj_t uhashlib_sha256_update(mp_obj_t self_in, mp_obj_t arg) {
|
||||
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
uhashlib_ensure_not_final(self);
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ);
|
||||
sha256_update((CRYAL_SHA256_CTX *)self->state, bufinfo.buf, bufinfo.len);
|
||||
@ -127,6 +140,8 @@ STATIC mp_obj_t uhashlib_sha256_update(mp_obj_t self_in, mp_obj_t arg) {
|
||||
|
||||
STATIC mp_obj_t uhashlib_sha256_digest(mp_obj_t self_in) {
|
||||
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
uhashlib_ensure_not_final(self);
|
||||
self->final = true;
|
||||
vstr_t vstr;
|
||||
vstr_init_len(&vstr, SHA256_BLOCK_SIZE);
|
||||
sha256_final((CRYAL_SHA256_CTX *)self->state, (byte *)vstr.buf);
|
||||
@ -160,6 +175,7 @@ STATIC mp_obj_t uhashlib_sha1_make_new(const mp_obj_type_t *type, size_t n_args,
|
||||
mp_arg_check_num(n_args, n_kw, 0, 1, false);
|
||||
mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(SHA1_CTX));
|
||||
o->base.type = type;
|
||||
o->final = false;
|
||||
SHA1_Init((SHA1_CTX *)o->state);
|
||||
if (n_args == 1) {
|
||||
uhashlib_sha1_update(MP_OBJ_FROM_PTR(o), args[0]);
|
||||
@ -169,6 +185,7 @@ STATIC mp_obj_t uhashlib_sha1_make_new(const mp_obj_type_t *type, size_t n_args,
|
||||
|
||||
STATIC mp_obj_t uhashlib_sha1_update(mp_obj_t self_in, mp_obj_t arg) {
|
||||
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
uhashlib_ensure_not_final(self);
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ);
|
||||
SHA1_Update((SHA1_CTX *)self->state, bufinfo.buf, bufinfo.len);
|
||||
@ -177,6 +194,8 @@ STATIC mp_obj_t uhashlib_sha1_update(mp_obj_t self_in, mp_obj_t arg) {
|
||||
|
||||
STATIC mp_obj_t uhashlib_sha1_digest(mp_obj_t self_in) {
|
||||
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
uhashlib_ensure_not_final(self);
|
||||
self->final = true;
|
||||
vstr_t vstr;
|
||||
vstr_init_len(&vstr, SHA1_SIZE);
|
||||
SHA1_Final((byte *)vstr.buf, (SHA1_CTX *)self->state);
|
||||
@ -196,6 +215,7 @@ STATIC mp_obj_t uhashlib_sha1_make_new(const mp_obj_type_t *type, size_t n_args,
|
||||
mp_arg_check_num(n_args, n_kw, 0, 1, false);
|
||||
mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(mbedtls_sha1_context));
|
||||
o->base.type = type;
|
||||
o->final = false;
|
||||
mbedtls_sha1_init((mbedtls_sha1_context *)o->state);
|
||||
mbedtls_sha1_starts_ret((mbedtls_sha1_context *)o->state);
|
||||
if (n_args == 1) {
|
||||
@ -206,6 +226,7 @@ STATIC mp_obj_t uhashlib_sha1_make_new(const mp_obj_type_t *type, size_t n_args,
|
||||
|
||||
STATIC mp_obj_t uhashlib_sha1_update(mp_obj_t self_in, mp_obj_t arg) {
|
||||
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
uhashlib_ensure_not_final(self);
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ);
|
||||
mbedtls_sha1_update_ret((mbedtls_sha1_context *)self->state, bufinfo.buf, bufinfo.len);
|
||||
@ -214,6 +235,8 @@ STATIC mp_obj_t uhashlib_sha1_update(mp_obj_t self_in, mp_obj_t arg) {
|
||||
|
||||
STATIC mp_obj_t uhashlib_sha1_digest(mp_obj_t self_in) {
|
||||
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
uhashlib_ensure_not_final(self);
|
||||
self->final = true;
|
||||
vstr_t vstr;
|
||||
vstr_init_len(&vstr, 20);
|
||||
mbedtls_sha1_finish_ret((mbedtls_sha1_context *)self->state, (byte *)vstr.buf);
|
||||
@ -247,6 +270,7 @@ STATIC mp_obj_t uhashlib_md5_make_new(const mp_obj_type_t *type, size_t n_args,
|
||||
mp_arg_check_num(n_args, n_kw, 0, 1, false);
|
||||
mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(MD5_CTX));
|
||||
o->base.type = type;
|
||||
o->final = false;
|
||||
MD5_Init((MD5_CTX *)o->state);
|
||||
if (n_args == 1) {
|
||||
uhashlib_md5_update(MP_OBJ_FROM_PTR(o), args[0]);
|
||||
@ -256,6 +280,7 @@ STATIC mp_obj_t uhashlib_md5_make_new(const mp_obj_type_t *type, size_t n_args,
|
||||
|
||||
STATIC mp_obj_t uhashlib_md5_update(mp_obj_t self_in, mp_obj_t arg) {
|
||||
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
uhashlib_ensure_not_final(self);
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ);
|
||||
MD5_Update((MD5_CTX *)self->state, bufinfo.buf, bufinfo.len);
|
||||
@ -264,6 +289,8 @@ STATIC mp_obj_t uhashlib_md5_update(mp_obj_t self_in, mp_obj_t arg) {
|
||||
|
||||
STATIC mp_obj_t uhashlib_md5_digest(mp_obj_t self_in) {
|
||||
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
uhashlib_ensure_not_final(self);
|
||||
self->final = true;
|
||||
vstr_t vstr;
|
||||
vstr_init_len(&vstr, MD5_SIZE);
|
||||
MD5_Final((byte *)vstr.buf, (MD5_CTX *)self->state);
|
||||
@ -283,6 +310,7 @@ STATIC mp_obj_t uhashlib_md5_make_new(const mp_obj_type_t *type, size_t n_args,
|
||||
mp_arg_check_num(n_args, n_kw, 0, 1, false);
|
||||
mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(mbedtls_md5_context));
|
||||
o->base.type = type;
|
||||
o->final = false;
|
||||
mbedtls_md5_init((mbedtls_md5_context *)o->state);
|
||||
mbedtls_md5_starts_ret((mbedtls_md5_context *)o->state);
|
||||
if (n_args == 1) {
|
||||
@ -293,6 +321,7 @@ STATIC mp_obj_t uhashlib_md5_make_new(const mp_obj_type_t *type, size_t n_args,
|
||||
|
||||
STATIC mp_obj_t uhashlib_md5_update(mp_obj_t self_in, mp_obj_t arg) {
|
||||
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
uhashlib_ensure_not_final(self);
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ);
|
||||
mbedtls_md5_update_ret((mbedtls_md5_context *)self->state, bufinfo.buf, bufinfo.len);
|
||||
@ -301,6 +330,8 @@ STATIC mp_obj_t uhashlib_md5_update(mp_obj_t self_in, mp_obj_t arg) {
|
||||
|
||||
STATIC mp_obj_t uhashlib_md5_digest(mp_obj_t self_in) {
|
||||
mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
uhashlib_ensure_not_final(self);
|
||||
self->final = true;
|
||||
vstr_t vstr;
|
||||
vstr_init_len(&vstr, 16);
|
||||
mbedtls_md5_finish_ret((mbedtls_md5_context *)self->state, (byte *)vstr.buf);
|
||||
|
||||
@ -87,8 +87,11 @@ STATIC uint32_t yasmarang_randbelow(uint32_t n) {
|
||||
|
||||
STATIC mp_obj_t mod_urandom_getrandbits(mp_obj_t num_in) {
|
||||
int n = mp_obj_get_int(num_in);
|
||||
if (n > 32 || n == 0) {
|
||||
mp_raise_ValueError(NULL);
|
||||
if (n > 32 || n < 0) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("bits must be 32 or less"));
|
||||
}
|
||||
if (n == 0) {
|
||||
return MP_OBJ_NEW_SMALL_INT(0);
|
||||
}
|
||||
uint32_t mask = ~0;
|
||||
// Beware of C undefined behavior when shifting by >= than bit size
|
||||
|
||||
@ -40,10 +40,6 @@
|
||||
// Flags for poll()
|
||||
#define FLAG_ONESHOT (1)
|
||||
|
||||
/// \module select - Provides select function to wait for events on a stream
|
||||
///
|
||||
/// This module provides the select function.
|
||||
|
||||
typedef struct _poll_obj_t {
|
||||
mp_obj_t obj;
|
||||
mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode);
|
||||
@ -111,7 +107,7 @@ STATIC mp_uint_t poll_map_poll(mp_map_t *poll_map, size_t *rwx_num) {
|
||||
return n_ready;
|
||||
}
|
||||
|
||||
/// \function select(rlist, wlist, xlist[, timeout])
|
||||
// select(rlist, wlist, xlist[, timeout])
|
||||
STATIC mp_obj_t select_select(size_t n_args, const mp_obj_t *args) {
|
||||
// get array data from tuple/list arguments
|
||||
size_t rwx_len[3];
|
||||
@ -148,7 +144,7 @@ STATIC mp_obj_t select_select(size_t n_args, const mp_obj_t *args) {
|
||||
// poll the objects
|
||||
mp_uint_t n_ready = poll_map_poll(&poll_map, rwx_len);
|
||||
|
||||
if (n_ready > 0 || (timeout != -1 && mp_hal_ticks_ms() - start_tick >= timeout)) {
|
||||
if (n_ready > 0 || (timeout != (mp_uint_t)-1 && mp_hal_ticks_ms() - start_tick >= timeout)) {
|
||||
// one or more objects are ready, or we had a timeout
|
||||
mp_obj_t list_array[3];
|
||||
list_array[0] = mp_obj_new_list(rwx_len[0], NULL);
|
||||
@ -178,8 +174,6 @@ STATIC mp_obj_t select_select(size_t n_args, const mp_obj_t *args) {
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_select_select_obj, 3, 4, select_select);
|
||||
|
||||
/// \class Poll - poll class
|
||||
|
||||
typedef struct _mp_obj_poll_t {
|
||||
mp_obj_base_t base;
|
||||
mp_map_t poll_map;
|
||||
@ -190,7 +184,7 @@ typedef struct _mp_obj_poll_t {
|
||||
mp_obj_t ret_tuple;
|
||||
} mp_obj_poll_t;
|
||||
|
||||
/// \method register(obj[, eventmask])
|
||||
// register(obj[, eventmask])
|
||||
STATIC mp_obj_t poll_register(size_t n_args, const mp_obj_t *args) {
|
||||
mp_obj_poll_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
mp_uint_t flags;
|
||||
@ -204,7 +198,7 @@ STATIC mp_obj_t poll_register(size_t n_args, const mp_obj_t *args) {
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_register_obj, 2, 3, poll_register);
|
||||
|
||||
/// \method unregister(obj)
|
||||
// unregister(obj)
|
||||
STATIC mp_obj_t poll_unregister(mp_obj_t self_in, mp_obj_t obj_in) {
|
||||
mp_obj_poll_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_map_lookup(&self->poll_map, mp_obj_id(obj_in), MP_MAP_LOOKUP_REMOVE_IF_FOUND);
|
||||
@ -213,7 +207,7 @@ STATIC mp_obj_t poll_unregister(mp_obj_t self_in, mp_obj_t obj_in) {
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(poll_unregister_obj, poll_unregister);
|
||||
|
||||
/// \method modify(obj, eventmask)
|
||||
// modify(obj, eventmask)
|
||||
STATIC mp_obj_t poll_modify(mp_obj_t self_in, mp_obj_t obj_in, mp_obj_t eventmask_in) {
|
||||
mp_obj_poll_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_map_elem_t *elem = mp_map_lookup(&self->poll_map, mp_obj_id(obj_in), MP_MAP_LOOKUP);
|
||||
@ -250,7 +244,7 @@ STATIC mp_uint_t poll_poll_internal(uint n_args, const mp_obj_t *args) {
|
||||
for (;;) {
|
||||
// poll the objects
|
||||
n_ready = poll_map_poll(&self->poll_map, NULL);
|
||||
if (n_ready > 0 || (timeout != -1 && mp_hal_ticks_ms() - start_tick >= timeout)) {
|
||||
if (n_ready > 0 || (timeout != (mp_uint_t)-1 && mp_hal_ticks_ms() - start_tick >= timeout)) {
|
||||
break;
|
||||
}
|
||||
MICROPY_EVENT_POLL_HOOK
|
||||
@ -348,7 +342,7 @@ STATIC const mp_obj_type_t mp_type_poll = {
|
||||
.locals_dict = (void *)&poll_locals_dict,
|
||||
};
|
||||
|
||||
/// \function poll()
|
||||
// poll()
|
||||
STATIC mp_obj_t select_poll(void) {
|
||||
mp_obj_poll_t *poll = m_new_obj(mp_obj_poll_t);
|
||||
poll->base.type = &mp_type_poll;
|
||||
|
||||
@ -167,10 +167,15 @@ STATIC mp_obj_ssl_socket_t *ussl_socket_new(mp_obj_t sock, struct ssl_args *args
|
||||
o->ssl_sock = ssl_client_new(o->ssl_ctx, (long)sock, NULL, 0, ext);
|
||||
|
||||
if (args->do_handshake.u_bool) {
|
||||
int res = ssl_handshake_status(o->ssl_sock);
|
||||
int r = ssl_handshake_status(o->ssl_sock);
|
||||
|
||||
if (res != SSL_OK) {
|
||||
ussl_raise_error(res);
|
||||
if (r != SSL_OK) {
|
||||
if (r == SSL_CLOSE_NOTIFY) { // EOF
|
||||
r = MP_ENOTCONN;
|
||||
} else if (r == SSL_EAGAIN) {
|
||||
r = MP_EAGAIN;
|
||||
}
|
||||
ussl_raise_error(r);
|
||||
}
|
||||
}
|
||||
|
||||
@ -242,8 +247,24 @@ STATIC mp_uint_t ussl_socket_write(mp_obj_t o_in, const void *buf, mp_uint_t siz
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
|
||||
mp_int_t r = ssl_write(o->ssl_sock, buf, size);
|
||||
mp_int_t r;
|
||||
eagain:
|
||||
r = ssl_write(o->ssl_sock, buf, size);
|
||||
if (r == 0) {
|
||||
// see comment in ussl_socket_read above
|
||||
if (o->blocking) {
|
||||
goto eagain;
|
||||
} else {
|
||||
r = SSL_EAGAIN;
|
||||
}
|
||||
}
|
||||
if (r < 0) {
|
||||
if (r == SSL_CLOSE_NOTIFY || r == SSL_ERROR_CONN_LOST) {
|
||||
return 0; // EOF
|
||||
}
|
||||
if (r == SSL_EAGAIN) {
|
||||
r = MP_EAGAIN;
|
||||
}
|
||||
*errcode = r;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
|
||||
@ -133,6 +133,7 @@ STATIC int _mbedtls_ssl_send(void *ctx, const byte *buf, size_t len) {
|
||||
}
|
||||
}
|
||||
|
||||
// _mbedtls_ssl_recv is called by mbedtls to receive bytes from the underlying socket
|
||||
STATIC int _mbedtls_ssl_recv(void *ctx, byte *buf, size_t len) {
|
||||
mp_obj_t sock = *(mp_obj_t *)ctx;
|
||||
|
||||
@ -171,7 +172,7 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) {
|
||||
mbedtls_pk_init(&o->pkey);
|
||||
mbedtls_ctr_drbg_init(&o->ctr_drbg);
|
||||
#ifdef MBEDTLS_DEBUG_C
|
||||
// Debug level (0-4)
|
||||
// Debug level (0-4) 1=warning, 2=info, 3=debug, 4=verbose
|
||||
mbedtls_debug_set_threshold(0);
|
||||
#endif
|
||||
|
||||
|
||||
@ -66,7 +66,7 @@ void hal_uart_start_tx(uint32_t port) {
|
||||
}
|
||||
|
||||
#if HCI_TRACE
|
||||
printf("< [% 8d] %02x", mp_hal_ticks_ms(), mp_bluetooth_hci_cmd_buf[0]);
|
||||
printf("< [% 8d] %02x", (int)mp_hal_ticks_ms(), mp_bluetooth_hci_cmd_buf[0]);
|
||||
for (size_t i = 1; i < len; ++i) {
|
||||
printf(":%02x", mp_bluetooth_hci_cmd_buf[i]);
|
||||
}
|
||||
@ -86,7 +86,7 @@ void mp_bluetooth_nimble_hci_uart_process(bool run_events) {
|
||||
int chr;
|
||||
while ((chr = mp_bluetooth_hci_uart_readchar()) >= 0) {
|
||||
#if HCI_TRACE
|
||||
printf("> %02x (%d)\n", chr);
|
||||
printf("> %02x\n", chr);
|
||||
#endif
|
||||
hal_uart_rx_cb(hal_uart_rx_arg, chr);
|
||||
|
||||
|
||||
@ -48,6 +48,11 @@
|
||||
#include "nimble/host/src/ble_l2cap_priv.h"
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_HCI_CMD || MICROPY_BLUETOOTH_USE_ZEPHYR_STATIC_ADDRESS
|
||||
// For ble_hs_hci_cmd_tx
|
||||
#include "nimble/host/src/ble_hs_hci_priv.h"
|
||||
#endif
|
||||
|
||||
#ifndef MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME
|
||||
#define MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME "MPY NIMBLE"
|
||||
#endif
|
||||
@ -75,6 +80,69 @@ STATIC int8_t ble_hs_err_to_errno_table[] = {
|
||||
[BLE_HS_EBADDATA] = MP_EINVAL,
|
||||
};
|
||||
|
||||
STATIC int ble_hs_err_to_errno(int err);
|
||||
|
||||
STATIC ble_uuid_t *create_nimble_uuid(const mp_obj_bluetooth_uuid_t *uuid, ble_uuid_any_t *storage);
|
||||
STATIC void reverse_addr_byte_order(uint8_t *addr_out, const uint8_t *addr_in);
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
STATIC mp_obj_bluetooth_uuid_t create_mp_uuid(const ble_uuid_any_t *uuid);
|
||||
STATIC ble_addr_t create_nimble_addr(uint8_t addr_type, const uint8_t *addr);
|
||||
#endif
|
||||
|
||||
STATIC void reset_cb(int reason);
|
||||
|
||||
STATIC bool has_public_address(void);
|
||||
STATIC void set_random_address(bool nrpa);
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING
|
||||
STATIC int load_irk(void);
|
||||
#endif
|
||||
|
||||
STATIC void sync_cb(void);
|
||||
|
||||
#if !MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY
|
||||
STATIC void ble_hs_shutdown_stop_cb(int status, void *arg);
|
||||
#endif
|
||||
|
||||
// Successfully registered service/char/desc handles.
|
||||
STATIC void gatts_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
|
||||
|
||||
// Events about a connected central (we're in peripheral role).
|
||||
STATIC int central_gap_event_cb(struct ble_gap_event *event, void *arg);
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
// Events about a connected peripheral (we're in central role).
|
||||
STATIC int peripheral_gap_event_cb(struct ble_gap_event *event, void *arg);
|
||||
#endif
|
||||
// Used by both of the above.
|
||||
STATIC int commmon_gap_event_cb(struct ble_gap_event *event, void *arg);
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
// Scan results.
|
||||
STATIC int gap_scan_cb(struct ble_gap_event *event, void *arg);
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
// Data available (either due to notify/indicate or successful read).
|
||||
STATIC void gattc_on_data_available(uint8_t event, uint16_t conn_handle, uint16_t value_handle, const struct os_mbuf *om);
|
||||
|
||||
// Client discovery callbacks.
|
||||
STATIC int ble_gattc_service_cb(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_svc *service, void *arg);
|
||||
STATIC int ble_gattc_characteristic_cb(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_chr *characteristic, void *arg);
|
||||
STATIC int ble_gattc_descriptor_cb(uint16_t conn_handle, const struct ble_gatt_error *error, uint16_t characteristic_val_handle, const struct ble_gatt_dsc *descriptor, void *arg);
|
||||
|
||||
// Client read/write handlers.
|
||||
STATIC int ble_gattc_attr_read_cb(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg);
|
||||
STATIC int ble_gattc_attr_write_cb(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg);
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING
|
||||
// Bonding store.
|
||||
STATIC int ble_store_ram_read(int obj_type, const union ble_store_key *key, union ble_store_value *value);
|
||||
STATIC int ble_store_ram_write(int obj_type, const union ble_store_value *val);
|
||||
STATIC int ble_store_ram_delete(int obj_type, const union ble_store_key *key);
|
||||
#endif
|
||||
|
||||
STATIC int ble_hs_err_to_errno(int err) {
|
||||
DEBUG_printf("ble_hs_err_to_errno: %d\n", err);
|
||||
if (!err) {
|
||||
@ -179,6 +247,14 @@ STATIC void set_random_address(bool nrpa) {
|
||||
// Mark it as STATIC (not RPA or NRPA).
|
||||
addr.val[5] |= 0xc0;
|
||||
} else
|
||||
#elif MICROPY_BLUETOOTH_USE_ZEPHYR_STATIC_ADDRESS
|
||||
if (!nrpa) {
|
||||
DEBUG_printf("set_random_address: Generating static address from Zephyr controller\n");
|
||||
uint8_t buf[23];
|
||||
rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_VENDOR, 0x09), NULL, 0, buf, sizeof(buf));
|
||||
assert(rc == 0);
|
||||
memcpy(addr.val, buf + 1, 6);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
DEBUG_printf("set_random_address: Generating random static address\n");
|
||||
@ -321,8 +397,54 @@ STATIC void gatts_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) {
|
||||
}
|
||||
}
|
||||
|
||||
STATIC int gap_event_cb(struct ble_gap_event *event, void *arg) {
|
||||
DEBUG_printf("gap_event_cb: type=%d\n", event->type);
|
||||
STATIC int commmon_gap_event_cb(struct ble_gap_event *event, void *arg) {
|
||||
struct ble_gap_conn_desc desc;
|
||||
|
||||
switch (event->type) {
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
case BLE_GAP_EVENT_NOTIFY_RX: {
|
||||
uint16_t ev = event->notify_rx.indication == 0 ? MP_BLUETOOTH_IRQ_GATTC_NOTIFY : MP_BLUETOOTH_IRQ_GATTC_INDICATE;
|
||||
gattc_on_data_available(ev, event->notify_rx.conn_handle, event->notify_rx.attr_handle, event->notify_rx.om);
|
||||
return 0;
|
||||
}
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
|
||||
case BLE_GAP_EVENT_CONN_UPDATE: {
|
||||
DEBUG_printf("commmon_gap_event_cb: connection update: status=%d\n", event->conn_update.status);
|
||||
if (ble_gap_conn_find(event->conn_update.conn_handle, &desc) == 0) {
|
||||
mp_bluetooth_gap_on_connection_update(event->conn_update.conn_handle, desc.conn_itvl, desc.conn_latency, desc.supervision_timeout, event->conn_update.status == 0 ? 0 : 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
case BLE_GAP_EVENT_MTU: {
|
||||
if (event->mtu.channel_id == BLE_L2CAP_CID_ATT) {
|
||||
DEBUG_printf("commmon_gap_event_cb: mtu update: conn_handle=%d cid=%d mtu=%d\n", event->mtu.conn_handle, event->mtu.channel_id, event->mtu.value);
|
||||
mp_bluetooth_gatts_on_mtu_exchanged(event->mtu.conn_handle, event->mtu.value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
case BLE_GAP_EVENT_ENC_CHANGE: {
|
||||
DEBUG_printf("commmon_gap_event_cb: enc change: status=%d\n", event->enc_change.status);
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING
|
||||
if (ble_gap_conn_find(event->enc_change.conn_handle, &desc) == 0) {
|
||||
mp_bluetooth_gatts_on_encryption_update(event->conn_update.conn_handle,
|
||||
desc.sec_state.encrypted, desc.sec_state.authenticated,
|
||||
desc.sec_state.bonded, desc.sec_state.key_size);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
default:
|
||||
DEBUG_printf("commmon_gap_event_cb: unknown type %d\n", event->type);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC int central_gap_event_cb(struct ble_gap_event *event, void *arg) {
|
||||
DEBUG_printf("central_gap_event_cb: type=%d\n", event->type);
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return 0;
|
||||
}
|
||||
@ -331,7 +453,7 @@ STATIC int gap_event_cb(struct ble_gap_event *event, void *arg) {
|
||||
|
||||
switch (event->type) {
|
||||
case BLE_GAP_EVENT_CONNECT:
|
||||
DEBUG_printf("gap_event_cb: connect: status=%d\n", event->connect.status);
|
||||
DEBUG_printf("central_gap_event_cb: connect: status=%d\n", event->connect.status);
|
||||
if (event->connect.status == 0) {
|
||||
// Connection established.
|
||||
ble_gap_conn_find(event->connect.conn_handle, &desc);
|
||||
@ -341,60 +463,32 @@ STATIC int gap_event_cb(struct ble_gap_event *event, void *arg) {
|
||||
// Connection failed.
|
||||
mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT, event->connect.conn_handle, 0xff, addr);
|
||||
}
|
||||
break;
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_DISCONNECT:
|
||||
// Disconnect.
|
||||
DEBUG_printf("gap_event_cb: disconnect: reason=%d\n", event->disconnect.reason);
|
||||
DEBUG_printf("central_gap_event_cb: disconnect: reason=%d\n", event->disconnect.reason);
|
||||
reverse_addr_byte_order(addr, event->disconnect.conn.peer_id_addr.val);
|
||||
mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT, event->disconnect.conn.conn_handle, event->disconnect.conn.peer_id_addr.type, addr);
|
||||
break;
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_NOTIFY_TX: {
|
||||
DEBUG_printf("gap_event_cb: notify_tx: %d %d\n", event->notify_tx.indication, event->notify_tx.status);
|
||||
DEBUG_printf("central_gap_event_cb: notify_tx: %d %d\n", event->notify_tx.indication, event->notify_tx.status);
|
||||
// This event corresponds to either a sent notify/indicate (status == 0), or an indication confirmation (status != 0).
|
||||
if (event->notify_tx.indication && event->notify_tx.status != 0) {
|
||||
// Map "done/ack" to 0, otherwise pass the status directly.
|
||||
mp_bluetooth_gatts_on_indicate_complete(event->notify_tx.conn_handle, event->notify_tx.attr_handle, event->notify_tx.status == BLE_HS_EDONE ? 0 : event->notify_tx.status);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case BLE_GAP_EVENT_MTU: {
|
||||
if (event->mtu.channel_id == BLE_L2CAP_CID_ATT) {
|
||||
DEBUG_printf("gap_event_cb: mtu update: conn_handle=%d cid=%d mtu=%d\n", event->mtu.conn_handle, event->mtu.channel_id, event->mtu.value);
|
||||
mp_bluetooth_gatts_on_mtu_exchanged(event->mtu.conn_handle, event->mtu.value);
|
||||
}
|
||||
break;
|
||||
return 0;
|
||||
}
|
||||
|
||||
case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE:
|
||||
DEBUG_printf("gap_event_cb: phy update: %d\n", event->phy_updated.tx_phy);
|
||||
break;
|
||||
|
||||
case BLE_GAP_EVENT_CONN_UPDATE: {
|
||||
DEBUG_printf("gap_event_cb: connection update: status=%d\n", event->conn_update.status);
|
||||
if (ble_gap_conn_find(event->conn_update.conn_handle, &desc) == 0) {
|
||||
mp_bluetooth_gap_on_connection_update(event->conn_update.conn_handle, desc.conn_itvl, desc.conn_latency, desc.supervision_timeout, event->conn_update.status == 0 ? 0 : 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case BLE_GAP_EVENT_ENC_CHANGE: {
|
||||
DEBUG_printf("gap_event_cb: enc change: status=%d\n", event->enc_change.status);
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING
|
||||
if (ble_gap_conn_find(event->enc_change.conn_handle, &desc) == 0) {
|
||||
mp_bluetooth_gatts_on_encryption_update(event->conn_update.conn_handle,
|
||||
desc.sec_state.encrypted, desc.sec_state.authenticated,
|
||||
desc.sec_state.bonded, desc.sec_state.key_size);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
DEBUG_printf("central_gap_event_cb: phy update: %d\n", event->phy_updated.tx_phy);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_REPEAT_PAIRING: {
|
||||
// We recognized this peer but the peer doesn't recognize us.
|
||||
DEBUG_printf("gap_event_cb: repeat pairing: conn_handle=%d\n", event->repeat_pairing.conn_handle);
|
||||
DEBUG_printf("central_gap_event_cb: repeat pairing: conn_handle=%d\n", event->repeat_pairing.conn_handle);
|
||||
|
||||
// TODO: Consider returning BLE_GAP_REPEAT_PAIRING_IGNORE (and
|
||||
// possibly an API to configure this).
|
||||
@ -410,7 +504,7 @@ STATIC int gap_event_cb(struct ble_gap_event *event, void *arg) {
|
||||
}
|
||||
|
||||
case BLE_GAP_EVENT_PASSKEY_ACTION: {
|
||||
DEBUG_printf("gap_event_cb: passkey action: conn_handle=%d action=%d num=" UINT_FMT "\n", event->passkey.conn_handle, event->passkey.params.action, (mp_uint_t)event->passkey.params.numcmp);
|
||||
DEBUG_printf("central_gap_event_cb: passkey action: conn_handle=%d action=%d num=" UINT_FMT "\n", event->passkey.conn_handle, event->passkey.params.action, (mp_uint_t)event->passkey.params.numcmp);
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING
|
||||
mp_bluetooth_gap_on_passkey_action(event->passkey.conn_handle, event->passkey.params.action, event->passkey.params.numcmp);
|
||||
@ -418,12 +512,9 @@ STATIC int gap_event_cb(struct ble_gap_event *event, void *arg) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
default:
|
||||
DEBUG_printf("gap_event_cb: unknown type %d\n", event->type);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
|
||||
return commmon_gap_event_cb(event, arg);
|
||||
}
|
||||
|
||||
#if !MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY
|
||||
@ -725,7 +816,7 @@ int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, cons
|
||||
.channel_map = 7, // all 3 channels.
|
||||
};
|
||||
|
||||
ret = ble_gap_adv_start(nimble_address_mode, NULL, BLE_HS_FOREVER, &adv_params, gap_event_cb, NULL);
|
||||
ret = ble_gap_adv_start(nimble_address_mode, NULL, BLE_HS_FOREVER, &adv_params, central_gap_event_cb, NULL);
|
||||
if (ret == 0) {
|
||||
return 0;
|
||||
}
|
||||
@ -939,7 +1030,6 @@ int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle,
|
||||
if (om == NULL) {
|
||||
return MP_ENOMEM;
|
||||
}
|
||||
// TODO: check that notify_custom takes ownership of om, if not os_mbuf_free_chain(om).
|
||||
return ble_hs_err_to_errno(ble_gattc_notify_custom(conn_handle, value_handle, om));
|
||||
}
|
||||
|
||||
@ -1012,38 +1102,6 @@ int mp_bluetooth_gap_passkey(uint16_t conn_handle, uint8_t action, mp_int_t pass
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
|
||||
STATIC void gattc_on_data_available(uint8_t event, uint16_t conn_handle, uint16_t value_handle, const struct os_mbuf *om) {
|
||||
// When the HCI data for an ATT payload arrives, the L2CAP channel will
|
||||
// buffer it into its receive buffer. We set BLE_L2CAP_JOIN_RX_FRAGS=1 in
|
||||
// syscfg.h so it should be rare that the mbuf is fragmented, but we do need
|
||||
// to be able to handle it. We pass all the fragments up to modbluetooth.c
|
||||
// which will create a temporary buffer on the MicroPython heap if necessary
|
||||
// to re-assemble them.
|
||||
|
||||
// Count how many links are in the mbuf chain.
|
||||
size_t n = 0;
|
||||
const struct os_mbuf *elem = om;
|
||||
while (elem) {
|
||||
n += 1;
|
||||
elem = SLIST_NEXT(elem, om_next);
|
||||
}
|
||||
|
||||
// Grab data pointers and lengths for each of the links.
|
||||
const uint8_t **data = mp_local_alloc(sizeof(uint8_t *) * n);
|
||||
uint16_t *data_len = mp_local_alloc(sizeof(uint16_t) * n);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
data[i] = OS_MBUF_DATA(om, const uint8_t *);
|
||||
data_len[i] = om->om_len;
|
||||
om = SLIST_NEXT(om, om_next);
|
||||
}
|
||||
|
||||
// Pass all the fragments together.
|
||||
mp_bluetooth_gattc_on_data_available(event, conn_handle, value_handle, data, data_len, n);
|
||||
|
||||
mp_local_free(data_len);
|
||||
mp_local_free(data);
|
||||
}
|
||||
|
||||
STATIC int gap_scan_cb(struct ble_gap_event *event, void *arg) {
|
||||
DEBUG_printf("gap_scan_cb: event=%d type=%d\n", event->type, event->type == BLE_GAP_EVENT_DISC ? event->disc.event_type : -1);
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
@ -1122,58 +1180,19 @@ STATIC int peripheral_gap_event_cb(struct ble_gap_event *event, void *arg) {
|
||||
// Connection failed.
|
||||
mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_PERIPHERAL_DISCONNECT, event->connect.conn_handle, 0xff, addr);
|
||||
}
|
||||
break;
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_DISCONNECT:
|
||||
// Disconnect.
|
||||
DEBUG_printf("peripheral_gap_event_cb: reason=%d\n", event->disconnect.reason);
|
||||
reverse_addr_byte_order(addr, event->disconnect.conn.peer_id_addr.val);
|
||||
mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_PERIPHERAL_DISCONNECT, event->disconnect.conn.conn_handle, event->disconnect.conn.peer_id_addr.type, addr);
|
||||
|
||||
break;
|
||||
|
||||
case BLE_GAP_EVENT_NOTIFY_RX: {
|
||||
uint16_t ev = event->notify_rx.indication == 0 ? MP_BLUETOOTH_IRQ_GATTC_NOTIFY : MP_BLUETOOTH_IRQ_GATTC_INDICATE;
|
||||
gattc_on_data_available(ev, event->notify_rx.conn_handle, event->notify_rx.attr_handle, event->notify_rx.om);
|
||||
break;
|
||||
}
|
||||
|
||||
case BLE_GAP_EVENT_CONN_UPDATE: {
|
||||
DEBUG_printf("peripheral_gap_event_cb: connection update: status=%d\n", event->conn_update.status);
|
||||
if (ble_gap_conn_find(event->conn_update.conn_handle, &desc) == 0) {
|
||||
mp_bluetooth_gap_on_connection_update(event->conn_update.conn_handle, desc.conn_itvl, desc.conn_latency, desc.supervision_timeout, event->conn_update.status == 0 ? 0 : 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case BLE_GAP_EVENT_MTU: {
|
||||
if (event->mtu.channel_id == BLE_L2CAP_CID_ATT) {
|
||||
DEBUG_printf("peripheral_gap_event_cb: mtu update: conn_handle=%d cid=%d mtu=%d\n", event->mtu.conn_handle, event->mtu.channel_id, event->mtu.value);
|
||||
mp_bluetooth_gatts_on_mtu_exchanged(event->mtu.conn_handle, event->mtu.value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case BLE_GAP_EVENT_ENC_CHANGE: {
|
||||
DEBUG_printf("peripheral_gap_event_cb: enc change: status=%d\n", event->enc_change.status);
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING
|
||||
if (ble_gap_conn_find(event->enc_change.conn_handle, &desc) == 0) {
|
||||
mp_bluetooth_gatts_on_encryption_update(event->conn_update.conn_handle,
|
||||
desc.sec_state.encrypted, desc.sec_state.authenticated,
|
||||
desc.sec_state.bonded, desc.sec_state.key_size);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
DEBUG_printf("peripheral_gap_event_cb: unknown type %d\n", event->type);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return commmon_gap_event_cb(event, arg);
|
||||
}
|
||||
|
||||
int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, int32_t duration_ms) {
|
||||
DEBUG_printf("mp_bluetooth_gap_peripheral_connect\n");
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
@ -1200,8 +1219,8 @@ int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr,
|
||||
return ble_hs_err_to_errno(err);
|
||||
}
|
||||
|
||||
STATIC int peripheral_discover_service_cb(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_svc *service, void *arg) {
|
||||
DEBUG_printf("peripheral_discover_service_cb: conn_handle=%d status=%d start_handle=%d\n", conn_handle, error->status, service ? service->start_handle : -1);
|
||||
STATIC int ble_gattc_service_cb(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_svc *service, void *arg) {
|
||||
DEBUG_printf("ble_gattc_service_cb: conn_handle=%d status=%d start_handle=%d\n", conn_handle, error->status, service ? service->start_handle : -1);
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return 0;
|
||||
}
|
||||
@ -1214,6 +1233,42 @@ STATIC int peripheral_discover_service_cb(uint16_t conn_handle, const struct ble
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
|
||||
STATIC void gattc_on_data_available(uint8_t event, uint16_t conn_handle, uint16_t value_handle, const struct os_mbuf *om) {
|
||||
// When the HCI data for an ATT payload arrives, the L2CAP channel will
|
||||
// buffer it into its receive buffer. We set BLE_L2CAP_JOIN_RX_FRAGS=1 in
|
||||
// syscfg.h so it should be rare that the mbuf is fragmented, but we do need
|
||||
// to be able to handle it. We pass all the fragments up to modbluetooth.c
|
||||
// which will create a temporary buffer on the MicroPython heap if necessary
|
||||
// to re-assemble them.
|
||||
|
||||
// Count how many links are in the mbuf chain.
|
||||
size_t n = 0;
|
||||
const struct os_mbuf *elem = om;
|
||||
while (elem) {
|
||||
n += 1;
|
||||
elem = SLIST_NEXT(elem, om_next);
|
||||
}
|
||||
|
||||
// Grab data pointers and lengths for each of the links.
|
||||
const uint8_t **data = mp_local_alloc(sizeof(uint8_t *) * n);
|
||||
uint16_t *data_len = mp_local_alloc(sizeof(uint16_t) * n);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
data[i] = OS_MBUF_DATA(om, const uint8_t *);
|
||||
data_len[i] = om->om_len;
|
||||
om = SLIST_NEXT(om, om_next);
|
||||
}
|
||||
|
||||
// Pass all the fragments together.
|
||||
mp_bluetooth_gattc_on_data_available(event, conn_handle, value_handle, data, data_len, n);
|
||||
|
||||
mp_local_free(data_len);
|
||||
mp_local_free(data);
|
||||
}
|
||||
|
||||
int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_obj_bluetooth_uuid_t *uuid) {
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return ERRNO_BLUETOOTH_NOT_ACTIVE;
|
||||
@ -1222,15 +1277,15 @@ int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_
|
||||
if (uuid) {
|
||||
ble_uuid_any_t nimble_uuid;
|
||||
create_nimble_uuid(uuid, &nimble_uuid);
|
||||
err = ble_gattc_disc_svc_by_uuid(conn_handle, &nimble_uuid.u, &peripheral_discover_service_cb, NULL);
|
||||
err = ble_gattc_disc_svc_by_uuid(conn_handle, &nimble_uuid.u, &ble_gattc_service_cb, NULL);
|
||||
} else {
|
||||
err = ble_gattc_disc_all_svcs(conn_handle, &peripheral_discover_service_cb, NULL);
|
||||
err = ble_gattc_disc_all_svcs(conn_handle, &ble_gattc_service_cb, NULL);
|
||||
}
|
||||
return ble_hs_err_to_errno(err);
|
||||
}
|
||||
|
||||
STATIC int ble_gatt_characteristic_cb(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_chr *characteristic, void *arg) {
|
||||
DEBUG_printf("ble_gatt_characteristic_cb: conn_handle=%d status=%d def_handle=%d val_handle=%d\n", conn_handle, error->status, characteristic ? characteristic->def_handle : -1, characteristic ? characteristic->val_handle : -1);
|
||||
STATIC int ble_gattc_characteristic_cb(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_chr *characteristic, void *arg) {
|
||||
DEBUG_printf("ble_gattc_characteristic_cb: conn_handle=%d status=%d def_handle=%d val_handle=%d\n", conn_handle, error->status, characteristic ? characteristic->def_handle : -1, characteristic ? characteristic->val_handle : -1);
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return 0;
|
||||
}
|
||||
@ -1251,15 +1306,15 @@ int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t s
|
||||
if (uuid) {
|
||||
ble_uuid_any_t nimble_uuid;
|
||||
create_nimble_uuid(uuid, &nimble_uuid);
|
||||
err = ble_gattc_disc_chrs_by_uuid(conn_handle, start_handle, end_handle, &nimble_uuid.u, &ble_gatt_characteristic_cb, NULL);
|
||||
err = ble_gattc_disc_chrs_by_uuid(conn_handle, start_handle, end_handle, &nimble_uuid.u, &ble_gattc_characteristic_cb, NULL);
|
||||
} else {
|
||||
err = ble_gattc_disc_all_chrs(conn_handle, start_handle, end_handle, &ble_gatt_characteristic_cb, NULL);
|
||||
err = ble_gattc_disc_all_chrs(conn_handle, start_handle, end_handle, &ble_gattc_characteristic_cb, NULL);
|
||||
}
|
||||
return ble_hs_err_to_errno(err);
|
||||
}
|
||||
|
||||
STATIC int ble_gatt_descriptor_cb(uint16_t conn_handle, const struct ble_gatt_error *error, uint16_t characteristic_val_handle, const struct ble_gatt_dsc *descriptor, void *arg) {
|
||||
DEBUG_printf("ble_gatt_descriptor_cb: conn_handle=%d status=%d chr_handle=%d dsc_handle=%d\n", conn_handle, error->status, characteristic_val_handle, descriptor ? descriptor->handle : -1);
|
||||
STATIC int ble_gattc_descriptor_cb(uint16_t conn_handle, const struct ble_gatt_error *error, uint16_t characteristic_val_handle, const struct ble_gatt_dsc *descriptor, void *arg) {
|
||||
DEBUG_printf("ble_gattc_descriptor_cb: conn_handle=%d status=%d chr_handle=%d dsc_handle=%d\n", conn_handle, error->status, characteristic_val_handle, descriptor ? descriptor->handle : -1);
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return 0;
|
||||
}
|
||||
@ -1276,19 +1331,20 @@ int mp_bluetooth_gattc_discover_descriptors(uint16_t conn_handle, uint16_t start
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return ERRNO_BLUETOOTH_NOT_ACTIVE;
|
||||
}
|
||||
int err = ble_gattc_disc_all_dscs(conn_handle, start_handle, end_handle, &ble_gatt_descriptor_cb, NULL);
|
||||
int err = ble_gattc_disc_all_dscs(conn_handle, start_handle, end_handle, &ble_gattc_descriptor_cb, NULL);
|
||||
return ble_hs_err_to_errno(err);
|
||||
}
|
||||
|
||||
STATIC int ble_gatt_attr_read_cb(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg) {
|
||||
DEBUG_printf("ble_gatt_attr_read_cb: conn_handle=%d status=%d handle=%d\n", conn_handle, error->status, attr ? attr->handle : -1);
|
||||
STATIC int ble_gattc_attr_read_cb(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg) {
|
||||
uint16_t handle = attr ? attr->handle : (error ? error->att_handle : 0xffff);
|
||||
DEBUG_printf("ble_gattc_attr_read_cb: conn_handle=%d status=%d handle=%d\n", conn_handle, error->status, handle);
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return 0;
|
||||
}
|
||||
if (error->status == 0) {
|
||||
gattc_on_data_available(MP_BLUETOOTH_IRQ_GATTC_READ_RESULT, conn_handle, attr->handle, attr->om);
|
||||
}
|
||||
mp_bluetooth_gattc_on_read_write_status(MP_BLUETOOTH_IRQ_GATTC_READ_DONE, conn_handle, attr ? attr->handle : -1, error->status);
|
||||
mp_bluetooth_gattc_on_read_write_status(MP_BLUETOOTH_IRQ_GATTC_READ_DONE, conn_handle, handle, error->status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1297,16 +1353,17 @@ int mp_bluetooth_gattc_read(uint16_t conn_handle, uint16_t value_handle) {
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return ERRNO_BLUETOOTH_NOT_ACTIVE;
|
||||
}
|
||||
int err = ble_gattc_read(conn_handle, value_handle, &ble_gatt_attr_read_cb, NULL);
|
||||
int err = ble_gattc_read(conn_handle, value_handle, &ble_gattc_attr_read_cb, NULL);
|
||||
return ble_hs_err_to_errno(err);
|
||||
}
|
||||
|
||||
STATIC int ble_gatt_attr_write_cb(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg) {
|
||||
DEBUG_printf("ble_gatt_attr_write_cb: conn_handle=%d status=%d handle=%d\n", conn_handle, error->status, attr ? attr->handle : -1);
|
||||
STATIC int ble_gattc_attr_write_cb(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg) {
|
||||
uint16_t handle = attr ? attr->handle : (error ? error->att_handle : 0xffff);
|
||||
DEBUG_printf("ble_gattc_attr_write_cb: conn_handle=%d status=%d handle=%d\n", conn_handle, error->status, handle);
|
||||
if (!mp_bluetooth_is_active()) {
|
||||
return 0;
|
||||
}
|
||||
mp_bluetooth_gattc_on_read_write_status(MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE, conn_handle, attr->handle, error->status);
|
||||
mp_bluetooth_gattc_on_read_write_status(MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE, conn_handle, handle, error->status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1319,7 +1376,7 @@ int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const
|
||||
if (mode == MP_BLUETOOTH_WRITE_MODE_NO_RESPONSE) {
|
||||
err = ble_gattc_write_no_rsp_flat(conn_handle, value_handle, value, *value_len);
|
||||
} else if (mode == MP_BLUETOOTH_WRITE_MODE_WITH_RESPONSE) {
|
||||
err = ble_gattc_write_flat(conn_handle, value_handle, value, *value_len, &ble_gatt_attr_write_cb, NULL);
|
||||
err = ble_gattc_write_flat(conn_handle, value_handle, value, *value_len, &ble_gattc_attr_write_cb, NULL);
|
||||
} else {
|
||||
err = BLE_HS_EINVAL;
|
||||
}
|
||||
@ -1333,7 +1390,7 @@ int mp_bluetooth_gattc_exchange_mtu(uint16_t conn_handle) {
|
||||
return ble_hs_err_to_errno(ble_gattc_exchange_mtu(conn_handle, NULL, NULL));
|
||||
}
|
||||
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS
|
||||
|
||||
@ -1360,6 +1417,11 @@ typedef struct _mp_bluetooth_nimble_l2cap_channel_t {
|
||||
os_membuf_t sdu_mem[];
|
||||
} mp_bluetooth_nimble_l2cap_channel_t;
|
||||
|
||||
STATIC void destroy_l2cap_channel();
|
||||
STATIC int l2cap_channel_event(struct ble_l2cap_event *event, void *arg);
|
||||
STATIC mp_bluetooth_nimble_l2cap_channel_t *get_l2cap_channel_for_conn_cid(uint16_t conn_handle, uint16_t cid);
|
||||
STATIC int create_l2cap_channel(uint16_t mtu, mp_bluetooth_nimble_l2cap_channel_t **out);
|
||||
|
||||
STATIC void destroy_l2cap_channel() {
|
||||
// Only free the l2cap channel if we're the one that initiated the connection.
|
||||
// Listeners continue listening on the same channel.
|
||||
@ -1380,9 +1442,9 @@ STATIC int l2cap_channel_event(struct ble_l2cap_event *event, void *arg) {
|
||||
|
||||
ble_l2cap_get_chan_info(event->connect.chan, &info);
|
||||
if (event->connect.status == 0) {
|
||||
mp_bluetooth_gattc_on_l2cap_connect(event->connect.conn_handle, info.scid, info.psm, info.our_coc_mtu, info.peer_coc_mtu);
|
||||
mp_bluetooth_on_l2cap_connect(event->connect.conn_handle, info.scid, info.psm, info.our_coc_mtu, info.peer_coc_mtu);
|
||||
} else {
|
||||
mp_bluetooth_gattc_on_l2cap_disconnect(event->connect.conn_handle, info.scid, info.psm, event->connect.status);
|
||||
mp_bluetooth_on_l2cap_disconnect(event->connect.conn_handle, info.scid, info.psm, event->connect.status);
|
||||
destroy_l2cap_channel();
|
||||
}
|
||||
break;
|
||||
@ -1390,7 +1452,7 @@ STATIC int l2cap_channel_event(struct ble_l2cap_event *event, void *arg) {
|
||||
case BLE_L2CAP_EVENT_COC_DISCONNECTED: {
|
||||
DEBUG_printf("l2cap_channel_event: disconnect: conn_handle=%d\n", event->disconnect.conn_handle);
|
||||
ble_l2cap_get_chan_info(event->disconnect.chan, &info);
|
||||
mp_bluetooth_gattc_on_l2cap_disconnect(event->disconnect.conn_handle, info.scid, info.psm, 0);
|
||||
mp_bluetooth_on_l2cap_disconnect(event->disconnect.conn_handle, info.scid, info.psm, 0);
|
||||
destroy_l2cap_channel();
|
||||
break;
|
||||
}
|
||||
@ -1398,7 +1460,7 @@ STATIC int l2cap_channel_event(struct ble_l2cap_event *event, void *arg) {
|
||||
DEBUG_printf("l2cap_channel_event: accept: conn_handle=%d peer_sdu_size=%d\n", event->accept.conn_handle, event->accept.peer_sdu_size);
|
||||
chan->chan = event->accept.chan;
|
||||
ble_l2cap_get_chan_info(event->accept.chan, &info);
|
||||
int ret = mp_bluetooth_gattc_on_l2cap_accept(event->accept.conn_handle, info.scid, info.psm, info.our_coc_mtu, info.peer_coc_mtu);
|
||||
int ret = mp_bluetooth_on_l2cap_accept(event->accept.conn_handle, info.scid, info.psm, info.our_coc_mtu, info.peer_coc_mtu);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
@ -1446,7 +1508,7 @@ STATIC int l2cap_channel_event(struct ble_l2cap_event *event, void *arg) {
|
||||
// Don't allow granting more credits until after the IRQ is handled.
|
||||
chan->irq_in_progress = true;
|
||||
|
||||
mp_bluetooth_gattc_on_l2cap_recv(event->receive.conn_handle, info.scid);
|
||||
mp_bluetooth_on_l2cap_recv(event->receive.conn_handle, info.scid);
|
||||
chan->irq_in_progress = false;
|
||||
|
||||
// If all data has been consumed by the IRQ handler, then now allow
|
||||
@ -1465,7 +1527,7 @@ STATIC int l2cap_channel_event(struct ble_l2cap_event *event, void *arg) {
|
||||
DEBUG_printf("l2cap_channel_event: tx_unstalled: conn_handle=%d status=%d\n", event->tx_unstalled.conn_handle, event->tx_unstalled.status);
|
||||
ble_l2cap_get_chan_info(event->receive.chan, &info);
|
||||
// Map status to {0,1} (i.e. "sent everything", or "partial send").
|
||||
mp_bluetooth_gattc_on_l2cap_send_ready(event->tx_unstalled.conn_handle, info.scid, event->tx_unstalled.status == 0 ? 0 : 1);
|
||||
mp_bluetooth_on_l2cap_send_ready(event->tx_unstalled.conn_handle, info.scid, event->tx_unstalled.status == 0 ? 0 : 1);
|
||||
break;
|
||||
}
|
||||
case BLE_L2CAP_EVENT_COC_RECONFIG_COMPLETED: {
|
||||
@ -1686,9 +1748,6 @@ int mp_bluetooth_l2cap_recvinto(uint16_t conn_handle, uint16_t cid, uint8_t *buf
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_ENABLE_HCI_CMD
|
||||
|
||||
// For ble_hs_hci_cmd_tx
|
||||
#include "nimble/host/src/ble_hs_hci_priv.h"
|
||||
|
||||
int mp_bluetooth_hci_cmd(uint16_t ogf, uint16_t ocf, const uint8_t *req, size_t req_len, uint8_t *resp, size_t resp_len, uint8_t *status) {
|
||||
int rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(ogf, ocf), req, req_len, resp, resp_len);
|
||||
if (rc < BLE_HS_ERR_HCI_BASE || rc >= BLE_HS_ERR_HCI_BASE + 0x100) {
|
||||
|
||||
@ -8,11 +8,20 @@
|
||||
((code ? memmove(code + at + num, code + at, pc - at) : 0), pc += num)
|
||||
#define REL(at, to) (to - at - 2)
|
||||
#define EMIT(at, byte) (code ? (code[at] = byte) : (at))
|
||||
#define EMIT_CHECKED(at, byte) (_emit_checked(at, code, byte, &err))
|
||||
#define PC (prog->bytelen)
|
||||
|
||||
static void _emit_checked(int at, char *code, int val, bool *err) {
|
||||
*err |= val != (int8_t)val;
|
||||
if (code) {
|
||||
code[at] = val;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *_compilecode(const char *re, ByteProg *prog, int sizecode)
|
||||
{
|
||||
char *code = sizecode ? NULL : prog->insts;
|
||||
bool err = false;
|
||||
int start = PC;
|
||||
int term = PC;
|
||||
int alt_label = 0;
|
||||
@ -64,7 +73,7 @@ static const char *_compilecode(const char *re, ByteProg *prog, int sizecode)
|
||||
}
|
||||
EMIT(PC++, *re);
|
||||
}
|
||||
EMIT(term + 1, cnt);
|
||||
EMIT_CHECKED(term + 1, cnt);
|
||||
break;
|
||||
}
|
||||
case '(': {
|
||||
@ -75,7 +84,7 @@ static const char *_compilecode(const char *re, ByteProg *prog, int sizecode)
|
||||
if (capture) {
|
||||
sub = ++prog->sub;
|
||||
EMIT(PC++, Save);
|
||||
EMIT(PC++, 2 * sub);
|
||||
EMIT_CHECKED(PC++, 2 * sub);
|
||||
prog->len++;
|
||||
} else {
|
||||
re += 2;
|
||||
@ -86,7 +95,7 @@ static const char *_compilecode(const char *re, ByteProg *prog, int sizecode)
|
||||
|
||||
if (capture) {
|
||||
EMIT(PC++, Save);
|
||||
EMIT(PC++, 2 * sub + 1);
|
||||
EMIT_CHECKED(PC++, 2 * sub + 1);
|
||||
prog->len++;
|
||||
}
|
||||
|
||||
@ -101,7 +110,7 @@ static const char *_compilecode(const char *re, ByteProg *prog, int sizecode)
|
||||
} else {
|
||||
EMIT(term, Split);
|
||||
}
|
||||
EMIT(term + 1, REL(term, PC));
|
||||
EMIT_CHECKED(term + 1, REL(term, PC));
|
||||
prog->len++;
|
||||
term = PC;
|
||||
break;
|
||||
@ -109,7 +118,7 @@ static const char *_compilecode(const char *re, ByteProg *prog, int sizecode)
|
||||
if (PC == term) return NULL; // nothing to repeat
|
||||
INSERT_CODE(term, 2, PC);
|
||||
EMIT(PC, Jmp);
|
||||
EMIT(PC + 1, REL(PC, term));
|
||||
EMIT_CHECKED(PC + 1, REL(PC, term));
|
||||
PC += 2;
|
||||
if (re[1] == '?') {
|
||||
EMIT(term, RSplit);
|
||||
@ -117,7 +126,7 @@ static const char *_compilecode(const char *re, ByteProg *prog, int sizecode)
|
||||
} else {
|
||||
EMIT(term, Split);
|
||||
}
|
||||
EMIT(term + 1, REL(term, PC));
|
||||
EMIT_CHECKED(term + 1, REL(term, PC));
|
||||
prog->len += 2;
|
||||
term = PC;
|
||||
break;
|
||||
@ -129,20 +138,20 @@ static const char *_compilecode(const char *re, ByteProg *prog, int sizecode)
|
||||
} else {
|
||||
EMIT(PC, RSplit);
|
||||
}
|
||||
EMIT(PC + 1, REL(PC, term));
|
||||
EMIT_CHECKED(PC + 1, REL(PC, term));
|
||||
PC += 2;
|
||||
prog->len++;
|
||||
term = PC;
|
||||
break;
|
||||
case '|':
|
||||
if (alt_label) {
|
||||
EMIT(alt_label, REL(alt_label, PC) + 1);
|
||||
EMIT_CHECKED(alt_label, REL(alt_label, PC) + 1);
|
||||
}
|
||||
INSERT_CODE(start, 2, PC);
|
||||
EMIT(PC++, Jmp);
|
||||
alt_label = PC++;
|
||||
EMIT(start, Split);
|
||||
EMIT(start + 1, REL(start, PC));
|
||||
EMIT_CHECKED(start + 1, REL(start, PC));
|
||||
prog->len += 2;
|
||||
term = PC;
|
||||
break;
|
||||
@ -160,9 +169,9 @@ static const char *_compilecode(const char *re, ByteProg *prog, int sizecode)
|
||||
}
|
||||
|
||||
if (alt_label) {
|
||||
EMIT(alt_label, REL(alt_label, PC) + 1);
|
||||
EMIT_CHECKED(alt_label, REL(alt_label, PC) + 1);
|
||||
}
|
||||
return re;
|
||||
return err ? NULL : re;
|
||||
}
|
||||
|
||||
int re1_5_sizecode(const char *re)
|
||||
|
||||
@ -10,6 +10,7 @@ _attrs = {
|
||||
"wait_for_ms": "funcs",
|
||||
"gather": "funcs",
|
||||
"Event": "event",
|
||||
"ThreadSafeFlag": "event",
|
||||
"Lock": "lock",
|
||||
"open_connection": "stream",
|
||||
"start_server": "stream",
|
||||
|
||||
@ -175,6 +175,10 @@ def run_until_complete(main_task=None):
|
||||
if not exc:
|
||||
t.coro.send(None)
|
||||
else:
|
||||
# If the task is finished and on the run queue and gets here, then it
|
||||
# had an exception and was not await'ed on. Throwing into it now will
|
||||
# raise StopIteration and the code below will catch this and run the
|
||||
# call_exception_handler function.
|
||||
t.data = None
|
||||
t.coro.throw(exc)
|
||||
except excs_all as er:
|
||||
@ -185,22 +189,32 @@ def run_until_complete(main_task=None):
|
||||
if isinstance(er, StopIteration):
|
||||
return er.value
|
||||
raise er
|
||||
# Schedule any other tasks waiting on the completion of this task
|
||||
if t.state:
|
||||
# Task was running but is now finished.
|
||||
waiting = False
|
||||
if hasattr(t, "waiting"):
|
||||
while t.waiting.peek():
|
||||
_task_queue.push_head(t.waiting.pop_head())
|
||||
if t.state is True:
|
||||
# "None" indicates that the task is complete and not await'ed on (yet).
|
||||
t.state = None
|
||||
else:
|
||||
# Schedule any other tasks waiting on the completion of this task.
|
||||
while t.state.peek():
|
||||
_task_queue.push_head(t.state.pop_head())
|
||||
waiting = True
|
||||
t.waiting = None # Free waiting queue head
|
||||
# "False" indicates that the task is complete and has been await'ed on.
|
||||
t.state = False
|
||||
if not waiting and not isinstance(er, excs_stop):
|
||||
# An exception ended this detached task, so queue it for later
|
||||
# execution to handle the uncaught exception if no other task retrieves
|
||||
# the exception in the meantime (this is handled by Task.throw).
|
||||
_task_queue.push_head(t)
|
||||
# Indicate task is done by setting coro to the task object itself
|
||||
t.coro = t
|
||||
# Save return value of coro to pass up to caller
|
||||
# Save return value of coro to pass up to caller.
|
||||
t.data = er
|
||||
elif t.state is None:
|
||||
# Task is already finished and nothing await'ed on the task,
|
||||
# so call the exception handler.
|
||||
_exc_context["exception"] = exc
|
||||
_exc_context["future"] = t
|
||||
Loop.call_exception_handler(_exc_context)
|
||||
|
||||
|
||||
# Create a new task from a coroutine and run it until it finishes
|
||||
@ -264,6 +278,10 @@ def get_event_loop(runq_len=0, waitq_len=0):
|
||||
return Loop
|
||||
|
||||
|
||||
def current_task():
|
||||
return cur_task
|
||||
|
||||
|
||||
def new_event_loop():
|
||||
global _task_queue, _io_queue
|
||||
# TaskQueue of Task instances
|
||||
|
||||
@ -14,6 +14,8 @@ class Event:
|
||||
|
||||
def set(self):
|
||||
# Event becomes set, schedule any tasks waiting on it
|
||||
# Note: This must not be called from anything except the thread running
|
||||
# the asyncio loop (i.e. neither hard or soft IRQ, or a different thread).
|
||||
while self.waiting.peek():
|
||||
core._task_queue.push_head(self.waiting.pop_head())
|
||||
self.state = True
|
||||
@ -29,3 +31,32 @@ class Event:
|
||||
core.cur_task.data = self.waiting
|
||||
yield
|
||||
return True
|
||||
|
||||
|
||||
# MicroPython-extension: This can be set from outside the asyncio event loop,
|
||||
# such as other threads, IRQs or scheduler context. Implementation is a stream
|
||||
# that asyncio will poll until a flag is set.
|
||||
# Note: Unlike Event, this is self-clearing.
|
||||
try:
|
||||
import uio
|
||||
|
||||
class ThreadSafeFlag(uio.IOBase):
|
||||
def __init__(self):
|
||||
self._flag = 0
|
||||
|
||||
def ioctl(self, req, flags):
|
||||
if req == 3: # MP_STREAM_POLL
|
||||
return self._flag * flags
|
||||
return None
|
||||
|
||||
def set(self):
|
||||
self._flag = 1
|
||||
|
||||
async def wait(self):
|
||||
if not self._flag:
|
||||
yield core._io_queue.queue_read(self)
|
||||
self._flag = 0
|
||||
|
||||
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
@ -30,6 +30,10 @@ class Stream:
|
||||
yield core._io_queue.queue_read(self.s)
|
||||
return self.s.read(n)
|
||||
|
||||
async def readinto(self, buf):
|
||||
yield core._io_queue.queue_read(self.s)
|
||||
return self.s.readinto(buf)
|
||||
|
||||
async def readexactly(self, n):
|
||||
r = b""
|
||||
while n:
|
||||
@ -82,7 +86,7 @@ async def open_connection(host, port):
|
||||
try:
|
||||
s.connect(ai[-1])
|
||||
except OSError as er:
|
||||
if er.args[0] != EINPROGRESS:
|
||||
if er.errno != EINPROGRESS:
|
||||
raise er
|
||||
yield core._io_queue.queue_write(s)
|
||||
return ss, ss
|
||||
@ -103,16 +107,7 @@ class Server:
|
||||
async def wait_closed(self):
|
||||
await self.task
|
||||
|
||||
async def _serve(self, cb, host, port, backlog):
|
||||
import usocket as socket
|
||||
|
||||
ai = socket.getaddrinfo(host, port)[0] # TODO this is blocking!
|
||||
s = socket.socket()
|
||||
s.setblocking(False)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.bind(ai[-1])
|
||||
s.listen(backlog)
|
||||
self.task = core.cur_task
|
||||
async def _serve(self, s, cb):
|
||||
# Accept incoming connections
|
||||
while True:
|
||||
try:
|
||||
@ -134,9 +129,20 @@ class Server:
|
||||
# Helper function to start a TCP stream server, running as a new task
|
||||
# TODO could use an accept-callback on socket read activity instead of creating a task
|
||||
async def start_server(cb, host, port, backlog=5):
|
||||
s = Server()
|
||||
core.create_task(s._serve(cb, host, port, backlog))
|
||||
return s
|
||||
import usocket as socket
|
||||
|
||||
# Create and bind server socket.
|
||||
host = socket.getaddrinfo(host, port)[0] # TODO this is blocking!
|
||||
s = socket.socket()
|
||||
s.setblocking(False)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.bind(host[-1])
|
||||
s.listen(backlog)
|
||||
|
||||
# Create and return server object and task.
|
||||
srv = Server()
|
||||
srv.task = core.create_task(srv._serve(s, cb))
|
||||
return srv
|
||||
|
||||
|
||||
################################################################################
|
||||
|
||||
@ -123,6 +123,7 @@ class Task:
|
||||
def __init__(self, coro, globals=None):
|
||||
self.coro = coro # Coroutine of this Task
|
||||
self.data = None # General data for queue it is waiting on
|
||||
self.state = True # None, False, True or a TaskQueue instance
|
||||
self.ph_key = 0 # Pairing heap
|
||||
self.ph_child = None # Paring heap
|
||||
self.ph_child_last = None # Paring heap
|
||||
@ -130,30 +131,30 @@ class Task:
|
||||
self.ph_rightmost_parent = None # Paring heap
|
||||
|
||||
def __iter__(self):
|
||||
if self.coro is self:
|
||||
# Signal that the completed-task has been await'ed on.
|
||||
self.waiting = None
|
||||
elif not hasattr(self, "waiting"):
|
||||
# Lazily allocated head of linked list of Tasks waiting on completion of this task.
|
||||
self.waiting = TaskQueue()
|
||||
if not self.state:
|
||||
# Task finished, signal that is has been await'ed on.
|
||||
self.state = False
|
||||
elif self.state is True:
|
||||
# Allocated head of linked list of Tasks waiting on completion of this task.
|
||||
self.state = TaskQueue()
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if self.coro is self:
|
||||
if not self.state:
|
||||
# Task finished, raise return value to caller so it can continue.
|
||||
raise self.data
|
||||
else:
|
||||
# Put calling task on waiting queue.
|
||||
self.waiting.push_head(core.cur_task)
|
||||
self.state.push_head(core.cur_task)
|
||||
# Set calling task's data to this task that it waits on, to double-link it.
|
||||
core.cur_task.data = self
|
||||
|
||||
def done(self):
|
||||
return self.coro is self
|
||||
return not self.state
|
||||
|
||||
def cancel(self):
|
||||
# Check if task is already finished.
|
||||
if self.coro is self:
|
||||
if not self.state:
|
||||
return False
|
||||
# Can't cancel self (not supported yet).
|
||||
if self is core.cur_task:
|
||||
@ -172,13 +173,3 @@ class Task:
|
||||
core._task_queue.push_head(self)
|
||||
self.data = core.CancelledError
|
||||
return True
|
||||
|
||||
def throw(self, value):
|
||||
# This task raised an exception which was uncaught; handle that now.
|
||||
# Set the data because it was cleared by the main scheduling loop.
|
||||
self.data = value
|
||||
if not hasattr(self, "waiting"):
|
||||
# Nothing await'ed on the task so call the exception handler.
|
||||
core._exc_context["exception"] = value
|
||||
core._exc_context["future"] = self
|
||||
core.Loop.call_exception_handler(core._exc_context)
|
||||
|
||||
@ -133,7 +133,7 @@ int mp_uos_dupterm_rx_chr(void) {
|
||||
nlr_pop();
|
||||
if (buf[0] == mp_interrupt_char) {
|
||||
// Signal keyboard interrupt to be raised as soon as the VM resumes
|
||||
mp_keyboard_interrupt();
|
||||
mp_sched_keyboard_interrupt();
|
||||
return -2;
|
||||
}
|
||||
return buf[0];
|
||||
|
||||
@ -256,7 +256,7 @@ STATIC mp_obj_t fat_vfs_mkdir(mp_obj_t vfs_in, mp_obj_t path_o) {
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_mkdir_obj, fat_vfs_mkdir);
|
||||
|
||||
/// Change current directory.
|
||||
// Change current directory.
|
||||
STATIC mp_obj_t fat_vfs_chdir(mp_obj_t vfs_in, mp_obj_t path_in) {
|
||||
mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
|
||||
const char *path;
|
||||
@ -272,7 +272,7 @@ STATIC mp_obj_t fat_vfs_chdir(mp_obj_t vfs_in, mp_obj_t path_in) {
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_chdir_obj, fat_vfs_chdir);
|
||||
|
||||
/// Get the current directory.
|
||||
// Get the current directory.
|
||||
STATIC mp_obj_t fat_vfs_getcwd(mp_obj_t vfs_in) {
|
||||
mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
|
||||
char buf[MICROPY_ALLOC_PATH_MAX + 1];
|
||||
@ -284,8 +284,7 @@ STATIC mp_obj_t fat_vfs_getcwd(mp_obj_t vfs_in) {
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_getcwd_obj, fat_vfs_getcwd);
|
||||
|
||||
/// \function stat(path)
|
||||
/// Get the status of a file or directory.
|
||||
// Get the status of a file or directory.
|
||||
STATIC mp_obj_t fat_vfs_stat(mp_obj_t vfs_in, mp_obj_t path_in) {
|
||||
mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
|
||||
const char *path = mp_obj_str_get_str(path_in);
|
||||
|
||||
@ -163,7 +163,11 @@ STATIC mp_uint_t vfs_posix_file_write(mp_obj_t o_in, const void *buf, mp_uint_t
|
||||
|
||||
STATIC mp_uint_t vfs_posix_file_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) {
|
||||
mp_obj_vfs_posix_file_t *o = MP_OBJ_TO_PTR(o_in);
|
||||
|
||||
if (request != MP_STREAM_CLOSE) {
|
||||
check_fd_is_open(o);
|
||||
}
|
||||
|
||||
switch (request) {
|
||||
case MP_STREAM_FLUSH: {
|
||||
int ret;
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit 3f2a0fbdd896653a3ba18c33d4ccf6ac9ea36bec
|
||||
Subproject commit b1305cadf109e4e7dad78fd891cdde828f4b4c5f
|
||||
@ -1 +1 @@
|
||||
Subproject commit 3f8d78411a26e833db18d9fbde0e2f0baeda87f0
|
||||
Subproject commit 1bc2c9cb8b8fe4659bd94b8ebba5a4c02029b7fa
|
||||
@ -1 +1 @@
|
||||
Subproject commit 2d5789eca89658a7f0a01e2d6010c0f254605d72
|
||||
Subproject commit fc10a97c386f65c1a44c68684fe52a56aaf50df0
|
||||
@ -213,3 +213,10 @@ mp_uint_t timeutils_mktime_2000(mp_uint_t year, mp_int_t month, mp_int_t mday,
|
||||
}
|
||||
return timeutils_seconds_since_2000(year, month, mday, hours, minutes, seconds);
|
||||
}
|
||||
|
||||
// Calculate the weekday from the date.
|
||||
// The result is zero based with 0 = Monday.
|
||||
// by Michael Keith and Tom Craver, 1990.
|
||||
int timeutils_calc_weekday(int y, int m, int d) {
|
||||
return ((d += m < 3 ? y-- : y - 2, 23 * m / 9 + d + 4 + y / 4 - y / 100 + y / 400) + 6) % 7;
|
||||
}
|
||||
|
||||
@ -100,4 +100,6 @@ static inline int64_t timeutils_nanoseconds_since_epoch_to_nanoseconds_since_197
|
||||
|
||||
#endif
|
||||
|
||||
int timeutils_calc_weekday(int y, int m, int d);
|
||||
|
||||
#endif // MICROPY_INCLUDED_LIB_TIMEUTILS_TIMEUTILS_H
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit a6b916ba85bef6aad50f1652532b02984dfe2484
|
||||
Subproject commit 7b62c71dd5ec42e61499d2d83902df9484842670
|
||||
@ -39,6 +39,8 @@ typedef uintptr_t gc_helper_regs_t[6];
|
||||
typedef uintptr_t gc_helper_regs_t[4];
|
||||
#elif defined(__thumb2__) || defined(__thumb__) || defined(__arm__)
|
||||
typedef uintptr_t gc_helper_regs_t[10];
|
||||
#elif defined(__aarch64__)
|
||||
typedef uintptr_t gc_helper_regs_t[11]; // x19-x29
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user