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:
Amir Gonnen 2021-07-08 00:40:02 +03:00
commit c067ac51cc
761 changed files with 22325 additions and 5748 deletions

35
.git-blame-ignore-revs Normal file
View 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
View 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

View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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)

View File

@ -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.

View File

@ -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).
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::
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.
my_project/
├── modules/
│ └──example1/
│ ├──example1.c
│ └──micropython.mk
│ └──example2/
│ ├──example2.c
│ └──micropython.mk
└── micropython/
├──ports/
... ├──stm32/
...
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 Make,
or to ``examples/usercmodule/micropython.cmake`` for CMake.
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

View File

@ -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`.

View File

@ -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

View File

@ -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
----------------

View File

@ -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).

View File

@ -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)
----------------------------------------

View File

@ -31,4 +31,5 @@ to `<https://www.python.org>`__.
neopixel.rst
apa102.rst
dht.rst
ssd1306.rst
nextsteps.rst

View File

@ -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
-------------

View 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()

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View 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.

View File

@ -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:

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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
------------

View File

@ -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
------------

View File

@ -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)
============= ===========

View File

@ -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, *, ...])

View File

@ -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)``

View File

@ -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)``

View File

@ -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``.

View File

@ -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)``

View File

@ -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)``

View File

@ -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`.

View File

@ -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.

View 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
View 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`.

View 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
View 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

View File

@ -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
----------
@ -184,7 +214,7 @@ TCP stream connections
This is a coroutine.
.. class:: Stream()
This represents a TCP stream connection. To minimise code this class implements
both a reader and a writer, and both ``StreamReader`` and ``StreamWriter`` alias to
this class.
@ -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.

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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
----------

View File

@ -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:

View File

@ -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.

View File

@ -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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

288
docs/rp2/quickref.rst Normal file
View 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.

View File

@ -0,0 +1,6 @@
.. _rp2_intro:
Getting started with MicroPython on the RP2xxx
==============================================
Let's get started!

View File

@ -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>

View File

@ -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
}

View File

@ -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];

View File

@ -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)

View File

@ -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")

View File

@ -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 \
)

View File

@ -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

View File

@ -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

View File

@ -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);

View 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)

View File

@ -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);

View 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)

View 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)

View File

@ -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)

View File

@ -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) {

View File

@ -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

View File

@ -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
View 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()

View File

@ -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

View File

@ -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

View File

@ -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);
}
if (TASK_IS_DONE(self)) {
// Signal that the completed-task has been await'ed on.
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;

View File

@ -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 = {

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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,56 +1180,17 @@ 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 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) {
@ -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) {

View File

@ -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)

View File

@ -10,6 +10,7 @@ _attrs = {
"wait_for_ms": "funcs",
"gather": "funcs",
"Event": "event",
"ThreadSafeFlag": "event",
"Lock": "lock",
"open_connection": "stream",
"start_server": "stream",

View File

@ -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
waiting = False
if hasattr(t, "waiting"):
while t.waiting.peek():
_task_queue.push_head(t.waiting.pop_head())
waiting = True
t.waiting = None # Free waiting queue head
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
t.data = er
if t.state:
# Task was running but is now finished.
waiting = False
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
# "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)
# 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

View File

@ -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

View File

@ -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
################################################################################

View File

@ -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)

View File

@ -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];

View File

@ -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);

View File

@ -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);
check_fd_is_open(o);
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

View File

@ -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;
}

View File

@ -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

View File

@ -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