kopia lustrzana https://github.com/micropython/micropython
Merge branch 'micropython:master' into master
commit
5f6150d5bb
|
@ -85,7 +85,7 @@ jobs:
|
|||
run: tests/run-tests.py --print-failures
|
||||
|
||||
coverage_32bit:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04 # use 20.04 to get libffi-dev:i386
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install packages
|
||||
|
@ -103,7 +103,7 @@ jobs:
|
|||
run: tests/run-tests.py --print-failures
|
||||
|
||||
nanbox:
|
||||
runs-on: ubuntu-20.04 # use 20.04 to get python2
|
||||
runs-on: ubuntu-20.04 # use 20.04 to get python2, and libffi-dev:i386
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install packages
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
# https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
|
||||
name: Python code lint with ruff
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
ruff:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- run: pip install --user ruff
|
||||
- run: ruff --format=github .
|
|
@ -11,3 +11,7 @@ repos:
|
|||
language: python
|
||||
verbose: true
|
||||
stages: [commit-msg]
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.265
|
||||
hooks:
|
||||
- id: ruff
|
||||
|
|
|
@ -11,7 +11,7 @@ It's also ok to drop file extensions.
|
|||
|
||||
Besides prefix, first line of a commit message should describe a
|
||||
change clearly and to the point, and be a grammatical sentence with
|
||||
final full stop. First line should fit within 72 characters. Examples
|
||||
final full stop. First line must fit within 72 characters. Examples
|
||||
of good first line of commit messages:
|
||||
|
||||
py/objstr: Add splitlines() method.
|
||||
|
@ -27,12 +27,9 @@ change beyond 5 lines would likely require such detailed description.
|
|||
To get good practical examples of good commits and their messages, browse
|
||||
the `git log` of the project.
|
||||
|
||||
When committing you are encouraged to sign-off your commit by adding
|
||||
"Signed-off-by" lines and similar, eg using "git commit -s". If you don't
|
||||
explicitly sign-off in this way then the commit message, which includes your
|
||||
name and email address in the "Author" line, implies your sign-off. In either
|
||||
case, of explicit or implicit sign-off, you are certifying and signing off
|
||||
against the following:
|
||||
When committing you must sign-off your commit by adding "Signed-off-by:"
|
||||
line(s) at the end of the commit message, e.g. using `git commit -s`. You
|
||||
are then certifying and signing off against the following:
|
||||
|
||||
* That you wrote the change yourself, or took it from a project with
|
||||
a compatible license (in the latter case the commit message, and possibly
|
||||
|
@ -49,10 +46,9 @@ against the following:
|
|||
* Your contribution including commit message will be publicly and
|
||||
indefinitely available for anyone to access, including redistribution
|
||||
under the terms of the project's license.
|
||||
* Your signature for all of the above, which is the "Signed-off-by" line
|
||||
or the "Author" line in the commit message, includes your full real name and
|
||||
a valid and active email address by which you can be contacted in the
|
||||
foreseeable future.
|
||||
* Your signature for all of the above, which is the "Signed-off-by" line,
|
||||
includes your full real name and a valid and active email address by
|
||||
which you can be contacted in the foreseeable future.
|
||||
|
||||
Code auto-formatting
|
||||
====================
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -36,7 +36,6 @@ used during the build process and is not part of the compiled source code.
|
|||
/ (MIT)
|
||||
/drivers
|
||||
/cc3100 (BSD-3-clause)
|
||||
/wiznet5k (BSD-3-clause)
|
||||
/lib
|
||||
/asf4 (Apache-2.0)
|
||||
/axtls (BSD-3-clause)
|
||||
|
@ -61,6 +60,7 @@ used during the build process and is not part of the compiled source code.
|
|||
/tinytest (BSD-3-clause)
|
||||
/tinyusb (MIT)
|
||||
/uzlib (Zlib)
|
||||
/wiznet5k (MIT)
|
||||
/logo (uses OFL-1.1)
|
||||
/ports
|
||||
/cc3200
|
||||
|
|
|
@ -233,7 +233,7 @@ latex_elements = {
|
|||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
# Include 3 levels of headers in PDF ToC
|
||||
'preamble': '\setcounter{tocdepth}{2}',
|
||||
'preamble': r'\setcounter{tocdepth}{2}',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
|
|
|
@ -38,6 +38,7 @@ The basic MicroPython firmware is implemented in the main port file, e.g ``main.
|
|||
|
||||
.. code-block:: c
|
||||
|
||||
#include "py/builtin.h"
|
||||
#include "py/compile.h"
|
||||
#include "py/gc.h"
|
||||
#include "py/mperrno.h"
|
||||
|
@ -110,6 +111,9 @@ We also need a Makefile at this point for the port:
|
|||
shared/runtime/pyexec.c \
|
||||
shared/runtime/stdout_helpers.c \
|
||||
|
||||
# Define source files containung qstrs.
|
||||
SRC_QSTR += shared/readline/readline.c shared/runtime/pyexec.c
|
||||
|
||||
# Define the required object files.
|
||||
OBJ = $(PY_CORE_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o))
|
||||
|
||||
|
|
|
@ -305,8 +305,8 @@ Use the :ref:`machine.PWM <machine.PWM>` class::
|
|||
|
||||
from machine import Pin, PWM
|
||||
|
||||
pwm0 = PWM(Pin(0)) # create PWM object from a pin
|
||||
freq = pwm0.freq() # get current frequency (default 5kHz)
|
||||
pwm0 = PWM(Pin(0), freq=5000, duty_u16=32768) # create PWM object from a pin
|
||||
freq = pwm0.freq() # get current frequency
|
||||
pwm0.freq(1000) # set PWM frequency from 1Hz to 40MHz
|
||||
|
||||
duty = pwm0.duty() # get current duty cycle, range 0-1023 (default 512, 50%)
|
||||
|
|
|
@ -279,6 +279,14 @@ For more details see Espressif's `ESP-IDF RMT documentation.
|
|||
Ultra-Low-Power co-processor
|
||||
----------------------------
|
||||
|
||||
This class gives access to the Ultra Low Power (ULP) co-processor on the ESP32,
|
||||
ESP32-S2 and ESP32-S3 chips.
|
||||
|
||||
.. warning::
|
||||
|
||||
This class does not provide access to the RISCV ULP co-processor available
|
||||
on the ESP32-S2 and ESP32-S3 chips.
|
||||
|
||||
.. class:: ULP()
|
||||
|
||||
This class provides access to the Ultra-Low-Power co-processor.
|
||||
|
|
|
@ -0,0 +1,936 @@
|
|||
:mod:`espnow` --- support for the ESP-NOW wireless protocol
|
||||
===========================================================
|
||||
|
||||
.. module:: espnow
|
||||
:synopsis: ESP-NOW wireless protocol support
|
||||
|
||||
This module provides an interface to the `ESP-NOW <https://www.espressif.com/
|
||||
en/products/software/esp-now/overview>`_ protocol provided by Espressif on
|
||||
ESP32 and ESP8266 devices (`API docs <https://docs.espressif.com/
|
||||
projects/esp-idf/en/latest/api-reference/network/esp_now.html>`_).
|
||||
|
||||
Table of Contents:
|
||||
------------------
|
||||
|
||||
- `Introduction`_
|
||||
- `Configuration`_
|
||||
- `Sending and Receiving Data`_
|
||||
- `Peer Management`_
|
||||
- `Callback Methods`_
|
||||
- `Exceptions`_
|
||||
- `Constants`_
|
||||
- `Wifi Signal Strength (RSSI) - (ESP32 Only)`_
|
||||
- `Supporting asyncio`_
|
||||
- `Broadcast and Multicast`_
|
||||
- `ESPNow and Wifi Operation`_
|
||||
- `ESPNow and Sleep Modes`_
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
ESP-NOW is a connection-less wireless communication protocol supporting:
|
||||
|
||||
- Direct communication between up to 20 registered peers:
|
||||
|
||||
- Without the need for a wireless access point (AP),
|
||||
|
||||
- Encrypted and unencrypted communication (up to 6 encrypted peers),
|
||||
|
||||
- Message sizes up to 250 bytes,
|
||||
|
||||
- Can operate alongside Wifi operation (:doc:`network.WLAN<network.WLAN>`) on
|
||||
ESP32 and ESP8266 devices.
|
||||
|
||||
It is especially useful for small IoT networks, latency sensitive or power
|
||||
sensitive applications (such as battery operated devices) and for long-range
|
||||
communication between devices (hundreds of metres).
|
||||
|
||||
This module also supports tracking the Wifi signal strength (RSSI) of peer
|
||||
devices.
|
||||
|
||||
A simple example would be:
|
||||
|
||||
**Sender:** ::
|
||||
|
||||
import network
|
||||
import espnow
|
||||
|
||||
# A WLAN interface must be active to send()/recv()
|
||||
sta = network.WLAN(network.STA_IF) # Or network.AP_IF
|
||||
sta.active(True)
|
||||
sta.disconnect() # For ESP8266
|
||||
|
||||
e = espnow.ESPNow()
|
||||
e.active(True)
|
||||
peer = b'\xbb\xbb\xbb\xbb\xbb\xbb' # MAC address of peer's wifi interface
|
||||
e.add_peer(peer) # Must add_peer() before send()
|
||||
|
||||
e.send(peer, "Starting...")
|
||||
for i in range(100):
|
||||
e.send(peer, str(i)*20, True)
|
||||
e.send(peer, b'end')
|
||||
|
||||
**Receiver:** ::
|
||||
|
||||
import network
|
||||
import espnow
|
||||
|
||||
# A WLAN interface must be active to send()/recv()
|
||||
sta = network.WLAN(network.STA_IF)
|
||||
sta.active(True)
|
||||
sta.disconnect() # Because ESP8266 auto-connects to last Access Point
|
||||
|
||||
e = espnow.ESPNow()
|
||||
e.active(True)
|
||||
|
||||
while True:
|
||||
host, msg = e.recv()
|
||||
if msg: # msg == None if timeout in recv()
|
||||
print(host, msg)
|
||||
if msg == b'end':
|
||||
break
|
||||
|
||||
class ESPNow
|
||||
------------
|
||||
|
||||
Constructor
|
||||
-----------
|
||||
|
||||
.. class:: ESPNow()
|
||||
|
||||
Returns the singleton ESPNow object. As this is a singleton, all calls to
|
||||
`espnow.ESPNow()` return a reference to the same object.
|
||||
|
||||
.. note::
|
||||
Some methods are available only on the ESP32 due to code size
|
||||
restrictions on the ESP8266 and differences in the Espressif API.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
.. method:: ESPNow.active([flag])
|
||||
|
||||
Initialise or de-initialise the ESP-NOW communication protocol depending on
|
||||
the value of the ``flag`` optional argument.
|
||||
|
||||
.. data:: Arguments:
|
||||
|
||||
- *flag*: Any python value which can be converted to a boolean type.
|
||||
|
||||
- ``True``: Prepare the software and hardware for use of the ESP-NOW
|
||||
communication protocol, including:
|
||||
|
||||
- initialise the ESPNow data structures,
|
||||
- allocate the recv data buffer,
|
||||
- invoke esp_now_init() and
|
||||
- register the send and recv callbacks.
|
||||
|
||||
- ``False``: De-initialise the Espressif ESP-NOW software stack
|
||||
(esp_now_deinit()), disable callbacks, deallocate the recv
|
||||
data buffer and deregister all peers.
|
||||
|
||||
If *flag* is not provided, return the current status of the ESPNow
|
||||
interface.
|
||||
|
||||
.. data:: Returns:
|
||||
|
||||
``True`` if interface is currently *active*, else ``False``.
|
||||
|
||||
.. method:: ESPNow.config(param=value, ...)
|
||||
ESPNow.config('param') (ESP32 only)
|
||||
|
||||
Set or get configuration values of the ESPNow interface. To set values, use
|
||||
the keyword syntax, and one or more parameters can be set at a time. To get
|
||||
a value the parameter name should be quoted as a string, and just one
|
||||
parameter is queried at a time.
|
||||
|
||||
**Note:** *Getting* parameters is not supported on the ESP8266.
|
||||
|
||||
.. data:: Options:
|
||||
|
||||
*rxbuf*: (default=526) Get/set the size in bytes of the internal
|
||||
buffer used to store incoming ESPNow packet data. The default size is
|
||||
selected to fit two max-sized ESPNow packets (250 bytes) with associated
|
||||
mac_address (6 bytes), a message byte count (1 byte) and RSSI data plus
|
||||
buffer overhead. Increase this if you expect to receive a lot of large
|
||||
packets or expect bursty incoming traffic.
|
||||
|
||||
**Note:** The recv buffer is allocated by `ESPNow.active()`. Changing
|
||||
this value will have no effect until the next call of
|
||||
`ESPNow.active(True)<ESPNow.active()>`.
|
||||
|
||||
*timeout_ms*: (default=300,000) Default timeout (in milliseconds)
|
||||
for receiving ESPNow messages. If *timeout_ms* is less than zero, then
|
||||
wait forever. The timeout can also be provided as arg to
|
||||
`recv()`/`irecv()`/`recvinto()`.
|
||||
|
||||
*rate*: (ESP32 only, IDF>=4.3.0 only) Set the transmission speed for
|
||||
ESPNow packets. Must be set to a number from the allowed numeric values
|
||||
in `enum wifi_phy_rate_t
|
||||
<https://docs.espressif.com/projects/esp-idf/en/v4.4.1/esp32/
|
||||
api-reference/network/esp_wifi.html#_CPPv415wifi_phy_rate_t>`_.
|
||||
|
||||
.. data:: Returns:
|
||||
|
||||
``None`` or the value of the parameter being queried.
|
||||
|
||||
.. data:: Raises:
|
||||
|
||||
- ``OSError(num, "ESP_ERR_ESPNOW_NOT_INIT")`` if not initialised.
|
||||
- ``ValueError()`` on invalid configuration options or values.
|
||||
|
||||
Sending and Receiving Data
|
||||
--------------------------
|
||||
|
||||
A wifi interface (``network.STA_IF`` or ``network.AP_IF``) must be
|
||||
`active()<network.WLAN.active>` before messages can be sent or received,
|
||||
but it is not necessary to connect or configure the WLAN interface.
|
||||
For example::
|
||||
|
||||
import network
|
||||
|
||||
sta = network.WLAN(network.STA_IF)
|
||||
sta.active(True)
|
||||
sta.disconnect() # For ESP8266
|
||||
|
||||
**Note:** The ESP8266 has a *feature* that causes it to automatically reconnect
|
||||
to the last wifi Access Point when set `active(True)<network.WLAN.active>` (even
|
||||
after reboot/reset). This reduces the reliability of receiving ESP-NOW messages
|
||||
(see `ESPNow and Wifi Operation`_). You can avoid this by calling
|
||||
`disconnect()<network.WLAN.disconnect>` after
|
||||
`active(True)<network.WLAN.active>`.
|
||||
|
||||
.. method:: ESPNow.send(mac, msg[, sync])
|
||||
ESPNow.send(msg) (ESP32 only)
|
||||
|
||||
Send the data contained in ``msg`` to the peer with given network ``mac``
|
||||
address. In the second form, ``mac=None`` and ``sync=True``. The peer must
|
||||
be registered with `ESPNow.add_peer()<ESPNow.add_peer()>` before the
|
||||
message can be sent.
|
||||
|
||||
.. data:: Arguments:
|
||||
|
||||
- *mac*: byte string exactly ``espnow.ADDR_LEN`` (6 bytes) long or
|
||||
``None``. If *mac* is ``None`` (ESP32 only) the message will be sent
|
||||
to all registered peers, except any broadcast or multicast MAC
|
||||
addresses.
|
||||
|
||||
- *msg*: string or byte-string up to ``espnow.MAX_DATA_LEN`` (250)
|
||||
bytes long.
|
||||
|
||||
- *sync*:
|
||||
|
||||
- ``True``: (default) send ``msg`` to the peer(s) and wait for a
|
||||
response (or not).
|
||||
|
||||
- ``False`` send ``msg`` and return immediately. Responses from the
|
||||
peers will be discarded.
|
||||
|
||||
.. data:: Returns:
|
||||
|
||||
``True`` if ``sync=False`` or if ``sync=True`` and *all* peers respond,
|
||||
else ``False``.
|
||||
|
||||
.. data:: Raises:
|
||||
|
||||
- ``OSError(num, "ESP_ERR_ESPNOW_NOT_INIT")`` if not initialised.
|
||||
- ``OSError(num, "ESP_ERR_ESPNOW_NOT_FOUND")`` if peer is not registered.
|
||||
- ``OSError(num, "ESP_ERR_ESPNOW_IF")`` the wifi interface is not
|
||||
`active()<network.WLAN.active>`.
|
||||
- ``OSError(num, "ESP_ERR_ESPNOW_NO_MEM")`` internal ESP-NOW buffers are
|
||||
full.
|
||||
- ``ValueError()`` on invalid values for the parameters.
|
||||
|
||||
**Note**: A peer will respond with success if its wifi interface is
|
||||
`active()<network.WLAN.active>` and set to the same channel as the sender,
|
||||
regardless of whether it has initialised it's ESP-NOW system or is
|
||||
actively listening for ESP-NOW traffic (see the Espressif ESP-NOW docs).
|
||||
|
||||
.. method:: ESPNow.recv([timeout_ms])
|
||||
|
||||
Wait for an incoming message and return the ``mac`` address of the peer and
|
||||
the message. **Note**: It is **not** necessary to register a peer (using
|
||||
`add_peer()<ESPNow.add_peer()>`) to receive a message from that peer.
|
||||
|
||||
.. data:: Arguments:
|
||||
|
||||
- *timeout_ms*: (Optional): May have the following values.
|
||||
|
||||
- ``0``: No timeout. Return immediately if no data is available;
|
||||
- ``> 0``: Specify a timeout value in milliseconds;
|
||||
- ``< 0``: Do not timeout, ie. wait forever for new messages; or
|
||||
- ``None`` (or not provided): Use the default timeout value set with
|
||||
`ESPNow.config()`.
|
||||
|
||||
.. data:: Returns:
|
||||
|
||||
- ``(None, None)`` if timeout is reached before a message is received, or
|
||||
|
||||
- ``[mac, msg]``: where:
|
||||
|
||||
- ``mac`` is a bytestring containing the address of the device which
|
||||
sent the message, and
|
||||
- ``msg`` is a bytestring containing the message.
|
||||
|
||||
.. data:: Raises:
|
||||
|
||||
- ``OSError(num, "ESP_ERR_ESPNOW_NOT_INIT")`` if not initialised.
|
||||
- ``OSError(num, "ESP_ERR_ESPNOW_IF")`` if the wifi interface is not
|
||||
`active()<network.WLAN.active>`.
|
||||
- ``ValueError()`` on invalid *timeout_ms* values.
|
||||
|
||||
`ESPNow.recv()` will allocate new storage for the returned list and the
|
||||
``peer`` and ``msg`` bytestrings. This can lead to memory fragmentation if
|
||||
the data rate is high. See `ESPNow.irecv()` for a memory-friendly
|
||||
alternative.
|
||||
|
||||
|
||||
.. method:: ESPNow.irecv([timeout_ms])
|
||||
|
||||
Works like `ESPNow.recv()` but will re-use internal bytearrays to store the
|
||||
return values: ``[mac, msg]``, so that no new memory is allocated on each
|
||||
call.
|
||||
|
||||
.. data:: Arguments:
|
||||
|
||||
*timeout_ms*: (Optional) Timeout in milliseconds (see `ESPNow.recv()`).
|
||||
|
||||
.. data:: Returns:
|
||||
|
||||
- As for `ESPNow.recv()`, except that ``msg`` is a bytearray, instead of
|
||||
a bytestring. On the ESP8266, ``mac`` will also be a bytearray.
|
||||
|
||||
.. data:: Raises:
|
||||
|
||||
- See `ESPNow.recv()`.
|
||||
|
||||
**Note:** You may also read messages by iterating over the ESPNow object,
|
||||
which will use the `irecv()` method for alloc-free reads, eg: ::
|
||||
|
||||
import espnow
|
||||
e = espnow.ESPNow(); e.active(True)
|
||||
for mac, msg in e:
|
||||
print(mac, msg)
|
||||
if mac is None: # mac, msg will equal (None, None) on timeout
|
||||
break
|
||||
|
||||
.. method:: ESPNow.recvinto(data[, timeout_ms])
|
||||
|
||||
Wait for an incoming message and return the length of the message in bytes.
|
||||
This is the low-level method used by both `recv()<ESPNow.recv()>` and
|
||||
`irecv()` to read messages.
|
||||
|
||||
.. data:: Arguments:
|
||||
|
||||
*data*: A list of at least two elements, ``[peer, msg]``. ``msg`` must
|
||||
be a bytearray large enough to hold the message (250 bytes). On the
|
||||
ESP8266, ``peer`` should be a bytearray of 6 bytes. The MAC address of
|
||||
the sender and the message will be stored in these bytearrays (see Note
|
||||
on ESP32 below).
|
||||
|
||||
*timeout_ms*: (Optional) Timeout in milliseconds (see `ESPNow.recv()`).
|
||||
|
||||
.. data:: Returns:
|
||||
|
||||
- Length of message in bytes or 0 if *timeout_ms* is reached before a
|
||||
message is received.
|
||||
|
||||
.. data:: Raises:
|
||||
|
||||
- See `ESPNow.recv()`.
|
||||
|
||||
**Note:** On the ESP32:
|
||||
|
||||
- It is unnecessary to provide a bytearray in the first element of the
|
||||
``data`` list because it will be replaced by a reference to a unique
|
||||
``peer`` address in the **peer device table** (see `ESPNow.peers_table`).
|
||||
- If the list is at least 4 elements long, the rssi and timestamp values
|
||||
will be saved as the 3rd and 4th elements.
|
||||
|
||||
.. method:: ESPNow.any()
|
||||
|
||||
Check if data is available to be read with `ESPNow.recv()`.
|
||||
|
||||
For more sophisticated querying of available characters use `select.poll()`::
|
||||
|
||||
import select
|
||||
import espnow
|
||||
|
||||
e = espnow.ESPNow()
|
||||
poll = select.poll()
|
||||
poll.register(e, select.POLLIN)
|
||||
poll.poll(timeout)
|
||||
|
||||
.. data:: Returns:
|
||||
|
||||
``True`` if data is available to be read, else ``False``.
|
||||
|
||||
.. method:: ESPNow.stats() (ESP32 only)
|
||||
|
||||
.. data:: Returns:
|
||||
|
||||
A 5-tuple containing the number of packets sent/received/lost:
|
||||
|
||||
``(tx_pkts, tx_responses, tx_failures, rx_packets, rx_dropped_packets)``
|
||||
|
||||
Incoming packets are *dropped* when the recv buffers are full. To reduce
|
||||
packet loss, increase the ``rxbuf`` config parameters and ensure you are
|
||||
reading messages as quickly as possible.
|
||||
|
||||
**Note**: Dropped packets will still be acknowledged to the sender as
|
||||
received.
|
||||
|
||||
Peer Management
|
||||
---------------
|
||||
|
||||
On ESP32 devices, the Espressif ESP-NOW software requires that other devices
|
||||
(peers) must be *registered* using `add_peer()` before we can
|
||||
`send()<ESPNow.send()>` them messages (this is *not* enforced on ESP8266
|
||||
devices). It is **not** necessary to register a peer to receive an
|
||||
un-encrypted message from that peer.
|
||||
|
||||
**Encrypted messages**: To receive an *encrypted* message, the receiving device
|
||||
must first register the sender and use the same encryption keys as the sender
|
||||
(PMK and LMK) (see `set_pmk()` and `add_peer()`.
|
||||
|
||||
.. method:: ESPNow.set_pmk(pmk)
|
||||
|
||||
Set the Primary Master Key (PMK) which is used to encrypt the Local Master
|
||||
Keys (LMK) for encrypting messages. If this is not set, a default PMK is
|
||||
used by the underlying Espressif ESP-NOW software stack.
|
||||
|
||||
**Note:** messages will only be encrypted if *lmk* is also set in
|
||||
`ESPNow.add_peer()` (see `Security
|
||||
<https://docs.espressif.com/projects/esp-idf/en/latest/
|
||||
esp32/api-reference/network/esp_now.html#security>`_ in the Espressif API
|
||||
docs).
|
||||
|
||||
.. data:: Arguments:
|
||||
|
||||
*pmk*: Must be a byte string, bytearray or string of length
|
||||
`espnow.KEY_LEN` (16 bytes).
|
||||
|
||||
.. data:: Returns:
|
||||
|
||||
``None``
|
||||
|
||||
.. data:: Raises:
|
||||
|
||||
``ValueError()`` on invalid *pmk* values.
|
||||
|
||||
.. method:: ESPNow.add_peer(mac, [lmk], [channel], [ifidx], [encrypt])
|
||||
ESPNow.add_peer(mac, param=value, ...) (ESP32 only)
|
||||
|
||||
Add/register the provided *mac* address as a peer. Additional parameters may
|
||||
also be specified as positional or keyword arguments (any parameter set to
|
||||
``None`` will be set to it's default value):
|
||||
|
||||
.. data:: Arguments:
|
||||
|
||||
- *mac*: The MAC address of the peer (as a 6-byte byte-string).
|
||||
|
||||
- *lmk*: The Local Master Key (LMK) key used to encrypt data
|
||||
transfers with this peer (unless the *encrypt* parameter is set to
|
||||
``False``). Must be:
|
||||
|
||||
- a byte-string or bytearray or string of length ``espnow.KEY_LEN``
|
||||
(16 bytes), or
|
||||
|
||||
- any non ``True`` python value (default= ``b''``), signifying an
|
||||
*empty* key which will disable encryption.
|
||||
|
||||
- *channel*: The wifi channel (2.4GHz) to communicate with this peer.
|
||||
Must be an integer from 0 to 14. If channel is set to 0 the current
|
||||
channel of the wifi device will be used. (default=0)
|
||||
|
||||
- *ifidx*: (ESP32 only) Index of the wifi interface which will be
|
||||
used to send data to this peer. Must be an integer set to
|
||||
``network.STA_IF`` (=0) or ``network.AP_IF`` (=1).
|
||||
(default=0/``network.STA_IF``). See `ESPNow and Wifi Operation`_
|
||||
below for more information.
|
||||
|
||||
- *encrypt*: (ESP32 only) If set to ``True`` data exchanged with
|
||||
this peer will be encrypted with the PMK and LMK. (default =
|
||||
``True`` if *lmk* is set to a valid key, else ``False``)
|
||||
|
||||
**ESP8266**: Keyword args may not be used on the ESP8266.
|
||||
|
||||
**Note:** The maximum number of peers which may be registered is 20
|
||||
(`espnow.MAX_TOTAL_PEER_NUM`), with a maximum of 6
|
||||
(`espnow.MAX_ENCRYPT_PEER_NUM`) of those peers with encryption enabled
|
||||
(see `ESP_NOW_MAX_ENCRYPT_PEER_NUM <https://docs.espressif.com/
|
||||
projects/esp-idf/en/latest/esp32/api-reference/network/
|
||||
esp_now.html#c.ESP_NOW_MAX_ENCRYPT_PEER_NUM>`_ in the Espressif API
|
||||
docs).
|
||||
|
||||
.. data:: Raises:
|
||||
|
||||
- ``OSError(num, "ESP_ERR_ESPNOW_NOT_INIT")`` if not initialised.
|
||||
- ``OSError(num, "ESP_ERR_ESPNOW_EXIST")`` if *mac* is already
|
||||
registered.
|
||||
- ``OSError(num, "ESP_ERR_ESPNOW_FULL")`` if too many peers are
|
||||
already registered.
|
||||
- ``ValueError()`` on invalid keyword args or values.
|
||||
|
||||
.. method:: ESPNow.del_peer(mac)
|
||||
|
||||
Deregister the peer associated with the provided *mac* address.
|
||||
|
||||
.. data:: Returns:
|
||||
|
||||
``None``
|
||||
|
||||
.. data:: Raises:
|
||||
|
||||
- ``OSError(num, "ESP_ERR_ESPNOW_NOT_INIT")`` if not initialised.
|
||||
- ``OSError(num, "ESP_ERR_ESPNOW_NOT_FOUND")`` if *mac* is not
|
||||
registered.
|
||||
- ``ValueError()`` on invalid *mac* values.
|
||||
|
||||
.. method:: ESPNow.get_peer(mac) (ESP32 only)
|
||||
|
||||
Return information on a registered peer.
|
||||
|
||||
.. data:: Returns:
|
||||
|
||||
``(mac, lmk, channel, ifidx, encrypt)``: a tuple of the "peer
|
||||
info" associated with the given *mac* address.
|
||||
|
||||
.. data:: Raises:
|
||||
|
||||
- ``OSError(num, "ESP_ERR_ESPNOW_NOT_INIT")`` if not initialised.
|
||||
- ``OSError(num, "ESP_ERR_ESPNOW_NOT_FOUND")`` if *mac* is not
|
||||
registered.
|
||||
- ``ValueError()`` on invalid *mac* values.
|
||||
|
||||
.. method:: ESPNow.peer_count() (ESP32 only)
|
||||
|
||||
Return the number of registered peers:
|
||||
|
||||
- ``(peer_num, encrypt_num)``: where
|
||||
|
||||
- ``peer_num`` is the number of peers which are registered, and
|
||||
- ``encrypt_num`` is the number of encrypted peers.
|
||||
|
||||
.. method:: ESPNow.get_peers() (ESP32 only)
|
||||
|
||||
Return the "peer info" parameters for all the registered peers (as a tuple
|
||||
of tuples).
|
||||
|
||||
.. method:: ESPNow.mod_peer(mac, lmk, [channel], [ifidx], [encrypt]) (ESP32 only)
|
||||
ESPNow.mod_peer(mac, 'param'=value, ...) (ESP32 only)
|
||||
|
||||
Modify the parameters of the peer associated with the provided *mac*
|
||||
address. Parameters may be provided as positional or keyword arguments
|
||||
(see `ESPNow.add_peer()`). Any parameter that is not set (or set to
|
||||
``None``) will retain the existing value for that parameter.
|
||||
|
||||
Callback Methods
|
||||
----------------
|
||||
|
||||
.. method:: ESPNow.irq(callback) (ESP32 only)
|
||||
|
||||
Set a callback function to be called *as soon as possible* after a message has
|
||||
been received from another ESPNow device. The callback function will be called
|
||||
with the `ESPNow` instance object as an argument. For more reliable operation,
|
||||
it is recommended to read out as many messages as are available when the
|
||||
callback is invoked and to set the read timeout to zero, eg: ::
|
||||
|
||||
def recv_cb(e):
|
||||
while True: # Read out all messages waiting in the buffer
|
||||
mac, msg = e.irecv(0) # Don't wait if no messages left
|
||||
if mac is None:
|
||||
return
|
||||
print(mac, msg)
|
||||
e.irq(recv_cb)
|
||||
|
||||
The `irq()<ESPNow.irq()>` callback method is an alternative method for
|
||||
processing incoming messages, especially if the data rate is moderate
|
||||
and the device is *not too busy* but there are some caveats:
|
||||
|
||||
- The scheduler stack *can* overflow and callbacks will be missed if
|
||||
packets are arriving at a sufficient rate or if other MicroPython components
|
||||
(eg, bluetooth, machine.Pin.irq(), machine.timer, i2s, ...) are exercising
|
||||
the scheduler stack. This method may be less reliable for dealing with
|
||||
bursts of messages, or high throughput or on a device which is busy dealing
|
||||
with other hardware operations.
|
||||
|
||||
- For more information on *scheduled* function callbacks see:
|
||||
`micropython.schedule()<micropython.schedule>`.
|
||||
|
||||
Constants
|
||||
---------
|
||||
|
||||
.. data:: espnow.MAX_DATA_LEN(=250)
|
||||
espnow.KEY_LEN(=16)
|
||||
espnow.ADDR_LEN(=6)
|
||||
espnow.MAX_TOTAL_PEER_NUM(=20)
|
||||
espnow.MAX_ENCRYPT_PEER_NUM(=6)
|
||||
|
||||
Exceptions
|
||||
----------
|
||||
|
||||
If the underlying Espressif ESP-NOW software stack returns an error code,
|
||||
the MicroPython espnow module will raise an ``OSError(errnum, errstring)``
|
||||
exception where ``errstring`` is set to the name of one of the error codes
|
||||
identified in the
|
||||
`Espressif ESP-NOW docs
|
||||
<https://docs.espressif.com/projects/esp-idf/en/latest/
|
||||
api-reference/network/esp_now.html#api-reference>`_. For example::
|
||||
|
||||
try:
|
||||
e.send(peer, 'Hello')
|
||||
except OSError as err:
|
||||
if len(err.args) < 2:
|
||||
raise err
|
||||
if err.args[1] == 'ESP_ERR_ESPNOW_NOT_INIT':
|
||||
e.active(True)
|
||||
elif err.args[1] == 'ESP_ERR_ESPNOW_NOT_FOUND':
|
||||
e.add_peer(peer)
|
||||
elif err.args[1] == 'ESP_ERR_ESPNOW_IF':
|
||||
network.WLAN(network.STA_IF).active(True)
|
||||
else:
|
||||
raise err
|
||||
|
||||
Wifi Signal Strength (RSSI) - (ESP32 only)
|
||||
------------------------------------------
|
||||
|
||||
The ESPNow object maintains a **peer device table** which contains the signal
|
||||
strength and timestamp of the last received message from all hosts. The **peer
|
||||
device table** can be accessed using `ESPNow.peers_table` and can be used to
|
||||
track device proximity and identify *nearest neighbours* in a network of peer
|
||||
devices. This feature is **not** available on ESP8266 devices.
|
||||
|
||||
.. data:: ESPNow.peers_table
|
||||
|
||||
A reference to the **peer device table**: a dict of known peer devices
|
||||
and rssi values::
|
||||
|
||||
{peer: [rssi, time_ms], ...}
|
||||
|
||||
where:
|
||||
|
||||
- ``peer`` is the peer MAC address (as `bytes`);
|
||||
- ``rssi`` is the wifi signal strength in dBm (-127 to 0) of the last
|
||||
message received from the peer; and
|
||||
- ``time_ms`` is the time the message was received (in milliseconds since
|
||||
system boot - wraps every 12 days).
|
||||
|
||||
Example::
|
||||
|
||||
>>> e.peers_table
|
||||
{b'\xaa\xaa\xaa\xaa\xaa\xaa': [-31, 18372],
|
||||
b'\xbb\xbb\xbb\xbb\xbb\xbb': [-43, 12541]}
|
||||
|
||||
**Note**: the ``mac`` addresses returned by `recv()` are references to
|
||||
the ``peer`` key values in the **peer device table**.
|
||||
|
||||
**Note**: RSSI and timestamp values in the device table are updated only
|
||||
when the message is read by the application.
|
||||
|
||||
Supporting asyncio
|
||||
------------------
|
||||
|
||||
A supplementary module (`aioespnow`) is available to provide
|
||||
:doc:`asyncio<uasyncio>` support.
|
||||
|
||||
**Note:** Asyncio support is available on all ESP32 targets as well as those
|
||||
ESP8266 boards which include the asyncio module (ie. ESP8266 devices with at
|
||||
least 2MB flash memory).
|
||||
|
||||
A small async server example::
|
||||
|
||||
import network
|
||||
import aioespnow
|
||||
import uasyncio as asyncio
|
||||
|
||||
# A WLAN interface must be active to send()/recv()
|
||||
network.WLAN(network.STA_IF).active(True)
|
||||
|
||||
e = aioespnow.AIOESPNow() # Returns AIOESPNow enhanced with async support
|
||||
e.active(True)
|
||||
peer = b'\xbb\xbb\xbb\xbb\xbb\xbb'
|
||||
e.add_peer(peer)
|
||||
|
||||
# Send a periodic ping to a peer
|
||||
async def heartbeat(e, peer, period=30):
|
||||
while True:
|
||||
if not await e.asend(peer, b'ping'):
|
||||
print("Heartbeat: peer not responding:", peer)
|
||||
else:
|
||||
print("Heartbeat: ping", peer)
|
||||
await asyncio.sleep(period)
|
||||
|
||||
# Echo any received messages back to the sender
|
||||
async def echo_server(e):
|
||||
async for mac, msg in e:
|
||||
print("Echo:", msg)
|
||||
try:
|
||||
await e.asend(mac, msg)
|
||||
except OSError as err:
|
||||
if len(err.args) > 1 and err.args[1] == 'ESP_ERR_ESPNOW_NOT_FOUND':
|
||||
e.add_peer(mac)
|
||||
await e.asend(mac, msg)
|
||||
|
||||
async def main(e, peer, timeout, period):
|
||||
asyncio.create_task(heartbeat(e, peer, period))
|
||||
asyncio.create_task(echo_server(e))
|
||||
await asyncio.sleep(timeout)
|
||||
|
||||
asyncio.run(main(e, peer, 120, 10))
|
||||
|
||||
.. module:: aioespnow
|
||||
:synopsis: ESP-NOW :doc:`uasyncio` support
|
||||
|
||||
.. class:: AIOESPNow()
|
||||
|
||||
The `AIOESPNow` class inherits all the methods of `ESPNow<espnow.ESPNow>`
|
||||
and extends the interface with the following async methods.
|
||||
|
||||
.. method:: async AIOESPNow.arecv()
|
||||
|
||||
Asyncio support for `ESPNow.recv()`. Note that this method does not take a
|
||||
timeout value as argument.
|
||||
|
||||
.. method:: async AIOESPNow.airecv()
|
||||
|
||||
Asyncio support for `ESPNow.irecv()`. Note that this method does not take a
|
||||
timeout value as argument.
|
||||
|
||||
.. method:: async AIOESPNow.asend(mac, msg, sync=True)
|
||||
async AIOESPNow.asend(msg)
|
||||
|
||||
Asyncio support for `ESPNow.send()`.
|
||||
|
||||
.. method:: AIOESPNow._aiter__() / async AIOESPNow.__anext__()
|
||||
|
||||
`AIOESPNow` also supports reading incoming messages by asynchronous
|
||||
iteration using ``async for``; eg::
|
||||
|
||||
e = AIOESPNow()
|
||||
e.active(True)
|
||||
async def recv_till_halt(e):
|
||||
async for mac, msg in e:
|
||||
print(mac, msg)
|
||||
if msg == b'halt':
|
||||
break
|
||||
asyncio.run(recv_till_halt(e))
|
||||
|
||||
Broadcast and Multicast
|
||||
-----------------------
|
||||
|
||||
All active ESPNow clients will receive messages sent to their MAC address and
|
||||
all devices (**except ESP8266 devices**) will also receive messages sent to the
|
||||
*broadcast* MAC address (``b'\xff\xff\xff\xff\xff\xff'``) or any multicast
|
||||
MAC address.
|
||||
|
||||
All ESPNow devices (including ESP8266 devices) can also send messages to the
|
||||
broadcast MAC address or any multicast MAC address.
|
||||
|
||||
To `send()<ESPNow.send()>` a broadcast message, the broadcast (or
|
||||
multicast) MAC address must first be registered using
|
||||
`add_peer()<ESPNow.add_peer()>`. `send()<ESPNow.send()>` will always return
|
||||
``True`` for broadcasts, regardless of whether any devices receive the
|
||||
message. It is not permitted to encrypt messages sent to the broadcast
|
||||
address or any multicast address.
|
||||
|
||||
**Note**: `ESPNow.send(None, msg)<ESPNow.send()>` will send to all registered
|
||||
peers *except* the broadcast address. To send a broadcast or multicast
|
||||
message, you must specify the broadcast (or multicast) MAC address as the
|
||||
peer. For example::
|
||||
|
||||
bcast = b'\xff' * 6
|
||||
e.add_peer(bcast)
|
||||
e.send(bcast, "Hello World!")
|
||||
|
||||
ESPNow and Wifi Operation
|
||||
-------------------------
|
||||
|
||||
ESPNow messages may be sent and received on any `active()<network.WLAN.active>`
|
||||
`WLAN<network.WLAN()>` interface (``network.STA_IF`` or ``network.AP_IF``), even
|
||||
if that interface is also connected to a wifi network or configured as an access
|
||||
point. When an ESP32 or ESP8266 device connects to a Wifi Access Point (see
|
||||
`ESP32 Quickref <../esp32/quickref.html#networking>`__) the following things
|
||||
happen which affect ESPNow communications:
|
||||
|
||||
1. Wifi Power-saving Mode (`network.WLAN.PM_PERFORMANCE`)
|
||||
is automatically activated and
|
||||
2. The radio on the esp device changes wifi ``channel`` to match the channel
|
||||
used by the Access Point.
|
||||
|
||||
**Wifi Power-saving Mode:** (see `Espressif Docs <https://docs.espressif.com/
|
||||
projects/esp-idf/en/latest/esp32/api-guides/
|
||||
wifi.html#esp32-wi-fi-power-saving-mode>`_) The power saving mode causes the
|
||||
device to turn off the radio periodically (typically for hundreds of
|
||||
milliseconds), making it unreliable in receiving ESPNow messages. This can be
|
||||
resolved by either of:
|
||||
|
||||
1. Disabling the power-saving mode on the STA_IF interface;
|
||||
|
||||
- Use ``sta.config(pm=sta.PM_NONE)``
|
||||
|
||||
2. Turning on the AP_IF interface, which will disable the power saving mode.
|
||||
However, the device will then be advertising an active wifi access point.
|
||||
|
||||
- You **may** also choose to send your messages via the AP_IF interface, but
|
||||
this is not necessary.
|
||||
- ESP8266 peers must send messages to this AP_IF interface (see below).
|
||||
|
||||
3. Configuring ESPNow clients to retry sending messages.
|
||||
|
||||
**Receiving messages from an ESP8266 device:** Strangely, an ESP32 device
|
||||
connected to a wifi network using method 1 or 2 above, will receive ESPNow
|
||||
messages sent to the STA_IF MAC address from another ESP32 device, but will
|
||||
**reject** messages from an ESP8266 device!!!. To receive messages from an
|
||||
ESP8266 device, the AP_IF interface must be set to ``active(True)`` **and**
|
||||
messages must be sent to the AP_IF MAC address.
|
||||
|
||||
**Managing wifi channels:** Any other ESPNow devices wishing to communicate with
|
||||
a device which is also connected to a Wifi Access Point MUST use the same
|
||||
channel. A common scenario is where one ESPNow device is connected to a wifi
|
||||
router and acts as a proxy for messages from a group of sensors connected via
|
||||
ESPNow:
|
||||
|
||||
**Proxy:** ::
|
||||
|
||||
import network, time, espnow
|
||||
|
||||
sta, ap = wifi_reset() # Reset wifi to AP off, STA on and disconnected
|
||||
sta.connect('myssid', 'mypassword')
|
||||
while not sta.isconnected(): # Wait until connected...
|
||||
time.sleep(0.1)
|
||||
sta.config(pm=sta.PM_NONE) # ..then disable power saving
|
||||
|
||||
# Print the wifi channel used AFTER finished connecting to access point
|
||||
print("Proxy running on channel:", sta.config("channel"))
|
||||
e = espnow.ESPNow(); e.active(True)
|
||||
for peer, msg in e:
|
||||
# Receive espnow messages and forward them to MQTT broker over wifi
|
||||
|
||||
**Sensor:** ::
|
||||
|
||||
import network, espnow
|
||||
|
||||
sta, ap = wifi_reset() # Reset wifi to AP off, STA on and disconnected
|
||||
sta.config(channel=6) # Change to the channel used by the proxy above.
|
||||
peer = b'0\xaa\xaa\xaa\xaa\xaa' # MAC address of proxy
|
||||
e = espnow.ESPNow(); e.active(True);
|
||||
e.add_peer(peer)
|
||||
while True:
|
||||
msg = read_sensor()
|
||||
e.send(peer, msg)
|
||||
time.sleep(1)
|
||||
|
||||
Other issues to take care with when using ESPNow with wifi are:
|
||||
|
||||
- **Set WIFI to known state on startup:** MicroPython does not reset the wifi
|
||||
peripheral after a soft reset. This can lead to unexpected behaviour. To
|
||||
guarantee the wifi is reset to a known state after a soft reset make sure you
|
||||
deactivate the STA_IF and AP_IF before setting them to the desired state at
|
||||
startup, eg.::
|
||||
|
||||
import network, time
|
||||
|
||||
def wifi_reset(): # Reset wifi to AP_IF off, STA_IF on and disconnected
|
||||
sta = network.WLAN(network.STA_IF); sta.active(False)
|
||||
ap = network.WLAN(network.AP_IF); ap.active(False)
|
||||
sta.active(True)
|
||||
while not sta.active():
|
||||
time.sleep(0.1)
|
||||
sta.disconnect() # For ESP8266
|
||||
while sta.isconnected():
|
||||
time.sleep(0.1)
|
||||
return sta, ap
|
||||
|
||||
sta, ap = wifi_reset()
|
||||
|
||||
Remember that a soft reset occurs every time you connect to the device REPL
|
||||
and when you type ``ctrl-D``.
|
||||
|
||||
- **STA_IF and AP_IF always operate on the same channel:** the AP_IF will change
|
||||
channel when you connect to a wifi network; regardless of the channel you set
|
||||
for the AP_IF (see `Attention Note 3
|
||||
<https://docs.espressif.com/
|
||||
projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html
|
||||
#_CPPv419esp_wifi_set_config16wifi_interface_tP13wifi_config_t>`_
|
||||
). After all, there is really only one wifi radio on the device, which is
|
||||
shared by the STA_IF and AP_IF virtual devices.
|
||||
|
||||
- **Disable automatic channel assignment on your wifi router:** If the wifi
|
||||
router for your wifi network is configured to automatically assign the wifi
|
||||
channel, it may change the channel for the network if it detects interference
|
||||
from other wifi routers. When this occurs, the ESP devices connected to the
|
||||
wifi network will also change channels to match the router, but other
|
||||
ESPNow-only devices will remain on the previous channel and communication will
|
||||
be lost. To mitigate this, either set your wifi router to use a fixed wifi
|
||||
channel or configure your devices to re-scan the wifi channels if they are
|
||||
unable to find their expected peers on the current channel.
|
||||
|
||||
- **MicroPython re-scans wifi channels when trying to reconnect:** If the esp
|
||||
device is connected to a Wifi Access Point that goes down, MicroPython will
|
||||
automatically start scanning channels in an attempt to reconnect to the
|
||||
Access Point. This means ESPNow messages will be lost while scanning for the
|
||||
AP. This can be disabled by ``sta.config(reconnects=0)``, which will also
|
||||
disable the automatic reconnection after losing connection.
|
||||
|
||||
- Some versions of the ESP IDF only permit sending ESPNow packets from the
|
||||
STA_IF interface to peers which have been registered on the same wifi
|
||||
channel as the STA_IF::
|
||||
|
||||
ESPNOW: Peer channel is not equal to the home channel, send fail!
|
||||
|
||||
ESPNow and Sleep Modes
|
||||
----------------------
|
||||
|
||||
The `machine.lightsleep([time_ms])<machine.lightsleep>` and
|
||||
`machine.deepsleep([time_ms])<machine.deepsleep>` functions can be used to put
|
||||
the ESP32 and peripherals (including the WiFi and Bluetooth radios) to sleep.
|
||||
This is useful in many applications to conserve battery power. However,
|
||||
applications must disable the WLAN peripheral (using
|
||||
`active(False)<network.WLAN.active>`) before entering light or deep sleep (see
|
||||
`Sleep Modes <https://docs.espressif.com/
|
||||
projects/esp-idf/en/latest/esp32/api-reference/system/sleep_modes.html>`_).
|
||||
Otherwise the WiFi radio may not be initialised properly after wake from
|
||||
sleep. If the ``STA_IF`` and ``AP_IF`` interfaces have both been set
|
||||
`active(True)<network.WLAN.active()>` then both interfaces should be set
|
||||
`active(False)<network.WLAN.active()>` before entering any sleep mode.
|
||||
|
||||
**Example:** deep sleep::
|
||||
|
||||
import network, machine, espnow
|
||||
|
||||
sta, ap = wifi_reset() # Reset wifi to AP off, STA on and disconnected
|
||||
peer = b'0\xaa\xaa\xaa\xaa\xaa' # MAC address of peer
|
||||
e = espnow.ESPNow()
|
||||
e.active(True)
|
||||
e.add_peer(peer) # Register peer on STA_IF
|
||||
|
||||
print('Sending ping...')
|
||||
if not e.send(peer, b'ping'):
|
||||
print('Ping failed!')
|
||||
e.active(False)
|
||||
sta.active(False) # Disable the wifi before sleep
|
||||
print('Going to sleep...')
|
||||
machine.deepsleep(10000) # Sleep for 10 seconds then reboot
|
||||
|
||||
**Example:** light sleep::
|
||||
|
||||
import network, machine, espnow
|
||||
|
||||
sta, ap = wifi_reset() # Reset wifi to AP off, STA on and disconnected
|
||||
sta.config(channel=6)
|
||||
peer = b'0\xaa\xaa\xaa\xaa\xaa' # MAC address of peer
|
||||
e = espnow.ESPNow()
|
||||
e.active(True)
|
||||
e.add_peer(peer) # Register peer on STA_IF
|
||||
|
||||
while True:
|
||||
print('Sending ping...')
|
||||
if not e.send(peer, b'ping'):
|
||||
print('Ping failed!')
|
||||
sta.active(False) # Disable the wifi before sleep
|
||||
print('Going to sleep...')
|
||||
machine.lightsleep(10000) # Sleep for 10 seconds
|
||||
sta.active(True)
|
||||
sta.config(channel=6) # Wifi loses config after lightsleep()
|
||||
|
|
@ -8,15 +8,17 @@ MicroPython libraries
|
|||
Important summary of this section
|
||||
|
||||
* MicroPython provides built-in modules that mirror the functionality of the
|
||||
Python standard library (e.g. :mod:`os`, :mod:`time`), as well as
|
||||
MicroPython-specific modules (e.g. :mod:`bluetooth`, :mod:`machine`).
|
||||
* Most standard library modules implement a subset of the functionality of
|
||||
the equivalent Python module, and in a few cases provide some
|
||||
MicroPython-specific extensions (e.g. :mod:`array`, :mod:`os`)
|
||||
:ref:`Python standard library <micropython_lib_python>` (e.g. :mod:`os`,
|
||||
:mod:`time`), as well as :ref:`MicroPython-specific modules <micropython_lib_micropython>`
|
||||
(e.g. :mod:`bluetooth`, :mod:`machine`).
|
||||
* Most Python standard library modules implement a subset of the
|
||||
functionality of the equivalent Python module, and in a few cases provide
|
||||
some MicroPython-specific extensions (e.g. :mod:`array`, :mod:`os`)
|
||||
* Due to resource constraints or other limitations, some ports or firmware
|
||||
versions may not include all the functionality documented here.
|
||||
* To allow for extensibility, the built-in modules can be extended from
|
||||
Python code loaded onto the device.
|
||||
* To allow for extensibility, some built-in modules can be
|
||||
:ref:`extended from Python code <micropython_lib_extending>` loaded onto
|
||||
the device filesystem.
|
||||
|
||||
This chapter describes modules (function and class libraries) which are built
|
||||
into MicroPython. This documentation in general aspires to describe all modules
|
||||
|
@ -41,6 +43,8 @@ Beyond the built-in libraries described in this documentation, many more
|
|||
modules from the Python standard library, as well as further MicroPython
|
||||
extensions to it, can be found in :term:`micropython-lib`.
|
||||
|
||||
.. _micropython_lib_python:
|
||||
|
||||
Python standard libraries and micro-libraries
|
||||
---------------------------------------------
|
||||
|
||||
|
@ -77,6 +81,7 @@ library.
|
|||
zlib.rst
|
||||
_thread.rst
|
||||
|
||||
.. _micropython_lib_micropython:
|
||||
|
||||
MicroPython-specific libraries
|
||||
------------------------------
|
||||
|
@ -155,6 +160,11 @@ The following libraries are specific to the ESP8266 and ESP32.
|
|||
esp.rst
|
||||
esp32.rst
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
espnow.rst
|
||||
|
||||
|
||||
Libraries specific to the RP2040
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -176,23 +186,48 @@ The following libraries are specific to the Zephyr port.
|
|||
|
||||
zephyr.rst
|
||||
|
||||
.. _micropython_lib_extending:
|
||||
|
||||
Extending built-in libraries from Python
|
||||
----------------------------------------
|
||||
|
||||
In most cases, the above modules are actually named ``umodule`` rather than
|
||||
``module``, but MicroPython will alias any module prefixed with a ``u`` to the
|
||||
non-``u`` version. However a file (or :term:`frozen module`) named
|
||||
``module.py`` will take precedence over this alias.
|
||||
Many built-in modules are actually named ``umodule`` rather than ``module``, but
|
||||
MicroPython will alias any module prefixed with a ``u`` to the non-``u``
|
||||
version. This means that, for example, ``import time`` will first attempt to
|
||||
resolve from the filesystem, and then failing that will fall back to the
|
||||
built-in ``utime``. On the other hand, ``import utime`` will always go directly
|
||||
to the built-in.
|
||||
|
||||
This allows the user to provide an extended implementation of a built-in library
|
||||
(perhaps to provide additional CPython compatibility). The user-provided module
|
||||
(in ``module.py``) can still use the built-in functionality by importing
|
||||
``umodule`` directly. This is used extensively in :term:`micropython-lib`. See
|
||||
:ref:`packages` for more information.
|
||||
(perhaps to provide additional CPython compatibility or missing functionality).
|
||||
The user-provided module (in ``module.py``) can still use the built-in
|
||||
functionality by importing ``umodule`` directly (e.g. typically an extension
|
||||
module ``time.py`` will do ``from utime import *``). This is used extensively
|
||||
in :term:`micropython-lib`. See :ref:`packages` for more information.
|
||||
|
||||
This applies to both the Python standard libraries (e.g. ``os``, ``time``, etc),
|
||||
but also the MicroPython libraries too (e.g. ``machine``, ``bluetooth``, etc).
|
||||
The main exception is the port-specific libraries (``pyb``, ``esp``, etc).
|
||||
This extensibility applies to the following Python standard library modules
|
||||
which are built-in to the firmware: ``array``, ``binascii``, ``collections``,
|
||||
``errno``, ``hashlib``, ``heapq``, ``io``, ``json``, ``os``, ``platform``,
|
||||
``random``, ``re``, ``select``, ``socket``, ``ssl``, ``struct``, ``sys``,
|
||||
``time``, ``zlib``, as well as the MicroPython-specific libraries: ``bluepy``,
|
||||
``bluetooth``, ``machine``, ``timeq``, ``websocket``. All other built-in
|
||||
modules cannot be extended from the filesystem.
|
||||
|
||||
*Other than when you specifically want to force the use of the built-in module,
|
||||
we recommend always using* ``import module`` *rather than* ``import umodule``.
|
||||
|
||||
**Note:** In MicroPython v1.21.0 and higher, it is now possible to force an
|
||||
import of the built-in module by clearing ``sys.path`` during the import. For
|
||||
example, in ``time.py``, you can write::
|
||||
|
||||
_path = sys.path
|
||||
sys.path = ()
|
||||
try:
|
||||
from time import *
|
||||
finally:
|
||||
sys.path = _path
|
||||
del _path
|
||||
|
||||
This is now the preferred way (instead of ``from utime import *``), as the
|
||||
``u``-prefix will be removed from the names of built-in modules in a future
|
||||
version of MicroPython.
|
||||
|
|
|
@ -10,7 +10,8 @@ Example usage::
|
|||
|
||||
from machine import PWM
|
||||
|
||||
pwm = PWM(pin) # create a PWM object on a pin
|
||||
pwm = PWM(pin, freq=50, duty_u16=8192) # create a PWM object on a pin
|
||||
# and set freq and duty
|
||||
pwm.duty_u16(32768) # set duty to 50%
|
||||
|
||||
# reinitialise with a period of 200us, duty of 5us
|
||||
|
@ -23,7 +24,7 @@ Example usage::
|
|||
Constructors
|
||||
------------
|
||||
|
||||
.. class:: PWM(dest, *, freq, duty_u16, duty_ns)
|
||||
.. class:: PWM(dest, *, freq, duty_u16, duty_ns, invert)
|
||||
|
||||
Construct and return a new PWM object using the following parameters:
|
||||
|
||||
|
@ -34,10 +35,12 @@ Constructors
|
|||
PWM cycle.
|
||||
- *duty_u16* sets the duty cycle as a ratio ``duty_u16 / 65535``.
|
||||
- *duty_ns* sets the pulse width in nanoseconds.
|
||||
- *invert* inverts the respective output if the value is True
|
||||
|
||||
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.
|
||||
*invert* is not available at all ports.
|
||||
|
||||
Methods
|
||||
-------
|
||||
|
|
|
@ -133,4 +133,20 @@ Methods
|
|||
hostname The hostname that will be sent to DHCP (STA interfaces) and mDNS (if supported, both STA and AP). (Deprecated, use :func:`network.hostname` instead)
|
||||
reconnects Number of reconnect attempts to make (integer, 0=none, -1=unlimited)
|
||||
txpower Maximum transmit power in dBm (integer or float)
|
||||
pm WiFi Power Management setting (see below for allowed values)
|
||||
============= ===========
|
||||
|
||||
Constants
|
||||
---------
|
||||
|
||||
.. data:: WLAN.PM_PERFORMANCE
|
||||
WLAN.PM_POWERSAVE
|
||||
WLAN.PM_NONE
|
||||
|
||||
Allowed values for the ``WLAN.config(pm=...)`` network interface parameter:
|
||||
|
||||
* ``PM_PERFORMANCE``: enable WiFi power management to balance power
|
||||
savings and WiFi performance
|
||||
* ``PM_POWERSAVE``: enable WiFi power management with additional power
|
||||
savings and reduced WiFi performance
|
||||
* ``PM_NONE``: disable wifi power management
|
||||
|
|
|
@ -82,11 +82,18 @@ Methods
|
|||
|
||||
.. method:: StateMachine.exec(instr)
|
||||
|
||||
Execute a single PIO instruction. Uses `asm_pio_encode` to encode the
|
||||
instruction from the given string *instr*.
|
||||
Execute a single PIO instruction.
|
||||
|
||||
If *instr* is a string then uses `asm_pio_encode` to encode the instruction
|
||||
from the given string.
|
||||
|
||||
>>> sm.exec("set(0, 1)")
|
||||
|
||||
If *instr* is an integer then it is treated as an already encoded PIO
|
||||
machine code instruction to be executed.
|
||||
|
||||
>>> sm.exec(rp2.asm_pio_encode("out(y, 8)", 0))
|
||||
|
||||
.. method:: StateMachine.get(buf=None, shift=0)
|
||||
|
||||
Pull a word from the state machine's RX FIFO.
|
||||
|
|
|
@ -14,9 +14,9 @@ The pin assignment of UARTs to pins is fixed.
|
|||
The UARTs are numbered 0..8. The rx/tx pins are assigned according to the
|
||||
tables below:
|
||||
|
||||
================ =========== =========== =========== ===========
|
||||
================= =========== =========== =========== ===========
|
||||
Board / Pin UART0 UART1 UART2 UART3
|
||||
================ =========== =========== =========== ===========
|
||||
================= =========== =========== =========== ===========
|
||||
Teensy 4.0 - 0/1 7/8 14/15
|
||||
Teensy 4.1 - 0/1 7/8 14/15
|
||||
MIMXRT1010-EVK Debug USB D0/D1 D7/D6 -
|
||||
|
@ -27,9 +27,10 @@ MIMXRT1050-EVKB Debug USB D0/D1 D7/D6 D8/D9
|
|||
MIMXRT1060-EVK Debug USB D0/D1 D7/D6 D8/D9
|
||||
MIMXRT1064-EVK Debug USB D0/D1 D7/D6 D8/D9
|
||||
MIMXRT1170-EVK Debug USB D0/D1 D12/D11 D10/D13
|
||||
Adafruit Metro M7 - D0/D1 D7/D3 A1/A0
|
||||
Olimex RT1010Py - RxD/TxD D5/D6 -
|
||||
Seeed ARCH MIX - J3_19/J3_20 J4_16/J4_17 J4_06/J4_07
|
||||
================ =========== =========== =========== ===========
|
||||
Seeed ARCH MIX - J3_19/J3_20 J4_16/J4_17 J4_06/J4_07
|
||||
================= =========== =========== =========== ===========
|
||||
|
||||
|
|
||||
|
||||
|
@ -61,38 +62,38 @@ PWM pin assignment
|
|||
Pins are specified in the same way as for the Pin class. The following tables show
|
||||
the assignment of the board Pins to PWM modules:
|
||||
|
||||
=========== ========== ========== ====== ============== ======
|
||||
Pin/ MIMXRT 1010 1015 1020 1050/1060/1064 1170
|
||||
=========== ========== ========== ====== ============== ======
|
||||
D0 - Q1/1 F1/1/B - -
|
||||
D1 - Q1/0 F1/1/A - -
|
||||
D2 F1/3/B F1/3/A - F1/3/B -
|
||||
D3 F1/3/A F1/0/A F2/3/B F4/0/A F1/2/A
|
||||
D4 F1/3/A (*) Q1/2 Q2/1 F2/3/A Q4/2
|
||||
D5 F1/0/B (*) F1/0/B F2/3/A F1/3/A F1/2/B
|
||||
D6 - F1/2/B F2/0/A Q3/2 F1/0/A
|
||||
D7 - - F1/0/A Q3/3 -
|
||||
D8 F1/0/A F1/1/B F1/0/B F1/1/X Q4/3
|
||||
D9 F1/1/B (*) F1/2/A F2/0/B F1/0/X F1/0/B
|
||||
D10 F1/3/B - F2/2/B F1/0/B (*) F2/2/B
|
||||
D11 F1/2/A - F2/1/A F1/1/A (*) -
|
||||
D12 F1/2/B - F2/1/B F1/1/B (*) -
|
||||
D13 F1/3/A - F2/2/A F1/0/A (*) F2/2/A
|
||||
D14 F1/0/B - - F2/3/B -
|
||||
D15 F1/0/A - - F2/3/A -
|
||||
A0 - - F1/2/A - -
|
||||
A1 F1/3/X F1/3/B F1/2/B - -
|
||||
A2 F1/2/X F1/3/A F1/3/A - -
|
||||
A3 - F1/2/A F1/3/B - -
|
||||
A4 - - - Q3/1 -
|
||||
A5 - - - Q3/0 -
|
||||
D31 - - - - F1/2/B
|
||||
D32 - - - - F1/2/A
|
||||
D33 - - - - F1/1/B
|
||||
D34 - - - - F1/1/A
|
||||
D35 - - - - F1/0/B
|
||||
D36 - - - - F1/0/A
|
||||
=========== ========== ========== ====== ============== ======
|
||||
=========== ========== ========== ====== ========== ====== ========
|
||||
Pin/ MIMXRT 1010 1015 1020 1050/60/64 1170 Metro M7
|
||||
=========== ========== ========== ====== ========== ====== ========
|
||||
D0 - Q1/1 F1/1/B - - -
|
||||
D1 - Q1/0 F1/1/A - - -
|
||||
D2 F1/3/B F1/3/A - F1/3/B - -
|
||||
D3 F1/3/A F1/0/A F2/3/B F4/0/A F1/2/A -
|
||||
D4 F1/3/A (*) Q1/2 Q2/1 F2/3/A Q4/2 F1/0/B
|
||||
D5 F1/0/B (*) F1/0/B F2/3/A F1/3/A F1/2/B F1/0/A
|
||||
D6 - F1/2/B F2/0/A Q3/2 F1/0/A -
|
||||
D7 - - F1/0/A Q3/3 - -
|
||||
D8 F1/0/A F1/1/B F1/0/B F1/1/X Q4/3 F1/3/A
|
||||
D9 F1/1/B (*) F1/2/A F2/0/B F1/0/X F1/0/B F1/3/B
|
||||
D10 F1/3/B - F2/2/B F1/0/B (*) F2/2/B F1/2/A
|
||||
D11 F1/2/A - F2/1/A F1/1/A (*) - F1/2/B
|
||||
D12 F1/2/B - F2/1/B F1/1/B (*) - F1/1/A
|
||||
D13 F1/3/A - F2/2/A F1/0/A (*) F2/2/A F1/1/B
|
||||
D14 F1/0/B - - F2/3/B - F1/0/B
|
||||
D15 F1/0/A - - F2/3/A - F1/0/A
|
||||
A0 - - F1/2/A - - -
|
||||
A1 F1/3/X F1/3/B F1/2/B - - -
|
||||
A2 F1/2/X F1/3/A F1/3/A - - -
|
||||
A3 - F1/2/A F1/3/B - - F1/3/B
|
||||
A4 - - - Q3/1 - F1/2/X
|
||||
A5 - - - Q3/0 - -
|
||||
D31 - - - - F1/2/B -
|
||||
D32 - - - - F1/2/A -
|
||||
D33 - - - - F1/1/B -
|
||||
D34 - - - - F1/1/A -
|
||||
D35 - - - - F1/0/B -
|
||||
D36 - - - - F1/0/A -
|
||||
=========== ========== ========== ====== ========== ====== ========
|
||||
|
||||
Pins denoted with (*) are by default not wired at the board.
|
||||
|
||||
|
@ -318,6 +319,7 @@ MIXMXRT1050-EVKB D10/-/D11/D12/D13 (*) - -
|
|||
MIXMXRT1060-EVK D10/-/D11/D12/D13 (*) - -
|
||||
MIXMXRT1064-EVK D10/-/D11/D12/D13 (*) - -
|
||||
MIXMXRT1170-EVK D10/-/D11/D12/D13 D28/-/D25/D24/D26 -/-/D14/D15/D24
|
||||
Adafruit Metro M7 -/-/MOSI/MISO/SCK - -
|
||||
Olimex RT1010Py - CS0/-/SDO/SDI/SCK SDCARD with CS1
|
||||
Seeed ARCH MIX J4_12/-/J4_14/J4_13/J4_15 J3_09/J3_05/J3_08_J3_11
|
||||
================= ========================= ======================= ===============
|
||||
|
@ -350,6 +352,7 @@ MIXMXRT1050-EVKB A4/A5 D1/D0 - - -
|
|||
MIXMXRT1060-EVK A4/A5 D1/D0 - - -
|
||||
MIXMXRT1064-EVK A4/A5 D1/D0 - - -
|
||||
MIXMXRT1170-EVK D14/D15 D1/D0 A4/A5 D26/D25 D19/D18
|
||||
Adafruit Metro M7 D14/D15 D0/D1
|
||||
Olimex RT1010Py - SDA1/SCL1 SDA2/SCL2 - -
|
||||
Seeed ARCH MIX J3_17/J3_16 J4_06/J4_07 J5_05/J5_04 - -
|
||||
================= =========== =========== =========== ======= =======
|
||||
|
@ -364,18 +367,19 @@ Hardware I2S pin assignment
|
|||
|
||||
Pin assignments for a few MIMXRT boards:
|
||||
|
||||
=============== == ===== ======== ======= ======= ======== ======= =======
|
||||
Board ID MCK SCK_TX WS_TX SD_TX SCK_RX WS_RX SD_RX
|
||||
=============== == ===== ======== ======= ======= ======== ======= =======
|
||||
Teensy 4.0 1 23 26 27 7 21 20 8
|
||||
Teensy 4.0 2 33 4 3 2 - - 5
|
||||
Teensy 4.1 1 23 26 27 7 21 20 8
|
||||
Teensy 4.1 2 33 4 3 2 - - 5
|
||||
Seeed Arch MIX 1 J4_09 J4_14 J4_15 J14_13 J4_11 J4_10 J4_10
|
||||
Olimex RT1010Py 1 D8 D6 D7 D4 D1 D2 D3
|
||||
Olimex RT1010Py 3 - D10 D9 D11 - - -
|
||||
MIMXRT_DEV 1 "MCK" "SCK_TX" "WS_TX" "SD_TX" "SCK_RX" "WS_RX" "SD_RX"
|
||||
=============== == ===== ======== ======= ======= ======== ======= =======
|
||||
================= == ===== ======== ======= ======= ======== ======= =======
|
||||
Board ID MCK SCK_TX WS_TX SD_TX SCK_RX WS_RX SD_RX
|
||||
================= == ===== ======== ======= ======= ======== ======= =======
|
||||
Teensy 4.0 1 23 26 27 7 21 20 8
|
||||
Teensy 4.0 2 33 4 3 2 - - 5
|
||||
Teensy 4.1 1 23 26 27 7 21 20 8
|
||||
Teensy 4.1 2 33 4 3 2 - - 5
|
||||
Seeed Arch MIX 1 J4_09 J4_14 J4_15 J14_13 J4_11 J4_10 J4_10
|
||||
Adafruit Metro M7 1 D8 D10 D9 D12 D14 D15 D13
|
||||
Olimex RT1010Py 1 D8 D6 D7 D4 D1 D2 D3
|
||||
Olimex RT1010Py 3 - D10 D9 D11 - - -
|
||||
MIMXRT_DEV 1 "MCK" "SCK_TX" "WS_TX" "SD_TX" "SCK_RX" "WS_RX" "SD_RX"
|
||||
================= == ===== ======== ======= ======= ======== ======= =======
|
||||
|
||||
Symbolic pin names are provided for the MIMXRT_10xx_DEV boards.
|
||||
These are provided for the other boards as well.
|
||||
These are provided for the other boards as well.
|
||||
|
|
|
@ -145,11 +145,12 @@ handling signal groups. ::
|
|||
|
||||
from machine import Pin, PWM
|
||||
|
||||
pwm2 = PWM(Pin(2)) # create PWM object from a pin
|
||||
pwm2.freq() # get current frequency
|
||||
pwm2.freq(1000) # set frequency
|
||||
pwm2.duty_u16() # get current duty cycle, range 0-65535
|
||||
pwm2.duty_u16(200) # set duty cycle, range 0-65535
|
||||
# create PWM object from a pin and set the frequency and duty cycle
|
||||
pwm2 = PWM(Pin(2), freq=2000, duty_u16=32768)
|
||||
pwm2.freq() # get the current frequency
|
||||
pwm2.freq(1000) # set/change the frequency
|
||||
pwm2.duty_u16() # get the current duty cycle, range 0-65535
|
||||
pwm2.duty_u16(200) # set the duty cycle, range 0-65535
|
||||
pwm2.deinit() # turn off PWM on the pin
|
||||
# create a complementary signal pair on Pin 2 and 3
|
||||
pwm2 = PWM((2, 3), freq=2000, duty_ns=20000)
|
||||
|
@ -206,8 +207,9 @@ PWM Constructor
|
|||
- *align*\=value. Shortcuts for the pulse center setting, causing the pulse either at
|
||||
the center of the frame (value=0), the leading edge at the begin (value=1) or the
|
||||
trailing edge at the end of a pulse period (value=2).
|
||||
- *invert*\=True|False channel_mask. Setting a bit in the mask inverts the respective channel.
|
||||
Bit 0 inverts the first specified channel, bit 2 the second. The default is 0.
|
||||
- *invert*\=value channel_mask. Setting a bit in the mask inverts the respective channel.
|
||||
Bit 0 inverts the first specified channel, bit 1 the second. The default is 0. For a
|
||||
PWM object with a single channel, True and False may be used as values.
|
||||
- *sync*\=True|False. If a channel of a module's submodule 0 is already active, other
|
||||
submodules of the same module can be forced to be synchronous to submodule 0. Their
|
||||
pulse period start then at at same clock cycle. The default is False.
|
||||
|
|
|
@ -120,7 +120,7 @@ Glossary
|
|||
<https://github.com/micropython/micropython-lib>`_ which provides
|
||||
implementations for many modules from CPython's standard library.
|
||||
|
||||
Some of the modules are are implemented in pure Python, and are able to
|
||||
Some of the modules are implemented in pure Python, and are able to
|
||||
be used on all ports. However, the majority of these modules use
|
||||
:term:`FFI` to access operating system functionality, and as such can
|
||||
only be used on the :term:`MicroPython Unix port` (with limited support
|
||||
|
|
|
@ -4,46 +4,88 @@ MicroPython remote control: mpremote
|
|||
====================================
|
||||
|
||||
The ``mpremote`` command line tool provides an integrated set of utilities to
|
||||
remotely interact with and automate a MicroPython device over a serial
|
||||
connection.
|
||||
remotely interact with, manage the filesystem on, and automate a MicroPython
|
||||
device over a serial connection.
|
||||
|
||||
To use mpremote install it via ``pip``:
|
||||
To use mpremote, first install it via ``pip``:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip install mpremote
|
||||
$ pip install --user mpremote
|
||||
|
||||
Or via `pipx <https://pypa.github.io/pipx/>`_:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pipx install mpremote
|
||||
|
||||
The simplest way to use this tool is just by invoking it without any arguments:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote
|
||||
$ mpremote
|
||||
|
||||
This command automatically detects and connects to the first available USB
|
||||
serial device and provides an interactive REPL. Serial ports are opened in
|
||||
exclusive mode, so running a second (or third, etc) instance of ``mpremote``
|
||||
will connect to subsequent serial devices, if any are available.
|
||||
serial device and provides an interactive terminal that you can use to access
|
||||
the REPL and your program's output. Serial ports are opened in exclusive mode,
|
||||
so running a second (or third, etc) instance of ``mpremote`` will connect to
|
||||
subsequent serial devices, if any are available.
|
||||
|
||||
Additionally ``pipx`` also allows you to directly run ``mpremote`` without
|
||||
installing first:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pipx run mpremote ...args
|
||||
|
||||
Commands
|
||||
--------
|
||||
|
||||
For REPL access, running ``mpremote`` without any arguments is usually all that
|
||||
is needed. ``mpremote`` also supports a set of commands given at the command
|
||||
line which will perform various actions on remote MicroPython devices.
|
||||
``mpremote`` supports being given a series of commands given at the command line
|
||||
which will perform various actions in sequence on a remote MicroPython device.
|
||||
See the :ref:`examples section <mpremote_examples>` below to get an idea of how
|
||||
this works and for some common combinations of commands.
|
||||
|
||||
For commands that support multiple arguments (e.g. a list of files), the
|
||||
argument list can be terminated with ``+``.
|
||||
Each command is of the form ``<command name> [--options] [args...]``. For commands
|
||||
that support multiple arguments (e.g. a list of files), the argument list can
|
||||
be terminated with ``+``.
|
||||
|
||||
If no command is specified, the default command is ``repl``. Additionally, if
|
||||
any command needs to access the device, and no earlier ``connect`` has been
|
||||
specified, then an implicit ``connect auto`` is added.
|
||||
|
||||
In order to get the device into a known state for any action command
|
||||
(except ``repl``), once connected ``mpremote`` will stop any running program
|
||||
and soft-reset the device before running the first command. You can control
|
||||
this behavior using the ``resume`` and ``soft-reset`` commands.
|
||||
See :ref:`auto-connection and auto-soft-reset <mpremote_reset>` for more details.
|
||||
|
||||
Multiple commands can be specified and they will be run sequentially.
|
||||
|
||||
The full list of supported commands are:
|
||||
|
||||
- connect to a specified device via a device-name shortcut:
|
||||
- `connect <mpremote_command_connect>`
|
||||
- `disconnect <mpremote_command_disconnect>`
|
||||
- `resume <mpremote_command_resume>`
|
||||
- `soft_reset <mpremote_command_soft_reset>`
|
||||
- `repl <mpremote_command_repl>`
|
||||
- `eval <mpremote_command_eval>`
|
||||
- `exec <mpremote_command_exec>`
|
||||
- `run <mpremote_command_run>`
|
||||
- `fs <mpremote_command_fs>`
|
||||
- `df <mpremote_command_df>`
|
||||
- `edit <mpremote_command_edit>`
|
||||
- `mip <mpremote_command_mip>`
|
||||
- `mount <mpremote_command_mount>`
|
||||
- `unmount <mpremote_command_unmount>`
|
||||
- `rtc <mpremote_command_rtc>`
|
||||
- `sleep <mpremote_command_sleep>`
|
||||
- `reset <mpremote_command_reset>`
|
||||
- `bootloader <mpremote_command_bootloader>`
|
||||
|
||||
.. code-block:: bash
|
||||
.. _mpremote_command_connect:
|
||||
|
||||
$ mpremote <device-shortcut>
|
||||
|
||||
- connect to specified device via name:
|
||||
- **connect** -- connect to specified device via name:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
|
@ -54,88 +96,182 @@ The full list of supported commands are:
|
|||
- ``list``: list available devices
|
||||
- ``auto``: connect to the first available USB serial port
|
||||
- ``id:<serial>``: connect to the device with USB serial number
|
||||
``<serial>`` (the second entry in the output from the ``connect list``
|
||||
command)
|
||||
- ``port:<path>``: connect to the device with the given path
|
||||
``<serial>`` (the second column from the ``connect list``
|
||||
command output)
|
||||
- ``port:<path>``: connect to the device with the given path (the first column
|
||||
from the ``connect list`` command output
|
||||
- any valid device name/path, to connect to that device
|
||||
|
||||
- disconnect current device:
|
||||
**Note:** Instead of using the ``connect`` command, there are several
|
||||
:ref:`pre-defined shortcuts <mpremote_shortcuts>` for common device paths. For
|
||||
example the ``a0`` shortcut command is equivalent to
|
||||
``connect /dev/ttyACM0`` (Linux), or ``c0`` for ``COM0`` (Windows).
|
||||
|
||||
**Note:** The ``auto`` option will only detect USB serial ports, i.e. a serial
|
||||
port that has an associated USB VID/PID (i.e. CDC/ACM or FTDI-style
|
||||
devices). Other types of serial ports
|
||||
|
||||
.. _mpremote_command_disconnect:
|
||||
|
||||
- **disconnect** -- disconnect current device:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ mpremote disconnect
|
||||
|
||||
After a disconnect, auto soft-reset is enabled.
|
||||
After a disconnect, :ref:`auto-soft-reset <mpremote_reset>` is enabled.
|
||||
|
||||
- resume a previous ``mpremote`` session:
|
||||
.. _mpremote_command_resume:
|
||||
|
||||
- **resume** -- maintain existing interpreter state for subsequent commands:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ mpremote resume
|
||||
|
||||
This disables auto soft-reset.
|
||||
This disables :ref:`auto-soft-reset <mpremote_reset>`. This is useful if you
|
||||
want to run a subsequent command on a board without first soft-resetting it.
|
||||
|
||||
- perform a soft-reset of the device:
|
||||
.. _mpremote_command_soft_reset:
|
||||
|
||||
- **soft-reset** -- perform a soft-reset of the device:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ mpremote soft-reset
|
||||
|
||||
This will clear out the Python heap and restart the interpreter. It also
|
||||
disables auto soft-reset.
|
||||
prevents the subsequent command from triggering :ref:`auto-soft-reset <mpremote_reset>`.
|
||||
|
||||
- enter the REPL on the connected device:
|
||||
.. _mpremote_command_repl:
|
||||
|
||||
- **repl** -- enter the REPL on the connected device:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ mpremote repl [options]
|
||||
$ mpremote repl [--options]
|
||||
|
||||
Options are:
|
||||
|
||||
- ``--escape-non-printable``, to print non-printable bytes/characters as their hex code
|
||||
- ``--capture <file>``, to capture output of the REPL session to the given
|
||||
file
|
||||
- ``--inject-code <string>``, to specify characters to inject at the REPL when
|
||||
Ctrl-J is pressed
|
||||
``Ctrl-J`` is pressed. This allows you to automate a common command.
|
||||
- ``--inject-file <file>``, to specify a file to inject at the REPL when
|
||||
Ctrl-K is pressed
|
||||
``Ctrl-K`` is pressed. This allows you to run a file (e.g. containing some
|
||||
useful setup code, or even the program you are currently working on).
|
||||
|
||||
- evaluate and print the result of a Python expression:
|
||||
While the ``repl`` command running, you can use ``Ctrl-]`` or ``Ctrl-x`` to
|
||||
exit.
|
||||
|
||||
**Note:** The name "REPL" here reflects that the common usage of this command
|
||||
to access the Read Eval Print Loop that is running on the MicroPython
|
||||
device. Strictly, the ``repl`` command is just functioning as a terminal
|
||||
(or "serial monitor") to access the device. Because this command does not
|
||||
trigger the :ref:`auto-reset behavior <mpremote_reset>`, this means that if
|
||||
a program is currently running, you will first need to interrupt it with
|
||||
``Ctrl-C`` to get to the REPL, which will then allow you to access program
|
||||
state. You can also use ``mpremote soft-reset repl`` to get a "clean" REPL
|
||||
with all program state cleared.
|
||||
|
||||
.. _mpremote_command_eval:
|
||||
|
||||
- **eval** -- evaluate and print the result of a Python expression:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ mpremote eval <string>
|
||||
|
||||
- execute the given Python code:
|
||||
.. _mpremote_command_exec:
|
||||
|
||||
- **exec** -- execute the given Python code:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ mpremote exec <string>
|
||||
|
||||
- run a script from the local filesystem:
|
||||
By default, ``mpremote exec`` will display any output from the expression until it
|
||||
terminates. The ``--no-follow`` flag can be specified to return immediately and leave
|
||||
the device running the expression in the background.
|
||||
|
||||
.. _mpremote_command_run:
|
||||
|
||||
- **run** -- run a script from the local filesystem:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ mpremote run <file>
|
||||
$ mpremote run <file.py>
|
||||
|
||||
- execute filesystem commands on the device:
|
||||
This will execute the file directly from RAM on the device without copying it
|
||||
to the filesystem. This is a very useful way to iterate on the development of
|
||||
a single piece of code without having to worry about deploying it to the
|
||||
filesystem.
|
||||
|
||||
By default, ``mpremote run`` will display any output from the script until it
|
||||
terminates. The ``--no-follow`` flag can be specified to return immediately and leave
|
||||
the device running the script in the background.
|
||||
|
||||
.. _mpremote_command_fs:
|
||||
|
||||
- **fs** -- execute filesystem commands on the device:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ mpremote fs <command>
|
||||
$ mpremote fs <sub-command>
|
||||
|
||||
``<command>`` may be:
|
||||
``<sub-command>`` may be:
|
||||
|
||||
- ``cat <file..>`` to show the contents of a file or files on the device
|
||||
- ``ls`` to list the current directory
|
||||
- ``ls <dirs...>`` to list the given directories
|
||||
- ``cp [-r] <src...> <dest>`` to copy files; use ":" as a prefix to specify
|
||||
a file on the device
|
||||
- ``cp [-r] <src...> <dest>`` to copy files
|
||||
- ``rm <src...>`` to remove files on the device
|
||||
- ``mkdir <dirs...>`` to create directories on the device
|
||||
- ``rmdir <dirs...>`` to remove directories on the device
|
||||
- ``touch <file..>`` to create the files (if they don't already exist)
|
||||
|
||||
- edit a file on the device:
|
||||
The ``cp`` command uses a convention where a leading ``:`` represents a remote
|
||||
path. Without a leading ``:`` means a local path. This is based on the
|
||||
convention used by the `Secure Copy Protocol (scp) client
|
||||
<https://en.wikipedia.org/wiki/Secure_copy_protocol>`_. All other commands
|
||||
implicitly assume the path is a remote path, but the ``:`` can be optionally
|
||||
used for clarity.
|
||||
|
||||
So for example, ``mpremote fs cp main.py :main.py`` copies ``main.py`` from
|
||||
the current local directory to the remote filesystem, whereas
|
||||
``mpremote fs cp :main.py main.py`` copies ``main.py`` from the device back
|
||||
to the current directory.
|
||||
|
||||
All of the filesystem sub-commands take multiple path arguments, so if there
|
||||
is another command in the sequence, you must use ``+`` to terminate the
|
||||
arguments, e.g.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ mpremote fs cp main.py :main.py + repl
|
||||
|
||||
This will copy the file to the device then enter the REPL. The ``+`` prevents
|
||||
``"repl"`` being interpreted as a path.
|
||||
|
||||
**Note:** For convenience, all of the filesystem sub-commands are also
|
||||
:ref:`aliased as regular commands <mpremote_shortcuts>`, i.e. you can write
|
||||
``mpremote cp ...`` instead of ``mpremote fs cp ...``.
|
||||
|
||||
.. _mpremote_command_df:
|
||||
|
||||
- **df** -- query device free/used space
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ mpremote df
|
||||
|
||||
The ``df`` command will print size/used/free statistics for the device
|
||||
filesystem, similar to the Unix ``df`` command.
|
||||
|
||||
.. _mpremote_command_edit:
|
||||
|
||||
- **edit** -- edit a file on the device:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
|
@ -146,7 +282,9 @@ The full list of supported commands are:
|
|||
variable ``$EDITOR``). If the editor exits successfully, the updated file will
|
||||
be copied back to the device.
|
||||
|
||||
- install packages from :term:`micropython-lib` (or GitHub) using the ``mip`` tool:
|
||||
.. _mpremote_command_mip:
|
||||
|
||||
- **mip** -- install packages from :term:`micropython-lib` (or GitHub) using the ``mip`` tool:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
|
@ -154,16 +292,34 @@ The full list of supported commands are:
|
|||
|
||||
See :ref:`packages` for more information.
|
||||
|
||||
- mount the local directory on the remote device:
|
||||
.. _mpremote_command_mount:
|
||||
|
||||
- **mount** -- mount the local directory on the remote device:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ mpremote mount [options] <local-dir>
|
||||
|
||||
During usage, Ctrl-D will soft-reboot and normally reconnect the mount automatically.
|
||||
If the unit has a main.py running at startup however the remount cannot occur.
|
||||
In this case a raw mode soft reboot can be used: Ctrl-A Ctrl-D to reboot,
|
||||
then Ctrl-B to get back to normal repl at which point the mount will be ready.
|
||||
This allows the remote device to see the local host directory as if it were
|
||||
its own filesystem. This is useful for development, and avoids the need to
|
||||
copy files to the device while you are working on them.
|
||||
|
||||
The device installs a filesystem driver, which is then mounted in the
|
||||
:ref:`device VFS <filesystem>` as ``/remote``, which uses the serial
|
||||
connection to ``mpremote`` as a side-channel to access files. The device
|
||||
will have its current working directory (via ``os.chdir``) set to
|
||||
``/remote`` so that imports and file access will occur there instead of the
|
||||
default filesystem path while the mount is active.
|
||||
|
||||
**Note:** If the ``mount`` command is not followed by another action in the
|
||||
sequence, a ``repl`` command will be implicitly added to the end of the
|
||||
sequence.
|
||||
|
||||
During usage, Ctrl-D will trigger a soft-reset as normal, but the mount will
|
||||
automatically be re-connected. If the unit has a main.py running at startup
|
||||
however the remount cannot occur. In this case a raw mode soft reboot can be
|
||||
used: Ctrl-A Ctrl-D to reboot, then Ctrl-B to get back to normal repl at
|
||||
which point the mount will be ready.
|
||||
|
||||
Options are:
|
||||
|
||||
|
@ -172,22 +328,74 @@ The full list of supported commands are:
|
|||
local directory that is mounted. This option disables this check for symbolic
|
||||
links, allowing the device to follow symbolic links outside of the local directory.
|
||||
|
||||
- unmount the local directory from the remote device:
|
||||
.. _mpremote_command_unmount:
|
||||
|
||||
- **unmount** -- unmount the local directory from the remote device:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ mpremote umount
|
||||
|
||||
Multiple commands can be specified and they will be run sequentially.
|
||||
This happens automatically when ``mpremote`` terminates, but it can be used
|
||||
in a sequence to unmount an earlier mount before subsequent command are run.
|
||||
|
||||
.. _mpremote_command_rtc:
|
||||
|
||||
- **rtc** -- set/get the device clock (RTC):
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ mpremote rtc
|
||||
|
||||
This will query the device RTC for the current time and print it as a datetime
|
||||
tuple.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ mpremote rtc --set
|
||||
|
||||
This will set the device RTC to the host PC's current time.
|
||||
|
||||
.. _mpremote_command_sleep:
|
||||
|
||||
- **sleep** -- sleep (delay) before executing the next command
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ mpremote sleep 0.5
|
||||
|
||||
This will pause execution of the command sequence for the specified duration
|
||||
in seconds, e.g. to wait for the device to do something.
|
||||
|
||||
.. _mpremote_command_reset:
|
||||
|
||||
- **reset** -- hard reset the device
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ mpremote reset
|
||||
|
||||
**Note:** hard reset is equivalent to :func:`machine.reset`.
|
||||
|
||||
.. _mpremote_command_bootloader:
|
||||
|
||||
- **bootloader** enter the bootloader
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ mpremote bootloader
|
||||
|
||||
This will make the device enter its bootloader. The bootloader is port- and
|
||||
board-specific (e.g. DFU on stm32, UF2 on rp2040/Pico).
|
||||
|
||||
.. _mpremote_reset:
|
||||
|
||||
Auto connection and soft-reset
|
||||
------------------------------
|
||||
|
||||
Connection and disconnection will be done automatically at the start and end of
|
||||
the execution of the tool, if such commands are not explicitly given. Automatic
|
||||
connection will search for the first available USB serial device. If no action
|
||||
is specified then the REPL will be entered.
|
||||
connection will search for the first available USB serial device.
|
||||
|
||||
Once connected to a device, ``mpremote`` will automatically soft-reset the
|
||||
device if needed. This clears the Python heap and restarts the interpreter,
|
||||
|
@ -197,38 +405,60 @@ executed: ``mount``, ``eval``, ``exec``, ``run``, ``fs``. After doing a
|
|||
soft-reset for the first time, it will not be done again automatically, until a
|
||||
``disconnect`` command is issued.
|
||||
|
||||
Auto soft-reset behaviour can be controlled by the ``resume`` command. And the
|
||||
``soft-reset`` command can be used to perform an explicit soft reset.
|
||||
Auto-soft-reset behaviour can be controlled by the ``resume`` command. This
|
||||
might be useful to use the ``eval`` command to inspect the state of of the
|
||||
device. The ``soft-reset`` command can be used to perform an explicit soft
|
||||
reset in the middle of a sequence of commands.
|
||||
|
||||
.. _mpremote_shortcuts:
|
||||
|
||||
Shortcuts
|
||||
---------
|
||||
|
||||
Shortcuts can be defined using the macro system. Built-in shortcuts are::
|
||||
Shortcuts can be defined using the macro system. Built-in shortcuts are:
|
||||
|
||||
- ``devs``: list available devices (shortcut for ``connect list``)
|
||||
- ``devs``: Alias for ``connect list``
|
||||
|
||||
- ``a0``, ``a1``, ``a2``, ``a3``: connect to /dev/ttyACM?
|
||||
- ``a0``, ``a1``, ``a2``, ``a3``: Aliases for ``connect /dev/ttyACMn``
|
||||
|
||||
- ``u0``, ``u1``, ``u2``, ``u3``: connect to /dev/ttyUSB?
|
||||
- ``u0``, ``u1``, ``u2``, ``u3``: Aliases for ``connect /dev/ttyUSBn``
|
||||
|
||||
- ``c0``, ``c1``, ``c2``, ``c3``: connect to COM?
|
||||
- ``c0``, ``c1``, ``c2``, ``c3``: Aliases for ``connect COMn``
|
||||
|
||||
- ``cat``, ``ls``, ``cp``, ``rm``, ``mkdir``, ``rmdir``, ``touch``, ``df``:
|
||||
filesystem commands
|
||||
- ``cat``, ``edit``, ``ls``, ``cp``, ``rm``, ``mkdir``, ``rmdir``, ``touch``: Aliases for ``fs <sub-command>``
|
||||
|
||||
- ``reset``: reset the device
|
||||
Additional shortcuts can be defined by in user-configuration files, which is
|
||||
located at ``.config/mpremote/config.py``. This file should define a
|
||||
dictionary named ``commands``. The keys of this dictionary are the shortcuts
|
||||
and the values are either a string or a list-of-strings:
|
||||
|
||||
- ``bootloader``: make the device enter its bootloader
|
||||
.. code-block:: python3
|
||||
|
||||
Any user configuration, including user-defined shortcuts, can be placed in the file
|
||||
``.config/mpremote/config.py``. For example:
|
||||
"c33": "connect id:334D335C3138",
|
||||
|
||||
The command ``c33`` is replaced by ``connect id:334D335C3138``.
|
||||
|
||||
.. code-block:: python3
|
||||
|
||||
"test": ["mount", ".", "exec", "import test"],
|
||||
|
||||
The command ``test`` is replaced by ``mount . exec "import test"``.
|
||||
|
||||
Shortcuts can also accept arguments. For example:
|
||||
|
||||
.. code-block:: python3
|
||||
|
||||
"multiply x=4 y=7": "eval x*y",
|
||||
|
||||
Running ``mpremote times 3 7`` will set ``x`` and ``y`` as variables on the device, then evaluate the expression ``x*y``.
|
||||
|
||||
An example ``config.py`` might look like:
|
||||
|
||||
.. code-block:: python3
|
||||
|
||||
commands = {
|
||||
"c33": "connect id:334D335C3138",
|
||||
"bl": "bootloader",
|
||||
"c33": "connect id:334D335C3138", # Connect to a specific device by ID.
|
||||
"bl": "bootloader", # Shorter alias for bootloader.
|
||||
"double x=4": "eval x*2", # x is an argument, with default 4
|
||||
"wl_scan": ["exec", """
|
||||
import network
|
||||
|
@ -236,10 +466,16 @@ Any user configuration, including user-defined shortcuts, can be placed in the f
|
|||
wl.active(1)
|
||||
for ap in wl.scan():
|
||||
print(ap)
|
||||
""",],
|
||||
"test": ["mount", ".", "exec", "import test"],
|
||||
""",], # Print out nearby WiFi networks.
|
||||
"wl_ifconfig": [
|
||||
"exec",
|
||||
"import network; sta_if = network.WLAN(network.STA_IF); print(sta_if.ifconfig())",
|
||||
""",], # Print ip address of station interface.
|
||||
"test": ["mount", ".", "exec", "import test"], # Mount current directory and run test.py.
|
||||
"demo": ["run", "path/to/demo.py"], # Execute demo.py on the device.
|
||||
}
|
||||
|
||||
.. _mpremote_examples:
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
@ -248,38 +484,205 @@ Examples
|
|||
|
||||
mpremote
|
||||
|
||||
Connect to the first available device and implicitly run the ``repl`` command.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote a1
|
||||
|
||||
mpremote connect /dev/ttyUSB0 repl
|
||||
Connect to the device at ``/dev/ttyACM1`` (Linux) and implicitly run the
|
||||
``repl`` command. See :ref:`shortcuts <mpremote_shortcuts>` above.
|
||||
|
||||
mpremote ls
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote c1
|
||||
|
||||
Connect to the device at ``COM1`` (Windows) and implicitly run the ``repl``
|
||||
command. See :ref:`shortcuts <mpremote_shortcuts>` above.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote connect /dev/ttyUSB0
|
||||
|
||||
Explicitly specify which device to connect to, and as above, implicitly run the
|
||||
``repl`` command.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote a1 ls
|
||||
|
||||
Connect to the device at ``/dev/ttyACM0`` and then run the ``ls`` command.
|
||||
|
||||
It is equivalent to ``mpremote connect /dev/ttyACM1 fs ls``.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote exec "import micropython; micropython.mem_info()"
|
||||
|
||||
Run the specified Python command and display any output. This is equivalent to
|
||||
typing the command at the REPL prompt.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote eval 1/2 eval 3/4
|
||||
|
||||
Evaluate each expression in turn and print the results.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote a0 eval 1/2 a1 eval 3/4
|
||||
|
||||
Evaluate ``1/2`` on the device at ``/dev/ttyACM0``, then ``3/4`` on the
|
||||
device at ``/dev/ttyACM1``, printing each result.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote resume exec "print_state_info()" soft-reset
|
||||
|
||||
Connect to the device without triggering a soft reset and execute the
|
||||
``print_state_info()`` function (e.g. to find out information about the current
|
||||
program state), then trigger a soft reset.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote reset sleep 0.5 bootloader
|
||||
|
||||
Hard-reset the device, wait 500ms for it to become available, then enter the
|
||||
bootloader.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote cp utils/driver.py :utils/driver.py + run test.py
|
||||
|
||||
Update the copy of utils/driver.py on the device, then execute the local
|
||||
``test.py`` script on the device. ``test.py`` is never copied to the device
|
||||
filesystem, rather it is run from RAM.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote cp utils/driver.py :utils/driver.py + exec "import app"
|
||||
|
||||
Update the copy of utils/driver.py on the device, then execute app.py on the
|
||||
device.
|
||||
|
||||
This is a common development workflow to update a single file and then re-start
|
||||
your program. In this scenario, your ``main.py`` on the device would also do
|
||||
``import app``.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote cp utils/driver.py :utils/driver.py + soft-reset repl
|
||||
|
||||
Update the copy of utils/driver.py on the device, then trigger a soft-reset to
|
||||
restart your program, and then monitor the output via the ``repl`` command.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote cp -r utils/ :utils/ + soft-reset repl
|
||||
|
||||
Same as above, but update the entire utils directory first.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote mount .
|
||||
|
||||
mpremote mount . exec "import local_script"
|
||||
Mount the current local directory at ``/remote`` on the device and starts a
|
||||
``repl`` session which will use ``/remote`` as the working directory.
|
||||
|
||||
mpremote ls
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote mount . exec "import demo"
|
||||
|
||||
After mounting the current local directory, executes ``demo.py`` from the
|
||||
mounted directory.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote mount app run test.py
|
||||
|
||||
After mounting the local directory ``app`` as ``/remote`` on the device,
|
||||
executes the local ``test.py`` from the host's current directory without
|
||||
copying it to the filesystem.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote mount . repl --inject-code "import demo"
|
||||
|
||||
After mounting the current local directory, executes ``demo.py`` from the
|
||||
mounted directory each time ``Ctrl-J`` is pressed.
|
||||
|
||||
You will first need to press ``Ctrl-D`` to reset the interpreter state
|
||||
(which will preserve the mount) before pressing ``Ctrl-J`` to re-import
|
||||
``demo.py``.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote mount app repl --inject-file demo.py
|
||||
|
||||
Same as above, but executes the contents of the local file demo.py at the REPL
|
||||
every time ``Ctrl-K`` is pressed. As above, use Ctrl-D to reset the interpreter
|
||||
state first.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote cat boot.py
|
||||
|
||||
Displays the contents of ``boot.py`` on the device.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote edit utils/driver.py
|
||||
|
||||
Edit ``utils/driver.py`` on the device using your local ``$EDITOR``.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote cp :main.py .
|
||||
|
||||
Copy ``main.py`` from the device to the local directory.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote cp main.py :
|
||||
|
||||
Copy ``main.py`` from the local directory to the device.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote cp :a.py :b.py
|
||||
|
||||
Copy ``a.py`` on the device to ``b.py`` on the device.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote cp -r dir/ :
|
||||
|
||||
Recursively copy the local directory ``dir`` to the remote device.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote cp a.py b.py : + repl
|
||||
|
||||
Copy ``a.py`` and ``b.py`` from the local directory to the device, then run the
|
||||
``repl`` command.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote mip install aioble
|
||||
|
||||
Install the ``aioble`` package from :term:`micropython-lib` to the device.
|
||||
See :ref:`packages`.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote mip install github:org/repo@branch
|
||||
|
||||
Install the package from the specified branch at org/repo on GitHub to the
|
||||
device. See :ref:`packages`.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mpremote mip install --target /flash/third-party functools
|
||||
|
||||
Install the ``functools`` package from :term:`micropython-lib` to the
|
||||
``/flash/third-party`` directory on the device. See :ref:`packages`.
|
||||
|
|
|
@ -86,7 +86,8 @@ and .mpy version.
|
|||
=================== ============
|
||||
MicroPython release .mpy version
|
||||
=================== ============
|
||||
v1.19 and up 6
|
||||
v1.20 and up 6.1
|
||||
v1.19.x 6
|
||||
v1.12 - v1.18 5
|
||||
v1.11 4
|
||||
v1.9.3 - v1.10 3
|
||||
|
@ -100,6 +101,7 @@ MicroPython repository at which the .mpy version was changed.
|
|||
=================== ========================================
|
||||
.mpy version change Git commit
|
||||
=================== ========================================
|
||||
6 to 6.1 d94141e1473aebae0d3c63aeaa8397651ad6fa01
|
||||
5 to 6 f2040bfc7ee033e48acef9f289790f3b4e6b74e5
|
||||
4 to 5 5716c5cf65e9b2cb46c2906f40302401bdd27517
|
||||
3 to 4 9a5f92ea72754c01cc03e5efcdfe94021120531e
|
||||
|
@ -146,8 +148,8 @@ The .mpy header is:
|
|||
size field
|
||||
====== ================================
|
||||
byte value 0x4d (ASCII 'M')
|
||||
byte .mpy version number
|
||||
byte feature flags
|
||||
byte .mpy major version number
|
||||
byte native arch and minor version number (was feature flags in older versions)
|
||||
byte number of bits in a small int
|
||||
====== ================================
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ can be downloaded by specifying the path to their ``package.json``.
|
|||
If no json file is specified, then "package.json" is implicitly added::
|
||||
|
||||
>>> mip.install("http://example.com/x/")
|
||||
>>> mip.install("github:org/repo")
|
||||
>>> mip.install("github:org/repo") # Uses default branch of that repo
|
||||
>>> mip.install("github:org/repo", version="branch-or-tag")
|
||||
|
||||
|
||||
|
@ -108,9 +108,9 @@ https://github.com/micropython/micropython-lib for more information.
|
|||
|
||||
To write a "self-hosted" package that can be downloaded by ``mip`` or
|
||||
``mpremote``, you need a static webserver (or GitHub) to host either a
|
||||
single .py file, or a package.json file alongside your .py files.
|
||||
single .py file, or a ``package.json`` file alongside your .py files.
|
||||
|
||||
A typical package.json for an example ``mlx90640`` library looks like::
|
||||
A typical ``package.json`` for an example ``mlx90640`` library looks like::
|
||||
|
||||
{
|
||||
"urls": [
|
||||
|
@ -119,7 +119,8 @@ A typical package.json for an example ``mlx90640`` library looks like::
|
|||
],
|
||||
"deps": [
|
||||
["collections-defaultdict", "latest"],
|
||||
["os-path", "latest"]
|
||||
["os-path", "latest"],
|
||||
["github:org/micropython-additions", "main"]
|
||||
],
|
||||
"version": "0.2"
|
||||
}
|
||||
|
@ -127,7 +128,9 @@ A typical package.json for an example ``mlx90640`` library looks like::
|
|||
This includes two files, hosted at a GitHub repo named
|
||||
``org/micropython-mlx90640``, which install into the ``mlx90640`` directory on
|
||||
the device. It depends on ``collections-defaultdict`` and ``os-path`` which will
|
||||
be installed automatically.
|
||||
be installed automatically from the :term:`micropython-lib`. The third
|
||||
dependency installs the content as defined by the ``package.json`` file of the
|
||||
``main`` branch of the GitHub repo ``org/micropython-additions``.
|
||||
|
||||
Freezing packages
|
||||
-----------------
|
||||
|
|
|
@ -247,7 +247,6 @@ Python will interpret the result as 2**32 -1 rather than as -1.
|
|||
|
||||
In addition to the restrictions imposed by the native emitter the following constraints apply:
|
||||
|
||||
* Functions may have up to four arguments.
|
||||
* Default argument values are not permitted.
|
||||
* Floating point may be used but is not optimised.
|
||||
|
||||
|
|
|
@ -146,19 +146,30 @@ See :ref:`machine.UART <machine.UART>`. ::
|
|||
PWM (pulse width modulation)
|
||||
----------------------------
|
||||
|
||||
There are 8 independent channels each of which have 2 outputs making it 16
|
||||
PWM channels in total which can be clocked from 7Hz to 125Mhz.
|
||||
There are 8 independent PWM generators called slices, which each have two
|
||||
channels making it 16 PWM channels in total which can be clocked from
|
||||
8Hz to 62.5Mhz at a machine.freq() of 125Mhz. The two channels of a
|
||||
slice run at the same frequency, but can have a different duty rate.
|
||||
The two channels are usually assigned to adjacent GPIO pin pairs with
|
||||
even/odd numbers. So GPIO0 and GPIO1 are at slice 0, GPIO2 and GPIO3
|
||||
are at slice 1, and so on. A certain channel can be assigned to
|
||||
different GPIO pins (see Pinout). For instance slice 0, channel A can be assigned
|
||||
to both GPIO0 and GPIO16.
|
||||
|
||||
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
|
||||
# create PWM object from a pin and set the frequency of slice 0
|
||||
# and duty cycle for channel A
|
||||
pwm0 = PWM(Pin(0), freq=2000, duty_u16=32768)
|
||||
pwm0.freq() # get the current frequency of slice 0
|
||||
pwm0.freq(1000) # set/change the frequency of slice 0
|
||||
pwm0.duty_u16() # get the current duty cycle of channel A, range 0-65535
|
||||
pwm0.duty_u16(200) # set the duty cycle of channel A, range 0-65535
|
||||
pwm0.duty_u16(0) # stop the output at channel A
|
||||
print(pwm0) # show the properties of the PWM object.
|
||||
pwm0.deinit() # turn off PWM of slice 0, stopping channels A and B
|
||||
|
||||
ADC (analog to digital conversion)
|
||||
----------------------------------
|
||||
|
|
|
@ -83,7 +83,9 @@ Examples for Adafruit ItsyBitsy M0 Express:
|
|||
- uart 4 at pins D2/D5
|
||||
- uart 5 at pins SCL/SDA
|
||||
|
||||
or other combinations.
|
||||
or other combinations. For hardware flow control, tx must be at pad 0, rx at pad 1,
|
||||
rts at pad 2 and cts at pad 3. This applies for instance to
|
||||
UART 3 or UART 1 at the pins D13/D11/D10/D12 for rx/tx/rts/cts.
|
||||
|
||||
SAMD21 I2C assignments
|
||||
``````````````````````
|
||||
|
@ -105,7 +107,7 @@ or other combinations.
|
|||
|
||||
SAMD21 SPI assignments
|
||||
``````````````````````
|
||||
The I2C devices and signals must be chosen according to the following rules:
|
||||
The SPI devices and signals must be chosen according to the following rules:
|
||||
|
||||
- The following pad number pairs are suitable for MOSI/SCK: 0/1, 2/3, 3/1, and 0/3.
|
||||
- The MISO signal must be at a Pin with a different pad number than MOSI or SCK.
|
||||
|
@ -158,14 +160,14 @@ Pin GPIO Pin name IRQ ADC ADC Serial Serial TC PWM PWM
|
|||
22 PA22 D13 6 - - 3/0 5/1 4/0 1/6 0/2
|
||||
34 PB02 DOTSTAR_CLK 2 14 - - 5/0 6/0 2/2 -
|
||||
35 PB03 DOTSTAR_DATA 9 15 - - 5/1 6/1 - -
|
||||
43 PB11 FLASH_CS 12 - - - 4/3 5/1 0/5 1/1
|
||||
11 PA11 FLASH_HOLD 11 11 - 0/3 2/3 1/1 0/3 1/7
|
||||
9 PA09 FLASH_MISO 9 9 3 0/1 2/0 0/1 0/1 1/5
|
||||
8 PA08 FLASH_MOSI - 8 2 0/0 2/1 0/0 0/0 1/4
|
||||
42 PB10 FLASH_SCK 10 - - - 4/2 5/0 0/4 1/0
|
||||
10 PA10 FLASH_WP 10 10 - 0/2 2/2 1/0 0/2 1/6
|
||||
55 PB23 MISO 7 - - 1/3 5/3 7/1 - -
|
||||
0 PA00 MOSI 0 - - - 1/0 2/0 - -
|
||||
43 PB11 QSPI_CS 12 - - - 4/3 5/1 0/5 1/1
|
||||
8 PA08 QSPI_D0 - 8 2 0/0 2/1 0/0 0/0 1/4
|
||||
9 PA09 QSPI_D1 9 9 3 0/1 2/0 0/1 0/1 1/5
|
||||
10 PA10 QSPI_D2 10 10 - 0/2 2/2 1/0 0/2 1/6
|
||||
11 PA11 QSPI_D3 11 11 - 0/3 2/3 1/1 0/3 1/7
|
||||
42 PB10 QSPI_SCK 10 - - - 4/2 5/0 0/4 1/0
|
||||
1 PA01 SCK 1 - - - 1/1 2/1 - -
|
||||
13 PA13 SCL 13 - - 2/1 4/0 2/1 0/7 1/3
|
||||
12 PA12 SDA 12 - - 2/0 4/1 2/0 0/6 1/2
|
||||
|
@ -214,7 +216,9 @@ Examples for Adafruit ItsyBitsy 4 Express:
|
|||
- uart 4 at pins SDA/SCL
|
||||
- uart 5 at pins D12/D13
|
||||
|
||||
or other combinations.
|
||||
or other combinations. For hardware flow control, tx must be at pad 0, rx at pad 1,
|
||||
rts at pad 2 and cts at pad 3. This applies for instance to
|
||||
UART 5 at the pins D12/D13/D10/D11 for rx/tx/rts/cts.
|
||||
|
||||
SAMD51 I2C assignments
|
||||
``````````````````````
|
||||
|
@ -284,15 +288,15 @@ Pin GPIO Pin name IRQ ADC ADC Serial Serial TC PWM PWM
|
|||
21 PA21 D11 5 - - 5/3 3/3 7/1 1/5 0/1
|
||||
22 PA22 D12 6 - - 3/0 5/1 4/0 1/6 0/2
|
||||
23 PA23 D13 7 - - 3/1 5/0 4/1 1/7 0/3
|
||||
43 PB11 FLASH_CS 12 - - - 4/3 5/1 0/5 1/1
|
||||
11 PA11 FLASH_HOLD 11 11 - 0/3 2/3 1/1 0/3 1/7
|
||||
9 PA09 FLASH_MISO 9 9 3 0/1 2/0 0/1 0/1 1/5
|
||||
8 PA08 FLASH_MOSI - 8 2 0/0 2/1 0/0 0/0 1/4
|
||||
42 PB10 FLASH_SCK 10 - - - 4/2 5/0 0/4 1/0
|
||||
10 PA10 FLASH_WP 10 10 - 0/2 2/2 1/0 0/2 1/6
|
||||
54 PB22 MISO 22 - - 1/2 5/2 7/0 - -
|
||||
55 PB23 MOSI 7 - - 1/3 5/3 7/1 - -
|
||||
35 PB03 NEOPIXEL 9 15 - - 5/1 6/1 - -
|
||||
43 PB11 QSPI_CS 12 - - - 4/3 5/1 0/5 1/1
|
||||
8 PA08 QSPI_D0 - 8 2 0/0 2/1 0/0 0/0 1/4
|
||||
9 PA09 QSPI_D1 9 9 3 0/1 2/0 0/1 0/1 1/5
|
||||
10 PA10 QSPI_D2 10 10 - 0/2 2/2 1/0 0/2 1/6
|
||||
11 PA11 QSPI_D3 11 11 - 0/3 2/3 1/1 0/3 1/7
|
||||
42 PB10 QSPI_SCK 10 - - - 4/2 5/0 0/4 1/0
|
||||
17 PA17 SCK 1 - - 1/1 3/0 2/1 1/1 0/5
|
||||
13 PA13 SCL 13 - - 2/1 4/0 2/1 0/7 1/3
|
||||
12 PA12 SDA 12 - - 2/0 4/1 2/0 0/6 1/2
|
||||
|
@ -330,6 +334,75 @@ The default devices at the board are:
|
|||
- SPI 1 at pins PA23/PA22/PA17, labelled MOSI, MISO and SCK
|
||||
- DAC output on pins PA02 and PA05, labelled A0 and A1
|
||||
|
||||
Adafruit Metro M4 Airlift pin assignment table
|
||||
----------------------------------------------
|
||||
|
||||
=== ==== ============ ==== ==== ==== ====== ====== ===== ===== =====
|
||||
Pin GPIO Pin name IRQ ADC ADC Serial Serial TC PWM PWM
|
||||
=== ==== ============ ==== ==== ==== ====== ====== ===== ===== =====
|
||||
2 PA02 A0 2 0 - - - - - -
|
||||
5 PA05 A1 5 5 - - 0/1 0/1 - -
|
||||
6 PA06 A2 6 6 - - 0/2 1/0 - -
|
||||
32 PB00 A3 9 12 - - 5/2 7/0 - -
|
||||
40 PB08 A4 8 2 0 - 4/0 4/0 - -
|
||||
41 PB09 A5 9 3 1 - 4/1 4/1 - -
|
||||
23 PA23 D0 7 - - 3/1 5/0 4/1 1/7 0/3
|
||||
22 PA22 D1 6 - - 3/0 5/1 4/0 1/6 0/2
|
||||
49 PB17 D2 1 - - 5/1 - 6/1 3/1 0/5
|
||||
48 PB16 D3 0 - - 5/0 - 6/0 3/0 0/4
|
||||
45 PB13 D4 13 - - 4/1 - 4/1 3/1 0/1
|
||||
46 PB14 D5 14 - - 4/2 - 5/0 4/0 0/2
|
||||
47 PB15 D6 15 - - 4/3 - 5/1 4/1 0/3
|
||||
44 PB12 D7 12 - - 4/0 - 4/0 3/0 0/0
|
||||
21 PA21 D8 5 - - 5/3 3/3 7/1 1/5 0/1
|
||||
20 PA20 D9 4 - - 5/2 3/2 7/0 1/4 0/0
|
||||
3 PA03 AREF 3 10 - - - - - -
|
||||
18 PA18 D10 2 - - 1/2 3/2 3/0 1/2 0/6
|
||||
19 PA19 D11 3 - - 1/3 3/3 3/1 1/3 0/7
|
||||
16 PA16 D13 0 - - 1/0 3/1 2/0 1/0 0/4
|
||||
36 PB04 ESP_BUSY 4 - 6 - - - - -
|
||||
15 PA15 ESP_CS 15 - - 2/3 4/3 3/1 2/1 1/3
|
||||
33 PB01 ESP_GPIO0 1 13 - - 5/3 7/1 - -
|
||||
37 PB05 ESP_RESET 5 - 7 - - - - -
|
||||
55 PB23 ESP_RTS 7 - - 1/3 5/3 7/1 - -
|
||||
7 PA07 ESP_RX 7 7 - - 0/3 1/1 - -
|
||||
4 PA04 ESP_TX 4 4 - - 0/0 0/0 - -
|
||||
43 PB11 FLASH_CS 12 - - - 4/3 5/1 0/5 1/1
|
||||
11 PA11 FLASH_HOLD 11 11 - 0/3 2/3 1/1 0/3 1/7
|
||||
9 PA09 FLASH_MISO 9 9 3 0/1 2/0 0/1 0/1 1/5
|
||||
8 PA08 FLASH_MOSI - 8 2 0/0 2/1 0/0 0/0 1/4
|
||||
42 PB10 FLASH_SCK 10 - - - 4/2 5/0 0/4 1/0
|
||||
10 PA10 FLASH_WP 10 10 - 0/2 2/2 1/0 0/2 1/6
|
||||
14 PA14 MISO 14 - - 2/2 4/2 3/0 2/0 1/2
|
||||
12 PA12 MOSI 12 - - 2/0 4/1 2/0 0/6 1/2
|
||||
54 PB22 NEOPIXEL 22 - - 1/2 5/2 7/0 - -
|
||||
38 PB06 RXLED 6 - 8 - - - - -
|
||||
13 PA13 SCK 13 - - 2/1 4/0 2/1 0/7 1/3
|
||||
35 PB03 SCL 9 15 - - 5/1 6/1 - -
|
||||
34 PB02 SDA 2 14 - - 5/0 6/0 2/2 -
|
||||
30 PA30 SWCLK 14 - - 7/2 1/2 6/0 2/0 -
|
||||
31 PA31 SWDIO 15 - - 7/3 1/3 6/1 2/1 -
|
||||
62 PB30 SWO 14 - - 7/0 5/1 0/0 4/0 0/6
|
||||
39 PB07 TXLED 7 - 9 - - - - -
|
||||
24 PA24 USB_DM 8 - - 3/2 5/2 5/0 2/2 -
|
||||
25 PA25 USB_DP 9 - - 3/3 5/3 5/1 - -
|
||||
17 PA17 USB_HOSTEN 1 - - 1/1 3/0 2/1 1/1 0/5
|
||||
0 PA00 - 0 - - - 1/0 2/0 - -
|
||||
1 PA01 - 1 - - - 1/1 2/1 - -
|
||||
27 PA27 - 11 - - - - - - -
|
||||
63 PB31 - 15 - - 7/1 5/0 0/1 4/1 0/7
|
||||
=== ==== ============ ==== ==== ==== ====== ====== ===== ===== =====
|
||||
|
||||
For the definition of the table columns see the explanation at the table
|
||||
for Adafruit ItsyBitsy M4 Express :ref:`samd51_pinout_table`.
|
||||
|
||||
The default devices at the board are:
|
||||
|
||||
- UART 3 at pins PA23/PA22, labelled D0/D1 resp. RX/TX
|
||||
- I2C 5 at pins PB02/PB03, labelled SDA/SCL
|
||||
- SPI 4 at pins PA12/PA14/PA13, labelled MOSI, MISO and SCK
|
||||
- DAC output on pins PA02 and PA05, labelled A0 and A1
|
||||
|
||||
SEEED XIAO pin assignment table
|
||||
-------------------------------
|
||||
|
||||
|
@ -577,6 +650,12 @@ Pin GPIO Pin name IRQ ADC ADC Serial Serial TC PWM PWM
|
|||
15 PA15 LED 15 - - 2/3 4/3 3/1 2/1 1/3
|
||||
55 PB23 MISO 7 - - 1/3 5/3 7/1 - -
|
||||
54 PB22 MOSI 22 - - 1/2 5/2 7/0 - -
|
||||
43 PB11 QSPI_CS 12 - - - 4/3 5/1 0/5 1/1
|
||||
8 PA08 QSPI_D0 - 8 2 0/0 2/1 0/0 0/0 1/4
|
||||
9 PA09 QSPI_D1 9 9 3 0/1 2/0 0/1 0/1 1/5
|
||||
10 PA10 QSPI_D2 10 10 - 0/2 2/2 1/0 0/2 1/6
|
||||
11 PA11 QSPI_D3 11 11 - 0/3 2/3 1/1 0/3 1/7
|
||||
42 PB10 QSPI_SCK 10 - - - 4/2 5/0 0/4 1/0
|
||||
1 PA01 SCK 1 - - - 1/1 2/1 - -
|
||||
13 PA13 SCL 13 - - 2/1 4/0 2/1 0/7 1/3
|
||||
12 PA12 SDA 12 - - 2/0 4/1 2/0 0/6 1/2
|
||||
|
@ -584,17 +663,11 @@ Pin GPIO Pin name IRQ ADC ADC Serial Serial TC PWM PWM
|
|||
31 PA31 SWDIO 15 - - 7/3 1/3 6/1 2/1 -
|
||||
24 PA24 USB_DM 8 - - 3/2 5/2 5/0 2/2 -
|
||||
25 PA25 USB_DP 9 - - 3/3 5/3 5/1 - -
|
||||
8 PA08 - - 8 2 0/0 2/1 0/0 0/0 1/4
|
||||
9 PA09 - 9 9 3 0/1 2/0 0/1 0/1 1/5
|
||||
10 PA10 - 10 10 - 0/2 2/2 1/0 0/2 1/6
|
||||
11 PA11 - 11 11 - 0/3 2/3 1/1 0/3 1/7
|
||||
14 PA14 - 14 - - 2/2 4/2 3/0 2/0 1/2
|
||||
18 PA18 - 2 - - 1/2 3/2 3/0 1/2 0/6
|
||||
22 PA22 - 6 - - 3/0 5/1 4/0 1/6 0/2
|
||||
23 PA23 - 7 - - 3/1 5/0 4/1 1/7 0/3
|
||||
27 PA27 - 11 - - - - - - -
|
||||
42 PB10 - 10 - - - 4/2 5/0 0/4 1/0
|
||||
43 PB11 - 12 - - - 4/3 5/1 0/5 1/1
|
||||
=== ==== ============ ==== ==== ==== ====== ====== ===== ===== =====
|
||||
|
||||
For the definition of the table columns see the explanation at the table for
|
||||
|
@ -661,6 +734,12 @@ Pin GPIO Pin name IRQ ADC ADC Serial Serial TC PWM PWM
|
|||
117 PD21 SD_DET 11 - - 1/3 3/3 - 1/1 -
|
||||
83 PC19 SD_CS 3 - - 6/3 0/3 - 0/3 -
|
||||
82 PC18 SD_MISO 2 - - 6/2 0/2 - 0/2 -
|
||||
43 PB11 QSPI_CS 12 - - - 4/3 5/1 0/5 1/1
|
||||
8 PA08 QSPI_D0 - 8 2 0/0 2/1 0/0 0/0 1/4
|
||||
9 PA09 QSPI_D1 9 9 3 0/1 2/0 0/1 0/1 1/5
|
||||
10 PA10 QSPI_D2 10 10 - 0/2 2/2 1/0 0/2 1/6
|
||||
11 PA11 QSPI_D3 11 11 - 0/3 2/3 1/1 0/3 1/7
|
||||
42 PB10 QSPI_SCK 10 - - - 4/2 5/0 0/4 1/0
|
||||
80 PC16 SD_MOSI 0 - - 6/0 0/1 - 0/0 -
|
||||
81 PC17 SD_SCK 1 - - 6/1 0/0 - 0/1 -
|
||||
30 PA30 SWCLK 14 - - 7/2 1/2 6/0 2/0 -
|
||||
|
@ -677,17 +756,11 @@ Pin GPIO Pin name IRQ ADC ADC Serial Serial TC PWM PWM
|
|||
2 PA02 - 2 0 - - - - - -
|
||||
3 PA03 - 3 10 - - - - - -
|
||||
5 PA05 - 5 5 - - 0/1 0/1 - -
|
||||
8 PA08 - - 8 2 0/0 2/1 0/0 0/0 1/4
|
||||
9 PA09 - 9 9 3 0/1 2/0 0/1 0/1 1/5
|
||||
10 PA10 - 10 10 - 0/2 2/2 1/0 0/2 1/6
|
||||
11 PA11 - 11 11 - 0/3 2/3 1/1 0/3 1/7
|
||||
14 PA14 - 14 - - 2/2 4/2 3/0 2/0 1/2
|
||||
18 PA18 - 2 - - 1/2 3/2 3/0 1/2 0/6
|
||||
19 PA19 - 3 - - 1/3 3/3 3/1 1/3 0/7
|
||||
23 PA23 - 7 - - 3/1 5/0 4/1 1/7 0/3
|
||||
27 PA27 - 11 - - - - - - -
|
||||
42 PB10 - 10 - - - 4/2 5/0 0/4 1/0
|
||||
43 PB11 - 12 - - - 4/3 5/1 0/5 1/1
|
||||
46 PB14 - 14 - - 4/2 - 5/0 4/0 0/2
|
||||
49 PB17 - 1 - - 5/1 - 6/1 3/1 0/5
|
||||
54 PB22 - 22 - - 1/2 5/2 7/0 - -
|
||||
|
|
|
@ -132,7 +132,7 @@ Use the :ref:`machine.Pin <machine.Pin>` class::
|
|||
print(p2.value()) # get value, 0 or 1
|
||||
|
||||
p4 = Pin('D4', Pin.IN, Pin.PULL_UP) # enable internal pull-up resistor
|
||||
p7 = Pin("PA07", Pin.OUT, value=1) # set pin high on creation
|
||||
p7 = Pin('PA07', Pin.OUT, value=1) # set pin high on creation
|
||||
|
||||
Pins can be denoted by a string or a number. The string is either the
|
||||
pin label of the respective board, like "D0" or "SDA", or in the form
|
||||
|
@ -157,7 +157,7 @@ See :ref:`machine.UART <machine.UART>`. ::
|
|||
# Use UART 3 on a ItsyBitsy M4 board
|
||||
from machine import UART
|
||||
|
||||
uart3 = UART(3, tx=Pin(1), rx=Pin(0), baudrate=115200)
|
||||
uart3 = UART(3, tx=Pin('D1'), rx=Pin('D0'), baudrate=115200)
|
||||
uart3.write('hello') # write 5 bytes
|
||||
uart3.read(5) # read up to 5 bytes
|
||||
|
||||
|
@ -178,11 +178,12 @@ It supports all basic methods listed for that class. ::
|
|||
|
||||
from machine import Pin, PWM
|
||||
|
||||
pwm = PWM(Pin(7)) # create PWM object from a pin
|
||||
pwm.freq() # get current frequency
|
||||
pwm.freq(1000) # set frequency
|
||||
pwm.duty_u16() # get current duty cycle, range 0-65535
|
||||
pwm.duty_u16(200) # set duty cycle, range 0-65535
|
||||
# create PWM object from a pin and set the frequency and duty cycle
|
||||
pwm = PWM(Pin('D7'), freq=2000, duty_u16=32768)
|
||||
pwm.freq() # get the current frequency
|
||||
pwm.freq(1000) # set/change the frequency
|
||||
pwm.duty_u16() # get the current duty cycle, range 0-65535
|
||||
pwm.duty_u16(200) # set the duty cycle, range 0-65535
|
||||
pwm.deinit() # turn off PWM on the pin
|
||||
|
||||
pwm # show the PWM objects properties
|
||||
|
@ -191,7 +192,7 @@ It supports all basic methods listed for that class. ::
|
|||
PWM Constructor
|
||||
```````````````
|
||||
|
||||
.. class:: PWM(dest, freq, duty_u16, duty_ns, *, invert, device)
|
||||
.. class:: PWM(dest, *, freq, duty_u16, duty_ns, invert, device)
|
||||
:noindex:
|
||||
|
||||
Construct and return a new PWM object using the following parameters:
|
||||
|
@ -213,9 +214,6 @@ PWM Constructor
|
|||
- *freq* should be an integer which sets the frequency in Hz for the
|
||||
PWM cycle. The valid frequency range is 1 Hz to 24 MHz.
|
||||
- *duty_u16* sets the duty cycle as a ratio ``duty_u16 / 65536``.
|
||||
The duty cycle of a X channel can only be changed, if the A and B channel
|
||||
of the respective submodule is not used. Otherwise the duty_16 value of the
|
||||
X channel is 32768 (50%).
|
||||
- *duty_ns* sets the pulse width in nanoseconds. The limitation for X channels
|
||||
apply as well.
|
||||
- *invert*\=True|False. Setting a bit inverts the respective output.
|
||||
|
@ -245,9 +243,9 @@ Use the :ref:`machine.ADC <machine.ADC>` class::
|
|||
|
||||
from machine import ADC
|
||||
|
||||
adc0 = ADC(Pin("A0")) # create ADC object on ADC pin, average=16
|
||||
adc0 = ADC(Pin('A0')) # create ADC object on ADC pin, average=16
|
||||
adc0.read_u16() # read value, 0-65536 across voltage range 0.0v - 3.3v
|
||||
adc1 = ADC(Pin("A1"), average=1) # create ADC object on ADC pin, average=1
|
||||
adc1 = ADC(Pin('A1'), average=1) # create ADC object on ADC pin, average=1
|
||||
|
||||
The resolution of the ADC is 12 bit with 12 bit accuracy, irrespective of the
|
||||
value returned by read_u16(). If you need a higher resolution or better accuracy, use
|
||||
|
@ -341,7 +339,7 @@ Software SPI (using bit-banging) works on all pins, and is accessed via the
|
|||
# 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(7), mosi=Pin(9), miso=Pin(10))
|
||||
spi = SoftSPI(baudrate=100000, polarity=1, phase=0, sck=Pin('D7'), mosi=Pin('D9'), miso=Pin('D10'))
|
||||
|
||||
spi.init(baudrate=200000) # set the baud rate
|
||||
|
||||
|
@ -388,7 +386,7 @@ accessed via the :ref:`machine.SoftI2C <machine.SoftI2C>` class::
|
|||
|
||||
from machine import Pin, SoftI2C
|
||||
|
||||
i2c = SoftI2C(scl=Pin(10), sda=Pin(11), freq=100000)
|
||||
i2c = SoftI2C(scl=Pin('D10'), sda=Pin('D11'), freq=100000)
|
||||
|
||||
i2c.scan() # scan for devices
|
||||
|
||||
|
@ -424,7 +422,7 @@ 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 = onewire.OneWire(Pin('D12')) # 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
|
||||
|
@ -454,12 +452,12 @@ The DHT driver is implemented in software and works on all pins::
|
|||
import dht
|
||||
import machine
|
||||
|
||||
d = dht.DHT11(machine.Pin(4))
|
||||
d = dht.DHT11(machine.Pin('D4'))
|
||||
d.measure()
|
||||
d.temperature() # eg. 23 (°C)
|
||||
d.humidity() # eg. 41 (% RH)
|
||||
|
||||
d = dht.DHT22(machine.Pin(4))
|
||||
d = dht.DHT22(machine.Pin('D4'))
|
||||
d.measure()
|
||||
d.temperature() # eg. 23.6 (°C)
|
||||
d.humidity() # eg. 41.3 (% RH)
|
||||
|
@ -474,7 +472,7 @@ The APA102 on some Adafruit boards can be controlled using SoftSPI::
|
|||
|
||||
from machine import SoftSPI, Pin
|
||||
# create the SPI object. miso can be any unused pin.
|
||||
spi=SoftSPI(sck=Pin(25), mosi=Pin(26), miso=Pin(14))
|
||||
spi=SoftSPI(sck=Pin('D25'), mosi=Pin('D26'), miso=Pin('D14'))
|
||||
|
||||
# define a little function that writes the data with
|
||||
# preamble and postfix
|
||||
|
@ -499,7 +497,7 @@ with the Neopixel driver from the MicroPython driver library::
|
|||
import machine
|
||||
|
||||
# 1 LED connected to Pin D8 on Adafruit Feather boards
|
||||
p = machine.Pin(8, machine.Pin.OUT)
|
||||
p = machine.Pin('D8', machine.Pin.OUT)
|
||||
n = neopixel.NeoPixel(p, 1)
|
||||
|
||||
# set the led to red.
|
||||
|
|
|
@ -0,0 +1,461 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_DEVICES_H
|
||||
#define MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_DEVICES_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
uint32_t total_size;
|
||||
uint16_t start_up_time_us;
|
||||
|
||||
// Three response bytes to 0x9f JEDEC ID command.
|
||||
uint8_t manufacturer_id;
|
||||
uint8_t memory_type;
|
||||
uint8_t capacity;
|
||||
|
||||
// Max clock speed for all operations and the fastest read mode.
|
||||
uint8_t max_clock_speed_mhz;
|
||||
|
||||
// Bitmask for Quad Enable bit if present. 0x00 otherwise. This is for the highest byte in the
|
||||
// status register.
|
||||
uint8_t quad_enable_bit_mask;
|
||||
|
||||
bool has_sector_protection : 1;
|
||||
|
||||
// Supports the 0x0b fast read command with 8 dummy cycles.
|
||||
bool supports_fast_read : 1;
|
||||
|
||||
// Supports the fast read, quad output command 0x6b with 8 dummy cycles.
|
||||
bool supports_qspi : 1;
|
||||
|
||||
// Supports the quad input page program command 0x32. This is known as 1-1-4 because it only
|
||||
// uses all four lines for data.
|
||||
bool supports_qspi_writes : 1;
|
||||
|
||||
// Requires a separate command 0x31 to write to the second byte of the status register.
|
||||
// Otherwise two byte are written via 0x01.
|
||||
bool write_status_register_split : 1;
|
||||
|
||||
// True when the status register is a single byte. This implies the Quad Enable bit is in the
|
||||
// first byte and the Read Status Register 2 command (0x35) is unsupported.
|
||||
bool single_status_byte : 1;
|
||||
} external_flash_device;
|
||||
|
||||
// Settings for the Adesto Tech AT25DF081A 1MiB SPI flash. Its on the SAMD21
|
||||
// Xplained board.
|
||||
// Datasheet: https://www.adestotech.com/wp-content/uploads/doc8715.pdf
|
||||
#define AT25DF081A { \
|
||||
.total_size = (1 << 20), /* 1 MiB */ \
|
||||
.start_up_time_us = 10000, \
|
||||
.manufacturer_id = 0x1f, \
|
||||
.memory_type = 0x45, \
|
||||
.capacity = 0x01, \
|
||||
.max_clock_speed_mhz = 85, \
|
||||
.quad_enable_bit_mask = 0x00, \
|
||||
.has_sector_protection = true, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = false, \
|
||||
.supports_qspi_writes = false, \
|
||||
.write_status_register_split = false, \
|
||||
.single_status_byte = false, \
|
||||
}
|
||||
|
||||
// Settings for the Gigadevice GD25Q16C 2MiB SPI flash.
|
||||
// Datasheet: http://www.gigadevice.com/datasheet/gd25q16c/
|
||||
#define GD25Q16C { \
|
||||
.total_size = (1 << 21), /* 2 MiB */ \
|
||||
.start_up_time_us = 5000, \
|
||||
.manufacturer_id = 0xc8, \
|
||||
.memory_type = 0x40, \
|
||||
.capacity = 0x15, \
|
||||
.max_clock_speed_mhz = 104, /* if we need 120 then we can turn on high performance mode */ \
|
||||
.quad_enable_bit_mask = 0x02, \
|
||||
.has_sector_protection = false, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = true, \
|
||||
.supports_qspi_writes = true, \
|
||||
.write_status_register_split = false, \
|
||||
.single_status_byte = false, \
|
||||
}
|
||||
|
||||
// Settings for the Gigadevice GD25Q64C 8MiB SPI flash.
|
||||
// Datasheet: http://www.elm-tech.com/en/products/spi-flash-memory/gd25q64/gd25q64.pdf
|
||||
#define GD25Q64C { \
|
||||
.total_size = (1 << 23), /* 8 MiB */ \
|
||||
.start_up_time_us = 5000, \
|
||||
.manufacturer_id = 0xc8, \
|
||||
.memory_type = 0x40, \
|
||||
.capacity = 0x17, \
|
||||
.max_clock_speed_mhz = 104, /* if we need 120 then we can turn on high performance mode */ \
|
||||
.quad_enable_bit_mask = 0x02, \
|
||||
.has_sector_protection = false, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = true, \
|
||||
.supports_qspi_writes = true, \
|
||||
.write_status_register_split = true, \
|
||||
.single_status_byte = false, \
|
||||
}
|
||||
|
||||
// Settings for the Cypress (was Spansion) S25FL064L 8MiB SPI flash.
|
||||
// Datasheet: http://www.cypress.com/file/316661/download
|
||||
#define S25FL064L { \
|
||||
.total_size = (1 << 23), /* 8 MiB */ \
|
||||
.start_up_time_us = 300, \
|
||||
.manufacturer_id = 0x01, \
|
||||
.memory_type = 0x60, \
|
||||
.capacity = 0x17, \
|
||||
.max_clock_speed_mhz = 108, \
|
||||
.quad_enable_bit_mask = 0x02, \
|
||||
.has_sector_protection = false, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = true, \
|
||||
.supports_qspi_writes = true, \
|
||||
.write_status_register_split = false, \
|
||||
.single_status_byte = false, \
|
||||
}
|
||||
|
||||
// Settings for the Cypress (was Spansion) S25FL116K 2MiB SPI flash.
|
||||
// Datasheet: http://www.cypress.com/file/196886/download
|
||||
#define S25FL116K { \
|
||||
.total_size = (1 << 21), /* 2 MiB */ \
|
||||
.start_up_time_us = 10000, \
|
||||
.manufacturer_id = 0x01, \
|
||||
.memory_type = 0x40, \
|
||||
.capacity = 0x15, \
|
||||
.max_clock_speed_mhz = 108, \
|
||||
.quad_enable_bit_mask = 0x02, \
|
||||
.has_sector_protection = false, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = true, \
|
||||
.supports_qspi_writes = false, \
|
||||
.write_status_register_split = false, \
|
||||
.single_status_byte = false, \
|
||||
}
|
||||
|
||||
// Settings for the Cypress (was Spansion) S25FL216K 2MiB SPI flash.
|
||||
// Datasheet: http://www.cypress.com/file/197346/download
|
||||
#define S25FL216K { \
|
||||
.total_size = (1 << 21), /* 2 MiB */ \
|
||||
.start_up_time_us = 10000, \
|
||||
.manufacturer_id = 0x01, \
|
||||
.memory_type = 0x40, \
|
||||
.capacity = 0x15, \
|
||||
.max_clock_speed_mhz = 65, \
|
||||
.quad_enable_bit_mask = 0x02, \
|
||||
.has_sector_protection = false, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = false, \
|
||||
.supports_qspi_writes = false, \
|
||||
.write_status_register_split = false, \
|
||||
.single_status_byte = false, \
|
||||
}
|
||||
|
||||
// Settings for the Winbond W25Q16FW 2MiB SPI flash.
|
||||
// Datasheet: https://www.winbond.com/resource-files/w25q16fw%20revj%2005182017%20sfdp.pdf
|
||||
#define W25Q16FW { \
|
||||
.total_size = (1 << 21), /* 2 MiB */ \
|
||||
.start_up_time_us = 5000, \
|
||||
.manufacturer_id = 0xef, \
|
||||
.memory_type = 0x60, \
|
||||
.capacity = 0x15, \
|
||||
.max_clock_speed_mhz = 133, \
|
||||
.quad_enable_bit_mask = 0x02, \
|
||||
.has_sector_protection = false, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = true, \
|
||||
.supports_qspi_writes = true, \
|
||||
.write_status_register_split = false, \
|
||||
.single_status_byte = false, \
|
||||
}
|
||||
|
||||
// Settings for the Winbond W25Q16JV-IQ 2MiB SPI flash. Note that JV-IM has a different .memory_type (0x70)
|
||||
// Datasheet: https://www.winbond.com/resource-files/w25q16jv%20spi%20revf%2005092017.pdf
|
||||
#define W25Q16JV_IQ { \
|
||||
.total_size = (1 << 21), /* 2 MiB */ \
|
||||
.start_up_time_us = 5000, \
|
||||
.manufacturer_id = 0xef, \
|
||||
.memory_type = 0x40, \
|
||||
.capacity = 0x15, \
|
||||
.max_clock_speed_mhz = 133, \
|
||||
.quad_enable_bit_mask = 0x02, \
|
||||
.has_sector_protection = false, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = true, \
|
||||
.supports_qspi_writes = true, \
|
||||
.write_status_register_split = false, \
|
||||
.single_status_byte = false, \
|
||||
}
|
||||
|
||||
// Settings for the Winbond W25Q16JV-IM 2MiB SPI flash. Note that JV-IQ has a different .memory_type (0x40)
|
||||
// Datasheet: https://www.winbond.com/resource-files/w25q16jv%20spi%20revf%2005092017.pdf
|
||||
#define W25Q16JV_IM { \
|
||||
.total_size = (1 << 21), /* 2 MiB */ \
|
||||
.start_up_time_us = 5000, \
|
||||
.manufacturer_id = 0xef, \
|
||||
.memory_type = 0x70, \
|
||||
.capacity = 0x15, \
|
||||
.max_clock_speed_mhz = 133, \
|
||||
.quad_enable_bit_mask = 0x02, \
|
||||
.has_sector_protection = false, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = true, \
|
||||
.supports_qspi_writes = true, \
|
||||
.write_status_register_split = false, \
|
||||
}
|
||||
|
||||
// Settings for the Winbond W25Q32BV 4MiB SPI flash.
|
||||
// Datasheet: https://www.winbond.com/resource-files/w25q32bv_revi_100413_wo_automotive.pdf
|
||||
#define W25Q32BV { \
|
||||
.total_size = (1 << 22), /* 4 MiB */ \
|
||||
.start_up_time_us = 10000, \
|
||||
.manufacturer_id = 0xef, \
|
||||
.memory_type = 0x60, \
|
||||
.capacity = 0x16, \
|
||||
.max_clock_speed_mhz = 104, \
|
||||
.quad_enable_bit_mask = 0x02, \
|
||||
.has_sector_protection = false, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = true, \
|
||||
.supports_qspi_writes = false, \
|
||||
.write_status_register_split = false, \
|
||||
.single_status_byte = false, \
|
||||
}
|
||||
// Settings for the Winbond W25Q32JV-IM 4MiB SPI flash.
|
||||
// Datasheet: https://www.winbond.com/resource-files/w25q32jv%20revg%2003272018%20plus.pdf
|
||||
#define W25Q32JV_IM { \
|
||||
.total_size = (1 << 22), /* 4 MiB */ \
|
||||
.start_up_time_us = 5000, \
|
||||
.manufacturer_id = 0xef, \
|
||||
.memory_type = 0x70, \
|
||||
.capacity = 0x16, \
|
||||
.max_clock_speed_mhz = 133, \
|
||||
.quad_enable_bit_mask = 0x02, \
|
||||
.has_sector_protection = false, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = true, \
|
||||
.supports_qspi_writes = true, \
|
||||
.write_status_register_split = false, \
|
||||
}
|
||||
|
||||
// Settings for the Winbond W25Q32JV-IM 4MiB SPI flash.
|
||||
// Datasheet: https://www.winbond.com/resource-files/w25q32jv%20revg%2003272018%20plus.pdf
|
||||
#define W25Q32JV_IQ { \
|
||||
.total_size = (1 << 22), /* 4 MiB */ \
|
||||
.start_up_time_us = 5000, \
|
||||
.manufacturer_id = 0xef, \
|
||||
.memory_type = 0x40, \
|
||||
.capacity = 0x16, \
|
||||
.max_clock_speed_mhz = 133, \
|
||||
.quad_enable_bit_mask = 0x02, \
|
||||
.has_sector_protection = false, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = true, \
|
||||
.supports_qspi_writes = true, \
|
||||
.write_status_register_split = false, \
|
||||
}
|
||||
|
||||
// Settings for the Winbond W25Q64JV-IM 8MiB SPI flash. Note that JV-IQ has a different .memory_type (0x40)
|
||||
// Datasheet: http://www.winbond.com/resource-files/w25q64jv%20revj%2003272018%20plus.pdf
|
||||
#define W25Q64JV_IM { \
|
||||
.total_size = (1 << 23), /* 8 MiB */ \
|
||||
.start_up_time_us = 5000, \
|
||||
.manufacturer_id = 0xef, \
|
||||
.memory_type = 0x70, \
|
||||
.capacity = 0x17, \
|
||||
.max_clock_speed_mhz = 133, \
|
||||
.quad_enable_bit_mask = 0x02, \
|
||||
.has_sector_protection = false, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = true, \
|
||||
.supports_qspi_writes = true, \
|
||||
.write_status_register_split = false, \
|
||||
.single_status_byte = false, \
|
||||
}
|
||||
|
||||
// Settings for the Winbond W25Q64JV-IQ 8MiB SPI flash. Note that JV-IM has a different .memory_type (0x70)
|
||||
// Datasheet: http://www.winbond.com/resource-files/w25q64jv%20revj%2003272018%20plus.pdf
|
||||
#define W25Q64JV_IQ { \
|
||||
.total_size = (1 << 23), /* 8 MiB */ \
|
||||
.start_up_time_us = 5000, \
|
||||
.manufacturer_id = 0xef, \
|
||||
.memory_type = 0x40, \
|
||||
.capacity = 0x17, \
|
||||
.max_clock_speed_mhz = 133, \
|
||||
.quad_enable_bit_mask = 0x02, \
|
||||
.has_sector_protection = false, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = true, \
|
||||
.supports_qspi_writes = true, \
|
||||
.write_status_register_split = false, \
|
||||
.single_status_byte = false, \
|
||||
}
|
||||
|
||||
// Settings for the Winbond W25Q80DL 1MiB SPI flash.
|
||||
// Datasheet: https://www.winbond.com/resource-files/w25q80dv%20dl_revh_10022015.pdf
|
||||
#define W25Q80DL { \
|
||||
.total_size = (1 << 20), /* 1 MiB */ \
|
||||
.start_up_time_us = 5000, \
|
||||
.manufacturer_id = 0xef, \
|
||||
.memory_type = 0x60, \
|
||||
.capacity = 0x14, \
|
||||
.max_clock_speed_mhz = 104, \
|
||||
.quad_enable_bit_mask = 0x02, \
|
||||
.has_sector_protection = false, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = true, \
|
||||
.supports_qspi_writes = false, \
|
||||
.write_status_register_split = false, \
|
||||
.single_status_byte = false, \
|
||||
}
|
||||
|
||||
|
||||
// Settings for the Winbond W25Q128JV-SQ 16MiB SPI flash. Note that JV-IM has a different .memory_type (0x70)
|
||||
// Datasheet: https://www.winbond.com/resource-files/w25q128jv%20revf%2003272018%20plus.pdf
|
||||
#define W25Q128JV_SQ { \
|
||||
.total_size = (1 << 24), /* 16 MiB */ \
|
||||
.start_up_time_us = 5000, \
|
||||
.manufacturer_id = 0xef, \
|
||||
.memory_type = 0x40, \
|
||||
.capacity = 0x18, \
|
||||
.max_clock_speed_mhz = 133, \
|
||||
.quad_enable_bit_mask = 0x02, \
|
||||
.has_sector_protection = false, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = true, \
|
||||
.supports_qspi_writes = true, \
|
||||
.write_status_register_split = false, \
|
||||
.single_status_byte = false, \
|
||||
}
|
||||
|
||||
// Settings for the Macronix MX25L1606 2MiB SPI flash.
|
||||
// Datasheet:
|
||||
#define MX25L1606 { \
|
||||
.total_size = (1 << 21), /* 2 MiB */ \
|
||||
.start_up_time_us = 5000, \
|
||||
.manufacturer_id = 0xc2, \
|
||||
.memory_type = 0x20, \
|
||||
.capacity = 0x15, \
|
||||
.max_clock_speed_mhz = 8, \
|
||||
.quad_enable_bit_mask = 0x40, \
|
||||
.has_sector_protection = false, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = true, \
|
||||
.supports_qspi_writes = true, \
|
||||
.write_status_register_split = false, \
|
||||
.single_status_byte = true, \
|
||||
}
|
||||
|
||||
// Settings for the Macronix MX25L3233F 4MiB SPI flash.
|
||||
// Datasheet: http://www.macronix.com/Lists/Datasheet/Attachments/7426/MX25L3233F,%203V,%2032Mb,%20v1.6.pdf
|
||||
#define MX25L3233F { \
|
||||
.total_size = (1 << 22), /* 4 MiB */ \
|
||||
.start_up_time_us = 5000, \
|
||||
.manufacturer_id = 0xc2, \
|
||||
.memory_type = 0x20, \
|
||||
.capacity = 0x16, \
|
||||
.max_clock_speed_mhz = 133, \
|
||||
.quad_enable_bit_mask = 0x40, \
|
||||
.has_sector_protection = false, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = true, \
|
||||
.supports_qspi_writes = true, \
|
||||
.write_status_register_split = false, \
|
||||
.single_status_byte = true, \
|
||||
}
|
||||
|
||||
// Settings for the Macronix MX25R6435F 8MiB SPI flash.
|
||||
// Datasheet: http://www.macronix.com/Lists/Datasheet/Attachments/7428/MX25R6435F,%20Wide%20Range,%2064Mb,%20v1.4.pdf
|
||||
// By default its in lower power mode which can only do 8mhz. In high power mode it can do 80mhz.
|
||||
#define MX25R6435F { \
|
||||
.total_size = (1 << 23), /* 8 MiB */ \
|
||||
.start_up_time_us = 5000, \
|
||||
.manufacturer_id = 0xc2, \
|
||||
.memory_type = 0x28, \
|
||||
.capacity = 0x17, \
|
||||
.max_clock_speed_mhz = 8, \
|
||||
.quad_enable_bit_mask = 0x40, \
|
||||
.has_sector_protection = false, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = true, \
|
||||
.supports_qspi_writes = true, \
|
||||
.write_status_register_split = false, \
|
||||
.single_status_byte = true, \
|
||||
}
|
||||
|
||||
// Settings for the Winbond W25Q128JV-PM 16MiB SPI flash. Note that JV-IM has a different .memory_type (0x70)
|
||||
// Datasheet: https://www.winbond.com/resource-files/w25q128jv%20revf%2003272018%20plus.pdf
|
||||
#define W25Q128JV_PM { \
|
||||
.total_size = (1 << 24), /* 16 MiB */ \
|
||||
.start_up_time_us = 5000, \
|
||||
.manufacturer_id = 0xef, \
|
||||
.memory_type = 0x70, \
|
||||
.capacity = 0x18, \
|
||||
.max_clock_speed_mhz = 133, \
|
||||
.quad_enable_bit_mask = 0x02, \
|
||||
.has_sector_protection = false, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = true, \
|
||||
.supports_qspi_writes = true, \
|
||||
.write_status_register_split = false, \
|
||||
}
|
||||
|
||||
// Settings for the Winbond W25Q32FV 4MiB SPI flash.
|
||||
// Datasheet:http://www.winbond.com/resource-files/w25q32fv%20revj%2006032016.pdf?__locale=en
|
||||
#define W25Q32FV { \
|
||||
.total_size = (1 << 22), /* 4 MiB */ \
|
||||
.start_up_time_us = 5000, \
|
||||
.manufacturer_id = 0xef, \
|
||||
.memory_type = 0x40, \
|
||||
.capacity = 0x16, \
|
||||
.max_clock_speed_mhz = 104, \
|
||||
.quad_enable_bit_mask = 0x00, \
|
||||
.has_sector_protection = false, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = false, \
|
||||
.supports_qspi_writes = false, \
|
||||
.write_status_register_split = false, \
|
||||
.single_status_byte = false, \
|
||||
}
|
||||
|
||||
// Settings for a GENERIC device with the most common setting
|
||||
#define GENERIC { \
|
||||
.total_size = (1 << 21), /* 2 MiB */ \
|
||||
.start_up_time_us = 5000, \
|
||||
.manufacturer_id = 0x00, \
|
||||
.memory_type = 0x40, \
|
||||
.capacity = 0x15, \
|
||||
.max_clock_speed_mhz = 48, \
|
||||
.quad_enable_bit_mask = 0x02, \
|
||||
.has_sector_protection = false, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = true, \
|
||||
.supports_qspi_writes = true, \
|
||||
.write_status_register_split = false, \
|
||||
.single_status_byte = false, \
|
||||
}
|
||||
#endif // MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_DEVICES_H
|
|
@ -28,7 +28,6 @@ def main():
|
|||
while True:
|
||||
res = s.accept()
|
||||
client_s = res[0]
|
||||
client_addr = res[1]
|
||||
req = client_s.recv(4096)
|
||||
print("Request:")
|
||||
print(req)
|
||||
|
|
|
@ -68,7 +68,7 @@ MP_DEFINE_CONST_OBJ_TYPE(
|
|||
locals_dict, &example_Timer_locals_dict
|
||||
);
|
||||
|
||||
// Define all properties of the module.
|
||||
// Define all attributes of the module.
|
||||
// Table entries are key/value pairs of the attribute name (a string)
|
||||
// and the MicroPython object reference.
|
||||
// All identifiers and strings are written as MP_QSTR_xxx and will be
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
EXAMPLE_MOD_DIR := $(USERMOD_DIR)
|
||||
CEXAMPLE_MOD_DIR := $(USERMOD_DIR)
|
||||
|
||||
# Add all C files to SRC_USERMOD.
|
||||
SRC_USERMOD += $(EXAMPLE_MOD_DIR)/examplemodule.c
|
||||
SRC_USERMOD += $(CEXAMPLE_MOD_DIR)/examplemodule.c
|
||||
|
||||
# We can add our module folder to include paths if needed
|
||||
# This is not actually needed in this example.
|
||||
CFLAGS_USERMOD += -I$(EXAMPLE_MOD_DIR)
|
||||
CEXAMPLE_MOD_DIR := $(USERMOD_DIR)
|
||||
CFLAGS_USERMOD += -I$(CEXAMPLE_MOD_DIR)
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
// See example.cpp for the definition.
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(cppfunc_obj, cppfunc);
|
||||
|
||||
// Define all properties of the module.
|
||||
// Define all attributes of the module.
|
||||
// Table entries are key/value pairs of the attribute name (a string)
|
||||
// and the MicroPython object reference.
|
||||
// All identifiers and strings are written as MP_QSTR_xxx and will be
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
This is an example of a user C module that includes subpackages.
|
|
@ -0,0 +1,19 @@
|
|||
# Create an INTERFACE library for our C module.
|
||||
add_library(usermod_subpackage_example INTERFACE)
|
||||
|
||||
# Add our source files to the lib
|
||||
target_sources(usermod_subpackage_example INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/examplemodule.c
|
||||
)
|
||||
|
||||
# Add the current directory as an include directory.
|
||||
target_include_directories(usermod_subpackage_example INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
)
|
||||
|
||||
target_compile_definitions(usermod_subpackage_example INTERFACE
|
||||
MICROPY_MODULE_BUILTIN_SUBPACKAGES=1
|
||||
)
|
||||
|
||||
# Link our INTERFACE library to the usermod target.
|
||||
target_link_libraries(usermod INTERFACE usermod_subpackage_example)
|
|
@ -0,0 +1,10 @@
|
|||
SUBPACKAGE_EXAMPLE_MOD_DIR := $(USERMOD_DIR)
|
||||
|
||||
# Add all C files to SRC_USERMOD.
|
||||
SRC_USERMOD += $(SUBPACKAGE_EXAMPLE_MOD_DIR)/modexamplepackage.c
|
||||
|
||||
# We can add our module folder to include paths if needed
|
||||
# This is not actually needed in this example.
|
||||
CFLAGS_USERMOD += -I$(SUBPACKAGE_EXAMPLE_MOD_DIR) -DMICROPY_MODULE_BUILTIN_SUBPACKAGES=1
|
||||
|
||||
QSTR_DEFS += $(SUBPACKAGE_EXAMPLE_MOD_DIR)/qstrdefsexamplepackage.h
|
|
@ -0,0 +1,84 @@
|
|||
// Include MicroPython API.
|
||||
#include "py/runtime.h"
|
||||
|
||||
// Define example_package.foo.bar.f()
|
||||
STATIC mp_obj_t example_package_foo_bar_f(void) {
|
||||
mp_printf(&mp_plat_print, "example_package.foo.bar.f\n");
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(example_package_foo_bar_f_obj, example_package_foo_bar_f);
|
||||
|
||||
// Define all attributes of the second-level sub-package (example_package.foo.bar).
|
||||
STATIC const mp_rom_map_elem_t example_package_foo_bar_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_example_package_dot_foo_dot_bar) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_f), MP_ROM_PTR(&example_package_foo_bar_f_obj) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(example_package_foo_bar_globals, example_package_foo_bar_globals_table);
|
||||
|
||||
// Define example_package.foo.bar module object.
|
||||
const mp_obj_module_t example_package_foo_bar_user_cmodule = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&example_package_foo_bar_globals,
|
||||
};
|
||||
|
||||
// Define example_package.foo.f()
|
||||
STATIC mp_obj_t example_package_foo_f(void) {
|
||||
mp_printf(&mp_plat_print, "example_package.foo.f\n");
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(example_package_foo_f_obj, example_package_foo_f);
|
||||
|
||||
// Define all attributes of the first-level sub-package (example_package.foo).
|
||||
STATIC const mp_rom_map_elem_t example_package_foo_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_example_package_dot_foo) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_bar), MP_ROM_PTR(&example_package_foo_bar_user_cmodule) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_f), MP_ROM_PTR(&example_package_foo_f_obj) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(example_package_foo_globals, example_package_foo_globals_table);
|
||||
|
||||
// Define example_package.foo module object.
|
||||
const mp_obj_module_t example_package_foo_user_cmodule = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&example_package_foo_globals,
|
||||
};
|
||||
|
||||
// Define example_package.f()
|
||||
STATIC mp_obj_t example_package_f(void) {
|
||||
mp_printf(&mp_plat_print, "example_package.f\n");
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(example_package_f_obj, example_package_f);
|
||||
|
||||
STATIC mp_obj_t example_package___init__(void) {
|
||||
if (!MP_STATE_VM(example_package_initialised)) {
|
||||
// __init__ for builtins is called each time the module is imported,
|
||||
// so ensure that initialisation only happens once.
|
||||
MP_STATE_VM(example_package_initialised) = true;
|
||||
mp_printf(&mp_plat_print, "example_package.__init__\n");
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(example_package___init___obj, example_package___init__);
|
||||
|
||||
// The "initialised" state is stored on mp_state so that it is cleared on soft
|
||||
// reset.
|
||||
MP_REGISTER_ROOT_POINTER(int example_package_initialised);
|
||||
|
||||
// Define all attributes of the top-level package (example_package).
|
||||
STATIC const mp_rom_map_elem_t example_package_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_example_package) },
|
||||
{ MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&example_package___init___obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_foo), MP_ROM_PTR(&example_package_foo_user_cmodule) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_f), MP_ROM_PTR(&example_package_f_obj) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(example_package_globals, example_package_globals_table);
|
||||
|
||||
// Define module object.
|
||||
const mp_obj_module_t example_package_user_cmodule = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&example_package_globals,
|
||||
};
|
||||
|
||||
// Register the module to make it available in Python.
|
||||
// Note: subpackages should not be registered with MP_REGISTER_MODULE.
|
||||
MP_REGISTER_MODULE(MP_QSTR_example_package, example_package_user_cmodule);
|
|
@ -0,0 +1,2 @@
|
|||
Q(example_package.foo)
|
||||
Q(example_package.foo.bar)
|
|
@ -154,7 +154,6 @@ if(MICROPY_SSL_MBEDTLS)
|
|||
${MICROPY_LIB_MBEDTLS_DIR}/library/md4.c
|
||||
${MICROPY_LIB_MBEDTLS_DIR}/library/md5.c
|
||||
${MICROPY_LIB_MBEDTLS_DIR}/library/md.c
|
||||
${MICROPY_LIB_MBEDTLS_DIR}/library/md_wrap.c
|
||||
${MICROPY_LIB_MBEDTLS_DIR}/library/oid.c
|
||||
${MICROPY_LIB_MBEDTLS_DIR}/library/padlock.c
|
||||
${MICROPY_LIB_MBEDTLS_DIR}/library/pem.c
|
||||
|
@ -179,9 +178,11 @@ if(MICROPY_SSL_MBEDTLS)
|
|||
${MICROPY_LIB_MBEDTLS_DIR}/library/ssl_cli.c
|
||||
${MICROPY_LIB_MBEDTLS_DIR}/library/ssl_cookie.c
|
||||
${MICROPY_LIB_MBEDTLS_DIR}/library/ssl_srv.c
|
||||
${MICROPY_LIB_MBEDTLS_DIR}/library/ssl_msg.c
|
||||
${MICROPY_LIB_MBEDTLS_DIR}/library/ssl_ticket.c
|
||||
${MICROPY_LIB_MBEDTLS_DIR}/library/ssl_tls.c
|
||||
${MICROPY_LIB_MBEDTLS_DIR}/library/timing.c
|
||||
${MICROPY_LIB_MBEDTLS_DIR}/library/constant_time.c
|
||||
${MICROPY_LIB_MBEDTLS_DIR}/library/x509.c
|
||||
${MICROPY_LIB_MBEDTLS_DIR}/library/x509_create.c
|
||||
${MICROPY_LIB_MBEDTLS_DIR}/library/x509_crl.c
|
||||
|
|
|
@ -172,7 +172,6 @@ SRC_THIRDPARTY_C += $(addprefix $(MBEDTLS_DIR)/library/,\
|
|||
md4.c \
|
||||
md5.c \
|
||||
md.c \
|
||||
md_wrap.c \
|
||||
oid.c \
|
||||
padlock.c \
|
||||
pem.c \
|
||||
|
@ -197,9 +196,11 @@ SRC_THIRDPARTY_C += $(addprefix $(MBEDTLS_DIR)/library/,\
|
|||
ssl_cli.c \
|
||||
ssl_cookie.c \
|
||||
ssl_srv.c \
|
||||
ssl_msg.c \
|
||||
ssl_ticket.c \
|
||||
ssl_tls.c \
|
||||
timing.c \
|
||||
constant_time.c \
|
||||
x509.c \
|
||||
x509_create.c \
|
||||
x509_crl.c \
|
||||
|
@ -293,7 +294,7 @@ SRC_THIRDPARTY_C += $(addprefix $(BTREE_DIR)/,\
|
|||
CFLAGS_EXTMOD += -DMICROPY_PY_BTREE=1
|
||||
# we need to suppress certain warnings to get berkeley-db to compile cleanly
|
||||
# and we have separate BTREE_DEFS so the definitions don't interfere with other source code
|
||||
$(BUILD)/$(BTREE_DIR)/%.o: CFLAGS += -Wno-old-style-definition -Wno-sign-compare -Wno-unused-parameter $(BTREE_DEFS)
|
||||
$(BUILD)/$(BTREE_DIR)/%.o: CFLAGS += -Wno-old-style-definition -Wno-sign-compare -Wno-unused-parameter -Wno-deprecated-non-prototype -Wno-unknown-warning-option $(BTREE_DEFS)
|
||||
$(BUILD)/extmod/modbtree.o: CFLAGS += $(BTREE_DEFS)
|
||||
endif
|
||||
|
||||
|
|
|
@ -34,13 +34,11 @@
|
|||
#include MICROPY_PY_MACHINE_PWM_INCLUDEFILE
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_MACHINE_PWM_INIT
|
||||
STATIC mp_obj_t machine_pwm_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
||||
mp_machine_pwm_init_helper(args[0], n_args - 1, args + 1, kw_args);
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_pwm_init_obj, 1, machine_pwm_init);
|
||||
#endif
|
||||
|
||||
// PWM.deinit()
|
||||
STATIC mp_obj_t machine_pwm_deinit(mp_obj_t self_in) {
|
||||
|
@ -82,8 +80,6 @@ STATIC mp_obj_t machine_pwm_duty(size_t n_args, const mp_obj_t *args) {
|
|||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pwm_duty_obj, 1, 2, machine_pwm_duty);
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_MACHINE_PWM_DUTY_U16_NS
|
||||
|
||||
// PWM.duty_u16([value])
|
||||
STATIC mp_obj_t machine_pwm_duty_u16(size_t n_args, const mp_obj_t *args) {
|
||||
machine_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
|
@ -114,21 +110,15 @@ STATIC mp_obj_t machine_pwm_duty_ns(size_t n_args, const mp_obj_t *args) {
|
|||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pwm_duty_ns_obj, 1, 2, machine_pwm_duty_ns);
|
||||
|
||||
#endif
|
||||
|
||||
STATIC const mp_rom_map_elem_t machine_pwm_locals_dict_table[] = {
|
||||
#if MICROPY_PY_MACHINE_PWM_INIT
|
||||
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_pwm_init_obj) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_pwm_deinit_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&machine_pwm_freq_obj) },
|
||||
#if MICROPY_PY_MACHINE_PWM_DUTY
|
||||
{ MP_ROM_QSTR(MP_QSTR_duty), MP_ROM_PTR(&machine_pwm_duty_obj) },
|
||||
#endif
|
||||
#if MICROPY_PY_MACHINE_PWM_DUTY_U16_NS
|
||||
{ MP_ROM_QSTR(MP_QSTR_duty_u16), MP_ROM_PTR(&machine_pwm_duty_u16_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_duty_ns), MP_ROM_PTR(&machine_pwm_duty_ns_obj) },
|
||||
#endif
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(machine_pwm_locals_dict, machine_pwm_locals_dict_table);
|
||||
|
||||
|
|
|
@ -582,7 +582,7 @@ STATIC mp_obj_t uctypes_struct_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_ob
|
|||
STATIC mp_obj_t uctypes_struct_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
|
||||
mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
switch (op) {
|
||||
case MP_UNARY_OP_INT:
|
||||
case MP_UNARY_OP_INT_MAYBE:
|
||||
if (mp_obj_is_type(self->desc, &mp_type_tuple)) {
|
||||
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->desc);
|
||||
mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(t->items[0]);
|
||||
|
|
|
@ -34,6 +34,10 @@
|
|||
|
||||
#if MICROPY_VFS_FAT
|
||||
#include "extmod/vfs_fat.h"
|
||||
#if MICROPY_PY_UOS_SYNC
|
||||
#include "lib/oofatfs/ff.h"
|
||||
#include "lib/oofatfs/diskio.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2
|
||||
|
@ -58,6 +62,21 @@
|
|||
#define MICROPY_BUILD_TYPE_PAREN
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_UOS_SYNC
|
||||
// sync()
|
||||
// Sync all filesystems.
|
||||
STATIC mp_obj_t mp_uos_sync(void) {
|
||||
#if MICROPY_VFS_FAT
|
||||
for (mp_vfs_mount_t *vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) {
|
||||
// this assumes that vfs->obj is fs_user_mount_t with block device functions
|
||||
disk_ioctl(MP_OBJ_TO_PTR(vfs->obj), CTRL_SYNC, NULL);
|
||||
}
|
||||
#endif
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_0(mp_uos_sync_obj, mp_uos_sync);
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_UOS_UNAME
|
||||
|
||||
#if MICROPY_PY_UOS_UNAME_RELEASE_DYNAMIC
|
||||
|
|
|
@ -39,6 +39,18 @@
|
|||
#include "lib/cyw43-driver/src/cyw43.h"
|
||||
#include "lib/cyw43-driver/src/cyw43_country.h"
|
||||
|
||||
// This is the same as cyw43_pm_value but as a macro, to make it a true constant.
|
||||
#define CYW43_PM_VALUE(pm_mode, pm2_sleep_ret_ms, li_beacon_period, li_dtim_period, li_assoc) \
|
||||
((li_assoc) << 20 \
|
||||
| (li_dtim_period) << 16 \
|
||||
| (li_beacon_period) << 12 \
|
||||
| ((pm2_sleep_ret_ms) / 10) << 4 \
|
||||
| (pm_mode))
|
||||
|
||||
#define PM_NONE (CYW43_PM_VALUE(CYW43_NO_POWERSAVE_MODE, 10, 0, 0, 0))
|
||||
#define PM_PERFORMANCE (CYW43_PM_VALUE(CYW43_PM2_POWERSAVE_MODE, 200, 1, 1, 10))
|
||||
#define PM_POWERSAVE (CYW43_PM_VALUE(CYW43_PM1_POWERSAVE_MODE, 10, 0, 0, 0))
|
||||
|
||||
typedef struct _network_cyw43_obj_t {
|
||||
mp_obj_base_t base;
|
||||
cyw43_t *cyw;
|
||||
|
@ -397,6 +409,11 @@ STATIC mp_obj_t network_cyw43_config(size_t n_args, const mp_obj_t *args, mp_map
|
|||
cyw43_wifi_get_mac(self->cyw, self->itf, buf);
|
||||
return mp_obj_new_bytes(buf, 6);
|
||||
}
|
||||
case MP_QSTR_pm: {
|
||||
uint32_t pm;
|
||||
cyw43_wifi_get_pm(self->cyw, &pm);
|
||||
return MP_OBJ_NEW_SMALL_INT(pm);
|
||||
}
|
||||
case MP_QSTR_txpower: {
|
||||
uint8_t buf[13];
|
||||
memcpy(buf, "qtxpower\x00\x00\x00\x00\x00", 13);
|
||||
|
@ -516,6 +533,11 @@ STATIC const mp_rom_map_elem_t network_cyw43_locals_dict_table[] = {
|
|||
{ MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&network_cyw43_ifconfig_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&network_cyw43_status_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&network_cyw43_config_obj) },
|
||||
|
||||
// Class constants.
|
||||
{ MP_ROM_QSTR(MP_QSTR_PM_NONE), MP_ROM_INT(PM_NONE) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_PM_PERFORMANCE), MP_ROM_INT(PM_PERFORMANCE) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_PM_POWERSAVE), MP_ROM_INT(PM_POWERSAVE) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(network_cyw43_locals_dict, network_cyw43_locals_dict_table);
|
||||
|
||||
|
|
|
@ -123,7 +123,7 @@ def gather(*aws, return_exceptions=False):
|
|||
|
||||
# Either this gather was cancelled, or one of the sub-tasks raised an exception with
|
||||
# return_exceptions==False, so reraise the exception here.
|
||||
if state is not 0:
|
||||
if state:
|
||||
raise state
|
||||
|
||||
# Return the list of return values of each sub-task.
|
||||
|
|
|
@ -323,7 +323,7 @@ STATIC mp_obj_t MP_VFS_LFSx(chdir)(mp_obj_t self_in, mp_obj_t path_in) {
|
|||
size_t from = 1;
|
||||
char *cwd = vstr_str(&self->cur_dir);
|
||||
while (from < CWD_LEN) {
|
||||
for (; cwd[from] == '/' && from < CWD_LEN; ++from) {
|
||||
for (; from < CWD_LEN && cwd[from] == '/'; ++from) {
|
||||
// Scan for the start
|
||||
}
|
||||
if (from > to) {
|
||||
|
@ -331,7 +331,7 @@ STATIC mp_obj_t MP_VFS_LFSx(chdir)(mp_obj_t self_in, mp_obj_t path_in) {
|
|||
vstr_cut_out_bytes(&self->cur_dir, to, from - to);
|
||||
from = to;
|
||||
}
|
||||
for (; cwd[from] != '/' && from < CWD_LEN; ++from) {
|
||||
for (; from < CWD_LEN && cwd[from] != '/'; ++from) {
|
||||
// Scan for the next /
|
||||
}
|
||||
if ((from - to) == 1 && cwd[to] == '.') {
|
||||
|
|
2
lib/fsp
2
lib/fsp
|
@ -1 +1 @@
|
|||
Subproject commit 55bffd3a71cbeed2104cf30e7a39b641d8c1ff48
|
||||
Subproject commit e78939d32d1ccea9f0ba8bb42c51aceffd386b9b
|
|
@ -1 +1 @@
|
|||
Subproject commit 1bc2c9cb8b8fe4659bd94b8ebba5a4c02029b7fa
|
||||
Subproject commit 981743de6fcdbe672e482b6fd724d31d0a0d2476
|
|
@ -110,7 +110,7 @@ void mbedtls_strerror(int ret, char *buf, size_t buflen) {
|
|||
if (got_hl) {
|
||||
use_ret = ret & 0xFF80;
|
||||
|
||||
// special case
|
||||
// special case, don't try to translate low level code
|
||||
#if defined(MBEDTLS_SSL_TLS_C)
|
||||
if (use_ret == -(MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE)) {
|
||||
strncpy(buf, "MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE", buflen);
|
||||
|
|
|
@ -0,0 +1,710 @@
|
|||
/*
|
||||
* Error message information
|
||||
*
|
||||
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is part of mbed TLS (https://tls.mbed.org)
|
||||
*/
|
||||
|
||||
#if !defined(MBEDTLS_CONFIG_FILE)
|
||||
#include "mbedtls/config.h"
|
||||
#else
|
||||
#include MBEDTLS_CONFIG_FILE
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_ERROR_C) || defined(MBEDTLS_ERROR_STRERROR_DUMMY)
|
||||
#include "mbedtls/error.h"
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_PLATFORM_C)
|
||||
#include "mbedtls/platform.h"
|
||||
#else
|
||||
#define mbedtls_snprintf snprintf
|
||||
#define mbedtls_time_t time_t
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_ERROR_C)
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(MBEDTLS_AES_C)
|
||||
#include "mbedtls/aes.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_ARC4_C)
|
||||
#include "mbedtls/arc4.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_ARIA_C)
|
||||
#include "mbedtls/aria.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_ASN1_PARSE_C)
|
||||
#include "mbedtls/asn1.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_BASE64_C)
|
||||
#include "mbedtls/base64.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_BIGNUM_C)
|
||||
#include "mbedtls/bignum.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_BLOWFISH_C)
|
||||
#include "mbedtls/blowfish.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_CAMELLIA_C)
|
||||
#include "mbedtls/camellia.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_CCM_C)
|
||||
#include "mbedtls/ccm.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_CHACHA20_C)
|
||||
#include "mbedtls/chacha20.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_CHACHAPOLY_C)
|
||||
#include "mbedtls/chachapoly.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_CIPHER_C)
|
||||
#include "mbedtls/cipher.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_CMAC_C)
|
||||
#include "mbedtls/cmac.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_CTR_DRBG_C)
|
||||
#include "mbedtls/ctr_drbg.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_DES_C)
|
||||
#include "mbedtls/des.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_DHM_C)
|
||||
#include "mbedtls/dhm.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_ECP_C)
|
||||
#include "mbedtls/ecp.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_ENTROPY_C)
|
||||
#include "mbedtls/entropy.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_GCM_C)
|
||||
#include "mbedtls/gcm.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_HKDF_C)
|
||||
#include "mbedtls/hkdf.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_HMAC_DRBG_C)
|
||||
#include "mbedtls/hmac_drbg.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_MD_C)
|
||||
#include "mbedtls/md.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_MD2_C)
|
||||
#include "mbedtls/md2.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_MD4_C)
|
||||
#include "mbedtls/md4.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_MD5_C)
|
||||
#include "mbedtls/md5.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_NET_C)
|
||||
#include "mbedtls/net_sockets.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_OID_C)
|
||||
#include "mbedtls/oid.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_PADLOCK_C)
|
||||
#include "mbedtls/padlock.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_PEM_PARSE_C) || defined(MBEDTLS_PEM_WRITE_C)
|
||||
#include "mbedtls/pem.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_PK_C)
|
||||
#include "mbedtls/pk.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_PKCS12_C)
|
||||
#include "mbedtls/pkcs12.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_PKCS5_C)
|
||||
#include "mbedtls/pkcs5.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_PLATFORM_C)
|
||||
#include "mbedtls/platform.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_POLY1305_C)
|
||||
#include "mbedtls/poly1305.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_RIPEMD160_C)
|
||||
#include "mbedtls/ripemd160.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_RSA_C)
|
||||
#include "mbedtls/rsa.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_SHA1_C)
|
||||
#include "mbedtls/sha1.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_SHA256_C)
|
||||
#include "mbedtls/sha256.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_SHA512_C)
|
||||
#include "mbedtls/sha512.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_SSL_TLS_C)
|
||||
#include "mbedtls/ssl.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_THREADING_C)
|
||||
#include "mbedtls/threading.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_X509_USE_C) || defined(MBEDTLS_X509_CREATE_C)
|
||||
#include "mbedtls/x509.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_XTEA_C)
|
||||
#include "mbedtls/xtea.h"
|
||||
#endif
|
||||
|
||||
|
||||
// Error code table type
|
||||
struct ssl_errs {
|
||||
int16_t errnum;
|
||||
const char *errstr;
|
||||
};
|
||||
|
||||
// Table of high level error codes
|
||||
static const struct ssl_errs mbedtls_high_level_error_tab[] = {
|
||||
// BEGIN generated code
|
||||
#if defined(MBEDTLS_CIPHER_C)
|
||||
{ -(MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE), "CIPHER_FEATURE_UNAVAILABLE" },
|
||||
{ -(MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA), "CIPHER_BAD_INPUT_DATA" },
|
||||
{ -(MBEDTLS_ERR_CIPHER_ALLOC_FAILED), "CIPHER_ALLOC_FAILED" },
|
||||
{ -(MBEDTLS_ERR_CIPHER_INVALID_PADDING), "CIPHER_INVALID_PADDING" },
|
||||
{ -(MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED), "CIPHER_FULL_BLOCK_EXPECTED" },
|
||||
{ -(MBEDTLS_ERR_CIPHER_AUTH_FAILED), "CIPHER_AUTH_FAILED" },
|
||||
{ -(MBEDTLS_ERR_CIPHER_INVALID_CONTEXT), "CIPHER_INVALID_CONTEXT" },
|
||||
{ -(MBEDTLS_ERR_CIPHER_HW_ACCEL_FAILED), "CIPHER_HW_ACCEL_FAILED" },
|
||||
#endif /* MBEDTLS_CIPHER_C */
|
||||
|
||||
#if defined(MBEDTLS_DHM_C)
|
||||
{ -(MBEDTLS_ERR_DHM_BAD_INPUT_DATA), "DHM_BAD_INPUT_DATA" },
|
||||
{ -(MBEDTLS_ERR_DHM_READ_PARAMS_FAILED), "DHM_READ_PARAMS_FAILED" },
|
||||
{ -(MBEDTLS_ERR_DHM_MAKE_PARAMS_FAILED), "DHM_MAKE_PARAMS_FAILED" },
|
||||
{ -(MBEDTLS_ERR_DHM_READ_PUBLIC_FAILED), "DHM_READ_PUBLIC_FAILED" },
|
||||
{ -(MBEDTLS_ERR_DHM_MAKE_PUBLIC_FAILED), "DHM_MAKE_PUBLIC_FAILED" },
|
||||
{ -(MBEDTLS_ERR_DHM_CALC_SECRET_FAILED), "DHM_CALC_SECRET_FAILED" },
|
||||
{ -(MBEDTLS_ERR_DHM_INVALID_FORMAT), "DHM_INVALID_FORMAT" },
|
||||
{ -(MBEDTLS_ERR_DHM_ALLOC_FAILED), "DHM_ALLOC_FAILED" },
|
||||
{ -(MBEDTLS_ERR_DHM_FILE_IO_ERROR), "DHM_FILE_IO_ERROR" },
|
||||
{ -(MBEDTLS_ERR_DHM_HW_ACCEL_FAILED), "DHM_HW_ACCEL_FAILED" },
|
||||
{ -(MBEDTLS_ERR_DHM_SET_GROUP_FAILED), "DHM_SET_GROUP_FAILED" },
|
||||
#endif /* MBEDTLS_DHM_C */
|
||||
|
||||
#if defined(MBEDTLS_ECP_C)
|
||||
{ -(MBEDTLS_ERR_ECP_BAD_INPUT_DATA), "ECP_BAD_INPUT_DATA" },
|
||||
{ -(MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL), "ECP_BUFFER_TOO_SMALL" },
|
||||
{ -(MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE), "ECP_FEATURE_UNAVAILABLE" },
|
||||
{ -(MBEDTLS_ERR_ECP_VERIFY_FAILED), "ECP_VERIFY_FAILED" },
|
||||
{ -(MBEDTLS_ERR_ECP_ALLOC_FAILED), "ECP_ALLOC_FAILED" },
|
||||
{ -(MBEDTLS_ERR_ECP_RANDOM_FAILED), "ECP_RANDOM_FAILED" },
|
||||
{ -(MBEDTLS_ERR_ECP_INVALID_KEY), "ECP_INVALID_KEY" },
|
||||
{ -(MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH), "ECP_SIG_LEN_MISMATCH" },
|
||||
{ -(MBEDTLS_ERR_ECP_HW_ACCEL_FAILED), "ECP_HW_ACCEL_FAILED" },
|
||||
{ -(MBEDTLS_ERR_ECP_IN_PROGRESS), "ECP_IN_PROGRESS" },
|
||||
#endif /* MBEDTLS_ECP_C */
|
||||
|
||||
#if defined(MBEDTLS_MD_C)
|
||||
{ -(MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE), "MD_FEATURE_UNAVAILABLE" },
|
||||
{ -(MBEDTLS_ERR_MD_BAD_INPUT_DATA), "MD_BAD_INPUT_DATA" },
|
||||
{ -(MBEDTLS_ERR_MD_ALLOC_FAILED), "MD_ALLOC_FAILED" },
|
||||
{ -(MBEDTLS_ERR_MD_FILE_IO_ERROR), "MD_FILE_IO_ERROR" },
|
||||
{ -(MBEDTLS_ERR_MD_HW_ACCEL_FAILED), "MD_HW_ACCEL_FAILED" },
|
||||
#endif /* MBEDTLS_MD_C */
|
||||
|
||||
#if defined(MBEDTLS_PEM_PARSE_C) || defined(MBEDTLS_PEM_WRITE_C)
|
||||
{ -(MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT), "PEM_NO_HEADER_FOOTER_PRESENT" },
|
||||
{ -(MBEDTLS_ERR_PEM_INVALID_DATA), "PEM_INVALID_DATA" },
|
||||
{ -(MBEDTLS_ERR_PEM_ALLOC_FAILED), "PEM_ALLOC_FAILED" },
|
||||
{ -(MBEDTLS_ERR_PEM_INVALID_ENC_IV), "PEM_INVALID_ENC_IV" },
|
||||
{ -(MBEDTLS_ERR_PEM_UNKNOWN_ENC_ALG), "PEM_UNKNOWN_ENC_ALG" },
|
||||
{ -(MBEDTLS_ERR_PEM_PASSWORD_REQUIRED), "PEM_PASSWORD_REQUIRED" },
|
||||
{ -(MBEDTLS_ERR_PEM_PASSWORD_MISMATCH), "PEM_PASSWORD_MISMATCH" },
|
||||
{ -(MBEDTLS_ERR_PEM_FEATURE_UNAVAILABLE), "PEM_FEATURE_UNAVAILABLE" },
|
||||
{ -(MBEDTLS_ERR_PEM_BAD_INPUT_DATA), "PEM_BAD_INPUT_DATA" },
|
||||
#endif /* MBEDTLS_PEM_PARSE_C || MBEDTLS_PEM_WRITE_C */
|
||||
|
||||
#if defined(MBEDTLS_PK_C)
|
||||
{ -(MBEDTLS_ERR_PK_ALLOC_FAILED), "PK_ALLOC_FAILED" },
|
||||
{ -(MBEDTLS_ERR_PK_TYPE_MISMATCH), "PK_TYPE_MISMATCH" },
|
||||
{ -(MBEDTLS_ERR_PK_BAD_INPUT_DATA), "PK_BAD_INPUT_DATA" },
|
||||
{ -(MBEDTLS_ERR_PK_FILE_IO_ERROR), "PK_FILE_IO_ERROR" },
|
||||
{ -(MBEDTLS_ERR_PK_KEY_INVALID_VERSION), "PK_KEY_INVALID_VERSION" },
|
||||
{ -(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT), "PK_KEY_INVALID_FORMAT" },
|
||||
{ -(MBEDTLS_ERR_PK_UNKNOWN_PK_ALG), "PK_UNKNOWN_PK_ALG" },
|
||||
{ -(MBEDTLS_ERR_PK_PASSWORD_REQUIRED), "PK_PASSWORD_REQUIRED" },
|
||||
{ -(MBEDTLS_ERR_PK_PASSWORD_MISMATCH), "PK_PASSWORD_MISMATCH" },
|
||||
{ -(MBEDTLS_ERR_PK_INVALID_PUBKEY), "PK_INVALID_PUBKEY" },
|
||||
{ -(MBEDTLS_ERR_PK_INVALID_ALG), "PK_INVALID_ALG" },
|
||||
{ -(MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE), "PK_UNKNOWN_NAMED_CURVE" },
|
||||
{ -(MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE), "PK_FEATURE_UNAVAILABLE" },
|
||||
{ -(MBEDTLS_ERR_PK_SIG_LEN_MISMATCH), "PK_SIG_LEN_MISMATCH" },
|
||||
{ -(MBEDTLS_ERR_PK_HW_ACCEL_FAILED), "PK_HW_ACCEL_FAILED" },
|
||||
#endif /* MBEDTLS_PK_C */
|
||||
|
||||
#if defined(MBEDTLS_PKCS12_C)
|
||||
{ -(MBEDTLS_ERR_PKCS12_BAD_INPUT_DATA), "PKCS12_BAD_INPUT_DATA" },
|
||||
{ -(MBEDTLS_ERR_PKCS12_FEATURE_UNAVAILABLE), "PKCS12_FEATURE_UNAVAILABLE" },
|
||||
{ -(MBEDTLS_ERR_PKCS12_PBE_INVALID_FORMAT), "PKCS12_PBE_INVALID_FORMAT" },
|
||||
{ -(MBEDTLS_ERR_PKCS12_PASSWORD_MISMATCH), "PKCS12_PASSWORD_MISMATCH" },
|
||||
#endif /* MBEDTLS_PKCS12_C */
|
||||
|
||||
#if defined(MBEDTLS_PKCS5_C)
|
||||
{ -(MBEDTLS_ERR_PKCS5_BAD_INPUT_DATA), "PKCS5_BAD_INPUT_DATA" },
|
||||
{ -(MBEDTLS_ERR_PKCS5_INVALID_FORMAT), "PKCS5_INVALID_FORMAT" },
|
||||
{ -(MBEDTLS_ERR_PKCS5_FEATURE_UNAVAILABLE), "PKCS5_FEATURE_UNAVAILABLE" },
|
||||
{ -(MBEDTLS_ERR_PKCS5_PASSWORD_MISMATCH), "PKCS5_PASSWORD_MISMATCH" },
|
||||
#endif /* MBEDTLS_PKCS5_C */
|
||||
|
||||
#if defined(MBEDTLS_RSA_C)
|
||||
{ -(MBEDTLS_ERR_RSA_BAD_INPUT_DATA), "RSA_BAD_INPUT_DATA" },
|
||||
{ -(MBEDTLS_ERR_RSA_INVALID_PADDING), "RSA_INVALID_PADDING" },
|
||||
{ -(MBEDTLS_ERR_RSA_KEY_GEN_FAILED), "RSA_KEY_GEN_FAILED" },
|
||||
{ -(MBEDTLS_ERR_RSA_KEY_CHECK_FAILED), "RSA_KEY_CHECK_FAILED" },
|
||||
{ -(MBEDTLS_ERR_RSA_PUBLIC_FAILED), "RSA_PUBLIC_FAILED" },
|
||||
{ -(MBEDTLS_ERR_RSA_PRIVATE_FAILED), "RSA_PRIVATE_FAILED" },
|
||||
{ -(MBEDTLS_ERR_RSA_VERIFY_FAILED), "RSA_VERIFY_FAILED" },
|
||||
{ -(MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE), "RSA_OUTPUT_TOO_LARGE" },
|
||||
{ -(MBEDTLS_ERR_RSA_RNG_FAILED), "RSA_RNG_FAILED" },
|
||||
{ -(MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION), "RSA_UNSUPPORTED_OPERATION" },
|
||||
{ -(MBEDTLS_ERR_RSA_HW_ACCEL_FAILED), "RSA_HW_ACCEL_FAILED" },
|
||||
#endif /* MBEDTLS_RSA_C */
|
||||
|
||||
#if defined(MBEDTLS_SSL_TLS_C)
|
||||
{ -(MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE), "SSL_FEATURE_UNAVAILABLE" },
|
||||
{ -(MBEDTLS_ERR_SSL_BAD_INPUT_DATA), "SSL_BAD_INPUT_DATA" },
|
||||
{ -(MBEDTLS_ERR_SSL_INVALID_MAC), "SSL_INVALID_MAC" },
|
||||
{ -(MBEDTLS_ERR_SSL_INVALID_RECORD), "SSL_INVALID_RECORD" },
|
||||
{ -(MBEDTLS_ERR_SSL_CONN_EOF), "SSL_CONN_EOF" },
|
||||
{ -(MBEDTLS_ERR_SSL_UNKNOWN_CIPHER), "SSL_UNKNOWN_CIPHER" },
|
||||
{ -(MBEDTLS_ERR_SSL_NO_CIPHER_CHOSEN), "SSL_NO_CIPHER_CHOSEN" },
|
||||
{ -(MBEDTLS_ERR_SSL_NO_RNG), "SSL_NO_RNG" },
|
||||
{ -(MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE), "SSL_NO_CLIENT_CERTIFICATE" },
|
||||
{ -(MBEDTLS_ERR_SSL_CERTIFICATE_TOO_LARGE), "SSL_CERTIFICATE_TOO_LARGE" },
|
||||
{ -(MBEDTLS_ERR_SSL_CERTIFICATE_REQUIRED), "SSL_CERTIFICATE_REQUIRED" },
|
||||
{ -(MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED), "SSL_PRIVATE_KEY_REQUIRED" },
|
||||
{ -(MBEDTLS_ERR_SSL_CA_CHAIN_REQUIRED), "SSL_CA_CHAIN_REQUIRED" },
|
||||
{ -(MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE), "SSL_UNEXPECTED_MESSAGE" },
|
||||
{ -(MBEDTLS_ERR_SSL_PEER_VERIFY_FAILED), "SSL_PEER_VERIFY_FAILED" },
|
||||
{ -(MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY), "SSL_PEER_CLOSE_NOTIFY" },
|
||||
{ -(MBEDTLS_ERR_SSL_BAD_HS_CLIENT_HELLO), "SSL_BAD_HS_CLIENT_HELLO" },
|
||||
{ -(MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO), "SSL_BAD_HS_SERVER_HELLO" },
|
||||
{ -(MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE), "SSL_BAD_HS_CERTIFICATE" },
|
||||
{ -(MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST), "SSL_BAD_HS_CERTIFICATE_REQUEST" },
|
||||
{ -(MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE), "SSL_BAD_HS_SERVER_KEY_EXCHANGE" },
|
||||
{ -(MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO_DONE), "SSL_BAD_HS_SERVER_HELLO_DONE" },
|
||||
{ -(MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE), "SSL_BAD_HS_CLIENT_KEY_EXCHANGE" },
|
||||
{ -(MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_RP), "SSL_BAD_HS_CLIENT_KEY_EXCHANGE_RP" },
|
||||
{ -(MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_CS), "SSL_BAD_HS_CLIENT_KEY_EXCHANGE_CS" },
|
||||
{ -(MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY), "SSL_BAD_HS_CERTIFICATE_VERIFY" },
|
||||
{ -(MBEDTLS_ERR_SSL_BAD_HS_CHANGE_CIPHER_SPEC), "SSL_BAD_HS_CHANGE_CIPHER_SPEC" },
|
||||
{ -(MBEDTLS_ERR_SSL_BAD_HS_FINISHED), "SSL_BAD_HS_FINISHED" },
|
||||
{ -(MBEDTLS_ERR_SSL_ALLOC_FAILED), "SSL_ALLOC_FAILED" },
|
||||
{ -(MBEDTLS_ERR_SSL_HW_ACCEL_FAILED), "SSL_HW_ACCEL_FAILED" },
|
||||
{ -(MBEDTLS_ERR_SSL_HW_ACCEL_FALLTHROUGH), "SSL_HW_ACCEL_FALLTHROUGH" },
|
||||
{ -(MBEDTLS_ERR_SSL_COMPRESSION_FAILED), "SSL_COMPRESSION_FAILED" },
|
||||
{ -(MBEDTLS_ERR_SSL_BAD_HS_PROTOCOL_VERSION), "SSL_BAD_HS_PROTOCOL_VERSION" },
|
||||
{ -(MBEDTLS_ERR_SSL_BAD_HS_NEW_SESSION_TICKET), "SSL_BAD_HS_NEW_SESSION_TICKET" },
|
||||
{ -(MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED), "SSL_SESSION_TICKET_EXPIRED" },
|
||||
{ -(MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH), "SSL_PK_TYPE_MISMATCH" },
|
||||
{ -(MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY), "SSL_UNKNOWN_IDENTITY" },
|
||||
{ -(MBEDTLS_ERR_SSL_INTERNAL_ERROR), "SSL_INTERNAL_ERROR" },
|
||||
{ -(MBEDTLS_ERR_SSL_COUNTER_WRAPPING), "SSL_COUNTER_WRAPPING" },
|
||||
{ -(MBEDTLS_ERR_SSL_WAITING_SERVER_HELLO_RENEGO), "SSL_WAITING_SERVER_HELLO_RENEGO" },
|
||||
{ -(MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED), "SSL_HELLO_VERIFY_REQUIRED" },
|
||||
{ -(MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL), "SSL_BUFFER_TOO_SMALL" },
|
||||
{ -(MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE), "SSL_NO_USABLE_CIPHERSUITE" },
|
||||
{ -(MBEDTLS_ERR_SSL_WANT_READ), "SSL_WANT_READ" },
|
||||
{ -(MBEDTLS_ERR_SSL_WANT_WRITE), "SSL_WANT_WRITE" },
|
||||
{ -(MBEDTLS_ERR_SSL_TIMEOUT), "SSL_TIMEOUT" },
|
||||
{ -(MBEDTLS_ERR_SSL_CLIENT_RECONNECT), "SSL_CLIENT_RECONNECT" },
|
||||
{ -(MBEDTLS_ERR_SSL_UNEXPECTED_RECORD), "SSL_UNEXPECTED_RECORD" },
|
||||
{ -(MBEDTLS_ERR_SSL_NON_FATAL), "SSL_NON_FATAL" },
|
||||
{ -(MBEDTLS_ERR_SSL_INVALID_VERIFY_HASH), "SSL_INVALID_VERIFY_HASH" },
|
||||
{ -(MBEDTLS_ERR_SSL_CONTINUE_PROCESSING), "SSL_CONTINUE_PROCESSING" },
|
||||
{ -(MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS), "SSL_ASYNC_IN_PROGRESS" },
|
||||
{ -(MBEDTLS_ERR_SSL_EARLY_MESSAGE), "SSL_EARLY_MESSAGE" },
|
||||
{ -(MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS), "SSL_CRYPTO_IN_PROGRESS" },
|
||||
{ -(MBEDTLS_ERR_SSL_BAD_CONFIG), "SSL_BAD_CONFIG" },
|
||||
#endif /* MBEDTLS_SSL_TLS_C */
|
||||
|
||||
#if defined(MBEDTLS_X509_USE_C) || defined(MBEDTLS_X509_CREATE_C)
|
||||
{ -(MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE), "X509_FEATURE_UNAVAILABLE" },
|
||||
{ -(MBEDTLS_ERR_X509_UNKNOWN_OID), "X509_UNKNOWN_OID" },
|
||||
{ -(MBEDTLS_ERR_X509_INVALID_FORMAT), "X509_INVALID_FORMAT" },
|
||||
{ -(MBEDTLS_ERR_X509_INVALID_VERSION), "X509_INVALID_VERSION" },
|
||||
{ -(MBEDTLS_ERR_X509_INVALID_SERIAL), "X509_INVALID_SERIAL" },
|
||||
{ -(MBEDTLS_ERR_X509_INVALID_ALG), "X509_INVALID_ALG" },
|
||||
{ -(MBEDTLS_ERR_X509_INVALID_NAME), "X509_INVALID_NAME" },
|
||||
{ -(MBEDTLS_ERR_X509_INVALID_DATE), "X509_INVALID_DATE" },
|
||||
{ -(MBEDTLS_ERR_X509_INVALID_SIGNATURE), "X509_INVALID_SIGNATURE" },
|
||||
{ -(MBEDTLS_ERR_X509_INVALID_EXTENSIONS), "X509_INVALID_EXTENSIONS" },
|
||||
{ -(MBEDTLS_ERR_X509_UNKNOWN_VERSION), "X509_UNKNOWN_VERSION" },
|
||||
{ -(MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG), "X509_UNKNOWN_SIG_ALG" },
|
||||
{ -(MBEDTLS_ERR_X509_SIG_MISMATCH), "X509_SIG_MISMATCH" },
|
||||
{ -(MBEDTLS_ERR_X509_CERT_VERIFY_FAILED), "X509_CERT_VERIFY_FAILED" },
|
||||
{ -(MBEDTLS_ERR_X509_CERT_UNKNOWN_FORMAT), "X509_CERT_UNKNOWN_FORMAT" },
|
||||
{ -(MBEDTLS_ERR_X509_BAD_INPUT_DATA), "X509_BAD_INPUT_DATA" },
|
||||
{ -(MBEDTLS_ERR_X509_ALLOC_FAILED), "X509_ALLOC_FAILED" },
|
||||
{ -(MBEDTLS_ERR_X509_FILE_IO_ERROR), "X509_FILE_IO_ERROR" },
|
||||
{ -(MBEDTLS_ERR_X509_BUFFER_TOO_SMALL), "X509_BUFFER_TOO_SMALL" },
|
||||
{ -(MBEDTLS_ERR_X509_FATAL_ERROR), "X509_FATAL_ERROR" },
|
||||
#endif /* MBEDTLS_X509_USE_C || MBEDTLS_X509_CREATE_C */
|
||||
// END generated code
|
||||
};
|
||||
|
||||
static const struct ssl_errs mbedtls_low_level_error_tab[] = {
|
||||
// Low level error codes
|
||||
//
|
||||
// BEGIN generated code
|
||||
#if defined(MBEDTLS_AES_C)
|
||||
{ -(MBEDTLS_ERR_AES_INVALID_KEY_LENGTH), "AES_INVALID_KEY_LENGTH" },
|
||||
{ -(MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH), "AES_INVALID_INPUT_LENGTH" },
|
||||
{ -(MBEDTLS_ERR_AES_BAD_INPUT_DATA), "AES_BAD_INPUT_DATA" },
|
||||
{ -(MBEDTLS_ERR_AES_FEATURE_UNAVAILABLE), "AES_FEATURE_UNAVAILABLE" },
|
||||
{ -(MBEDTLS_ERR_AES_HW_ACCEL_FAILED), "AES_HW_ACCEL_FAILED" },
|
||||
#endif /* MBEDTLS_AES_C */
|
||||
|
||||
#if defined(MBEDTLS_ARC4_C)
|
||||
{ -(MBEDTLS_ERR_ARC4_HW_ACCEL_FAILED), "ARC4_HW_ACCEL_FAILED" },
|
||||
#endif /* MBEDTLS_ARC4_C */
|
||||
|
||||
#if defined(MBEDTLS_ARIA_C)
|
||||
{ -(MBEDTLS_ERR_ARIA_BAD_INPUT_DATA), "ARIA_BAD_INPUT_DATA" },
|
||||
{ -(MBEDTLS_ERR_ARIA_INVALID_INPUT_LENGTH), "ARIA_INVALID_INPUT_LENGTH" },
|
||||
{ -(MBEDTLS_ERR_ARIA_FEATURE_UNAVAILABLE), "ARIA_FEATURE_UNAVAILABLE" },
|
||||
{ -(MBEDTLS_ERR_ARIA_HW_ACCEL_FAILED), "ARIA_HW_ACCEL_FAILED" },
|
||||
#endif /* MBEDTLS_ARIA_C */
|
||||
|
||||
#if defined(MBEDTLS_ASN1_PARSE_C)
|
||||
{ -(MBEDTLS_ERR_ASN1_OUT_OF_DATA), "ASN1_OUT_OF_DATA" },
|
||||
{ -(MBEDTLS_ERR_ASN1_UNEXPECTED_TAG), "ASN1_UNEXPECTED_TAG" },
|
||||
{ -(MBEDTLS_ERR_ASN1_INVALID_LENGTH), "ASN1_INVALID_LENGTH" },
|
||||
{ -(MBEDTLS_ERR_ASN1_LENGTH_MISMATCH), "ASN1_LENGTH_MISMATCH" },
|
||||
{ -(MBEDTLS_ERR_ASN1_INVALID_DATA), "ASN1_INVALID_DATA" },
|
||||
{ -(MBEDTLS_ERR_ASN1_ALLOC_FAILED), "ASN1_ALLOC_FAILED" },
|
||||
{ -(MBEDTLS_ERR_ASN1_BUF_TOO_SMALL), "ASN1_BUF_TOO_SMALL" },
|
||||
#endif /* MBEDTLS_ASN1_PARSE_C */
|
||||
|
||||
#if defined(MBEDTLS_BASE64_C)
|
||||
{ -(MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL), "BASE64_BUFFER_TOO_SMALL" },
|
||||
{ -(MBEDTLS_ERR_BASE64_INVALID_CHARACTER), "BASE64_INVALID_CHARACTER" },
|
||||
#endif /* MBEDTLS_BASE64_C */
|
||||
|
||||
#if defined(MBEDTLS_BIGNUM_C)
|
||||
{ -(MBEDTLS_ERR_MPI_FILE_IO_ERROR), "MPI_FILE_IO_ERROR" },
|
||||
{ -(MBEDTLS_ERR_MPI_BAD_INPUT_DATA), "MPI_BAD_INPUT_DATA" },
|
||||
{ -(MBEDTLS_ERR_MPI_INVALID_CHARACTER), "MPI_INVALID_CHARACTER" },
|
||||
{ -(MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL), "MPI_BUFFER_TOO_SMALL" },
|
||||
{ -(MBEDTLS_ERR_MPI_NEGATIVE_VALUE), "MPI_NEGATIVE_VALUE" },
|
||||
{ -(MBEDTLS_ERR_MPI_DIVISION_BY_ZERO), "MPI_DIVISION_BY_ZERO" },
|
||||
{ -(MBEDTLS_ERR_MPI_NOT_ACCEPTABLE), "MPI_NOT_ACCEPTABLE" },
|
||||
{ -(MBEDTLS_ERR_MPI_ALLOC_FAILED), "MPI_ALLOC_FAILED" },
|
||||
#endif /* MBEDTLS_BIGNUM_C */
|
||||
|
||||
#if defined(MBEDTLS_BLOWFISH_C)
|
||||
{ -(MBEDTLS_ERR_BLOWFISH_BAD_INPUT_DATA), "BLOWFISH_BAD_INPUT_DATA" },
|
||||
{ -(MBEDTLS_ERR_BLOWFISH_INVALID_INPUT_LENGTH), "BLOWFISH_INVALID_INPUT_LENGTH" },
|
||||
{ -(MBEDTLS_ERR_BLOWFISH_HW_ACCEL_FAILED), "BLOWFISH_HW_ACCEL_FAILED" },
|
||||
#endif /* MBEDTLS_BLOWFISH_C */
|
||||
|
||||
#if defined(MBEDTLS_CAMELLIA_C)
|
||||
{ -(MBEDTLS_ERR_CAMELLIA_BAD_INPUT_DATA), "CAMELLIA_BAD_INPUT_DATA" },
|
||||
{ -(MBEDTLS_ERR_CAMELLIA_INVALID_INPUT_LENGTH), "CAMELLIA_INVALID_INPUT_LENGTH" },
|
||||
{ -(MBEDTLS_ERR_CAMELLIA_HW_ACCEL_FAILED), "CAMELLIA_HW_ACCEL_FAILED" },
|
||||
#endif /* MBEDTLS_CAMELLIA_C */
|
||||
|
||||
#if defined(MBEDTLS_CCM_C)
|
||||
{ -(MBEDTLS_ERR_CCM_BAD_INPUT), "CCM_BAD_INPUT" },
|
||||
{ -(MBEDTLS_ERR_CCM_AUTH_FAILED), "CCM_AUTH_FAILED" },
|
||||
{ -(MBEDTLS_ERR_CCM_HW_ACCEL_FAILED), "CCM_HW_ACCEL_FAILED" },
|
||||
#endif /* MBEDTLS_CCM_C */
|
||||
|
||||
#if defined(MBEDTLS_CHACHA20_C)
|
||||
{ -(MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA), "CHACHA20_BAD_INPUT_DATA" },
|
||||
{ -(MBEDTLS_ERR_CHACHA20_FEATURE_UNAVAILABLE), "CHACHA20_FEATURE_UNAVAILABLE" },
|
||||
{ -(MBEDTLS_ERR_CHACHA20_HW_ACCEL_FAILED), "CHACHA20_HW_ACCEL_FAILED" },
|
||||
#endif /* MBEDTLS_CHACHA20_C */
|
||||
|
||||
#if defined(MBEDTLS_CHACHAPOLY_C)
|
||||
{ -(MBEDTLS_ERR_CHACHAPOLY_BAD_STATE), "CHACHAPOLY_BAD_STATE" },
|
||||
{ -(MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED), "CHACHAPOLY_AUTH_FAILED" },
|
||||
#endif /* MBEDTLS_CHACHAPOLY_C */
|
||||
|
||||
#if defined(MBEDTLS_CMAC_C)
|
||||
{ -(MBEDTLS_ERR_CMAC_HW_ACCEL_FAILED), "CMAC_HW_ACCEL_FAILED" },
|
||||
#endif /* MBEDTLS_CMAC_C */
|
||||
|
||||
#if defined(MBEDTLS_CTR_DRBG_C)
|
||||
{ -(MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED), "CTR_DRBG_ENTROPY_SOURCE_FAILED" },
|
||||
{ -(MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG), "CTR_DRBG_REQUEST_TOO_BIG" },
|
||||
{ -(MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG), "CTR_DRBG_INPUT_TOO_BIG" },
|
||||
{ -(MBEDTLS_ERR_CTR_DRBG_FILE_IO_ERROR), "CTR_DRBG_FILE_IO_ERROR" },
|
||||
#endif /* MBEDTLS_CTR_DRBG_C */
|
||||
|
||||
#if defined(MBEDTLS_DES_C)
|
||||
{ -(MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH), "DES_INVALID_INPUT_LENGTH" },
|
||||
{ -(MBEDTLS_ERR_DES_HW_ACCEL_FAILED), "DES_HW_ACCEL_FAILED" },
|
||||
#endif /* MBEDTLS_DES_C */
|
||||
|
||||
#if defined(MBEDTLS_ENTROPY_C)
|
||||
{ -(MBEDTLS_ERR_ENTROPY_SOURCE_FAILED), "ENTROPY_SOURCE_FAILED" },
|
||||
{ -(MBEDTLS_ERR_ENTROPY_MAX_SOURCES), "ENTROPY_MAX_SOURCES" },
|
||||
{ -(MBEDTLS_ERR_ENTROPY_NO_SOURCES_DEFINED), "ENTROPY_NO_SOURCES_DEFINED" },
|
||||
{ -(MBEDTLS_ERR_ENTROPY_NO_STRONG_SOURCE), "ENTROPY_NO_STRONG_SOURCE" },
|
||||
{ -(MBEDTLS_ERR_ENTROPY_FILE_IO_ERROR), "ENTROPY_FILE_IO_ERROR" },
|
||||
#endif /* MBEDTLS_ENTROPY_C */
|
||||
|
||||
#if defined(MBEDTLS_GCM_C)
|
||||
{ -(MBEDTLS_ERR_GCM_AUTH_FAILED), "GCM_AUTH_FAILED" },
|
||||
{ -(MBEDTLS_ERR_GCM_HW_ACCEL_FAILED), "GCM_HW_ACCEL_FAILED" },
|
||||
{ -(MBEDTLS_ERR_GCM_BAD_INPUT), "GCM_BAD_INPUT" },
|
||||
#endif /* MBEDTLS_GCM_C */
|
||||
|
||||
#if defined(MBEDTLS_HKDF_C)
|
||||
{ -(MBEDTLS_ERR_HKDF_BAD_INPUT_DATA), "HKDF_BAD_INPUT_DATA" },
|
||||
#endif /* MBEDTLS_HKDF_C */
|
||||
|
||||
#if defined(MBEDTLS_HMAC_DRBG_C)
|
||||
{ -(MBEDTLS_ERR_HMAC_DRBG_REQUEST_TOO_BIG), "HMAC_DRBG_REQUEST_TOO_BIG" },
|
||||
{ -(MBEDTLS_ERR_HMAC_DRBG_INPUT_TOO_BIG), "HMAC_DRBG_INPUT_TOO_BIG" },
|
||||
{ -(MBEDTLS_ERR_HMAC_DRBG_FILE_IO_ERROR), "HMAC_DRBG_FILE_IO_ERROR" },
|
||||
{ -(MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED), "HMAC_DRBG_ENTROPY_SOURCE_FAILED" },
|
||||
#endif /* MBEDTLS_HMAC_DRBG_C */
|
||||
|
||||
#if defined(MBEDTLS_MD2_C)
|
||||
{ -(MBEDTLS_ERR_MD2_HW_ACCEL_FAILED), "MD2_HW_ACCEL_FAILED" },
|
||||
#endif /* MBEDTLS_MD2_C */
|
||||
|
||||
#if defined(MBEDTLS_MD4_C)
|
||||
{ -(MBEDTLS_ERR_MD4_HW_ACCEL_FAILED), "MD4_HW_ACCEL_FAILED" },
|
||||
#endif /* MBEDTLS_MD4_C */
|
||||
|
||||
#if defined(MBEDTLS_MD5_C)
|
||||
{ -(MBEDTLS_ERR_MD5_HW_ACCEL_FAILED), "MD5_HW_ACCEL_FAILED" },
|
||||
#endif /* MBEDTLS_MD5_C */
|
||||
|
||||
#if defined(MBEDTLS_NET_C)
|
||||
{ -(MBEDTLS_ERR_NET_SOCKET_FAILED), "NET_SOCKET_FAILED" },
|
||||
{ -(MBEDTLS_ERR_NET_CONNECT_FAILED), "NET_CONNECT_FAILED" },
|
||||
{ -(MBEDTLS_ERR_NET_BIND_FAILED), "NET_BIND_FAILED" },
|
||||
{ -(MBEDTLS_ERR_NET_LISTEN_FAILED), "NET_LISTEN_FAILED" },
|
||||
{ -(MBEDTLS_ERR_NET_ACCEPT_FAILED), "NET_ACCEPT_FAILED" },
|
||||
{ -(MBEDTLS_ERR_NET_RECV_FAILED), "NET_RECV_FAILED" },
|
||||
{ -(MBEDTLS_ERR_NET_SEND_FAILED), "NET_SEND_FAILED" },
|
||||
{ -(MBEDTLS_ERR_NET_CONN_RESET), "NET_CONN_RESET" },
|
||||
{ -(MBEDTLS_ERR_NET_UNKNOWN_HOST), "NET_UNKNOWN_HOST" },
|
||||
{ -(MBEDTLS_ERR_NET_BUFFER_TOO_SMALL), "NET_BUFFER_TOO_SMALL" },
|
||||
{ -(MBEDTLS_ERR_NET_INVALID_CONTEXT), "NET_INVALID_CONTEXT" },
|
||||
{ -(MBEDTLS_ERR_NET_POLL_FAILED), "NET_POLL_FAILED" },
|
||||
{ -(MBEDTLS_ERR_NET_BAD_INPUT_DATA), "NET_BAD_INPUT_DATA" },
|
||||
#endif /* MBEDTLS_NET_C */
|
||||
|
||||
#if defined(MBEDTLS_OID_C)
|
||||
{ -(MBEDTLS_ERR_OID_NOT_FOUND), "OID_NOT_FOUND" },
|
||||
{ -(MBEDTLS_ERR_OID_BUF_TOO_SMALL), "OID_BUF_TOO_SMALL" },
|
||||
#endif /* MBEDTLS_OID_C */
|
||||
|
||||
#if defined(MBEDTLS_PADLOCK_C)
|
||||
{ -(MBEDTLS_ERR_PADLOCK_DATA_MISALIGNED), "PADLOCK_DATA_MISALIGNED" },
|
||||
#endif /* MBEDTLS_PADLOCK_C */
|
||||
|
||||
#if defined(MBEDTLS_PLATFORM_C)
|
||||
{ -(MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED), "PLATFORM_HW_ACCEL_FAILED" },
|
||||
{ -(MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED), "PLATFORM_FEATURE_UNSUPPORTED" },
|
||||
#endif /* MBEDTLS_PLATFORM_C */
|
||||
|
||||
#if defined(MBEDTLS_POLY1305_C)
|
||||
{ -(MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA), "POLY1305_BAD_INPUT_DATA" },
|
||||
{ -(MBEDTLS_ERR_POLY1305_FEATURE_UNAVAILABLE), "POLY1305_FEATURE_UNAVAILABLE" },
|
||||
{ -(MBEDTLS_ERR_POLY1305_HW_ACCEL_FAILED), "POLY1305_HW_ACCEL_FAILED" },
|
||||
#endif /* MBEDTLS_POLY1305_C */
|
||||
|
||||
#if defined(MBEDTLS_RIPEMD160_C)
|
||||
{ -(MBEDTLS_ERR_RIPEMD160_HW_ACCEL_FAILED), "RIPEMD160_HW_ACCEL_FAILED" },
|
||||
#endif /* MBEDTLS_RIPEMD160_C */
|
||||
|
||||
#if defined(MBEDTLS_SHA1_C)
|
||||
{ -(MBEDTLS_ERR_SHA1_HW_ACCEL_FAILED), "SHA1_HW_ACCEL_FAILED" },
|
||||
{ -(MBEDTLS_ERR_SHA1_BAD_INPUT_DATA), "SHA1_BAD_INPUT_DATA" },
|
||||
#endif /* MBEDTLS_SHA1_C */
|
||||
|
||||
#if defined(MBEDTLS_SHA256_C)
|
||||
{ -(MBEDTLS_ERR_SHA256_HW_ACCEL_FAILED), "SHA256_HW_ACCEL_FAILED" },
|
||||
{ -(MBEDTLS_ERR_SHA256_BAD_INPUT_DATA), "SHA256_BAD_INPUT_DATA" },
|
||||
#endif /* MBEDTLS_SHA256_C */
|
||||
|
||||
#if defined(MBEDTLS_SHA512_C)
|
||||
{ -(MBEDTLS_ERR_SHA512_HW_ACCEL_FAILED), "SHA512_HW_ACCEL_FAILED" },
|
||||
{ -(MBEDTLS_ERR_SHA512_BAD_INPUT_DATA), "SHA512_BAD_INPUT_DATA" },
|
||||
#endif /* MBEDTLS_SHA512_C */
|
||||
|
||||
#if defined(MBEDTLS_THREADING_C)
|
||||
{ -(MBEDTLS_ERR_THREADING_FEATURE_UNAVAILABLE), "THREADING_FEATURE_UNAVAILABLE" },
|
||||
{ -(MBEDTLS_ERR_THREADING_BAD_INPUT_DATA), "THREADING_BAD_INPUT_DATA" },
|
||||
{ -(MBEDTLS_ERR_THREADING_MUTEX_ERROR), "THREADING_MUTEX_ERROR" },
|
||||
#endif /* MBEDTLS_THREADING_C */
|
||||
|
||||
#if defined(MBEDTLS_XTEA_C)
|
||||
{ -(MBEDTLS_ERR_XTEA_INVALID_INPUT_LENGTH), "XTEA_INVALID_INPUT_LENGTH" },
|
||||
{ -(MBEDTLS_ERR_XTEA_HW_ACCEL_FAILED), "XTEA_HW_ACCEL_FAILED" },
|
||||
#endif /* MBEDTLS_XTEA_C */
|
||||
// END generated code
|
||||
};
|
||||
|
||||
static const char *mbedtls_err_prefix = "MBEDTLS_ERR_";
|
||||
#define MBEDTLS_ERR_PREFIX_LEN ( sizeof("MBEDTLS_ERR_")-1 )
|
||||
|
||||
// copy error text into buffer, ensure null termination, return strlen of result
|
||||
static size_t mbedtls_err_to_str(int err, const struct ssl_errs tab[], int tab_len, char *buf, size_t buflen) {
|
||||
if (buflen == 0) return 0;
|
||||
|
||||
// prefix for all error names
|
||||
strncpy(buf, mbedtls_err_prefix, buflen);
|
||||
if (buflen <= MBEDTLS_ERR_PREFIX_LEN+1) {
|
||||
buf[buflen-1] = 0;
|
||||
return buflen-1;
|
||||
}
|
||||
|
||||
// append error name from table
|
||||
for (int i = 0; i < tab_len; i++) {
|
||||
if (tab[i].errnum == err) {
|
||||
strncpy(buf+MBEDTLS_ERR_PREFIX_LEN, tab[i].errstr, buflen-MBEDTLS_ERR_PREFIX_LEN);
|
||||
buf[buflen-1] = 0;
|
||||
return strlen(buf);
|
||||
}
|
||||
}
|
||||
|
||||
mbedtls_snprintf(buf+MBEDTLS_ERR_PREFIX_LEN, buflen-MBEDTLS_ERR_PREFIX_LEN, "UNKNOWN (0x%04X)",
|
||||
err);
|
||||
return strlen(buf);
|
||||
}
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
|
||||
|
||||
void mbedtls_strerror(int ret, char *buf, size_t buflen) {
|
||||
int use_ret;
|
||||
|
||||
if (buflen == 0) return;
|
||||
|
||||
buf[buflen-1] = 0;
|
||||
|
||||
if (ret < 0) ret = -ret;
|
||||
|
||||
//
|
||||
// High-level error codes
|
||||
//
|
||||
uint8_t got_hl = (ret & 0xFF80) != 0;
|
||||
if (got_hl) {
|
||||
use_ret = ret & 0xFF80;
|
||||
|
||||
// special case
|
||||
#if defined(MBEDTLS_SSL_TLS_C)
|
||||
if (use_ret == -(MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE)) {
|
||||
strncpy(buf, "MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE", buflen);
|
||||
buf[buflen-1] = 0;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t len = mbedtls_err_to_str(use_ret, mbedtls_high_level_error_tab,
|
||||
ARRAY_SIZE(mbedtls_high_level_error_tab), buf, buflen);
|
||||
|
||||
buf += len;
|
||||
buflen -= len;
|
||||
if (buflen == 0) return;
|
||||
}
|
||||
|
||||
//
|
||||
// Low-level error codes
|
||||
//
|
||||
use_ret = ret & ~0xFF80;
|
||||
|
||||
if (use_ret == 0) return;
|
||||
|
||||
// If high level code is present, make a concatenation between both error strings.
|
||||
if (got_hl) {
|
||||
if (buflen < 2) return;
|
||||
*buf++ = '+';
|
||||
buflen--;
|
||||
}
|
||||
|
||||
mbedtls_err_to_str(use_ret, mbedtls_low_level_error_tab,
|
||||
ARRAY_SIZE(mbedtls_low_level_error_tab), buf, buflen);
|
||||
}
|
||||
|
||||
#else /* MBEDTLS_ERROR_C */
|
||||
|
||||
#if defined(MBEDTLS_ERROR_STRERROR_DUMMY)
|
||||
|
||||
/*
|
||||
* Provide an non-function in case MBEDTLS_ERROR_C is not defined
|
||||
*/
|
||||
void mbedtls_strerror( int ret, char *buf, size_t buflen )
|
||||
{
|
||||
((void) ret);
|
||||
|
||||
if( buflen > 0 )
|
||||
buf[0] = '\0';
|
||||
}
|
||||
|
||||
#endif /* MBEDTLS_ERROR_STRERROR_DUMMY */
|
||||
|
||||
#endif /* MBEDTLS_ERROR_C */
|
|
@ -1,22 +1,29 @@
|
|||
--- generate_errors_orig.pl 2020-06-20 08:40:38.819060379 -0700
|
||||
+++ generate_errors.pl 2020-06-20 08:47:26.511163591 -0700
|
||||
@@ -162,16 +162,12 @@
|
||||
|
||||
if ($error_name eq "MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE")
|
||||
--- generate_errors_orig.pl 2023-04-30 17:58:23.503070758 +1000
|
||||
+++ generate_errors.py 2023-04-30 17:58:20.826338349 +1000
|
||||
@@ -162,7 +162,7 @@
|
||||
{
|
||||
- ${$code_check} .= "${white_space}if( use_ret == -($error_name) )\n".
|
||||
- "${white_space}\{\n".
|
||||
- "${white_space} mbedtls_snprintf( buf, buflen, \"$module_name - $description\" );\n".
|
||||
- "${white_space} return;\n".
|
||||
- "${white_space}}\n"
|
||||
+ # no-op, this case is hard-coded in error.fmt
|
||||
$code_check = \$ll_code_check;
|
||||
$old_define = \$ll_old_define;
|
||||
- $white_space = ' ';
|
||||
+ $white_space = ' ';
|
||||
}
|
||||
else
|
||||
{
|
||||
- ${$code_check} .= "${white_space}if( use_ret == -($error_name) )\n".
|
||||
- "${white_space} mbedtls_snprintf( buf, buflen, \"$module_name - $description\" );\n"
|
||||
+ my $error_text = $error_name =~ s/^MBEDTLS_ERR_//r;
|
||||
+ ${$code_check} .= "${white_space}{ -($error_name), \"$error_text\" },\n"
|
||||
@@ -203,8 +203,15 @@
|
||||
${$old_define} = $define_name;
|
||||
}
|
||||
|
||||
- ${$code_check} .= "${white_space}case -($error_name):\n".
|
||||
- "${white_space} return( \"$module_name - $description\" );\n"
|
||||
+ if ($error_name eq "MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE")
|
||||
+ {
|
||||
+ # no-op, this case is hard-coded in error.fmt
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ my $error_text = $error_name =~ s/^MBEDTLS_ERR_//r;
|
||||
+ ${$code_check} .= "${white_space}{ -($error_name), \"$error_text\" },\n"
|
||||
+ }
|
||||
};
|
||||
|
||||
if ($ll_old_define ne "")
|
||||
|
|
|
@ -53,6 +53,10 @@
|
|||
#include "mbedtls/aria.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_ASN1_PARSE_C)
|
||||
#include "mbedtls/asn1.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_BASE64_C)
|
||||
#include "mbedtls/base64.h"
|
||||
#endif
|
||||
|
@ -109,6 +113,10 @@
|
|||
#include "mbedtls/entropy.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_ERROR_C)
|
||||
#include "mbedtls/error.h"
|
||||
#endif
|
||||
|
||||
#if defined(MBEDTLS_GCM_C)
|
||||
#include "mbedtls/gcm.h"
|
||||
#endif
|
||||
|
@ -377,7 +385,10 @@ static const struct ssl_errs mbedtls_high_level_error_tab[] = {
|
|||
{ -(MBEDTLS_ERR_SSL_CONTINUE_PROCESSING), "SSL_CONTINUE_PROCESSING" },
|
||||
{ -(MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS), "SSL_ASYNC_IN_PROGRESS" },
|
||||
{ -(MBEDTLS_ERR_SSL_EARLY_MESSAGE), "SSL_EARLY_MESSAGE" },
|
||||
{ -(MBEDTLS_ERR_SSL_UNEXPECTED_CID), "SSL_UNEXPECTED_CID" },
|
||||
{ -(MBEDTLS_ERR_SSL_VERSION_MISMATCH), "SSL_VERSION_MISMATCH" },
|
||||
{ -(MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS), "SSL_CRYPTO_IN_PROGRESS" },
|
||||
{ -(MBEDTLS_ERR_SSL_BAD_CONFIG), "SSL_BAD_CONFIG" },
|
||||
#endif /* MBEDTLS_SSL_TLS_C */
|
||||
|
||||
#if defined(MBEDTLS_X509_USE_C) || defined(MBEDTLS_X509_CREATE_C)
|
||||
|
@ -507,6 +518,11 @@ static const struct ssl_errs mbedtls_low_level_error_tab[] = {
|
|||
{ -(MBEDTLS_ERR_ENTROPY_FILE_IO_ERROR), "ENTROPY_FILE_IO_ERROR" },
|
||||
#endif /* MBEDTLS_ENTROPY_C */
|
||||
|
||||
#if defined(MBEDTLS_ERROR_C)
|
||||
{ -(MBEDTLS_ERR_ERROR_GENERIC_ERROR), "ERROR_GENERIC_ERROR" },
|
||||
{ -(MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED), "ERROR_CORRUPTION_DETECTED" },
|
||||
#endif /* MBEDTLS_ERROR_C */
|
||||
|
||||
#if defined(MBEDTLS_GCM_C)
|
||||
{ -(MBEDTLS_ERR_GCM_AUTH_FAILED), "GCM_AUTH_FAILED" },
|
||||
{ -(MBEDTLS_ERR_GCM_HW_ACCEL_FAILED), "GCM_HW_ACCEL_FAILED" },
|
||||
|
@ -650,7 +666,7 @@ void mbedtls_strerror(int ret, char *buf, size_t buflen) {
|
|||
if (got_hl) {
|
||||
use_ret = ret & 0xFF80;
|
||||
|
||||
// special case
|
||||
// special case, don't try to translate low level code
|
||||
#if defined(MBEDTLS_SSL_TLS_C)
|
||||
if (use_ret == -(MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE)) {
|
||||
strncpy(buf, "MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE", buflen);
|
||||
|
|
|
@ -124,7 +124,7 @@ class Pins:
|
|||
continue
|
||||
if not row[pin_col].isdigit():
|
||||
raise ValueError(
|
||||
"Invalid pin number {:s} in row {:s}".format(row[pin_col]), row
|
||||
"Invalid pin number {:s} in row {:s}".format(row[pin_col], row)
|
||||
)
|
||||
# Pin numbers must start from 0 when used with the TI API
|
||||
pin_num = int(row[pin_col]) - 1
|
||||
|
|
|
@ -28,5 +28,5 @@
|
|||
#include "py/builtin.h"
|
||||
|
||||
const char cc3200_help_text[] = "Welcome to MicroPython!\n"
|
||||
"For online help please visit http://micropython.org/help/.\n"
|
||||
"For online docs please visit http://docs.micropython.org/\n"
|
||||
"For further help on a specific object, type help(obj)\n";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
r"""
|
||||
Flash the WiPy (format, update service pack and program).
|
||||
|
||||
Example:
|
||||
|
|
|
@ -109,7 +109,7 @@ def reset_board(args):
|
|||
finally:
|
||||
try:
|
||||
tn.close()
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
pass
|
||||
return success
|
||||
|
||||
|
@ -167,7 +167,7 @@ def verify_update(args):
|
|||
finally:
|
||||
try:
|
||||
tn.close()
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
pass
|
||||
return success
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ set(SDKCONFIG_DEFAULTS
|
|||
boards/sdkconfig.base
|
||||
boards/sdkconfig.spiram_sx
|
||||
boards/sdkconfig.usb
|
||||
boards/UM_FEATHERS2NEO/sdkconfig.board
|
||||
)
|
||||
|
||||
set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py)
|
||||
set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py)
|
||||
|
|
|
@ -3,6 +3,8 @@ CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
|
|||
CONFIG_USB_AND_UART=y
|
||||
CONFIG_ESPTOOLPY_AFTER_NORESET=y
|
||||
|
||||
CONFIG_SPIRAM_MEMTEST=
|
||||
|
||||
# LWIP
|
||||
CONFIG_LWIP_LOCAL_HOSTNAME="UMFeatherS2Neo"
|
||||
# end of LWIP
|
||||
|
|
|
@ -3,4 +3,7 @@ set(SDKCONFIG_DEFAULTS
|
|||
boards/sdkconfig.base
|
||||
boards/sdkconfig.spiram_sx
|
||||
boards/sdkconfig.usb
|
||||
boards/UM_TINYS2/sdkconfig.board
|
||||
)
|
||||
|
||||
set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py)
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
CONFIG_FLASHMODE_QIO=y
|
||||
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
|
||||
CONFIG_USB_AND_UART=y
|
||||
CONFIG_ESPTOOLPY_AFTER_NORESET=y
|
||||
|
||||
CONFIG_SPIRAM_MEMTEST=
|
||||
|
||||
# LWIP
|
||||
CONFIG_LWIP_LOCAL_HOSTNAME="UMTinyS2"
|
||||
# end of LWIP
|
||||
|
|
|
@ -5,6 +5,7 @@ include("$(MPY_DIR)/extmod/uasyncio")
|
|||
require("bundle-networking")
|
||||
|
||||
# Require some micropython-lib modules.
|
||||
# require("aioespnow")
|
||||
require("dht")
|
||||
require("ds18x20")
|
||||
require("neopixel")
|
||||
|
|
|
@ -26,7 +26,8 @@
|
|||
|
||||
#include "py/runtime.h"
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
|
||||
|
||||
#include "esp32/ulp.h"
|
||||
#include "esp_err.h"
|
||||
|
@ -87,7 +88,13 @@ STATIC const mp_rom_map_elem_t esp32_ulp_locals_dict_table[] = {
|
|||
{ MP_ROM_QSTR(MP_QSTR_set_wakeup_period), MP_ROM_PTR(&esp32_ulp_set_wakeup_period_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_load_binary), MP_ROM_PTR(&esp32_ulp_load_binary_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_run), MP_ROM_PTR(&esp32_ulp_run_obj) },
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
{ MP_ROM_QSTR(MP_QSTR_RESERVE_MEM), MP_ROM_INT(CONFIG_ESP32_ULP_COPROC_RESERVE_MEM) },
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
{ MP_ROM_QSTR(MP_QSTR_RESERVE_MEM), MP_ROM_INT(CONFIG_ESP32S2_ULP_COPROC_RESERVE_MEM) },
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
{ MP_ROM_QSTR(MP_QSTR_RESERVE_MEM), MP_ROM_INT(CONFIG_ESP32S3_ULP_COPROC_RESERVE_MEM) },
|
||||
#endif
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(esp32_ulp_locals_dict, esp32_ulp_locals_dict_table);
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
const char esp32_help_text[] =
|
||||
"Welcome to MicroPython on the ESP32!\n"
|
||||
"\n"
|
||||
"For generic online docs please visit http://docs.micropython.org/\n"
|
||||
"For online docs please visit http://docs.micropython.org/\n"
|
||||
"\n"
|
||||
"For access to the hardware use the 'machine' module:\n"
|
||||
"\n"
|
||||
|
|
|
@ -319,6 +319,10 @@ STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args,
|
|||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
.rx_flow_ctrl_thresh = 0
|
||||
};
|
||||
#if SOC_UART_SUPPORT_XTAL_CLK
|
||||
// works independently of APB frequency
|
||||
uartcfg.source_clk = UART_SCLK_XTAL; // ESP32C3, ESP32S3
|
||||
#endif
|
||||
|
||||
// create instance
|
||||
machine_uart_obj_t *self = mp_obj_malloc(machine_uart_obj_t, &machine_uart_type);
|
||||
|
|
|
@ -67,6 +67,10 @@
|
|||
#include "extmod/modbluetooth.h"
|
||||
#endif
|
||||
|
||||
#if MICROPY_ESPNOW
|
||||
#include "modespnow.h"
|
||||
#endif
|
||||
|
||||
// MicroPython runs as a task under FreeRTOS
|
||||
#define MP_TASK_PRIORITY (ESP_TASK_PRIO_MIN + 1)
|
||||
#define MP_TASK_STACK_SIZE (16 * 1024)
|
||||
|
@ -190,6 +194,11 @@ soft_reset_exit:
|
|||
mp_bluetooth_deinit();
|
||||
#endif
|
||||
|
||||
#if MICROPY_ESPNOW
|
||||
espnow_deinit(mp_const_none);
|
||||
MP_STATE_PORT(espnow_singleton) = NULL;
|
||||
#endif
|
||||
|
||||
machine_timer_deinit_all();
|
||||
|
||||
#if MICROPY_PY_THREAD
|
||||
|
|
|
@ -34,7 +34,7 @@ set(MICROPY_SOURCE_LIB
|
|||
${MICROPY_DIR}/lib/littlefs/lfs1_util.c
|
||||
${MICROPY_DIR}/lib/littlefs/lfs2.c
|
||||
${MICROPY_DIR}/lib/littlefs/lfs2_util.c
|
||||
${MICROPY_DIR}/lib/mbedtls_errors/mp_mbedtls_errors.c
|
||||
${MICROPY_DIR}/lib/mbedtls_errors/esp32_mbedtls_errors.c
|
||||
${MICROPY_DIR}/lib/oofatfs/ff.c
|
||||
${MICROPY_DIR}/lib/oofatfs/ffunicode.c
|
||||
)
|
||||
|
@ -84,6 +84,7 @@ set(MICROPY_SOURCE_PORT
|
|||
${PROJECT_DIR}/mpthreadport.c
|
||||
${PROJECT_DIR}/machine_rtc.c
|
||||
${PROJECT_DIR}/machine_sdcard.c
|
||||
${PROJECT_DIR}/modespnow.c
|
||||
)
|
||||
|
||||
set(MICROPY_SOURCE_QSTR
|
||||
|
|
|
@ -217,7 +217,7 @@ STATIC const mp_rom_map_elem_t esp32_module_globals_table[] = {
|
|||
{ MP_ROM_QSTR(MP_QSTR_NVS), MP_ROM_PTR(&esp32_nvs_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_Partition), MP_ROM_PTR(&esp32_partition_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_RMT), MP_ROM_PTR(&esp32_rmt_type) },
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
|
||||
{ MP_ROM_QSTR(MP_QSTR_ULP), MP_ROM_PTR(&esp32_ulp_type) },
|
||||
#endif
|
||||
|
||||
|
|
|
@ -0,0 +1,884 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2017-2020 Nick Moore
|
||||
* Copyright (c) 2018 shawwwn <shawwwn1@gmail.com>
|
||||
* Copyright (c) 2020-2021 Glenn Moloney @glenn20
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_now.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_wifi_types.h"
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/mphal.h"
|
||||
#include "py/mperrno.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/objstr.h"
|
||||
#include "py/objarray.h"
|
||||
#include "py/stream.h"
|
||||
#include "py/binary.h"
|
||||
#include "py/ringbuf.h"
|
||||
|
||||
#include "mpconfigport.h"
|
||||
#include "mphalport.h"
|
||||
#include "modnetwork.h"
|
||||
#include "modespnow.h"
|
||||
|
||||
#ifndef MICROPY_ESPNOW_RSSI
|
||||
// Include code to track rssi of peers
|
||||
#define MICROPY_ESPNOW_RSSI 1
|
||||
#endif
|
||||
#ifndef MICROPY_ESPNOW_EXTRA_PEER_METHODS
|
||||
// Include mod_peer(),get_peer(),peer_count()
|
||||
#define MICROPY_ESPNOW_EXTRA_PEER_METHODS 1
|
||||
#endif
|
||||
|
||||
// Relies on gcc Variadic Macros and Statement Expressions
|
||||
#define NEW_TUPLE(...) \
|
||||
({mp_obj_t _z[] = {__VA_ARGS__}; mp_obj_new_tuple(MP_ARRAY_SIZE(_z), _z); })
|
||||
|
||||
static const uint8_t ESPNOW_MAGIC = 0x99;
|
||||
|
||||
// ESPNow packet format for the receive buffer.
|
||||
// Use this for peeking at the header of the next packet in the buffer.
|
||||
typedef struct {
|
||||
uint8_t magic; // = ESPNOW_MAGIC
|
||||
uint8_t msg_len; // Length of the message
|
||||
#if MICROPY_ESPNOW_RSSI
|
||||
uint32_t time_ms; // Timestamp (ms) when packet is received
|
||||
int8_t rssi; // RSSI value (dBm) (-127 to 0)
|
||||
#endif // MICROPY_ESPNOW_RSSI
|
||||
} __attribute__((packed)) espnow_hdr_t;
|
||||
|
||||
typedef struct {
|
||||
espnow_hdr_t hdr; // The header
|
||||
uint8_t peer[6]; // Peer address
|
||||
uint8_t msg[0]; // Message is up to 250 bytes
|
||||
} __attribute__((packed)) espnow_pkt_t;
|
||||
|
||||
// The maximum length of an espnow packet (bytes)
|
||||
static const size_t MAX_PACKET_LEN = (
|
||||
(sizeof(espnow_pkt_t) + ESP_NOW_MAX_DATA_LEN));
|
||||
|
||||
// Enough for 2 full-size packets: 2 * (6 + 7 + 250) = 526 bytes
|
||||
// Will allocate an additional 7 bytes for buffer overhead
|
||||
static const size_t DEFAULT_RECV_BUFFER_SIZE = (2 * MAX_PACKET_LEN);
|
||||
|
||||
// Default timeout (millisec) to wait for incoming ESPNow messages (5 minutes).
|
||||
static const size_t DEFAULT_RECV_TIMEOUT_MS = (5 * 60 * 1000);
|
||||
|
||||
// Time to wait (millisec) for responses from sent packets: (2 seconds).
|
||||
static const size_t DEFAULT_SEND_TIMEOUT_MS = (2 * 1000);
|
||||
|
||||
// Number of milliseconds to wait for pending responses to sent packets.
|
||||
// This is a fallback which should never be reached.
|
||||
static const mp_uint_t PENDING_RESPONSES_TIMEOUT_MS = 100;
|
||||
static const mp_uint_t PENDING_RESPONSES_BUSY_POLL_MS = 10;
|
||||
|
||||
// The data structure for the espnow_singleton.
|
||||
typedef struct _esp_espnow_obj_t {
|
||||
mp_obj_base_t base;
|
||||
|
||||
ringbuf_t *recv_buffer; // A buffer for received packets
|
||||
size_t recv_buffer_size; // The size of the recv_buffer
|
||||
mp_int_t recv_timeout_ms; // Timeout for recv()
|
||||
volatile size_t rx_packets; // # of received packets
|
||||
size_t dropped_rx_pkts; // # of dropped packets (buffer full)
|
||||
size_t tx_packets; // # of sent packets
|
||||
volatile size_t tx_responses; // # of sent packet responses received
|
||||
volatile size_t tx_failures; // # of sent packet responses failed
|
||||
size_t peer_count; // Cache the # of peers for send(sync=True)
|
||||
mp_obj_t recv_cb; // Callback when a packet is received
|
||||
mp_obj_t recv_cb_arg; // Argument passed to callback
|
||||
#if MICROPY_ESPNOW_RSSI
|
||||
mp_obj_t peers_table; // A dictionary of discovered peers
|
||||
#endif // MICROPY_ESPNOW_RSSI
|
||||
} esp_espnow_obj_t;
|
||||
|
||||
const mp_obj_type_t esp_espnow_type;
|
||||
|
||||
// ### Initialisation and Config functions
|
||||
//
|
||||
|
||||
// Return a pointer to the ESPNow module singleton
|
||||
// If state == INITIALISED check the device has been initialised.
|
||||
// Raises OSError if not initialised and state == INITIALISED.
|
||||
static esp_espnow_obj_t *_get_singleton() {
|
||||
return MP_STATE_PORT(espnow_singleton);
|
||||
}
|
||||
|
||||
static esp_espnow_obj_t *_get_singleton_initialised() {
|
||||
esp_espnow_obj_t *self = _get_singleton();
|
||||
// assert(self);
|
||||
if (self->recv_buffer == NULL) {
|
||||
// Throw an espnow not initialised error
|
||||
check_esp_err(ESP_ERR_ESPNOW_NOT_INIT);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// Allocate and initialise the ESPNow module as a singleton.
|
||||
// Returns the initialised espnow_singleton.
|
||||
STATIC mp_obj_t espnow_make_new(const mp_obj_type_t *type, size_t n_args,
|
||||
size_t n_kw, const mp_obj_t *all_args) {
|
||||
|
||||
// The espnow_singleton must be defined in MICROPY_PORT_ROOT_POINTERS
|
||||
// (see mpconfigport.h) to prevent memory allocated here from being
|
||||
// garbage collected.
|
||||
// NOTE: on soft reset the espnow_singleton MUST be set to NULL and the
|
||||
// ESP-NOW functions de-initialised (see main.c).
|
||||
esp_espnow_obj_t *self = MP_STATE_PORT(espnow_singleton);
|
||||
if (self != NULL) {
|
||||
return self;
|
||||
}
|
||||
self = m_new_obj(esp_espnow_obj_t);
|
||||
self->base.type = &esp_espnow_type;
|
||||
self->recv_buffer_size = DEFAULT_RECV_BUFFER_SIZE;
|
||||
self->recv_timeout_ms = DEFAULT_RECV_TIMEOUT_MS;
|
||||
self->recv_buffer = NULL; // Buffer is allocated in espnow_init()
|
||||
self->recv_cb = mp_const_none;
|
||||
#if MICROPY_ESPNOW_RSSI
|
||||
self->peers_table = mp_obj_new_dict(0);
|
||||
// Prevent user code modifying the dict
|
||||
mp_obj_dict_get_map(self->peers_table)->is_fixed = 1;
|
||||
#endif // MICROPY_ESPNOW_RSSI
|
||||
|
||||
// Set the global singleton pointer for the espnow protocol.
|
||||
MP_STATE_PORT(espnow_singleton) = self;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// Forward declare the send and recv ESPNow callbacks
|
||||
STATIC void send_cb(const uint8_t *mac_addr, esp_now_send_status_t status);
|
||||
|
||||
STATIC void recv_cb(const uint8_t *mac_addr, const uint8_t *data, int len);
|
||||
|
||||
// ESPNow.init(): Initialise the data buffers and ESP-NOW functions.
|
||||
// Initialise the Espressif ESPNOW software stack, register callbacks and
|
||||
// allocate the recv data buffers.
|
||||
// Returns None.
|
||||
static mp_obj_t espnow_init(mp_obj_t _) {
|
||||
esp_espnow_obj_t *self = _get_singleton();
|
||||
if (self->recv_buffer == NULL) { // Already initialised
|
||||
self->recv_buffer = m_new_obj(ringbuf_t);
|
||||
ringbuf_alloc(self->recv_buffer, self->recv_buffer_size);
|
||||
|
||||
esp_initialise_wifi(); // Call the wifi init code in network_wlan.c
|
||||
check_esp_err(esp_now_init());
|
||||
check_esp_err(esp_now_register_recv_cb(recv_cb));
|
||||
check_esp_err(esp_now_register_send_cb(send_cb));
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
// ESPNow.deinit(): De-initialise the ESPNOW software stack, disable callbacks
|
||||
// and deallocate the recv data buffers.
|
||||
// Note: this function is called from main.c:mp_task() to cleanup before soft
|
||||
// reset, so cannot be declared STATIC and must guard against self == NULL;.
|
||||
mp_obj_t espnow_deinit(mp_obj_t _) {
|
||||
esp_espnow_obj_t *self = _get_singleton();
|
||||
if (self != NULL && self->recv_buffer != NULL) {
|
||||
check_esp_err(esp_now_unregister_recv_cb());
|
||||
check_esp_err(esp_now_unregister_send_cb());
|
||||
check_esp_err(esp_now_deinit());
|
||||
self->recv_buffer->buf = NULL;
|
||||
self->recv_buffer = NULL;
|
||||
self->peer_count = 0; // esp_now_deinit() removes all peers.
|
||||
self->tx_packets = self->tx_responses;
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
STATIC mp_obj_t espnow_active(size_t n_args, const mp_obj_t *args) {
|
||||
esp_espnow_obj_t *self = _get_singleton();
|
||||
if (n_args > 1) {
|
||||
if (mp_obj_is_true(args[1])) {
|
||||
espnow_init(self);
|
||||
} else {
|
||||
espnow_deinit(self);
|
||||
}
|
||||
}
|
||||
return self->recv_buffer != NULL ? mp_const_true : mp_const_false;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(espnow_active_obj, 1, 2, espnow_active);
|
||||
|
||||
// ESPNow.config(['param'|param=value, ..])
|
||||
// Get or set configuration values. Supported config params:
|
||||
// buffer: size of buffer for rx packets (default=514 bytes)
|
||||
// timeout: Default read timeout (default=300,000 milliseconds)
|
||||
STATIC mp_obj_t espnow_config(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
esp_espnow_obj_t *self = _get_singleton();
|
||||
enum { ARG_get, ARG_buffer, ARG_timeout_ms, ARG_rate };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
|
||||
{ MP_QSTR_buffer, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
|
||||
{ MP_QSTR_timeout_ms, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MIN} },
|
||||
{ MP_QSTR_rate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
|
||||
};
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args,
|
||||
MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
if (args[ARG_buffer].u_int >= 0) {
|
||||
self->recv_buffer_size = args[ARG_buffer].u_int;
|
||||
}
|
||||
if (args[ARG_timeout_ms].u_int != INT_MIN) {
|
||||
self->recv_timeout_ms = args[ARG_timeout_ms].u_int;
|
||||
}
|
||||
if (args[ARG_rate].u_int >= 0) {
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)
|
||||
esp_initialise_wifi(); // Call the wifi init code in network_wlan.c
|
||||
check_esp_err(esp_wifi_config_espnow_rate(ESP_IF_WIFI_STA, args[ARG_rate].u_int));
|
||||
check_esp_err(esp_wifi_config_espnow_rate(ESP_IF_WIFI_AP, args[ARG_rate].u_int));
|
||||
#else
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("rate option not supported"));
|
||||
#endif
|
||||
}
|
||||
if (args[ARG_get].u_obj == MP_OBJ_NULL) {
|
||||
return mp_const_none;
|
||||
}
|
||||
#define QS(x) (uintptr_t)MP_OBJ_NEW_QSTR(x)
|
||||
// Return the value of the requested parameter
|
||||
uintptr_t name = (uintptr_t)args[ARG_get].u_obj;
|
||||
if (name == QS(MP_QSTR_buffer)) {
|
||||
return mp_obj_new_int(self->recv_buffer_size);
|
||||
} else if (name == QS(MP_QSTR_timeout_ms)) {
|
||||
return mp_obj_new_int(self->recv_timeout_ms);
|
||||
} else {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("unknown config param"));
|
||||
}
|
||||
#undef QS
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(espnow_config_obj, 1, espnow_config);
|
||||
|
||||
// ESPNow.irq(recv_cb)
|
||||
// Set callback function to be invoked when a message is received.
|
||||
STATIC mp_obj_t espnow_irq(size_t n_args, const mp_obj_t *args) {
|
||||
esp_espnow_obj_t *self = _get_singleton();
|
||||
mp_obj_t recv_cb = args[1];
|
||||
if (recv_cb != mp_const_none && !mp_obj_is_callable(recv_cb)) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("invalid handler"));
|
||||
}
|
||||
self->recv_cb = recv_cb;
|
||||
self->recv_cb_arg = (n_args > 2) ? args[2] : mp_const_none;
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(espnow_irq_obj, 2, 3, espnow_irq);
|
||||
|
||||
// ESPnow.stats(): Provide some useful stats.
|
||||
// Returns a tuple of:
|
||||
// (tx_pkts, tx_responses, tx_failures, rx_pkts, dropped_rx_pkts)
|
||||
STATIC mp_obj_t espnow_stats(mp_obj_t _) {
|
||||
const esp_espnow_obj_t *self = _get_singleton();
|
||||
return NEW_TUPLE(
|
||||
mp_obj_new_int(self->tx_packets),
|
||||
mp_obj_new_int(self->tx_responses),
|
||||
mp_obj_new_int(self->tx_failures),
|
||||
mp_obj_new_int(self->rx_packets),
|
||||
mp_obj_new_int(self->dropped_rx_pkts));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(espnow_stats_obj, espnow_stats);
|
||||
|
||||
#if MICROPY_ESPNOW_RSSI
|
||||
// ### Maintaining the peer table and reading RSSI values
|
||||
//
|
||||
// We maintain a peers table for several reasons, to:
|
||||
// - support monitoring the RSSI values for all peers; and
|
||||
// - to return unique bytestrings for each peer which supports more efficient
|
||||
// application memory usage and peer handling.
|
||||
|
||||
// Get the RSSI value from the wifi packet header
|
||||
static inline int8_t _get_rssi_from_wifi_pkt(const uint8_t *msg) {
|
||||
// Warning: Secret magic to get the rssi from the wifi packet header
|
||||
// See espnow.c:espnow_recv_cb() at https://github.com/espressif/esp-now/
|
||||
// In the wifi packet the msg comes after a wifi_promiscuous_pkt_t
|
||||
// and a espnow_frame_format_t.
|
||||
// Backtrack to get a pointer to the wifi_promiscuous_pkt_t.
|
||||
static const size_t sizeof_espnow_frame_format = 39;
|
||||
wifi_promiscuous_pkt_t *wifi_pkt =
|
||||
(wifi_promiscuous_pkt_t *)(msg - sizeof_espnow_frame_format -
|
||||
sizeof(wifi_promiscuous_pkt_t));
|
||||
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 2, 0)
|
||||
return wifi_pkt->rx_ctrl.rssi - 100; // Offset rssi for IDF 4.0.2
|
||||
#else
|
||||
return wifi_pkt->rx_ctrl.rssi;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Lookup a peer in the peers table and return a reference to the item in the
|
||||
// peers_table. Add peer to the table if it is not found (may alloc memory).
|
||||
// Will not return NULL.
|
||||
static mp_map_elem_t *_lookup_add_peer(esp_espnow_obj_t *self, const uint8_t *peer) {
|
||||
// We do not want to allocate any new memory in the case that the peer
|
||||
// already exists in the peers_table (which is almost all the time).
|
||||
// So, we use a byte string on the stack and look that up in the dict.
|
||||
mp_map_t *map = mp_obj_dict_get_map(self->peers_table);
|
||||
mp_obj_str_t peer_obj = {{&mp_type_bytes}, 0, ESP_NOW_ETH_ALEN, peer};
|
||||
mp_map_elem_t *item = mp_map_lookup(map, &peer_obj, MP_MAP_LOOKUP);
|
||||
if (item == NULL) {
|
||||
// If not found, add the peer using a new bytestring
|
||||
map->is_fixed = 0; // Allow to modify the dict
|
||||
mp_obj_t new_peer = mp_obj_new_bytes(peer, ESP_NOW_ETH_ALEN);
|
||||
item = mp_map_lookup(map, new_peer, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
|
||||
item->value = mp_obj_new_list(2, NULL);
|
||||
map->is_fixed = 1; // Relock the dict
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
// Update the peers table with the new rssi value from a received pkt and
|
||||
// return a reference to the item in the peers_table.
|
||||
static mp_map_elem_t *_update_rssi(const uint8_t *peer, int8_t rssi, uint32_t time_ms) {
|
||||
esp_espnow_obj_t *self = _get_singleton_initialised();
|
||||
// Lookup the peer in the device table
|
||||
mp_map_elem_t *item = _lookup_add_peer(self, peer);
|
||||
mp_obj_list_t *list = MP_OBJ_TO_PTR(item->value);
|
||||
list->items[0] = MP_OBJ_NEW_SMALL_INT(rssi);
|
||||
list->items[1] = mp_obj_new_int(time_ms);
|
||||
return item;
|
||||
}
|
||||
#endif // MICROPY_ESPNOW_RSSI
|
||||
|
||||
// Return C pointer to byte memory string/bytes/bytearray in obj.
|
||||
// Raise ValueError if the length does not match expected len.
|
||||
static uint8_t *_get_bytes_len_rw(mp_obj_t obj, size_t len, mp_uint_t rw) {
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(obj, &bufinfo, rw);
|
||||
if (bufinfo.len != len) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("invalid buffer length"));
|
||||
}
|
||||
return (uint8_t *)bufinfo.buf;
|
||||
}
|
||||
|
||||
static uint8_t *_get_bytes_len(mp_obj_t obj, size_t len) {
|
||||
return _get_bytes_len_rw(obj, len, MP_BUFFER_READ);
|
||||
}
|
||||
|
||||
static uint8_t *_get_bytes_len_w(mp_obj_t obj, size_t len) {
|
||||
return _get_bytes_len_rw(obj, len, MP_BUFFER_WRITE);
|
||||
}
|
||||
|
||||
// Return C pointer to the MAC address.
|
||||
// Raise ValueError if mac_addr is wrong type or is not 6 bytes long.
|
||||
static const uint8_t *_get_peer(mp_obj_t mac_addr) {
|
||||
return mp_obj_is_true(mac_addr)
|
||||
? _get_bytes_len(mac_addr, ESP_NOW_ETH_ALEN) : NULL;
|
||||
}
|
||||
|
||||
// Copy data from the ring buffer - wait if buffer is empty up to timeout_ms
|
||||
// 0: Success
|
||||
// -1: Not enough data available to complete read (try again later)
|
||||
// -2: Requested read is larger than buffer - will never succeed
|
||||
static int ringbuf_get_bytes_wait(ringbuf_t *r, uint8_t *data, size_t len, mp_int_t timeout_ms) {
|
||||
mp_uint_t start = mp_hal_ticks_ms();
|
||||
int status = 0;
|
||||
while (((status = ringbuf_get_bytes(r, data, len)) == -1)
|
||||
&& (timeout_ms < 0 || (mp_uint_t)(mp_hal_ticks_ms() - start) < (mp_uint_t)timeout_ms)) {
|
||||
MICROPY_EVENT_POLL_HOOK;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
// ESPNow.recvinto(buffers[, timeout_ms]):
|
||||
// Waits for an espnow message and copies the peer_addr and message into
|
||||
// the buffers list.
|
||||
// Arguments:
|
||||
// buffers: (Optional) list of bytearrays to store return values.
|
||||
// timeout_ms: (Optional) timeout in milliseconds (or None).
|
||||
// Buffers should be a list: [bytearray(6), bytearray(250)]
|
||||
// If buffers is 4 elements long, the rssi and timestamp values will be
|
||||
// loaded into the 3rd and 4th elements.
|
||||
// Default timeout is set with ESPNow.config(timeout=milliseconds).
|
||||
// Return (None, None) on timeout.
|
||||
STATIC mp_obj_t espnow_recvinto(size_t n_args, const mp_obj_t *args) {
|
||||
esp_espnow_obj_t *self = _get_singleton_initialised();
|
||||
|
||||
mp_int_t timeout_ms = ((n_args > 2 && args[2] != mp_const_none)
|
||||
? mp_obj_get_int(args[2]) : self->recv_timeout_ms);
|
||||
|
||||
mp_obj_list_t *list = MP_OBJ_TO_PTR(args[1]);
|
||||
if (!mp_obj_is_type(list, &mp_type_list) || list->len < 2) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("ESPNow.recvinto(): Invalid argument"));
|
||||
}
|
||||
mp_obj_array_t *msg = MP_OBJ_TO_PTR(list->items[1]);
|
||||
if (mp_obj_is_type(msg, &mp_type_bytearray)) {
|
||||
msg->len += msg->free; // Make all the space in msg array available
|
||||
msg->free = 0;
|
||||
}
|
||||
#if MICROPY_ESPNOW_RSSI
|
||||
uint8_t peer_buf[ESP_NOW_ETH_ALEN];
|
||||
#else
|
||||
uint8_t *peer_buf = _get_bytes_len_w(list->items[0], ESP_NOW_ETH_ALEN);
|
||||
#endif // MICROPY_ESPNOW_RSSI
|
||||
uint8_t *msg_buf = _get_bytes_len_w(msg, ESP_NOW_MAX_DATA_LEN);
|
||||
|
||||
// Read the packet header from the incoming buffer
|
||||
espnow_hdr_t hdr;
|
||||
if (ringbuf_get_bytes_wait(self->recv_buffer, (uint8_t *)&hdr, sizeof(hdr), timeout_ms) < 0) {
|
||||
return MP_OBJ_NEW_SMALL_INT(0); // Timeout waiting for packet
|
||||
}
|
||||
int msg_len = hdr.msg_len;
|
||||
|
||||
// Check the message packet header format and read the message data
|
||||
if (hdr.magic != ESPNOW_MAGIC
|
||||
|| msg_len > ESP_NOW_MAX_DATA_LEN
|
||||
|| ringbuf_get_bytes(self->recv_buffer, peer_buf, ESP_NOW_ETH_ALEN) < 0
|
||||
|| ringbuf_get_bytes(self->recv_buffer, msg_buf, msg_len) < 0) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("ESPNow.recv(): buffer error"));
|
||||
}
|
||||
if (mp_obj_is_type(msg, &mp_type_bytearray)) {
|
||||
// Set the length of the message bytearray.
|
||||
size_t size = msg->len + msg->free;
|
||||
msg->len = msg_len;
|
||||
msg->free = size - msg_len;
|
||||
}
|
||||
|
||||
#if MICROPY_ESPNOW_RSSI
|
||||
// Update rssi value in the peer device table
|
||||
mp_map_elem_t *entry = _update_rssi(peer_buf, hdr.rssi, hdr.time_ms);
|
||||
list->items[0] = entry->key; // Set first element of list to peer
|
||||
if (list->len >= 4) {
|
||||
list->items[2] = MP_OBJ_NEW_SMALL_INT(hdr.rssi);
|
||||
list->items[3] = mp_obj_new_int(hdr.time_ms);
|
||||
}
|
||||
#endif // MICROPY_ESPNOW_RSSI
|
||||
|
||||
return MP_OBJ_NEW_SMALL_INT(msg_len);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(espnow_recvinto_obj, 2, 3, espnow_recvinto);
|
||||
|
||||
// Test if data is available to read from the buffers
|
||||
STATIC mp_obj_t espnow_any(const mp_obj_t _) {
|
||||
esp_espnow_obj_t *self = _get_singleton_initialised();
|
||||
|
||||
return ringbuf_avail(self->recv_buffer) ? mp_const_true : mp_const_false;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(espnow_any_obj, espnow_any);
|
||||
|
||||
// Used by espnow_send() for sends() with sync==True.
|
||||
// Wait till all pending sent packet responses have been received.
|
||||
// ie. self->tx_responses == self->tx_packets.
|
||||
static void _wait_for_pending_responses(esp_espnow_obj_t *self) {
|
||||
mp_uint_t start = mp_hal_ticks_ms();
|
||||
mp_uint_t t;
|
||||
while (self->tx_responses < self->tx_packets) {
|
||||
if ((t = mp_hal_ticks_ms() - start) > PENDING_RESPONSES_TIMEOUT_MS) {
|
||||
mp_raise_OSError(MP_ETIMEDOUT);
|
||||
}
|
||||
if (t > PENDING_RESPONSES_BUSY_POLL_MS) {
|
||||
// After 10ms of busy waiting give other tasks a look in.
|
||||
MICROPY_EVENT_POLL_HOOK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ESPNow.send(peer_addr, message, [sync (=true), size])
|
||||
// ESPNow.send(message)
|
||||
// Send a message to the peer's mac address. Optionally wait for a response.
|
||||
// If peer_addr == None or any non-true value, send to all registered peers.
|
||||
// If sync == True, wait for response after sending.
|
||||
// If size is provided it should be the number of bytes in message to send().
|
||||
// Returns:
|
||||
// True if sync==False and message sent successfully.
|
||||
// True if sync==True and message is received successfully by all recipients
|
||||
// False if sync==True and message is not received by at least one recipient
|
||||
// Raises: EAGAIN if the internal espnow buffers are full.
|
||||
STATIC mp_obj_t espnow_send(size_t n_args, const mp_obj_t *args) {
|
||||
esp_espnow_obj_t *self = _get_singleton_initialised();
|
||||
// Check the various combinations of input arguments
|
||||
const uint8_t *peer = (n_args > 2) ? _get_peer(args[1]) : NULL;
|
||||
mp_obj_t msg = (n_args > 2) ? args[2] : (n_args == 2) ? args[1] : MP_OBJ_NULL;
|
||||
bool sync = n_args <= 3 || args[3] == mp_const_none || mp_obj_is_true(args[3]);
|
||||
|
||||
// Get a pointer to the data buffer of the message
|
||||
mp_buffer_info_t message;
|
||||
mp_get_buffer_raise(msg, &message, MP_BUFFER_READ);
|
||||
|
||||
if (sync) {
|
||||
// Flush out any pending responses.
|
||||
// If the last call was sync==False there may be outstanding responses
|
||||
// still to be received (possible many if we just had a burst of
|
||||
// unsync send()s). We need to wait for all pending responses if this
|
||||
// call has sync=True.
|
||||
_wait_for_pending_responses(self);
|
||||
}
|
||||
int saved_failures = self->tx_failures;
|
||||
// Send the packet - try, try again if internal esp-now buffers are full.
|
||||
esp_err_t err;
|
||||
mp_uint_t start = mp_hal_ticks_ms();
|
||||
while ((ESP_ERR_ESPNOW_NO_MEM == (err = esp_now_send(peer, message.buf, message.len)))
|
||||
&& (mp_uint_t)(mp_hal_ticks_ms() - start) < (mp_uint_t)DEFAULT_SEND_TIMEOUT_MS) {
|
||||
MICROPY_EVENT_POLL_HOOK;
|
||||
}
|
||||
check_esp_err(err); // Will raise OSError if e != ESP_OK
|
||||
// Increment the sent packet count. If peer_addr==NULL msg will be
|
||||
// sent to all peers EXCEPT any broadcast or multicast addresses.
|
||||
self->tx_packets += ((peer == NULL) ? self->peer_count : 1);
|
||||
if (sync) {
|
||||
// Wait for and tally all the expected responses from peers
|
||||
_wait_for_pending_responses(self);
|
||||
}
|
||||
// Return False if sync and any peers did not respond.
|
||||
return mp_obj_new_bool(!(sync && self->tx_failures != saved_failures));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(espnow_send_obj, 2, 4, espnow_send);
|
||||
|
||||
// ### The ESP_Now send and recv callback routines
|
||||
//
|
||||
|
||||
// Callback triggered when a sent packet is acknowledged by the peer (or not).
|
||||
// Just count the number of responses and number of failures.
|
||||
// These are used in the send() logic.
|
||||
STATIC void send_cb(const uint8_t *mac_addr, esp_now_send_status_t status) {
|
||||
esp_espnow_obj_t *self = _get_singleton();
|
||||
self->tx_responses++;
|
||||
if (status != ESP_NOW_SEND_SUCCESS) {
|
||||
self->tx_failures++;
|
||||
}
|
||||
}
|
||||
|
||||
// Callback triggered when an ESP-Now packet is received.
|
||||
// Write the peer MAC address and the message into the recv_buffer as an
|
||||
// ESPNow packet.
|
||||
// If the buffer is full, drop the message and increment the dropped count.
|
||||
// Schedules the user callback if one has been registered (ESPNow.config()).
|
||||
STATIC void recv_cb(const uint8_t *mac_addr, const uint8_t *msg, int msg_len) {
|
||||
esp_espnow_obj_t *self = _get_singleton();
|
||||
ringbuf_t *buf = self->recv_buffer;
|
||||
// TODO: Test this works with ">".
|
||||
if (sizeof(espnow_pkt_t) + msg_len >= ringbuf_free(buf)) {
|
||||
self->dropped_rx_pkts++;
|
||||
return;
|
||||
}
|
||||
espnow_hdr_t header;
|
||||
header.magic = ESPNOW_MAGIC;
|
||||
header.msg_len = msg_len;
|
||||
#if MICROPY_ESPNOW_RSSI
|
||||
header.rssi = _get_rssi_from_wifi_pkt(msg);
|
||||
header.time_ms = mp_hal_ticks_ms();
|
||||
#endif // MICROPY_ESPNOW_RSSI
|
||||
|
||||
ringbuf_put_bytes(buf, (uint8_t *)&header, sizeof(header));
|
||||
ringbuf_put_bytes(buf, mac_addr, ESP_NOW_ETH_ALEN);
|
||||
ringbuf_put_bytes(buf, msg, msg_len);
|
||||
self->rx_packets++;
|
||||
if (self->recv_cb != mp_const_none) {
|
||||
mp_sched_schedule(self->recv_cb, self->recv_cb_arg);
|
||||
}
|
||||
}
|
||||
|
||||
// ### Peer Management Functions
|
||||
//
|
||||
|
||||
// Set the ESP-NOW Primary Master Key (pmk) (for encrypted communications).
|
||||
// Raise OSError if ESP-NOW functions are not initialised.
|
||||
// Raise ValueError if key is not a bytes-like object exactly 16 bytes long.
|
||||
STATIC mp_obj_t espnow_set_pmk(mp_obj_t _, mp_obj_t key) {
|
||||
check_esp_err(esp_now_set_pmk(_get_bytes_len(key, ESP_NOW_KEY_LEN)));
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(espnow_set_pmk_obj, espnow_set_pmk);
|
||||
|
||||
// Common code for add_peer() and mod_peer() to process the args and kw_args:
|
||||
// Raise ValueError if the LMK is not a bytes-like object of exactly 16 bytes.
|
||||
// Raise TypeError if invalid keyword args or too many positional args.
|
||||
// Return true if all args parsed correctly.
|
||||
STATIC bool _update_peer_info(
|
||||
esp_now_peer_info_t *peer, size_t n_args,
|
||||
const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
|
||||
enum { ARG_lmk, ARG_channel, ARG_ifidx, ARG_encrypt };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_lmk, MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||
{ MP_QSTR_channel, MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||
{ MP_QSTR_ifidx, MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||
{ MP_QSTR_encrypt, MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||
};
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
if (args[ARG_lmk].u_obj != mp_const_none) {
|
||||
mp_obj_t obj = args[ARG_lmk].u_obj;
|
||||
peer->encrypt = mp_obj_is_true(obj);
|
||||
if (peer->encrypt) {
|
||||
// Key must be 16 bytes in length.
|
||||
memcpy(peer->lmk, _get_bytes_len(obj, ESP_NOW_KEY_LEN), ESP_NOW_KEY_LEN);
|
||||
}
|
||||
}
|
||||
if (args[ARG_channel].u_obj != mp_const_none) {
|
||||
peer->channel = mp_obj_get_int(args[ARG_channel].u_obj);
|
||||
}
|
||||
if (args[ARG_ifidx].u_obj != mp_const_none) {
|
||||
peer->ifidx = mp_obj_get_int(args[ARG_ifidx].u_obj);
|
||||
}
|
||||
if (args[ARG_encrypt].u_obj != mp_const_none) {
|
||||
peer->encrypt = mp_obj_is_true(args[ARG_encrypt].u_obj);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Update the cached peer count in self->peer_count;
|
||||
// The peer_count ignores broadcast and multicast addresses and is used for the
|
||||
// send() logic and is updated from add_peer(), mod_peer() and del_peer().
|
||||
STATIC void _update_peer_count() {
|
||||
esp_espnow_obj_t *self = _get_singleton_initialised();
|
||||
|
||||
esp_now_peer_info_t peer = {0};
|
||||
bool from_head = true;
|
||||
int count = 0;
|
||||
// esp_now_fetch_peer() skips over any broadcast or multicast addresses
|
||||
while (esp_now_fetch_peer(from_head, &peer) == ESP_OK) {
|
||||
from_head = false;
|
||||
if (++count >= ESP_NOW_MAX_TOTAL_PEER_NUM) {
|
||||
break; // Should not happen
|
||||
}
|
||||
}
|
||||
self->peer_count = count;
|
||||
}
|
||||
|
||||
// ESPNow.add_peer(peer_mac, [lmk, [channel, [ifidx, [encrypt]]]]) or
|
||||
// ESPNow.add_peer(peer_mac, [lmk=b'0123456789abcdef'|b''|None|False],
|
||||
// [channel=1..11|0], [ifidx=0|1], [encrypt=True|False])
|
||||
// Positional args set to None will be left at defaults.
|
||||
// Raise OSError if ESPNow.init() has not been called.
|
||||
// Raise ValueError if mac or LMK are not bytes-like objects or wrong length.
|
||||
// Raise TypeError if invalid keyword args or too many positional args.
|
||||
// Return None.
|
||||
STATIC mp_obj_t espnow_add_peer(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
||||
esp_now_peer_info_t peer = {0};
|
||||
memcpy(peer.peer_addr, _get_peer(args[1]), ESP_NOW_ETH_ALEN);
|
||||
_update_peer_info(&peer, n_args - 2, args + 2, kw_args);
|
||||
|
||||
check_esp_err(esp_now_add_peer(&peer));
|
||||
_update_peer_count();
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(espnow_add_peer_obj, 2, espnow_add_peer);
|
||||
|
||||
// ESPNow.del_peer(peer_mac): Unregister peer_mac.
|
||||
// Raise OSError if ESPNow.init() has not been called.
|
||||
// Raise ValueError if peer is not a bytes-like objects or wrong length.
|
||||
// Return None.
|
||||
STATIC mp_obj_t espnow_del_peer(mp_obj_t _, mp_obj_t peer) {
|
||||
uint8_t peer_addr[ESP_NOW_ETH_ALEN];
|
||||
memcpy(peer_addr, _get_peer(peer), ESP_NOW_ETH_ALEN);
|
||||
|
||||
check_esp_err(esp_now_del_peer(peer_addr));
|
||||
_update_peer_count();
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(espnow_del_peer_obj, espnow_del_peer);
|
||||
|
||||
// Convert a peer_info struct to python tuple
|
||||
// Used by espnow_get_peer() and espnow_get_peers()
|
||||
static mp_obj_t _peer_info_to_tuple(const esp_now_peer_info_t *peer) {
|
||||
return NEW_TUPLE(
|
||||
mp_obj_new_bytes(peer->peer_addr, MP_ARRAY_SIZE(peer->peer_addr)),
|
||||
mp_obj_new_bytes(peer->lmk, MP_ARRAY_SIZE(peer->lmk)),
|
||||
mp_obj_new_int(peer->channel),
|
||||
mp_obj_new_int(peer->ifidx),
|
||||
(peer->encrypt) ? mp_const_true : mp_const_false);
|
||||
}
|
||||
|
||||
// ESPNow.get_peers(): Fetch peer_info records for all registered ESPNow peers.
|
||||
// Raise OSError if ESPNow.init() has not been called.
|
||||
// Return a tuple of tuples:
|
||||
// ((peer_addr, lmk, channel, ifidx, encrypt),
|
||||
// (peer_addr, lmk, channel, ifidx, encrypt), ...)
|
||||
STATIC mp_obj_t espnow_get_peers(mp_obj_t _) {
|
||||
esp_espnow_obj_t *self = _get_singleton_initialised();
|
||||
|
||||
// Build and initialise the peer info tuple.
|
||||
mp_obj_tuple_t *peerinfo_tuple = mp_obj_new_tuple(self->peer_count, NULL);
|
||||
esp_now_peer_info_t peer = {0};
|
||||
for (int i = 0; i < peerinfo_tuple->len; i++) {
|
||||
int status = esp_now_fetch_peer((i == 0), &peer);
|
||||
peerinfo_tuple->items[i] =
|
||||
(status == ESP_OK ? _peer_info_to_tuple(&peer) : mp_const_none);
|
||||
}
|
||||
|
||||
return peerinfo_tuple;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(espnow_get_peers_obj, espnow_get_peers);
|
||||
|
||||
#if MICROPY_ESPNOW_EXTRA_PEER_METHODS
|
||||
// ESPNow.get_peer(peer_mac): Get the peer info for peer_mac as a tuple.
|
||||
// Raise OSError if ESPNow.init() has not been called.
|
||||
// Raise ValueError if mac or LMK are not bytes-like objects or wrong length.
|
||||
// Return a tuple of (peer_addr, lmk, channel, ifidx, encrypt).
|
||||
STATIC mp_obj_t espnow_get_peer(mp_obj_t _, mp_obj_t arg1) {
|
||||
esp_now_peer_info_t peer = {0};
|
||||
memcpy(peer.peer_addr, _get_peer(arg1), ESP_NOW_ETH_ALEN);
|
||||
|
||||
check_esp_err(esp_now_get_peer(peer.peer_addr, &peer));
|
||||
|
||||
return _peer_info_to_tuple(&peer);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(espnow_get_peer_obj, espnow_get_peer);
|
||||
|
||||
// ESPNow.mod_peer(peer_mac, [lmk, [channel, [ifidx, [encrypt]]]]) or
|
||||
// ESPNow.mod_peer(peer_mac, [lmk=b'0123456789abcdef'|b''|None|False],
|
||||
// [channel=1..11|0], [ifidx=0|1], [encrypt=True|False])
|
||||
// Positional args set to None will be left at current values.
|
||||
// Raise OSError if ESPNow.init() has not been called.
|
||||
// Raise ValueError if mac or LMK are not bytes-like objects or wrong length.
|
||||
// Raise TypeError if invalid keyword args or too many positional args.
|
||||
// Return None.
|
||||
STATIC mp_obj_t espnow_mod_peer(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
||||
esp_now_peer_info_t peer = {0};
|
||||
memcpy(peer.peer_addr, _get_peer(args[1]), ESP_NOW_ETH_ALEN);
|
||||
check_esp_err(esp_now_get_peer(peer.peer_addr, &peer));
|
||||
|
||||
_update_peer_info(&peer, n_args - 2, args + 2, kw_args);
|
||||
|
||||
check_esp_err(esp_now_mod_peer(&peer));
|
||||
_update_peer_count();
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(espnow_mod_peer_obj, 2, espnow_mod_peer);
|
||||
|
||||
// ESPNow.espnow_peer_count(): Get the number of registered peers.
|
||||
// Raise OSError if ESPNow.init() has not been called.
|
||||
// Return a tuple of (num_total_peers, num_encrypted_peers).
|
||||
STATIC mp_obj_t espnow_peer_count(mp_obj_t _) {
|
||||
esp_now_peer_num_t peer_num = {0};
|
||||
check_esp_err(esp_now_get_peer_num(&peer_num));
|
||||
|
||||
return NEW_TUPLE(
|
||||
mp_obj_new_int(peer_num.total_num),
|
||||
mp_obj_new_int(peer_num.encrypt_num));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(espnow_peer_count_obj, espnow_peer_count);
|
||||
#endif
|
||||
|
||||
STATIC const mp_rom_map_elem_t esp_espnow_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&espnow_active_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&espnow_config_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&espnow_irq_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_stats), MP_ROM_PTR(&espnow_stats_obj) },
|
||||
|
||||
// Send and receive messages
|
||||
{ MP_ROM_QSTR(MP_QSTR_recvinto), MP_ROM_PTR(&espnow_recvinto_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&espnow_send_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_any), MP_ROM_PTR(&espnow_any_obj) },
|
||||
|
||||
// Peer management functions
|
||||
{ MP_ROM_QSTR(MP_QSTR_set_pmk), MP_ROM_PTR(&espnow_set_pmk_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_add_peer), MP_ROM_PTR(&espnow_add_peer_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_del_peer), MP_ROM_PTR(&espnow_del_peer_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_get_peers), MP_ROM_PTR(&espnow_get_peers_obj) },
|
||||
#if MICROPY_ESPNOW_EXTRA_PEER_METHODS
|
||||
{ MP_ROM_QSTR(MP_QSTR_mod_peer), MP_ROM_PTR(&espnow_mod_peer_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_get_peer), MP_ROM_PTR(&espnow_get_peer_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_peer_count), MP_ROM_PTR(&espnow_peer_count_obj) },
|
||||
#endif // MICROPY_ESPNOW_EXTRA_PEER_METHODS
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(esp_espnow_locals_dict, esp_espnow_locals_dict_table);
|
||||
|
||||
STATIC const mp_rom_map_elem_t espnow_globals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__espnow) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ESPNowBase), MP_ROM_PTR(&esp_espnow_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_MAX_DATA_LEN), MP_ROM_INT(ESP_NOW_MAX_DATA_LEN)},
|
||||
{ MP_ROM_QSTR(MP_QSTR_ADDR_LEN), MP_ROM_INT(ESP_NOW_ETH_ALEN)},
|
||||
{ MP_ROM_QSTR(MP_QSTR_KEY_LEN), MP_ROM_INT(ESP_NOW_KEY_LEN)},
|
||||
{ MP_ROM_QSTR(MP_QSTR_MAX_TOTAL_PEER_NUM), MP_ROM_INT(ESP_NOW_MAX_TOTAL_PEER_NUM)},
|
||||
{ MP_ROM_QSTR(MP_QSTR_MAX_ENCRYPT_PEER_NUM), MP_ROM_INT(ESP_NOW_MAX_ENCRYPT_PEER_NUM)},
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(espnow_globals_dict, espnow_globals_dict_table);
|
||||
|
||||
// ### Dummy Buffer Protocol support
|
||||
// ...so asyncio can poll.ipoll() on this device
|
||||
|
||||
// Support ioctl(MP_STREAM_POLL, ) for asyncio
|
||||
STATIC mp_uint_t espnow_stream_ioctl(
|
||||
mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
|
||||
if (request != MP_STREAM_POLL) {
|
||||
*errcode = MP_EINVAL;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
esp_espnow_obj_t *self = _get_singleton();
|
||||
return (self->recv_buffer == NULL) ? 0 : // If not initialised
|
||||
arg ^ (
|
||||
// If no data in the buffer, unset the Read ready flag
|
||||
((ringbuf_avail(self->recv_buffer) == 0) ? MP_STREAM_POLL_RD : 0) |
|
||||
// If still waiting for responses, unset the Write ready flag
|
||||
((self->tx_responses < self->tx_packets) ? MP_STREAM_POLL_WR : 0));
|
||||
}
|
||||
|
||||
STATIC const mp_stream_p_t espnow_stream_p = {
|
||||
.ioctl = espnow_stream_ioctl,
|
||||
};
|
||||
|
||||
#if MICROPY_ESPNOW_RSSI
|
||||
// Return reference to the dictionary of peers we have seen:
|
||||
// {peer1: (rssi, time_sec), peer2: (rssi, time_msec), ...}
|
||||
// where:
|
||||
// peerX is a byte string containing the 6-byte mac address of the peer,
|
||||
// rssi is the wifi signal strength from the last msg received
|
||||
// (in dBm from -127 to 0)
|
||||
// time_sec is the time in milliseconds since device last booted.
|
||||
STATIC void espnow_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
||||
esp_espnow_obj_t *self = _get_singleton();
|
||||
if (dest[0] != MP_OBJ_NULL) { // Only allow "Load" operation
|
||||
return;
|
||||
}
|
||||
if (attr == MP_QSTR_peers_table) {
|
||||
dest[0] = self->peers_table;
|
||||
return;
|
||||
}
|
||||
dest[1] = MP_OBJ_SENTINEL; // Attribute not found
|
||||
}
|
||||
#endif // MICROPY_ESPNOW_RSSI
|
||||
|
||||
MP_DEFINE_CONST_OBJ_TYPE(
|
||||
esp_espnow_type,
|
||||
MP_QSTR_ESPNowBase,
|
||||
MP_TYPE_FLAG_NONE,
|
||||
make_new, espnow_make_new,
|
||||
#if MICROPY_ESPNOW_RSSI
|
||||
attr, espnow_attr,
|
||||
#endif // MICROPY_ESPNOW_RSSI
|
||||
protocol, &espnow_stream_p,
|
||||
locals_dict, &esp_espnow_locals_dict
|
||||
);
|
||||
|
||||
const mp_obj_module_t mp_module_espnow = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&espnow_globals_dict,
|
||||
};
|
||||
|
||||
MP_REGISTER_MODULE(MP_QSTR__espnow, mp_module_espnow);
|
||||
MP_REGISTER_ROOT_POINTER(struct _esp_espnow_obj_t *espnow_singleton);
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Glenn Moloney @glenn20
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/obj.h"
|
||||
|
||||
// Called from main.c:mp_task() to reset the espnow software stack
|
||||
mp_obj_t espnow_deinit(mp_obj_t _);
|
|
@ -45,6 +45,8 @@ typedef struct _wlan_if_obj_t {
|
|||
int if_id;
|
||||
} wlan_if_obj_t;
|
||||
|
||||
extern const mp_obj_type_t esp_network_wlan_type;
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_0(esp_network_initialize_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_get_wlan_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(esp_network_get_lan_obj);
|
||||
|
@ -63,5 +65,6 @@ static inline void esp_exceptions(esp_err_t e) {
|
|||
|
||||
void usocket_events_deinit(void);
|
||||
void network_wlan_event_handler(system_event_t *event);
|
||||
void esp_initialise_wifi(void);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{ MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&esp_network_initialize_obj) },
|
||||
|
||||
#if MICROPY_PY_NETWORK_WLAN
|
||||
{ MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&esp_network_get_wlan_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&esp_network_wlan_type) },
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_NETWORK_LAN
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
# espnow module for MicroPython on ESP32
|
||||
# MIT license; Copyright (c) 2022 Glenn Moloney @glenn20
|
||||
|
||||
from _espnow import *
|
||||
|
||||
|
||||
class ESPNow(ESPNowBase):
|
||||
# Static buffers for alloc free receipt of messages with ESPNow.irecv().
|
||||
_data = [None, bytearray(MAX_DATA_LEN)]
|
||||
_none_tuple = (None, None)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def irecv(self, timeout_ms=None):
|
||||
n = self.recvinto(self._data, timeout_ms)
|
||||
return self._data if n else self._none_tuple
|
||||
|
||||
def recv(self, timeout_ms=None):
|
||||
n = self.recvinto(self._data, timeout_ms)
|
||||
return [bytes(x) for x in self._data] if n else self._none_tuple
|
||||
|
||||
def irq(self, callback):
|
||||
super().irq(callback, self)
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
return self.irecv() # Use alloc free irecv() method
|
|
@ -70,6 +70,9 @@
|
|||
#define MICROPY_PY_THREAD_GIL_VM_DIVISOR (32)
|
||||
|
||||
// extended modules
|
||||
#ifndef MICROPY_ESPNOW
|
||||
#define MICROPY_ESPNOW (1)
|
||||
#endif
|
||||
#ifndef MICROPY_PY_BLUETOOTH
|
||||
#define MICROPY_PY_BLUETOOTH (1)
|
||||
#define MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS (1)
|
||||
|
@ -88,6 +91,7 @@
|
|||
#define MICROPY_PY_UOS_INCLUDEFILE "ports/esp32/moduos.c"
|
||||
#define MICROPY_PY_OS_DUPTERM (1)
|
||||
#define MICROPY_PY_UOS_DUPTERM_NOTIFY (1)
|
||||
#define MICROPY_PY_UOS_SYNC (1)
|
||||
#define MICROPY_PY_UOS_UNAME (1)
|
||||
#define MICROPY_PY_UOS_URANDOM (1)
|
||||
#define MICROPY_PY_MACHINE (1)
|
||||
|
@ -95,9 +99,7 @@
|
|||
#define MICROPY_PY_MACHINE_BITSTREAM (1)
|
||||
#define MICROPY_PY_MACHINE_PULSE (1)
|
||||
#define MICROPY_PY_MACHINE_PWM (1)
|
||||
#define MICROPY_PY_MACHINE_PWM_INIT (1)
|
||||
#define MICROPY_PY_MACHINE_PWM_DUTY (1)
|
||||
#define MICROPY_PY_MACHINE_PWM_DUTY_U16_NS (1)
|
||||
#define MICROPY_PY_MACHINE_PWM_INCLUDEFILE "ports/esp32/machine_pwm.c"
|
||||
#define MICROPY_PY_MACHINE_I2C (1)
|
||||
#define MICROPY_PY_MACHINE_I2C_TRANSFER_WRITE1 (1)
|
||||
|
|
|
@ -159,16 +159,22 @@ STATIC void require_if(mp_obj_t wlan_if, int if_no) {
|
|||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t get_wlan(size_t n_args, const mp_obj_t *args) {
|
||||
static int initialized = 0;
|
||||
if (!initialized) {
|
||||
void esp_initialise_wifi() {
|
||||
static int wifi_initialized = 0;
|
||||
if (!wifi_initialized) {
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_LOGD("modnetwork", "Initializing WiFi");
|
||||
esp_exceptions(esp_wifi_init(&cfg));
|
||||
esp_exceptions(esp_wifi_set_storage(WIFI_STORAGE_RAM));
|
||||
ESP_LOGD("modnetwork", "Initialized");
|
||||
initialized = 1;
|
||||
wifi_initialized = 1;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t network_wlan_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
mp_arg_check_num(n_args, n_kw, 0, 1, false);
|
||||
|
||||
esp_initialise_wifi();
|
||||
|
||||
int idx = (n_args > 0) ? mp_obj_get_int(args[0]) : WIFI_IF_STA;
|
||||
if (idx == WIFI_IF_STA) {
|
||||
|
@ -179,7 +185,6 @@ STATIC mp_obj_t get_wlan(size_t n_args, const mp_obj_t *args) {
|
|||
mp_raise_ValueError(MP_ERROR_TEXT("invalid WLAN interface identifier"));
|
||||
}
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_get_wlan_obj, 0, 1, get_wlan);
|
||||
|
||||
STATIC mp_obj_t network_wlan_active(size_t n_args, const mp_obj_t *args) {
|
||||
wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
|
@ -504,6 +509,10 @@ STATIC mp_obj_t network_wlan_config(size_t n_args, const mp_obj_t *args, mp_map_
|
|||
esp_exceptions(esp_wifi_set_protocol(self->if_id, mp_obj_get_int(kwargs->table[i].value)));
|
||||
break;
|
||||
}
|
||||
case MP_QSTR_pm: {
|
||||
esp_exceptions(esp_wifi_set_ps(mp_obj_get_int(kwargs->table[i].value)));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
goto unknown;
|
||||
}
|
||||
|
@ -598,6 +607,12 @@ STATIC mp_obj_t network_wlan_config(size_t n_args, const mp_obj_t *args, mp_map_
|
|||
val = MP_OBJ_NEW_SMALL_INT(protocol_bitmap);
|
||||
break;
|
||||
}
|
||||
case MP_QSTR_pm: {
|
||||
wifi_ps_type_t ps_type;
|
||||
esp_exceptions(esp_wifi_get_ps(&ps_type));
|
||||
val = MP_OBJ_NEW_SMALL_INT(ps_type);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
goto unknown;
|
||||
}
|
||||
|
@ -623,17 +638,23 @@ STATIC const mp_rom_map_elem_t wlan_if_locals_dict_table[] = {
|
|||
{ MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&network_wlan_isconnected_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&network_wlan_config_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&esp_network_ifconfig_obj) },
|
||||
|
||||
// Constants
|
||||
{ MP_ROM_QSTR(MP_QSTR_PM_NONE), MP_ROM_INT(WIFI_PS_NONE) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_PM_PERFORMANCE), MP_ROM_INT(WIFI_PS_MIN_MODEM) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_PM_POWERSAVE), MP_ROM_INT(WIFI_PS_MAX_MODEM) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(wlan_if_locals_dict, wlan_if_locals_dict_table);
|
||||
|
||||
MP_DEFINE_CONST_OBJ_TYPE(
|
||||
wlan_if_type,
|
||||
esp_network_wlan_type,
|
||||
MP_QSTR_WLAN,
|
||||
MP_TYPE_FLAG_NONE,
|
||||
make_new, network_wlan_make_new,
|
||||
locals_dict, &wlan_if_locals_dict
|
||||
);
|
||||
|
||||
STATIC const wlan_if_obj_t wlan_sta_obj = {{&wlan_if_type}, WIFI_IF_STA};
|
||||
STATIC const wlan_if_obj_t wlan_ap_obj = {{&wlan_if_type}, WIFI_IF_AP};
|
||||
STATIC const wlan_if_obj_t wlan_sta_obj = {{&esp_network_wlan_type}, WIFI_IF_STA};
|
||||
STATIC const wlan_if_obj_t wlan_ap_obj = {{&esp_network_wlan_type}, WIFI_IF_AP};
|
||||
|
||||
#endif // MICROPY_PY_NETWORK_WLAN
|
||||
|
|
|
@ -46,6 +46,10 @@ void uart_stdout_init(void) {
|
|||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
.rx_flow_ctrl_thresh = 0
|
||||
};
|
||||
#if SOC_UART_SUPPORT_XTAL_CLK
|
||||
// works independently of APB frequency
|
||||
uartcfg.source_clk = UART_SCLK_XTAL; // ESP32C3, ESP32S3
|
||||
#endif
|
||||
uart_param_config(MICROPY_HW_UART_REPL, &uartcfg);
|
||||
|
||||
const uint32_t rxbuf = 129; // IDF requires > 128 min
|
||||
|
|
|
@ -70,6 +70,11 @@ LD_FILES ?= boards/esp8266_2m.ld
|
|||
LDFLAGS += -nostdlib -T $(LD_FILES) -Map=$(@:.elf=.map) --cref
|
||||
LIBS += -L$(ESP_SDK)/lib -lmain -ljson -llwip_open -lpp -lnet80211 -lwpa -lphy -lnet80211
|
||||
|
||||
ifeq ($(MICROPY_ESPNOW),1)
|
||||
CFLAGS += -DMICROPY_ESPNOW=1
|
||||
LIBS += -lespnow
|
||||
endif
|
||||
|
||||
LIBGCC_FILE_NAME = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
|
||||
LIBS += -L$(dir $(LIBGCC_FILE_NAME)) -lgcc
|
||||
|
||||
|
@ -113,6 +118,11 @@ SRC_C = \
|
|||
hspi.c \
|
||||
$(wildcard $(BOARD_DIR)/*.c) \
|
||||
|
||||
ifeq ($(MICROPY_ESPNOW),1)
|
||||
SRC_C += \
|
||||
modespnow.c
|
||||
endif
|
||||
|
||||
LIB_SRC_C = $(addprefix lib/,\
|
||||
libm/math.c \
|
||||
libm/fmodf.c \
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
LD_FILES = boards/esp8266_2m.ld
|
||||
|
||||
MICROPY_ESPNOW ?= 1
|
||||
MICROPY_PY_BTREE ?= 1
|
||||
MICROPY_VFS_FAT ?= 1
|
||||
MICROPY_VFS_LFS2 ?= 1
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
LD_FILES = boards/esp8266_1m.ld
|
||||
|
||||
MICROPY_ESPNOW ?= 1
|
||||
MICROPY_PY_BTREE ?= 1
|
||||
MICROPY_VFS_LFS2 ?= 1
|
||||
|
|
|
@ -83,6 +83,7 @@ SECTIONS
|
|||
*libnet80211.a:(.literal.* .text.*)
|
||||
*libwpa.a:(.literal.* .text.*)
|
||||
*libwpa2.a:(.literal.* .text.*)
|
||||
*libespnow.a:(.literal.* .text.*)
|
||||
|
||||
/* we put some specific text in this section */
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
freeze("$(PORT_DIR)/modules")
|
||||
# require("aioespnow")
|
||||
require("bundle-networking")
|
||||
require("dht")
|
||||
require("ds18x20")
|
||||
|
|
|
@ -29,7 +29,8 @@
|
|||
const char esp_help_text[] =
|
||||
"Welcome to MicroPython!\n"
|
||||
"\n"
|
||||
"For online docs please visit http://docs.micropython.org/en/latest/esp8266/ .\n"
|
||||
"For online docs please visit http://docs.micropython.org/\n"
|
||||
"\n"
|
||||
"For diagnostic information to include in bug reports execute 'import port_diag'.\n"
|
||||
"\n"
|
||||
"Basic WiFi configuration:\n"
|
||||
|
|
|
@ -35,8 +35,11 @@ typedef struct _machine_pwm_obj_t {
|
|||
pyb_pin_obj_t *pin;
|
||||
uint8_t active;
|
||||
uint8_t channel;
|
||||
int32_t duty_ns;
|
||||
} machine_pwm_obj_t;
|
||||
|
||||
STATIC void mp_machine_pwm_duty_set_ns(machine_pwm_obj_t *self, mp_int_t duty);
|
||||
|
||||
STATIC bool pwm_inited = false;
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -53,10 +56,12 @@ STATIC void mp_machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_p
|
|||
}
|
||||
|
||||
STATIC void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_freq, ARG_duty };
|
||||
enum { ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_freq, MP_ARG_INT, {.u_int = -1} },
|
||||
{ MP_QSTR_duty, MP_ARG_INT, {.u_int = -1} },
|
||||
{ MP_QSTR_duty_u16, MP_ARG_INT, {.u_int = -1} },
|
||||
{ MP_QSTR_duty_ns, MP_ARG_INT, {.u_int = -1} },
|
||||
};
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
@ -74,6 +79,15 @@ STATIC void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, size_t n_args, c
|
|||
if (args[ARG_duty].u_int != -1) {
|
||||
pwm_set_duty(args[ARG_duty].u_int, self->channel);
|
||||
}
|
||||
if (args[ARG_duty_u16].u_int != -1) {
|
||||
pwm_set_duty(args[ARG_duty_u16].u_int * 1000 / 65536, self->channel);
|
||||
}
|
||||
if (args[ARG_duty_ns].u_int != -1) {
|
||||
uint32_t freq = pwm_get_freq(0);
|
||||
if (freq > 0) {
|
||||
pwm_set_duty((uint64_t)args[ARG_duty_ns].u_int * freq / 1000000, self->channel);
|
||||
}
|
||||
}
|
||||
|
||||
if (pin_mode[self->pin->phys_port] == GPIO_MODE_OPEN_DRAIN) {
|
||||
mp_hal_pin_open_drain(self->pin->phys_port);
|
||||
|
@ -91,6 +105,7 @@ STATIC mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args
|
|||
self->pin = pin;
|
||||
self->active = 0;
|
||||
self->channel = -1;
|
||||
self->duty_ns = -1;
|
||||
|
||||
// start the PWM subsystem if it's not already running
|
||||
if (!pwm_inited) {
|
||||
|
@ -118,26 +133,57 @@ STATIC mp_obj_t mp_machine_pwm_freq_get(machine_pwm_obj_t *self) {
|
|||
|
||||
STATIC void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) {
|
||||
pwm_set_freq(freq, 0);
|
||||
pwm_start();
|
||||
if (self->duty_ns != -1) {
|
||||
mp_machine_pwm_duty_set_ns(self, self->duty_ns);
|
||||
} else {
|
||||
pwm_start();
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t mp_machine_pwm_duty_get(machine_pwm_obj_t *self) {
|
||||
STATIC void set_active(machine_pwm_obj_t *self, bool set_pin) {
|
||||
if (!self->active) {
|
||||
pwm_add(self->pin->phys_port, self->pin->periph, self->pin->func);
|
||||
self->active = 1;
|
||||
|
||||
if (pin_mode[self->pin->phys_port] == GPIO_MODE_OPEN_DRAIN) {
|
||||
if (set_pin && pin_mode[self->pin->phys_port] == GPIO_MODE_OPEN_DRAIN) {
|
||||
mp_hal_pin_open_drain(self->pin->phys_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t mp_machine_pwm_duty_get(machine_pwm_obj_t *self) {
|
||||
set_active(self, true);
|
||||
return MP_OBJ_NEW_SMALL_INT(pwm_get_duty(self->channel));
|
||||
}
|
||||
|
||||
STATIC void mp_machine_pwm_duty_set(machine_pwm_obj_t *self, mp_int_t duty) {
|
||||
if (!self->active) {
|
||||
pwm_add(self->pin->phys_port, self->pin->periph, self->pin->func);
|
||||
self->active = 1;
|
||||
}
|
||||
set_active(self, false);
|
||||
self->duty_ns = -1;
|
||||
pwm_set_duty(duty, self->channel);
|
||||
pwm_start();
|
||||
}
|
||||
|
||||
STATIC mp_obj_t mp_machine_pwm_duty_get_u16(machine_pwm_obj_t *self) {
|
||||
set_active(self, true);
|
||||
return MP_OBJ_NEW_SMALL_INT(pwm_get_duty(self->channel) * 65536 / 1024);
|
||||
}
|
||||
|
||||
STATIC void mp_machine_pwm_duty_set_u16(machine_pwm_obj_t *self, mp_int_t duty) {
|
||||
set_active(self, false);
|
||||
self->duty_ns = -1;
|
||||
pwm_set_duty(duty * 1024 / 65536, self->channel);
|
||||
pwm_start();
|
||||
}
|
||||
|
||||
STATIC mp_obj_t mp_machine_pwm_duty_get_ns(machine_pwm_obj_t *self) {
|
||||
set_active(self, true);
|
||||
uint32_t freq = pwm_get_freq(0);
|
||||
return MP_OBJ_NEW_SMALL_INT(pwm_get_duty(self->channel) * 976563 / freq);
|
||||
}
|
||||
|
||||
STATIC void mp_machine_pwm_duty_set_ns(machine_pwm_obj_t *self, mp_int_t duty) {
|
||||
set_active(self, false);
|
||||
self->duty_ns = duty;
|
||||
uint32_t freq = pwm_get_freq(0);
|
||||
pwm_set_duty(duty * freq / 976562, self->channel); // 1e9/1024 = 976562.5
|
||||
pwm_start();
|
||||
}
|
||||
|
|
|
@ -45,6 +45,10 @@
|
|||
#include "gccollect.h"
|
||||
#include "user_interface.h"
|
||||
|
||||
#if MICROPY_ESPNOW
|
||||
#include "modespnow.h"
|
||||
#endif
|
||||
|
||||
STATIC char heap[38 * 1024];
|
||||
|
||||
STATIC void mp_reset(void) {
|
||||
|
@ -73,6 +77,10 @@ STATIC void mp_reset(void) {
|
|||
mp_uos_dupterm_obj.fun.var(2, args);
|
||||
}
|
||||
|
||||
#if MICROPY_ESPNOW
|
||||
espnow_deinit(mp_const_none);
|
||||
#endif
|
||||
|
||||
#if MICROPY_MODULE_FROZEN
|
||||
pyexec_frozen_module("_boot.py", false);
|
||||
pyexec_file_if_exists("boot.py");
|
||||
|
|
|
@ -0,0 +1,507 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2017-2020 Nick Moore
|
||||
* Copyright (c) 2018 shawwwn <shawwwn1@gmail.com>
|
||||
* Copyright (c) 2020-2021 Glenn Moloney @glenn20
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/runtime.h"
|
||||
|
||||
#if MICROPY_ESPNOW
|
||||
|
||||
#include "c_types.h"
|
||||
#include "espnow.h"
|
||||
|
||||
#include "py/mphal.h"
|
||||
#include "py/mperrno.h"
|
||||
#include "py/qstr.h"
|
||||
#include "py/objstr.h"
|
||||
#include "py/objarray.h"
|
||||
#include "py/stream.h"
|
||||
#include "py/binary.h"
|
||||
#include "py/ringbuf.h"
|
||||
|
||||
#include "mpconfigport.h"
|
||||
|
||||
#include "modespnow.h"
|
||||
|
||||
// For the esp8266
|
||||
#define ESP_NOW_MAX_DATA_LEN (250)
|
||||
#define ESP_NOW_KEY_LEN (16)
|
||||
#define ESP_NOW_ETH_ALEN (6)
|
||||
#define ESP_NOW_SEND_SUCCESS (0)
|
||||
#define ESP_ERR_ESPNOW_NO_MEM (-77777)
|
||||
#define ESP_OK (0)
|
||||
#define ESP_NOW_MAX_TOTAL_PEER_NUM (20)
|
||||
#define ESP_NOW_MAX_ENCRYPT_PEER_NUM (6)
|
||||
#define ESP_ERR_ESPNOW_NOT_INIT (0x300 + 100 + 1)
|
||||
typedef int esp_err_t;
|
||||
|
||||
static const uint8_t ESPNOW_MAGIC = 0x99;
|
||||
|
||||
// Use this for peeking at the header of the next packet in the buffer.
|
||||
typedef struct {
|
||||
uint8_t magic; // = ESPNOW_MAGIC
|
||||
uint8_t msg_len; // Length of the message
|
||||
} __attribute__((packed)) espnow_hdr_t;
|
||||
|
||||
// ESPNow packet format for the receive buffer.
|
||||
typedef struct {
|
||||
espnow_hdr_t hdr; // The header
|
||||
uint8_t peer[6]; // Peer address
|
||||
uint8_t msg[0]; // Message is up to 250 bytes
|
||||
} __attribute__((packed)) espnow_pkt_t;
|
||||
|
||||
// The maximum length of an espnow packet (bytes)
|
||||
static const size_t MAX_PACKET_LEN = (
|
||||
sizeof(espnow_pkt_t) + ESP_NOW_MAX_DATA_LEN);
|
||||
|
||||
// Enough for 2 full-size packets: 2 * (6 + 2 + 250) = 516 bytes
|
||||
// Will allocate an additional 7 bytes for buffer overhead
|
||||
#define DEFAULT_RECV_BUFFER_SIZE \
|
||||
(2 * (sizeof(espnow_pkt_t) + ESP_NOW_MAX_DATA_LEN))
|
||||
|
||||
// Default timeout (millisec) to wait for incoming ESPNow messages (5 minutes).
|
||||
#define DEFAULT_RECV_TIMEOUT_MS (5 * 60 * 1000)
|
||||
|
||||
// Number of milliseconds to wait for pending responses to sent packets.
|
||||
// This is a fallback which should never be reached.
|
||||
#define PENDING_RESPONSES_TIMEOUT_MS 100
|
||||
|
||||
// The data structure for the espnow_singleton.
|
||||
typedef struct _esp_espnow_obj_t {
|
||||
mp_obj_base_t base;
|
||||
ringbuf_t *recv_buffer; // A buffer for received packets
|
||||
size_t recv_buffer_size; // Size of recv buffer
|
||||
size_t recv_timeout_ms; // Timeout for irecv()
|
||||
size_t tx_packets; // Count of sent packets
|
||||
volatile size_t tx_responses; // # of sent packet responses received
|
||||
volatile size_t tx_failures; // # of sent packet responses failed
|
||||
} esp_espnow_obj_t;
|
||||
|
||||
// Initialised below.
|
||||
const mp_obj_type_t esp_espnow_type;
|
||||
|
||||
static esp_espnow_obj_t espnow_singleton = {
|
||||
.base.type = &esp_espnow_type,
|
||||
.recv_buffer = NULL,
|
||||
.recv_buffer_size = DEFAULT_RECV_BUFFER_SIZE,
|
||||
.recv_timeout_ms = DEFAULT_RECV_TIMEOUT_MS,
|
||||
};
|
||||
|
||||
// ### Initialisation and Config functions
|
||||
//
|
||||
|
||||
static void check_esp_err(int e) {
|
||||
if (e != 0) {
|
||||
mp_raise_OSError(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Return a pointer to the ESPNow module singleton
|
||||
// If state == INITIALISED check the device has been initialised.
|
||||
// Raises OSError if not initialised and state == INITIALISED.
|
||||
static esp_espnow_obj_t *_get_singleton() {
|
||||
return &espnow_singleton;
|
||||
}
|
||||
|
||||
static esp_espnow_obj_t *_get_singleton_initialised() {
|
||||
esp_espnow_obj_t *self = _get_singleton();
|
||||
if (self->recv_buffer == NULL) {
|
||||
// Throw an espnow not initialised error
|
||||
check_esp_err(ESP_ERR_ESPNOW_NOT_INIT);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// Allocate and initialise the ESPNow module as a singleton.
|
||||
// Returns the initialised espnow_singleton.
|
||||
STATIC mp_obj_t espnow_make_new(const mp_obj_type_t *type, size_t n_args,
|
||||
size_t n_kw, const mp_obj_t *all_args) {
|
||||
|
||||
return _get_singleton();
|
||||
}
|
||||
|
||||
// Forward declare the send and recv ESPNow callbacks
|
||||
STATIC void send_cb(uint8_t *mac_addr, uint8_t status);
|
||||
|
||||
STATIC void recv_cb(uint8_t *mac_addr, uint8_t *data, uint8_t len);
|
||||
|
||||
// ESPNow.deinit(): De-initialise the ESPNOW software stack, disable callbacks
|
||||
// and deallocate the recv data buffers.
|
||||
// Note: this function is called from main.c:mp_task() to cleanup before soft
|
||||
// reset, so cannot be declared STATIC and must guard against self == NULL;.
|
||||
mp_obj_t espnow_deinit(mp_obj_t _) {
|
||||
esp_espnow_obj_t *self = _get_singleton();
|
||||
if (self->recv_buffer != NULL) {
|
||||
// esp_now_unregister_recv_cb();
|
||||
esp_now_deinit();
|
||||
self->recv_buffer->buf = NULL;
|
||||
self->recv_buffer = NULL;
|
||||
self->tx_packets = self->tx_responses;
|
||||
}
|
||||
MP_STATE_PORT(espnow_buffer) = NULL;
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
// ESPNow.active(): Initialise the data buffers and ESP-NOW functions.
|
||||
// Initialise the Espressif ESPNOW software stack, register callbacks and
|
||||
// allocate the recv data buffers.
|
||||
// Returns True if interface is active, else False.
|
||||
STATIC mp_obj_t espnow_active(size_t n_args, const mp_obj_t *args) {
|
||||
esp_espnow_obj_t *self = args[0];
|
||||
if (n_args > 1) {
|
||||
if (mp_obj_is_true(args[1])) {
|
||||
if (self->recv_buffer == NULL) { // Already initialised
|
||||
self->recv_buffer = m_new_obj(ringbuf_t);
|
||||
ringbuf_alloc(self->recv_buffer, self->recv_buffer_size);
|
||||
MP_STATE_PORT(espnow_buffer) = self->recv_buffer;
|
||||
esp_now_init();
|
||||
esp_now_set_self_role(ESP_NOW_ROLE_COMBO);
|
||||
esp_now_register_recv_cb(recv_cb);
|
||||
esp_now_register_send_cb(send_cb);
|
||||
}
|
||||
} else {
|
||||
espnow_deinit(self);
|
||||
}
|
||||
}
|
||||
return mp_obj_new_bool(self->recv_buffer != NULL);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(espnow_active_obj, 1, 2, espnow_active);
|
||||
|
||||
// ESPNow.config(): Initialise the data buffers and ESP-NOW functions.
|
||||
// Initialise the Espressif ESPNOW software stack, register callbacks and
|
||||
// allocate the recv data buffers.
|
||||
// Returns True if interface is active, else False.
|
||||
STATIC mp_obj_t espnow_config(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
esp_espnow_obj_t *self = _get_singleton();
|
||||
enum { ARG_rxbuf, ARG_timeout_ms };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_rxbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
|
||||
{ MP_QSTR_timeout_ms, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
|
||||
};
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args,
|
||||
MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
if (args[ARG_rxbuf].u_int >= 0) {
|
||||
self->recv_buffer_size = args[ARG_rxbuf].u_int;
|
||||
}
|
||||
if (args[ARG_timeout_ms].u_int >= 0) {
|
||||
self->recv_timeout_ms = args[ARG_timeout_ms].u_int;
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(espnow_config_obj, 1, espnow_config);
|
||||
|
||||
// ### The ESP_Now send and recv callback routines
|
||||
//
|
||||
|
||||
// Callback triggered when a sent packet is acknowledged by the peer (or not).
|
||||
// Just count the number of responses and number of failures.
|
||||
// These are used in the send()/write() logic.
|
||||
STATIC void send_cb(uint8_t *mac_addr, uint8_t status) {
|
||||
esp_espnow_obj_t *self = _get_singleton();
|
||||
self->tx_responses++;
|
||||
if (status != ESP_NOW_SEND_SUCCESS) {
|
||||
self->tx_failures++;
|
||||
}
|
||||
}
|
||||
|
||||
// Callback triggered when an ESP-Now packet is received.
|
||||
// Write the peer MAC address and the message into the recv_buffer as an
|
||||
// ESPNow packet.
|
||||
// If the buffer is full, drop the message and increment the dropped count.
|
||||
// Schedules the user callback if one has been registered (ESPNow.config()).
|
||||
STATIC void recv_cb(uint8_t *mac_addr, uint8_t *msg, uint8_t msg_len) {
|
||||
esp_espnow_obj_t *self = _get_singleton();
|
||||
ringbuf_t *buf = self->recv_buffer;
|
||||
// TODO: Test this works with ">".
|
||||
if (buf == NULL || sizeof(espnow_pkt_t) + msg_len >= ringbuf_free(buf)) {
|
||||
return;
|
||||
}
|
||||
espnow_hdr_t header;
|
||||
header.magic = ESPNOW_MAGIC;
|
||||
header.msg_len = msg_len;
|
||||
|
||||
ringbuf_put_bytes(buf, (uint8_t *)&header, sizeof(header));
|
||||
ringbuf_put_bytes(buf, mac_addr, ESP_NOW_ETH_ALEN);
|
||||
ringbuf_put_bytes(buf, msg, msg_len);
|
||||
}
|
||||
|
||||
// Return C pointer to byte memory string/bytes/bytearray in obj.
|
||||
// Raise ValueError if the length does not match expected len.
|
||||
static uint8_t *_get_bytes_len_rw(mp_obj_t obj, size_t len, mp_uint_t rw) {
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(obj, &bufinfo, rw);
|
||||
if (bufinfo.len != len) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("invalid buffer length"));
|
||||
}
|
||||
return (uint8_t *)bufinfo.buf;
|
||||
}
|
||||
|
||||
static uint8_t *_get_bytes_len(mp_obj_t obj, size_t len) {
|
||||
return _get_bytes_len_rw(obj, len, MP_BUFFER_READ);
|
||||
}
|
||||
|
||||
static uint8_t *_get_bytes_len_w(mp_obj_t obj, size_t len) {
|
||||
return _get_bytes_len_rw(obj, len, MP_BUFFER_WRITE);
|
||||
}
|
||||
|
||||
// ### Handling espnow packets in the recv buffer
|
||||
//
|
||||
|
||||
// Copy data from the ring buffer - wait if buffer is empty up to timeout_ms
|
||||
// 0: Success
|
||||
// -1: Not enough data available to complete read (try again later)
|
||||
// -2: Requested read is larger than buffer - will never succeed
|
||||
static int ringbuf_get_bytes_wait(ringbuf_t *r, uint8_t *data, size_t len, mp_int_t timeout_ms) {
|
||||
mp_uint_t start = mp_hal_ticks_ms();
|
||||
int status = 0;
|
||||
while (((status = ringbuf_get_bytes(r, data, len)) == -1)
|
||||
&& (timeout_ms < 0 || (mp_uint_t)(mp_hal_ticks_ms() - start) < (mp_uint_t)timeout_ms)) {
|
||||
MICROPY_EVENT_POLL_HOOK;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
// ESPNow.recvinto([timeout_ms, []]):
|
||||
// Returns a list of byte strings: (peer_addr, message) where peer_addr is
|
||||
// the MAC address of the sending peer.
|
||||
// Arguments:
|
||||
// timeout_ms: timeout in milliseconds (or None).
|
||||
// buffers: list of bytearrays to store values: [peer, message].
|
||||
// Default timeout is set with ESPNow.config(timeout=milliseconds).
|
||||
// Return (None, None) on timeout.
|
||||
STATIC mp_obj_t espnow_recvinto(size_t n_args, const mp_obj_t *args) {
|
||||
esp_espnow_obj_t *self = _get_singleton_initialised();
|
||||
|
||||
size_t timeout_ms = ((n_args > 2 && args[2] != mp_const_none)
|
||||
? mp_obj_get_int(args[2]) : self->recv_timeout_ms);
|
||||
|
||||
mp_obj_list_t *list = MP_OBJ_TO_PTR(args[1]);
|
||||
if (!mp_obj_is_type(list, &mp_type_list) || list->len < 2) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("ESPNow.recvinto(): Invalid argument"));
|
||||
}
|
||||
mp_obj_array_t *msg = MP_OBJ_TO_PTR(list->items[1]);
|
||||
size_t msg_size = msg->len + msg->free;
|
||||
if (mp_obj_is_type(msg, &mp_type_bytearray)) {
|
||||
msg->len = msg_size; // Make all the space in msg array available
|
||||
msg->free = 0;
|
||||
}
|
||||
uint8_t *peer_buf = _get_bytes_len_w(list->items[0], ESP_NOW_ETH_ALEN);
|
||||
uint8_t *msg_buf = _get_bytes_len_w(msg, ESP_NOW_MAX_DATA_LEN);
|
||||
|
||||
// Read the packet header from the incoming buffer
|
||||
espnow_hdr_t hdr;
|
||||
if (ringbuf_get_bytes_wait(self->recv_buffer, (uint8_t *)&hdr, sizeof(hdr), timeout_ms) < 0) {
|
||||
return MP_OBJ_NEW_SMALL_INT(0); // Timeout waiting for packet
|
||||
}
|
||||
int msg_len = hdr.msg_len;
|
||||
|
||||
// Check the message packet header format and read the message data
|
||||
if (hdr.magic != ESPNOW_MAGIC
|
||||
|| msg_len > ESP_NOW_MAX_DATA_LEN
|
||||
|| ringbuf_get_bytes(self->recv_buffer, peer_buf, ESP_NOW_ETH_ALEN) < 0
|
||||
|| ringbuf_get_bytes(self->recv_buffer, msg_buf, msg_len) < 0) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("ESPNow.recv(): buffer error"));
|
||||
}
|
||||
if (mp_obj_is_type(msg, &mp_type_bytearray)) {
|
||||
// Set the length of the message bytearray.
|
||||
msg->len = msg_len;
|
||||
msg->free = msg_size - msg_len;
|
||||
}
|
||||
|
||||
return MP_OBJ_NEW_SMALL_INT(msg_len);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(espnow_recvinto_obj, 2, 3, espnow_recvinto);
|
||||
|
||||
// Used by espnow_send() for sends() with sync==True.
|
||||
// Wait till all pending sent packet responses have been received.
|
||||
// ie. self->tx_responses == self->tx_packets.
|
||||
// Return the number of responses where status != ESP_NOW_SEND_SUCCESS.
|
||||
static void _wait_for_pending_responses(esp_espnow_obj_t *self) {
|
||||
for (int i = 0; i < PENDING_RESPONSES_TIMEOUT_MS; i++) {
|
||||
if (self->tx_responses >= self->tx_packets) {
|
||||
return;
|
||||
}
|
||||
mp_hal_delay_ms(1); // Allow other tasks to run
|
||||
}
|
||||
// Note: the loop timeout is just a fallback - in normal operation
|
||||
// we should never reach that timeout.
|
||||
}
|
||||
|
||||
// ESPNow.send(peer_addr, message, [sync (=true)])
|
||||
// ESPNow.send(message)
|
||||
// Send a message to the peer's mac address. Optionally wait for a response.
|
||||
// If sync == True, wait for response after sending.
|
||||
// Returns:
|
||||
// True if sync==False and message sent successfully.
|
||||
// True if sync==True and message is received successfully by all recipients
|
||||
// False if sync==True and message is not received by at least one recipient
|
||||
// Raises: EAGAIN if the internal espnow buffers are full.
|
||||
STATIC mp_obj_t espnow_send(size_t n_args, const mp_obj_t *args) {
|
||||
esp_espnow_obj_t *self = _get_singleton_initialised();
|
||||
|
||||
bool sync = n_args <= 3 || args[3] == mp_const_none || mp_obj_is_true(args[3]);
|
||||
// Get a pointer to the buffer of obj
|
||||
mp_buffer_info_t message;
|
||||
mp_get_buffer_raise(args[2], &message, MP_BUFFER_READ);
|
||||
|
||||
// Bugfix: esp_now_send() generates a panic if message buffer points
|
||||
// to an address in ROM (eg. a statically interned QSTR).
|
||||
// Fix: if message is not in gc pool, copy to a temp buffer.
|
||||
static char temp[ESP_NOW_MAX_DATA_LEN]; // Static to save code space
|
||||
byte *p = (byte *)message.buf;
|
||||
// if (p < MP_STATE_MEM(area.gc_pool_start) || MP_STATE_MEM(area.gc_pool_end) < p) {
|
||||
if (MP_STATE_MEM(area.gc_pool_end) < p) {
|
||||
// If buffer is not in GC pool copy from ROM to stack
|
||||
memcpy(temp, message.buf, message.len);
|
||||
message.buf = temp;
|
||||
}
|
||||
|
||||
if (sync) {
|
||||
// If the last call was sync==False there may be outstanding responses.
|
||||
// We need to wait for all pending responses if this call has sync=True.
|
||||
_wait_for_pending_responses(self);
|
||||
}
|
||||
int saved_failures = self->tx_failures;
|
||||
|
||||
check_esp_err(
|
||||
esp_now_send(_get_bytes_len(args[1], ESP_NOW_ETH_ALEN), message.buf, message.len));
|
||||
self->tx_packets++;
|
||||
if (sync) {
|
||||
// Wait for message to be received by peer
|
||||
_wait_for_pending_responses(self);
|
||||
}
|
||||
// Return False if sync and any peers did not respond.
|
||||
return mp_obj_new_bool(!(sync && self->tx_failures != saved_failures));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(espnow_send_obj, 3, 4, espnow_send);
|
||||
|
||||
// ### Peer Management Functions
|
||||
//
|
||||
|
||||
// Set the ESP-NOW Primary Master Key (pmk) (for encrypted communications).
|
||||
// Raise OSError if not initialised.
|
||||
// Raise ValueError if key is not a bytes-like object exactly 16 bytes long.
|
||||
STATIC mp_obj_t espnow_set_pmk(mp_obj_t _, mp_obj_t key) {
|
||||
check_esp_err(esp_now_set_kok(_get_bytes_len(key, ESP_NOW_KEY_LEN), ESP_NOW_KEY_LEN));
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(espnow_set_pmk_obj, espnow_set_pmk);
|
||||
|
||||
// ESPNow.add_peer(peer_mac, [lmk, [channel, [ifidx, [encrypt]]]])
|
||||
// Positional args set to None will be left at defaults.
|
||||
// Raise OSError if not initialised.
|
||||
// Raise ValueError if mac or LMK are not bytes-like objects or wrong length.
|
||||
// Raise TypeError if invalid keyword args or too many positional args.
|
||||
// Return None.
|
||||
STATIC mp_obj_t espnow_add_peer(size_t n_args, const mp_obj_t *args) {
|
||||
check_esp_err(
|
||||
esp_now_add_peer(
|
||||
_get_bytes_len(args[1], ESP_NOW_ETH_ALEN),
|
||||
ESP_NOW_ROLE_COMBO,
|
||||
(n_args > 3) ? mp_obj_get_int(args[3]) : 0,
|
||||
(n_args > 2) ? _get_bytes_len(args[2], ESP_NOW_KEY_LEN) : NULL,
|
||||
ESP_NOW_KEY_LEN));
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(espnow_add_peer_obj, 2, 4, espnow_add_peer);
|
||||
|
||||
// ESPNow.del_peer(peer_mac): Unregister peer_mac.
|
||||
// Raise OSError if not initialised.
|
||||
// Raise ValueError if peer is not a bytes-like objects or wrong length.
|
||||
// Return None.
|
||||
STATIC mp_obj_t espnow_del_peer(mp_obj_t _, mp_obj_t peer) {
|
||||
esp_now_del_peer(_get_bytes_len(peer, ESP_NOW_ETH_ALEN));
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(espnow_del_peer_obj, espnow_del_peer);
|
||||
|
||||
STATIC const mp_rom_map_elem_t esp_espnow_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&espnow_active_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&espnow_config_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_recvinto), MP_ROM_PTR(&espnow_recvinto_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&espnow_send_obj) },
|
||||
|
||||
// Peer management functions
|
||||
{ MP_ROM_QSTR(MP_QSTR_set_pmk), MP_ROM_PTR(&espnow_set_pmk_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_add_peer), MP_ROM_PTR(&espnow_add_peer_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_del_peer), MP_ROM_PTR(&espnow_del_peer_obj) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(esp_espnow_locals_dict, esp_espnow_locals_dict_table);
|
||||
|
||||
STATIC const mp_rom_map_elem_t espnow_globals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__espnow) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ESPNowBase), MP_ROM_PTR(&esp_espnow_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_MAX_DATA_LEN), MP_ROM_INT(ESP_NOW_MAX_DATA_LEN)},
|
||||
{ MP_ROM_QSTR(MP_QSTR_ADDR_LEN), MP_ROM_INT(ESP_NOW_ETH_ALEN)},
|
||||
{ MP_ROM_QSTR(MP_QSTR_KEY_LEN), MP_ROM_INT(ESP_NOW_KEY_LEN)},
|
||||
{ MP_ROM_QSTR(MP_QSTR_MAX_TOTAL_PEER_NUM), MP_ROM_INT(ESP_NOW_MAX_TOTAL_PEER_NUM)},
|
||||
{ MP_ROM_QSTR(MP_QSTR_MAX_ENCRYPT_PEER_NUM), MP_ROM_INT(ESP_NOW_MAX_ENCRYPT_PEER_NUM)},
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(espnow_globals_dict, espnow_globals_dict_table);
|
||||
|
||||
// ### Dummy Buffer Protocol support
|
||||
// ...so asyncio can poll.ipoll() on this device
|
||||
|
||||
// Support ioctl(MP_STREAM_POLL, ) for asyncio
|
||||
STATIC mp_uint_t espnow_stream_ioctl(mp_obj_t self_in, mp_uint_t request,
|
||||
uintptr_t arg, int *errcode) {
|
||||
if (request != MP_STREAM_POLL) {
|
||||
*errcode = MP_EINVAL;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
esp_espnow_obj_t *self = _get_singleton();
|
||||
return (self->recv_buffer == NULL) ? 0 : // If not initialised
|
||||
arg ^ ((ringbuf_avail(self->recv_buffer) == 0) ? MP_STREAM_POLL_RD : 0);
|
||||
}
|
||||
|
||||
STATIC const mp_stream_p_t espnow_stream_p = {
|
||||
.ioctl = espnow_stream_ioctl,
|
||||
};
|
||||
|
||||
MP_DEFINE_CONST_OBJ_TYPE(
|
||||
esp_espnow_type,
|
||||
MP_QSTR_ESPNowBase,
|
||||
MP_TYPE_FLAG_NONE,
|
||||
make_new, espnow_make_new,
|
||||
protocol, &espnow_stream_p,
|
||||
locals_dict, &esp_espnow_locals_dict
|
||||
);
|
||||
|
||||
const mp_obj_module_t mp_module_espnow = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&espnow_globals_dict,
|
||||
};
|
||||
|
||||
MP_REGISTER_MODULE(MP_QSTR__espnow, mp_module_espnow);
|
||||
MP_REGISTER_ROOT_POINTER(void *espnow_buffer);
|
||||
#endif
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Glenn Moloney @glenn20
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// Called from main.c:mp_task() to reset the espnow software stack
|
||||
mp_obj_t espnow_deinit(mp_obj_t _);
|
|
@ -1,2 +1,3 @@
|
|||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_get_wlan_obj);
|
||||
extern const mp_obj_type_t esp_network_wlan_type;
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_phy_mode_obj);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{ MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&esp_network_get_wlan_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&esp_network_wlan_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_phy_mode), MP_ROM_PTR(&esp_network_phy_mode_obj) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_STA_IF), MP_ROM_INT(STATION_IF)},
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
# espnow module for MicroPython on ESP8266
|
||||
# MIT license; Copyright (c) 2022 Glenn Moloney @glenn20
|
||||
|
||||
from _espnow import *
|
||||
from uselect import poll, POLLIN
|
||||
|
||||
|
||||
class ESPNow(ESPNowBase):
|
||||
# Static buffers for alloc free receipt of messages with ESPNow.irecv().
|
||||
_data = [bytearray(ADDR_LEN), bytearray(MAX_DATA_LEN)]
|
||||
_none_tuple = (None, None)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._poll = poll() # For any() method below...
|
||||
self._poll.register(self, POLLIN)
|
||||
|
||||
def irecv(self, timeout_ms=None):
|
||||
n = self.recvinto(self._data, timeout_ms)
|
||||
return self._data if n else self._none_tuple
|
||||
|
||||
def recv(self, timeout_ms=None):
|
||||
n = self.recvinto(self._data, timeout_ms)
|
||||
return [bytes(x) for x in self._data] if n else self._none_tuple
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
return self.irecv() # Use alloc free irecv() method
|
||||
|
||||
def any(self): # For the ESP8266 which does not have ESPNow.any()
|
||||
try:
|
||||
next(self._poll.ipoll(0))
|
||||
return True
|
||||
except StopIteration:
|
||||
return False
|
|
@ -66,7 +66,6 @@
|
|||
#define MICROPY_PY_MACHINE_BITSTREAM (1)
|
||||
#define MICROPY_PY_MACHINE_PULSE (1)
|
||||
#define MICROPY_PY_MACHINE_PWM (1)
|
||||
#define MICROPY_PY_MACHINE_PWM_INIT (1)
|
||||
#define MICROPY_PY_MACHINE_PWM_DUTY (1)
|
||||
#define MICROPY_PY_MACHINE_PWM_INCLUDEFILE "ports/esp8266/machine_pwm.c"
|
||||
#define MICROPY_PY_MACHINE_I2C (1)
|
||||
|
@ -88,6 +87,7 @@
|
|||
#define MICROPY_PY_OS_DUPTERM (2)
|
||||
#define MICROPY_PY_UOS_DUPTERM_NOTIFY (1)
|
||||
#define MICROPY_PY_UOS_DUPTERM_STREAM_DETACHED_ATTACHED (1)
|
||||
#define MICROPY_PY_UOS_SYNC (1)
|
||||
#define MICROPY_PY_UOS_UNAME (1)
|
||||
#define MICROPY_PY_UOS_UNAME_RELEASE_DYNAMIC (1)
|
||||
#define MICROPY_PY_UOS_URANDOM (1)
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "spi_flash.h"
|
||||
#include "ets_alt_task.h"
|
||||
#include "lwip/dns.h"
|
||||
#include "modnetwork.h"
|
||||
|
||||
typedef struct _wlan_if_obj_t {
|
||||
mp_obj_base_t base;
|
||||
|
@ -46,11 +47,10 @@ typedef struct _wlan_if_obj_t {
|
|||
} wlan_if_obj_t;
|
||||
|
||||
void error_check(bool status, const char *msg);
|
||||
const mp_obj_type_t wlan_if_type;
|
||||
|
||||
STATIC const wlan_if_obj_t wlan_objs[] = {
|
||||
{{&wlan_if_type}, STATION_IF},
|
||||
{{&wlan_if_type}, SOFTAP_IF},
|
||||
{{&esp_network_wlan_type}, STATION_IF},
|
||||
{{&esp_network_wlan_type}, SOFTAP_IF},
|
||||
};
|
||||
|
||||
STATIC void require_if(mp_obj_t wlan_if, int if_no) {
|
||||
|
@ -60,7 +60,8 @@ STATIC void require_if(mp_obj_t wlan_if, int if_no) {
|
|||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t get_wlan(size_t n_args, const mp_obj_t *args) {
|
||||
STATIC mp_obj_t esp_wlan_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
mp_arg_check_num(n_args, n_kw, 0, 1, false);
|
||||
int idx = 0;
|
||||
if (n_args > 0) {
|
||||
idx = mp_obj_get_int(args[0]);
|
||||
|
@ -70,7 +71,6 @@ STATIC mp_obj_t get_wlan(size_t n_args, const mp_obj_t *args) {
|
|||
}
|
||||
return MP_OBJ_FROM_PTR(&wlan_objs[idx]);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_get_wlan_obj, 0, 1, get_wlan);
|
||||
|
||||
STATIC mp_obj_t esp_active(size_t n_args, const mp_obj_t *args) {
|
||||
wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
|
@ -419,6 +419,10 @@ STATIC mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs
|
|||
system_phy_set_max_tpw(power);
|
||||
break;
|
||||
}
|
||||
case MP_QSTR_pm: {
|
||||
wifi_set_sleep_type(mp_obj_get_int(kwargs->table[i].value));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
goto unknown;
|
||||
}
|
||||
|
@ -486,6 +490,10 @@ STATIC mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs
|
|||
val = mp_obj_new_int(wifi_get_phy_mode());
|
||||
break;
|
||||
}
|
||||
case MP_QSTR_pm: {
|
||||
val = MP_OBJ_NEW_SMALL_INT(wifi_get_sleep_type());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
goto unknown;
|
||||
}
|
||||
|
@ -511,14 +519,20 @@ STATIC const mp_rom_map_elem_t wlan_if_locals_dict_table[] = {
|
|||
{ MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&esp_isconnected_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&esp_config_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&esp_ifconfig_obj) },
|
||||
|
||||
// Constants
|
||||
{ MP_ROM_QSTR(MP_QSTR_PM_NONE), MP_ROM_INT(NONE_SLEEP_T) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_PM_PERFORMANCE), MP_ROM_INT(MODEM_SLEEP_T) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_PM_POWERSAVE), MP_ROM_INT(LIGHT_SLEEP_T) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(wlan_if_locals_dict, wlan_if_locals_dict_table);
|
||||
|
||||
MP_DEFINE_CONST_OBJ_TYPE(
|
||||
wlan_if_type,
|
||||
esp_network_wlan_type,
|
||||
MP_QSTR_WLAN,
|
||||
MP_TYPE_FLAG_NONE,
|
||||
make_new, esp_wlan_make_new,
|
||||
locals_dict, &wlan_if_locals_dict
|
||||
);
|
||||
|
||||
|
|
|
@ -140,7 +140,13 @@ SRC_HAL_IMX_C += \
|
|||
$(MCU_DIR)/drivers/fsl_snvs_lp.c \
|
||||
$(MCU_DIR)/drivers/fsl_wdog.c \
|
||||
$(MCU_DIR)/system_$(MCU_SERIES)$(MCU_CORE).c \
|
||||
$(MCU_DIR)/xip/fsl_flexspi_nor_boot.c \
|
||||
|
||||
# Use a specific boot header for 1062 so the Teensy loader doesn't erase the filesystem.
|
||||
ifeq ($(MCU_SERIES), MIMXRT1062)
|
||||
SRC_HAL_IMX_C += hal/fsl_flexspi_nor_boot.c
|
||||
else
|
||||
SRC_HAL_IMX_C += $(MCU_DIR)/xip/fsl_flexspi_nor_boot.c
|
||||
endif
|
||||
|
||||
ifeq ($(MICROPY_HW_SDRAM_AVAIL),1)
|
||||
SRC_HAL_IMX_C += $(MCU_DIR)/drivers/fsl_semc.c
|
||||
|
@ -183,7 +189,9 @@ SRC_C += \
|
|||
drivers/dht/dht.c \
|
||||
eth.c \
|
||||
fatfs_port.c \
|
||||
flash.c \
|
||||
hal/pwm_backport.c \
|
||||
help.c \
|
||||
led.c \
|
||||
machine_adc.c \
|
||||
machine_bitstream.c \
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"deploy": [
|
||||
"deploy_metro_m7.md"
|
||||
],
|
||||
"docs": "",
|
||||
"features": [
|
||||
"USB-C",
|
||||
"SPI",
|
||||
"I2C",
|
||||
"UART",
|
||||
"RGB LED",
|
||||
"QSPI Flash",
|
||||
"QWIIC",
|
||||
"JLink",
|
||||
"WiFi",
|
||||
"BLE"
|
||||
],
|
||||
"images": [
|
||||
"Metro_M7.jpg"
|
||||
],
|
||||
"mcu": "mimxrt",
|
||||
"product": "Adafruit Metro M7",
|
||||
"thumbnail": "",
|
||||
"url": "https://www.adafruit.com/product/4950",
|
||||
"vendor": "Adafruit"
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
## 1. Deploy the MicroPython firmware to the Metro M7 board.
|
||||
|
||||
### 1.1 Deploy the firmware using the serial bootloader.
|
||||
|
||||
For initial deployment of the firmware a few preparation steps are required, which
|
||||
have to be done once.
|
||||
|
||||
1. Get the files ufconv.py and uf2families.json from the micropython/tools directory,
|
||||
e.g. at https://github.com/micropython/micropython/tree/master/tools.
|
||||
|
||||
2. Get the NXP program sdphost for your operating system, e.g. from
|
||||
https://github.com/adafruit/tinyuf2/tree/master/ports/mimxrt10xx/sdphost.
|
||||
You can also get them from the NXP web sites.
|
||||
|
||||
3. Get the UF2 boot-loader package https://github.com/adafruit/tinyuf2/releases/download/0.9.0/tinyuf2-imxrt1010_evk-0.9.0.zip
|
||||
and extract the file tinyuf2-imxrt1010_evk-0.9.0.bin.
|
||||
|
||||
Now you have all files at hand that you will need for updating.
|
||||
|
||||
1. Get the firmware you want to upload from the MicroPython download page.
|
||||
|
||||
2. Set the two BOOTSEL DIP switches to the 1/0 position, which is the opposite position of the normal use mode.
|
||||
|
||||
3. Push the reset button.
|
||||
|
||||
4. Run the commands:
|
||||
|
||||
```
|
||||
sudo ./sdphost -u 0x1fc9,0x0145 -- write-file 0x20206400 tinyuf2-imxrt1010_evk-0.9.0.bin
|
||||
sudo ./sdphost -u 0x1fc9,0x0145 -- jump-address 0x20207000
|
||||
```
|
||||
Wait until a drive icon appears on the computer (or mount it explicitly), and then run:
|
||||
```
|
||||
python3 uf2conv.py <firmware_xx.yy.zz.hex> --base 0x60000400 -f 0x4fb2d5bd
|
||||
```
|
||||
You can put all of that in a script. Just add a short wait before the 3rd command to let the drive connect.
|
||||
|
||||
5. Once the upload is finished, set the BOOTSEL DIP switches back to the 0/1 position and push reset.
|
||||
|
||||
Using sudo is Linux specific. You may not need it at all, if the access rights are set properly,
|
||||
and you will not need it for Windows.
|
||||
|
||||
### 1.2 Deploy the firmware using a JTAG adapter.
|
||||
|
||||
With a JTAG adapter the firmware can be easily installed. Appropriate tools are Segger JFlash Lite and
|
||||
the Segger Edu Mini adapter. Just use the firmware.hex file for loading, which will be loaded at the
|
||||
proper address.
|
||||
|
||||
|
||||
## 2. Deploy the WiFi firmware.
|
||||
|
||||
The NINA firmware in the NINA module has to be updated for use with MicroPython. That can be done
|
||||
using MicroPython and two small Python scripts.
|
||||
|
||||
The firmware binaries are available at
|
||||
https://github.com/micropython/micropython-lib/tree/master/micropython/espflash
|
||||
or https://github.com/robert-hh/Shared-Stuff. For the Metro M7 board, the
|
||||
NINA_FW_v1.5.0_Airlift.bin file is needed.
|
||||
|
||||
For firmware upload, the following connections to the WiFi module are required:
|
||||
|
||||
- Pin Reset (as above)
|
||||
- Pin GPIO0
|
||||
- UART RX
|
||||
- UART TX
|
||||
|
||||
The GPIO pins and UART device id varies between boards. At the Adafruit Metro M7 board,
|
||||
the UART is UART(1), and the Pin names for reset and GPIO0 are ESP_RESET and ESP_GPIO0.
|
||||
The firmware can be uploaded, using the espflash.py module, a short script
|
||||
using espflash.py and mpremote. espflash.py is available at
|
||||
https://github.com/micropython/micropython-lib/tree/master/micropython/espflash.
|
||||
This place also holds the example script.
|
||||
|
||||
```
|
||||
import espflash
|
||||
from machine import Pin
|
||||
from machine import UART
|
||||
import sys
|
||||
sys.path.append("/flash")
|
||||
|
||||
reset = Pin("ESP_RESET", Pin.OUT)
|
||||
gpio0 = Pin("ESP_GPIO0", Pin.OUT)
|
||||
uart = UART(0, 115200, timeout=350)
|
||||
|
||||
md5sum = b"b0b9ab23da820a469e597c41364acb3a"
|
||||
path = "/remote/NINA_FW_v1.5.0_Airlift.bin"
|
||||
|
||||
esp = espflash.ESPFlash(reset, gpio0, uart)
|
||||
# Enter bootloader download mode, at 115200
|
||||
esp.bootloader()
|
||||
# Can now change to higher/lower baud rate
|
||||
esp.set_baudrate(921600)
|
||||
# Must call this first before any flash functions.
|
||||
esp.flash_attach()
|
||||
# Read flash size
|
||||
size = esp.flash_read_size()
|
||||
# Configure flash parameters.
|
||||
esp.flash_config(size)
|
||||
# Write firmware image from internal storage.
|
||||
esp.flash_write_file(path)
|
||||
# Compares file and flash MD5 checksum.
|
||||
esp.flash_verify_file(path, md5sum)
|
||||
# Resets the ESP32 chip.
|
||||
esp.reboot()
|
||||
```
|
||||
|
||||
The script shows the set-up for the Metro M7 board.
|
||||
The md5sum is the one of the WiFi firmware. It may change and
|
||||
can be recalculated using e.g. the Linux `md5sum` command. It is used to
|
||||
verify the firmware upload. To upload the firmware, place the firmware
|
||||
and the above script (let's call it ninaflash.py) into the same directory
|
||||
on your PC, and run the command:
|
||||
```
|
||||
mpremote connect <port> mount . run ninaflash.py
|
||||
```
|
||||
After a while, the upload will start. A typical start sequence looks like:
|
||||
```
|
||||
Local directory . is mounted at /remote
|
||||
Failed to read response to command 8.
|
||||
Failed to read response to command 8.
|
||||
Changing baudrate => 921600
|
||||
Flash attached
|
||||
Flash size 2.0 MBytes
|
||||
Flash write size: 1310720 total_blocks: 320 block size: 4096
|
||||
Writing sequence number 0/320...
|
||||
Writing sequence number 1/320...
|
||||
Writing sequence number 2/320...
|
||||
Writing sequence number 3/320...
|
||||
Writing sequence number 4/320...
|
||||
....
|
||||
....
|
||||
Writing sequence number 317/320...
|
||||
Writing sequence number 318/320...
|
||||
Writing sequence number 319/320...
|
||||
Flash write finished
|
||||
Flash verify: File MD5 b'b0b9ab23da820a469e597c41364acb3a'
|
||||
Flash verify: Flash MD5 b'b0b9ab23da820a469e597c41364acb3a'
|
||||
Firmware verified.
|
||||
```
|
||||
The initial messages `Failed to read response to command 8.`
|
||||
can be ignored.
|
|
@ -0,0 +1,65 @@
|
|||
#define MICROPY_HW_BOARD_NAME "Adafruit Metro M7"
|
||||
#define MICROPY_HW_MCU_NAME "MIMXRT1011DAE5A"
|
||||
|
||||
// i.MX RT1010 EVK has 1 board LED
|
||||
#define MICROPY_HW_LED1_PIN (pin_GPIO_03)
|
||||
#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_high(pin))
|
||||
#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_low(pin))
|
||||
|
||||
#define MICROPY_HW_NUM_PIN_IRQS (2 * 32)
|
||||
|
||||
// Define mapping logical UART # to hardware UART #
|
||||
// LPUART1 on USB_DBG -> 0
|
||||
// LPUART1 on D0/D1 -> 1
|
||||
// LPUART3 on A0/D4 -> 3
|
||||
// LPUART4 on D6/D7 -> 2
|
||||
|
||||
#define MICROPY_HW_UART_NUM (sizeof(uart_index_table) / sizeof(uart_index_table)[0])
|
||||
#define MICROPY_HW_UART_INDEX { 1, 1, 2, 4 }
|
||||
|
||||
#define IOMUX_TABLE_UART \
|
||||
{ IOMUXC_GPIO_10_LPUART1_TXD }, { IOMUXC_GPIO_09_LPUART1_RXD }, \
|
||||
{ IOMUXC_GPIO_12_LPUART3_TXD }, { IOMUXC_GPIO_11_LPUART3_RXD }, \
|
||||
{ 0 }, { 0 }, \
|
||||
{ IOMUXC_GPIO_AD_02_LPUART4_TXD }, { IOMUXC_GPIO_AD_01_LPUART4_RXD },
|
||||
|
||||
#define MICROPY_HW_SPI_INDEX { 1 }
|
||||
|
||||
#define IOMUX_TABLE_SPI \
|
||||
{ IOMUXC_GPIO_AD_06_LPSPI1_SCK }, { IOMUXC_GPIO_AD_05_LPSPI1_PCS0 }, \
|
||||
{ IOMUXC_GPIO_AD_04_LPSPI1_SDO }, { IOMUXC_GPIO_AD_03_LPSPI1_SDI }, \
|
||||
{ IOMUXC_GPIO_AD_02_LPSPI1_PCS1 }
|
||||
|
||||
#define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx }
|
||||
#define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx }
|
||||
|
||||
// Define mapping hardware I2C # to logical I2C #
|
||||
// SDA/SCL HW-I2C Logical I2C
|
||||
// D14/D15 LPI2C1 -> 0
|
||||
// D0/D1 LPI2C2 -> 1
|
||||
// D6/D7 LPI2C2 -> 1 Alternatively possible GPIO_AD_01, GPIO_AD_02
|
||||
|
||||
#define MICROPY_HW_I2C_INDEX { 1, 2 }
|
||||
|
||||
#define IOMUX_TABLE_I2C \
|
||||
{ IOMUXC_GPIO_02_LPI2C1_SCL }, { IOMUXC_GPIO_01_LPI2C1_SDA }, \
|
||||
{ IOMUXC_GPIO_10_LPI2C2_SCL }, { IOMUXC_GPIO_09_LPI2C2_SDA },
|
||||
|
||||
// Wifi Deinitions
|
||||
#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-metro-m7"
|
||||
|
||||
#define MICROPY_HW_WIFI_SPI_ID (0)
|
||||
#define MICROPY_HW_WIFI_SPI_BAUDRATE (8000000)
|
||||
|
||||
#define MICROPY_HW_NINA_ACK pin_find(MP_OBJ_NEW_QSTR(MP_QSTR_ESP_BUSY))
|
||||
#define MICROPY_HW_NINA_CS pin_find(MP_OBJ_NEW_QSTR(MP_QSTR_ESP_CS))
|
||||
#define MICROPY_HW_NINA_RESET pin_find(MP_OBJ_NEW_QSTR(MP_QSTR_ESP_RESET))
|
||||
|
||||
// BLE definitions
|
||||
#define MICROPY_PY_BLUETOOTH_NINAW10 (1)
|
||||
|
||||
#define MICROPY_HW_BLE_UART_ID (1)
|
||||
#define MICROPY_HW_BLE_UART_BAUDRATE (115200)
|
||||
|
||||
#define MICROPY_HW_NINA_RTS pin_find(MP_OBJ_NEW_QSTR(MP_QSTR_MOSI))
|
||||
#define MICROPY_HW_NINA_CTS MICROPY_HW_NINA_ACK
|
|
@ -0,0 +1,11 @@
|
|||
MCU_SERIES = MIMXRT1011
|
||||
MCU_VARIANT = MIMXRT1011DAE5A
|
||||
|
||||
MICROPY_FLOAT_IMPL = single
|
||||
MICROPY_PY_MACHINE_SDCARD = 0
|
||||
MICROPY_HW_FLASH_TYPE ?= qspi_nor_flash
|
||||
MICROPY_HW_FLASH_SIZE ?= 0x800000 # 8MB
|
||||
|
||||
MICROPY_PY_NETWORK_NINAW10 ?= 1
|
||||
MICROPY_PY_USSL ?= 1
|
||||
MICROPY_SSL_MBEDTLS ?= 1
|
|
@ -0,0 +1,31 @@
|
|||
D0,GPIO_09
|
||||
D1,GPIO_10
|
||||
D2,GPIO_13
|
||||
D3,GPIO_12
|
||||
D4,GPIO_SD_00
|
||||
D5,GPIO_SD_01
|
||||
D6,GPIO_SD_02
|
||||
D7,GPIO_11
|
||||
D8,GPIO_08
|
||||
D9,GPIO_07
|
||||
D10,GPIO_06
|
||||
D11,GPIO_05
|
||||
D12,GPIO_04
|
||||
D13,GPIO_03
|
||||
D14,GPIO_01
|
||||
D15,GPIO_02
|
||||
A0,GPIO_AD_02
|
||||
A1,GPIO_AD_01
|
||||
A2,GPIO_AD_00
|
||||
A3,GPIO_AD_05
|
||||
A4,GPIO_AD_10
|
||||
A5,GPIO_AD_08
|
||||
LED,GPIO_03
|
||||
NEOPIXEL,GPIO_00
|
||||
ESP_RESET,GPIO_AD_07
|
||||
ESP_BUSY,GPIO_AD_11
|
||||
ESP_CS,GPIO_AD_14
|
||||
ESP_GPIO0,GPIO_SD_05
|
||||
SCK,GPIO_AD_06
|
||||
MISO,GPIO_AD_03
|
||||
MOSI,GPIO_AD_04
|
|
|
@ -20,7 +20,7 @@
|
|||
#define IOMUX_TABLE_UART \
|
||||
{ IOMUXC_GPIO_10_LPUART1_TXD }, { IOMUXC_GPIO_09_LPUART1_RXD }, \
|
||||
{ 0 }, { 0 }, \
|
||||
{ 0 }, { 0 }, \
|
||||
{ IOMUXC_GPIO_08_LPUART3_TXD }, { IOMUXC_GPIO_AD_07_LPUART3_RXD }, \
|
||||
{ IOMUXC_GPIO_AD_02_LPUART4_TXD }, { IOMUXC_GPIO_AD_01_LPUART4_RXD },
|
||||
|
||||
#define MICROPY_HW_SPI_INDEX { 1 }
|
||||
|
|
|
@ -226,7 +226,6 @@ class Pins(object):
|
|||
# Extract indexes from header row
|
||||
pad_col = header.index("Pad")
|
||||
adc_col = header.index("ADC")
|
||||
acmp_col = header.index("ACMP")
|
||||
#
|
||||
for idx, row in enumerate(rows):
|
||||
pad = row[pad_col]
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2023 Philipp Ebensberger
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "flash.h"
|
||||
|
||||
void flash_init(void) {
|
||||
// Upload the custom flash configuration
|
||||
// This should be performed by the boot ROM but for some reason it is not.
|
||||
FLEXSPI_UpdateLUT(BOARD_FLEX_SPI, 0,
|
||||
qspiflash_config.memConfig.lookupTable,
|
||||
ARRAY_SIZE(qspiflash_config.memConfig.lookupTable));
|
||||
|
||||
// Configure FLEXSPI IP FIFO access.
|
||||
BOARD_FLEX_SPI->MCR0 &= ~(FLEXSPI_MCR0_ARDFEN_MASK);
|
||||
BOARD_FLEX_SPI->MCR0 &= ~(FLEXSPI_MCR0_ATDFEN_MASK);
|
||||
BOARD_FLEX_SPI->MCR0 |= FLEXSPI_MCR0_ARDFEN(0);
|
||||
BOARD_FLEX_SPI->MCR0 |= FLEXSPI_MCR0_ATDFEN(0);
|
||||
}
|
||||
|
||||
// flash_erase_block(erase_addr)
|
||||
// erases the block starting at addr. Block size according to the flash properties.
|
||||
__attribute__((section(".ram_functions"))) status_t flash_erase_block(uint32_t erase_addr) {
|
||||
status_t status = kStatus_Fail;
|
||||
|
||||
SCB_CleanInvalidateDCache();
|
||||
SCB_DisableDCache();
|
||||
__disable_irq();
|
||||
|
||||
status = flexspi_nor_flash_erase_block(BOARD_FLEX_SPI, erase_addr);
|
||||
|
||||
__enable_irq();
|
||||
SCB_EnableDCache();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// flash_erase_sector(erase_addr_bytes)
|
||||
// erases the sector starting at addr. Sector size according to the flash properties.
|
||||
__attribute__((section(".ram_functions"))) status_t flash_erase_sector(uint32_t erase_addr) {
|
||||
status_t status = kStatus_Fail;
|
||||
|
||||
SCB_CleanInvalidateDCache();
|
||||
SCB_DisableDCache();
|
||||
__disable_irq();
|
||||
|
||||
status = flexspi_nor_flash_erase_sector(BOARD_FLEX_SPI, erase_addr);
|
||||
|
||||
__enable_irq();
|
||||
SCB_EnableDCache();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// flash_write_block(flash_dest_addr_bytes, data_source, length_bytes)
|
||||
// writes length_byte data to the destination address
|
||||
// the vfs driver takes care for erasing the sector if required
|
||||
__attribute__((section(".ram_functions"))) status_t flash_write_block(uint32_t dest_addr, const uint8_t *src, uint32_t length) {
|
||||
status_t status = kStatus_Fail;
|
||||
uint32_t write_length;
|
||||
uint32_t next_addr;
|
||||
|
||||
if (length == 0) {
|
||||
status = kStatus_Success; // Nothing to do
|
||||
} else {
|
||||
|
||||
SCB_CleanInvalidateDCache();
|
||||
SCB_DisableDCache();
|
||||
|
||||
// write data in chunks not crossing a page boundary
|
||||
do {
|
||||
next_addr = dest_addr - (dest_addr % PAGE_SIZE_BYTES) + PAGE_SIZE_BYTES; // next page boundary
|
||||
write_length = next_addr - dest_addr; // calculate write length based on destination address and subsequent page boundary.
|
||||
if (write_length > length) { // compare possible write_length against remaining data length
|
||||
write_length = length;
|
||||
}
|
||||
|
||||
__disable_irq();
|
||||
status = flexspi_nor_flash_page_program(BOARD_FLEX_SPI, dest_addr, (uint32_t *)src, write_length);
|
||||
__enable_irq();
|
||||
|
||||
// Update remaining data length
|
||||
length -= write_length;
|
||||
|
||||
// Move source and destination pointer
|
||||
src += write_length;
|
||||
dest_addr += write_length;
|
||||
} while ((length > 0) && (status == kStatus_Success));
|
||||
|
||||
SCB_EnableDCache();
|
||||
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
// flash_read_block(flash_src_addr_bytes, data_dest, length_bytes)
|
||||
// read length_byte data to the source address
|
||||
// It is just a shim to provide the same structure for read_block and write_block.
|
||||
__attribute__((section(".ram_functions"))) void flash_read_block(uint32_t src_addr, uint8_t *dest, uint32_t length) {
|
||||
memcpy(dest, (const uint8_t *)(BOARD_FLEX_SPI_ADDR_BASE + src_addr), length);
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2023 Philipp Ebensberger
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef MICROPY_INCLUDED_MIMXRT_FLASH_H
|
||||
#define MICROPY_INCLUDED_MIMXRT_FLASH_H
|
||||
|
||||
#include BOARD_FLASH_OPS_HEADER_H
|
||||
|
||||
#define SECTOR_SIZE_BYTES (qspiflash_config.sectorSize)
|
||||
#define PAGE_SIZE_BYTES (qspiflash_config.pageSize)
|
||||
#define BLOCK_SIZE_BYTES (qspiflash_config.blockSize)
|
||||
|
||||
#define SECTOR_SIZE_BYTES (qspiflash_config.sectorSize)
|
||||
#define PAGE_SIZE_BYTES (qspiflash_config.pageSize)
|
||||
|
||||
#ifndef MICROPY_HW_FLASH_STORAGE_BYTES
|
||||
#define MICROPY_HW_FLASH_STORAGE_BYTES (((uint32_t)&__vfs_end) - ((uint32_t)&__vfs_start))
|
||||
#endif
|
||||
|
||||
#ifndef MICROPY_HW_FLASH_STORAGE_BASE
|
||||
#define MICROPY_HW_FLASH_STORAGE_BASE (((uint32_t)&__vfs_start) - ((uint32_t)&__flash_start))
|
||||
#endif
|
||||
|
||||
// Linker symbols
|
||||
extern uint8_t __vfs_start;
|
||||
extern uint8_t __vfs_end;
|
||||
extern uint8_t __flash_start;
|
||||
|
||||
void flash_init(void);
|
||||
status_t flash_erase_sector(uint32_t erase_addr);
|
||||
status_t flash_erase_block(uint32_t erase_addr);
|
||||
void flash_read_block(uint32_t src_addr, uint8_t *dest, uint32_t length);
|
||||
status_t flash_write_block(uint32_t dest_addr, const uint8_t *src, uint32_t length);
|
||||
|
||||
#endif // MICROPY_INCLUDED_MIMXRT_FLASH_H
|
|
@ -222,6 +222,7 @@ typedef struct _FlexSPIConfig
|
|||
#define NOR_CMD_LUT_SEQ_IDX_ENTERQPI 10
|
||||
#define NOR_CMD_LUT_SEQ_IDX_CHIPERASE 11
|
||||
#define NOR_CMD_LUT_SEQ_IDX_EXITQPI 12
|
||||
#define NOR_CMD_LUT_SEQ_IDX_ERASEBLOCK 13
|
||||
|
||||
#define HYPERFLASH_CMD_LUT_SEQ_IDX_READDATA 0
|
||||
#define HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEDATA 1
|
||||
|
|
|
@ -175,6 +175,11 @@ status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address) {
|
|||
return status;
|
||||
}
|
||||
|
||||
status_t flexspi_nor_flash_erase_block(FLEXSPI_Type *base, uint32_t address) __attribute__((section(".ram_functions")));
|
||||
status_t flexspi_nor_flash_erase_block(FLEXSPI_Type *base, uint32_t address) {
|
||||
return flexspi_nor_flash_erase_sector(base, address); // HyperFlash does not support block erase!
|
||||
}
|
||||
|
||||
status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t address, const uint32_t *src, uint32_t size) __attribute__((section(".ram_functions")));
|
||||
status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t address, const uint32_t *src, uint32_t size) {
|
||||
status_t status;
|
||||
|
|
|
@ -49,6 +49,7 @@ status_t flexspi_nor_hyperflash_cfi(FLEXSPI_Type *base);
|
|||
void flexspi_hyper_flash_init(void);
|
||||
void flexspi_nor_update_lut(void);
|
||||
status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address);
|
||||
status_t flexspi_nor_flash_erase_block(FLEXSPI_Type *base, uint32_t address);
|
||||
status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t address, const uint32_t *src, uint32_t size);
|
||||
|
||||
static inline uint32_t flexspi_get_frequency(void) {
|
||||
|
|
|
@ -165,6 +165,37 @@ status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address) {
|
|||
return status;
|
||||
}
|
||||
|
||||
status_t flexspi_nor_flash_erase_block(FLEXSPI_Type *base, uint32_t address) __attribute__((section(".ram_functions")));
|
||||
status_t flexspi_nor_flash_erase_block(FLEXSPI_Type *base, uint32_t address) {
|
||||
status_t status;
|
||||
flexspi_transfer_t flashXfer;
|
||||
|
||||
/* Write enable */
|
||||
status = flexspi_nor_write_enable(base, address);
|
||||
|
||||
if (status != kStatus_Success) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Erase sector */
|
||||
flashXfer.deviceAddress = address;
|
||||
flashXfer.port = kFLEXSPI_PortA1;
|
||||
flashXfer.cmdType = kFLEXSPI_Command;
|
||||
flashXfer.SeqNumber = 1;
|
||||
flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_ERASEBLOCK;
|
||||
status = FLEXSPI_TransferBlocking(base, &flashXfer);
|
||||
|
||||
if (status != kStatus_Success) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = flexspi_nor_wait_bus_busy(base);
|
||||
|
||||
flexspi_nor_reset(base);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t dstAddr, const uint32_t *src, uint32_t size) __attribute__((section(".ram_functions")));
|
||||
status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t dstAddr, const uint32_t *src, uint32_t size) {
|
||||
status_t status;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Ładowanie…
Reference in New Issue