diff --git a/.github/workflows/ports_windows.yml b/.github/workflows/ports_windows.yml index 91a3192a10a4..84e018ba15d1 100644 --- a/.github/workflows/ports_windows.yml +++ b/.github/workflows/ports_windows.yml @@ -110,6 +110,11 @@ jobs: run: shell: msys2 {0} steps: + - uses: actions/setup-python@v5 + # note: can go back to installing mingw-w64-${{ matrix.env }}-python after + # MSYS2 updates to Python >3.12 (due to settrace compatibility issue) + with: + python-version: '3.11' - uses: msys2/setup-msys2@v2 with: msystem: ${{ matrix.sys }} @@ -118,9 +123,9 @@ jobs: make mingw-w64-${{ matrix.env }}-gcc pkg-config - mingw-w64-${{ matrix.env }}-python3 git diffutils + path-type: inherit # Remove when setup-python is removed - uses: actions/checkout@v4 - name: Build mpy-cross.exe run: make -C mpy-cross -j2 diff --git a/docs/conf.py b/docs/conf.py index 32cc2be10e34..e5c6ba98090a 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -36,6 +36,9 @@ "is_release": micropy_version != "latest", } +# Authors used in various parts of the documentation. +micropy_authors = "MicroPython authors and contributors" + # -- General configuration ------------------------------------------------ @@ -68,7 +71,7 @@ # General information about the project. project = "MicroPython" -copyright = "- The MicroPython Documentation is Copyright © 2014-2024, Damien P. George, Paul Sokolovsky, and contributors" +copyright = "- The MicroPython Documentation is Copyright © 2014-2024, " + micropy_authors # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -244,7 +247,7 @@ master_doc, "MicroPython.tex", "MicroPython Documentation", - "Damien P. George, Paul Sokolovsky, and contributors", + micropy_authors, "manual", ), ] @@ -281,7 +284,7 @@ "index", "micropython", "MicroPython Documentation", - ["Damien P. George, Paul Sokolovsky, and contributors"], + [micropy_authors], 1, ), ] @@ -300,7 +303,7 @@ master_doc, "MicroPython", "MicroPython Documentation", - "Damien P. George, Paul Sokolovsky, and contributors", + micropy_authors, "MicroPython", "One line description of project.", "Miscellaneous", diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index 6d4b14aef42d..d17040277167 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -79,11 +79,11 @@ Networking WLAN ^^^^ -The :mod:`network` module:: +The :class:`network.WLAN` class in the :mod:`network` module:: import network - wlan = network.WLAN(network.STA_IF) # create station interface + wlan = network.WLAN(network.WLAN.IF_STA) # create station interface wlan.active(True) # activate the interface wlan.scan() # scan for access points wlan.isconnected() # check if the station is connected to an AP @@ -91,7 +91,7 @@ The :mod:`network` module:: wlan.config('mac') # get the interface's MAC address wlan.ipconfig('addr4') # get the interface's IPv4 addresses - ap = network.WLAN(network.AP_IF) # create access-point interface + ap = network.WLAN(network.WLAN.IF_AP) # create access-point interface ap.config(ssid='ESP-AP') # set the SSID of the access point ap.config(max_clients=10) # set how many clients can connect to the network ap.active(True) # activate the interface @@ -100,7 +100,7 @@ A useful function for connecting to your local WiFi network is:: def do_connect(): import network - wlan = network.WLAN(network.STA_IF) + wlan = network.WLAN(network.WLAN.IF_STA) wlan.active(True) if not wlan.isconnected(): print('connecting to network...') @@ -124,7 +124,8 @@ to reconnect forever). LAN ^^^ -To use the wired interfaces one has to specify the pins and mode :: +To use the wired interfaces via :class:`network.LAN` one has to specify the pins +and mode :: import network @@ -770,20 +771,33 @@ APA102 (DotStar) uses a different driver as it has an additional clock pin. Capacitive touch ---------------- -Use the ``TouchPad`` class in the ``machine`` module:: +ESP32, ESP32-S2 and ESP32-S3 support capacitive touch via the ``TouchPad`` class +in the ``machine`` module:: from machine import TouchPad, Pin t = TouchPad(Pin(14)) t.read() # Returns a smaller number when touched -``TouchPad.read`` returns a value relative to the capacitive variation. Small numbers (typically in -the *tens*) are common when a pin is touched, larger numbers (above *one thousand*) when -no touch is present. However the values are *relative* and can vary depending on the board -and surrounding composition so some calibration may be required. - -There are ten capacitive touch-enabled pins that can be used on the ESP32: 0, 2, 4, 12, 13 -14, 15, 27, 32, 33. Trying to assign to any other pins will result in a ``ValueError``. +``TouchPad.read`` returns a value proportional to the capacitance between the +pin and the board's Ground connection. On ESP32 the number becomes smaller when +the pin (or connected touch pad) is touched, on ESP32-S2 and ESP32-S3 the number +becomes larger when the pin is touched. + +In all cases, a touch causes a significant change in the return value. Note the +returned values are *relative* and can vary depending on the board and +surrounding environment so some calibration (i.e. comparison to a baseline or +rolling average) may be required. + +========= ============================================== +Chip Touch-enabled pins +--------- ---------------------------------------------- +ESP32 0, 2, 4, 12, 13, 14, 15, 27, 32, 33 +ESP32-S2 1 to 14 inclusive +ESP32-S3 1 to 14 inclusive +========= ============================================== + +Trying to assign to any other pins will result in a ``ValueError``. Note that TouchPads can be used to wake an ESP32 from sleep:: diff --git a/docs/esp32/tutorial/index.rst b/docs/esp32/tutorial/index.rst index c6242d731f18..2435f2ecd2f5 100644 --- a/docs/esp32/tutorial/index.rst +++ b/docs/esp32/tutorial/index.rst @@ -21,3 +21,4 @@ to ``__. intro.rst pwm.rst peripheral_access.rst + reset.rst diff --git a/docs/esp32/tutorial/intro.rst b/docs/esp32/tutorial/intro.rst index be09599871ce..cf4d0bcbd2f5 100644 --- a/docs/esp32/tutorial/intro.rst +++ b/docs/esp32/tutorial/intro.rst @@ -50,6 +50,8 @@ features, there are daily builds. If your board has SPIRAM support you can use either the standard firmware or the firmware with SPIRAM support, and in the latter case you will have access to more RAM for Python objects. +.. _esp32_flashing: + Deploying the firmware ---------------------- diff --git a/docs/esp32/tutorial/reset.rst b/docs/esp32/tutorial/reset.rst new file mode 100644 index 000000000000..b3fc6a85bd43 --- /dev/null +++ b/docs/esp32/tutorial/reset.rst @@ -0,0 +1,25 @@ +Factory reset +============= + +If something unexpected happens and your ESP32-based board no longer boots +MicroPython, then you may have to factory reset it. For more details, see +:ref:`soft_bricking`. + +Factory resetting the MicroPython esp32 port involves fully erasing the flash +and resetting the flash memory, so you will need to re-flash the MicroPython +firmware afterwards and copy any Python files to the filesystem again. + +1. You will need the Espressif `esptool`_ installed on your system. This is the + same tool that you may have used to initially install MicroPython on your + board (see :ref:`installation instructions `). +2. Find the serial port name of your board, and then use esptool to erase the + entire flash contents:: + + esptool.py -p PORTNAME erase_flash + +3. Use esptool to flash the MicroPython file to your board again. If needed, + this file and flashing instructions can be found on the `MicroPython + downloads page`_. + +.. _esptool: https://github.com/espressif/esptool +.. _MicroPython downloads page: https://micropython.org/download/?port=esp32 diff --git a/docs/esp8266/general.rst b/docs/esp8266/general.rst index be0437e752ee..d7211aa5ab74 100644 --- a/docs/esp8266/general.rst +++ b/docs/esp8266/general.rst @@ -74,40 +74,7 @@ as possible after use. Boot process ------------ -On boot, MicroPython EPS8266 port executes ``_boot.py`` script from internal -frozen modules. It mounts filesystem in FlashROM, or if it's not available, -performs first-time setup of the module and creates the filesystem. This -part of the boot process is considered fixed, and not available for customization -for end users (even if you build from source, please refrain from changes to -it; customization of early boot process is available only to advanced users -and developers, who can diagnose themselves any issues arising from -modifying the standard process). - -Once the filesystem is mounted, ``boot.py`` is executed from it. The standard -version of this file is created during first-time module set up and has -commands to start a WebREPL daemon (disabled by default, configurable -with ``webrepl_setup`` module), etc. This -file is customizable by end users (for example, you may want to set some -parameters or add other services which should be run on -a module start-up). But keep in mind that incorrect modifications to boot.py -may still lead to boot loops or lock ups, requiring to reflash a module -from scratch. (In particular, it's recommended that you use either -``webrepl_setup`` module or manual editing to configure WebREPL, but not -both). - -As a final step of boot procedure, ``main.py`` is executed from filesystem, -if exists. This file is a hook to start up a user application each time -on boot (instead of going to REPL). For small test applications, you may -name them directly as ``main.py``, and upload to module, but instead it's -recommended to keep your application(s) in separate files, and have just -the following in ``main.py``:: - - import my_app - my_app.main() - -This will allow to keep the structure of your application clear, as well as -allow to install multiple applications on a board, and switch among them. - +See :doc:`/reference/reset_boot`. Known Issues ------------ diff --git a/docs/esp8266/quickref.rst b/docs/esp8266/quickref.rst index b130ce65db2b..635f1f834bb6 100644 --- a/docs/esp8266/quickref.rst +++ b/docs/esp8266/quickref.rst @@ -49,11 +49,11 @@ The :mod:`esp` module:: Networking ---------- -The :mod:`network` module:: +The :class:`network.WLAN` class in the :mod:`network` module:: import network - wlan = network.WLAN(network.STA_IF) # create station interface + wlan = network.WLAN(network.WLAN.IF_STA) # create station interface wlan.active(True) # activate the interface wlan.scan() # scan for access points wlan.isconnected() # check if the station is connected to an AP @@ -61,7 +61,7 @@ The :mod:`network` module:: wlan.config('mac') # get the interface's MAC address wlan.ipconfig('addr4') # get the interface's IPv4 addresses - ap = network.WLAN(network.AP_IF) # create access-point interface + ap = network.WLAN(network.WLAN.IF_AP) # create access-point interface ap.active(True) # activate the interface ap.config(ssid='ESP-AP') # set the SSID of the access point @@ -69,7 +69,7 @@ A useful function for connecting to your local WiFi network is:: def do_connect(): import network - wlan = network.WLAN(network.STA_IF) + wlan = network.WLAN(network.WLAN.IF_STA) wlan.active(True) if not wlan.isconnected(): print('connecting to network...') @@ -163,10 +163,10 @@ sys.stdin.read() if it's needed to read characters from the UART(0) while it's also used for the REPL (or detach, read, then reattach). When detached the UART(0) can be used for other purposes. -If there are no objects in any of the dupterm slots when the REPL is -started (on hard or soft reset) then UART(0) is automatically attached. -Without this, the only way to recover a board without a REPL would be to -completely erase and reflash (which would install the default boot.py which +If there are no objects in any of the dupterm slots when the REPL is started (on +:doc:`hard or soft reset `) then UART(0) is automatically +attached. Without this, the only way to recover a board without a REPL would be +to completely erase and reflash (which would install the default boot.py which attaches the REPL). To detach the REPL from UART0, use:: diff --git a/docs/esp8266/tutorial/network_basics.rst b/docs/esp8266/tutorial/network_basics.rst index 9d74a6283ac4..e383c00c6cee 100644 --- a/docs/esp8266/tutorial/network_basics.rst +++ b/docs/esp8266/tutorial/network_basics.rst @@ -1,14 +1,15 @@ Network basics ============== -The network module is used to configure the WiFi connection. There are two WiFi -interfaces, one for the station (when the ESP8266 connects to a router) and one -for the access point (for other devices to connect to the ESP8266). Create +The :class:`network.WLAN` class in the :mod:`network` module is used to +configure the WiFi connection. There are two WiFi interfaces, one for +the station (when the ESP8266 connects to a router) and one for the +access point (for other devices to connect to the ESP8266). Create instances of these objects using:: >>> import network - >>> sta_if = network.WLAN(network.STA_IF) - >>> ap_if = network.WLAN(network.AP_IF) + >>> sta_if = network.WLAN(network.WLAN.IF_STA) + >>> ap_if = network.WLAN(network.WLAN.IF_AP) You can check if the interfaces are active by:: @@ -57,7 +58,7 @@ connect to your WiFi network:: def do_connect(): import network - sta_if = network.WLAN(network.STA_IF) + sta_if = network.WLAN(network.WLAN.IF_STA) if not sta_if.isconnected(): print('connecting to network...') sta_if.active(True) diff --git a/docs/library/builtins.rst b/docs/library/builtins.rst index e489375b1f91..5956aea7ab20 100644 --- a/docs/library/builtins.rst +++ b/docs/library/builtins.rst @@ -170,6 +170,10 @@ Exceptions .. exception:: KeyboardInterrupt + |see_cpython| `python:KeyboardInterrupt`. + + See also in the context of :ref:`soft_bricking`. + .. exception:: KeyError .. exception:: MemoryError @@ -190,6 +194,12 @@ Exceptions |see_cpython| `python:SystemExit`. + On non-embedded ports (i.e. Windows and Unix), an unhandled ``SystemExit`` + exits the MicroPython process in a similar way to CPython. + + On embedded ports, an unhandled ``SystemExit`` currently causes a + :ref:`soft_reset` of MicroPython. + .. exception:: TypeError |see_cpython| `python:TypeError`. diff --git a/docs/library/espnow.rst b/docs/library/espnow.rst index f0b592dffc8a..379e60486f22 100644 --- a/docs/library/espnow.rst +++ b/docs/library/espnow.rst @@ -56,7 +56,7 @@ A simple example would be: import espnow # A WLAN interface must be active to send()/recv() - sta = network.WLAN(network.STA_IF) # Or network.AP_IF + sta = network.WLAN(network.WLAN.IF_STA) # Or network.WLAN.IF_AP sta.active(True) sta.disconnect() # For ESP8266 @@ -76,7 +76,7 @@ A simple example would be: import espnow # A WLAN interface must be active to send()/recv() - sta = network.WLAN(network.STA_IF) + sta = network.WLAN(network.WLAN.IF_STA) sta.active(True) sta.disconnect() # Because ESP8266 auto-connects to last Access Point @@ -182,14 +182,14 @@ Configuration Sending and Receiving Data -------------------------- -A wifi interface (``network.STA_IF`` or ``network.AP_IF``) must be +A wifi interface (``network.WLAN.IF_STA`` or ``network.WLAN.IF_AP``) must be `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 = network.WLAN(network.WLAN.IF_STA) sta.active(True) sta.disconnect() # For ESP8266 @@ -441,12 +441,14 @@ must first register the sender and use the same encryption keys as the sender - *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) + channel of the wifi device will be used, if channel is set to another + value then this must match the channel currently configured on the + interface (see :func:`WLAN.config`). (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`_ + ``network.WLAN.IF_STA`` (=0) or ``network.WLAN.IF_AP`` (=1). + (default=0/``network.WLAN.IF_STA``). See `ESPNow and Wifi Operation`_ below for more information. - *encrypt*: (ESP32 only) If set to ``True`` data exchanged with @@ -470,6 +472,9 @@ must first register the sender and use the same encryption keys as the sender registered. - ``OSError(num, "ESP_ERR_ESPNOW_FULL")`` if too many peers are already registered. + - ``OSError(num, "ESP_ERR_ESPNOW_CHAN")`` if a channel value was + set that doesn't match the channel currently configured for this + interface. - ``ValueError()`` on invalid keyword args or values. .. method:: ESPNow.del_peer(mac) @@ -588,7 +593,7 @@ api-reference/network/esp_now.html#api-reference>`_. For example:: 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) + network.WLAN(network.WLAN.IF_STA).active(True) else: raise err @@ -645,7 +650,7 @@ A small async server example:: import asyncio # A WLAN interface must be active to send()/recv() - network.WLAN(network.STA_IF).active(True) + network.WLAN(network.WLAN.IF_STA).active(True) e = aioespnow.AIOESPNow() # Returns AIOESPNow enhanced with async support e.active(True) @@ -747,8 +752,8 @@ ESPNow and Wifi Operation ------------------------- ESPNow messages may be sent and received on any `active()` -`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 +`WLAN` interface (``network.WLAN.IF_STA`` or ``network.WLAN.IF_AP``), +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: @@ -832,8 +837,8 @@ Other issues to take care with when using ESPNow with wifi are: 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 = network.WLAN(network.WLAN.IF_STA); sta.active(False) + ap = network.WLAN(network.WLAN.IF_AP); ap.active(False) sta.active(True) while not sta.active(): time.sleep(0.1) diff --git a/docs/library/machine.RTC.rst b/docs/library/machine.RTC.rst index fcd78f1c3986..ad0b0759efe5 100644 --- a/docs/library/machine.RTC.rst +++ b/docs/library/machine.RTC.rst @@ -83,7 +83,7 @@ Methods a `bytes` object. Data written to RTC user memory is persistent across restarts, including - `machine.soft_reset()` and `machine.deepsleep()`. + :ref:`soft_reset` and `machine.deepsleep()`. The maximum length of RTC user memory is 2048 bytes by default on esp32, and 492 bytes on esp8266. diff --git a/docs/library/machine.USBDevice.rst b/docs/library/machine.USBDevice.rst index a47fda2a28d3..5c18c49a75bb 100644 --- a/docs/library/machine.USBDevice.rst +++ b/docs/library/machine.USBDevice.rst @@ -32,10 +32,10 @@ Managing a runtime USB interface can be tricky, especially if you are communicat with MicroPython over a built-in USB-CDC serial port that's part of the same USB device. -- A MicroPython soft reset will always clear all runtime USB interfaces, which - results in the entire USB device disconnecting from the host. If MicroPython - is also providing a built-in USB-CDC serial port then this will re-appear - after the soft reset. +- A MicroPython :ref:`soft reset ` will always clear all runtime USB + interfaces, which results in the entire USB device disconnecting from the + host. If MicroPython is also providing a built-in USB-CDC serial port then + this will re-appear after the soft reset. This means some functions (like ``mpremote run``) that target the USB-CDC serial port will immediately fail if a runtime USB interface is active, @@ -44,9 +44,9 @@ device. no more runtime USB interface. - To configure a runtime USB device on every boot, it's recommended to place the - configuration code in the ``boot.py`` file on the :ref:`device VFS + configuration code in the :ref:`boot.py` file on the :ref:`device VFS `. On each reset this file is executed before the USB subsystem is - initialised (and before ``main.py``), so it allows the board to come up with the runtime + initialised (and before :ref:`main.py`), so it allows the board to come up with the runtime USB device immediately. - For development or debugging, it may be convenient to connect a hardware diff --git a/docs/library/machine.rst b/docs/library/machine.rst index 770e51c87d10..97533c254813 100644 --- a/docs/library/machine.rst +++ b/docs/library/machine.rst @@ -62,14 +62,13 @@ Reset related functions .. function:: reset() - Resets the device in a manner similar to pushing the external RESET - button. + :ref:`Hard resets ` the device in a manner similar to pushing the + external RESET button. .. function:: soft_reset() - Performs a soft reset of the interpreter, deleting all Python objects and - resetting the Python heap. It tries to retain the method by which the user - is connected to the MicroPython REPL (eg serial, USB, Wifi). + Performs a :ref:`soft reset ` of the interpreter, deleting all + Python objects and resetting the Python heap. .. function:: reset_cause() diff --git a/docs/library/network.PPP.rst b/docs/library/network.PPP.rst index 85f580ce540e..17a8d1c1cd89 100644 --- a/docs/library/network.PPP.rst +++ b/docs/library/network.PPP.rst @@ -70,8 +70,11 @@ Methods .. method:: PPP.config(config_parameters) - Sets or gets parameters of the PPP interface. There are currently no parameter that - can be set or retrieved. + Sets or gets parameters of the PPP interface. The only parameter that can be + retrieved and set is the underlying stream, using:: + + stream = PPP.config("stream") + PPP.config(stream=stream) .. method:: PPP.ipconfig('param') PPP.ipconfig(param=value, ...) diff --git a/docs/library/network.WLAN.rst b/docs/library/network.WLAN.rst index c1eb520961fc..09fefc5929f0 100644 --- a/docs/library/network.WLAN.rst +++ b/docs/library/network.WLAN.rst @@ -8,7 +8,7 @@ This class provides a driver for WiFi network processors. Example usage:: import network # enable station interface and connect to WiFi access point - nic = network.WLAN(network.STA_IF) + nic = network.WLAN(network.WLAN.IF_STA) nic.active(True) nic.connect('your-ssid', 'your-key') # now use sockets as usual @@ -18,8 +18,8 @@ Constructors .. class:: WLAN(interface_id) Create a WLAN network interface object. Supported interfaces are -``network.STA_IF`` (station aka client, connects to upstream WiFi access -points) and ``network.AP_IF`` (access point, allows other WiFi clients to +``network.WLAN.IF_STA`` (station aka client, connects to upstream WiFi access +points) and ``network.WLAN.IF_AP`` (access point, allows other WiFi clients to connect). Availability of the methods below depends on interface type. For example, only STA interface may `WLAN.connect()` to an access point. @@ -75,7 +75,7 @@ Methods Return the current status of the wireless connection. When called with no argument the return value describes the network link status. - The possible statuses are defined as constants: + The possible statuses are defined as constants in the :mod:`network` module: * ``STAT_IDLE`` -- no connection and no activity, * ``STAT_CONNECTING`` -- connecting in progress, @@ -126,7 +126,7 @@ Methods ============= =========== mac MAC address (bytes) ssid WiFi access point name (string) - channel WiFi channel (integer) + channel WiFi channel (integer). Depending on the port this may only be supported on the AP interface. hidden Whether SSID is hidden (boolean) security Security protocol supported (enumeration, see module constants) key Access key (string) diff --git a/docs/library/pyb.rst b/docs/library/pyb.rst index f169a77f3a6c..5eb75c22b608 100644 --- a/docs/library/pyb.rst +++ b/docs/library/pyb.rst @@ -147,10 +147,10 @@ Power related functions (internal oscillator) directly. The higher frequencies use the HSE to drive the PLL (phase locked loop), and then use the output of the PLL. - Note that if you change the frequency while the USB is enabled then - the USB may become unreliable. It is best to change the frequency - in boot.py, before the USB peripheral is started. Also note that sysclk - frequencies below 36MHz do not allow the USB to function correctly. + Note that if you change the frequency while the USB is enabled then the USB + may become unreliable. It is best to change the frequency in :ref:`boot.py`, + before the USB peripheral is started. Also note that sysclk frequencies below + 36MHz do not allow the USB to function correctly. .. function:: wfi() @@ -205,8 +205,9 @@ Miscellaneous functions .. function:: main(filename) - Set the filename of the main script to run after boot.py is finished. If - this function is not called then the default file main.py will be executed. + Set the filename of the main script to run after :ref:`boot.py` is finished. + If this function is not called then the default file :ref:`main.py` will be + executed. It only makes sense to call this function from within boot.py. diff --git a/docs/library/sys.rst b/docs/library/sys.rst index 7b34a0e31c6b..c72214c13108 100644 --- a/docs/library/sys.rst +++ b/docs/library/sys.rst @@ -12,9 +12,12 @@ Functions .. function:: exit(retval=0, /) Terminate current program with a given exit code. Underlyingly, this - function raise as `SystemExit` exception. If an argument is given, its + function raises a `SystemExit` exception. If an argument is given, its value given as an argument to `SystemExit`. + On embedded ports (i.e. all ports but Windows and Unix), an unhandled + `SystemExit` currently causes a :ref:`soft_reset` of MicroPython. + .. function:: atexit(func) Register *func* to be called upon termination. *func* must be a callable diff --git a/docs/mimxrt/quickref.rst b/docs/mimxrt/quickref.rst index cfd0605054a7..34e0aa79f139 100644 --- a/docs/mimxrt/quickref.rst +++ b/docs/mimxrt/quickref.rst @@ -193,7 +193,7 @@ PWM Constructor - *freq* should be an integer which sets the frequency in Hz for the PWM cycle. The valid frequency range is 15 Hz resp. 18Hz resp. 24Hz up to > 1 MHz. - - *duty_u16* sets the duty cycle as a ratio ``duty_u16 / 65536``. + - *duty_u16* sets the duty cycle as a ratio ``duty_u16 / 65535``. 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%). @@ -231,7 +231,7 @@ is created by dividing the pwm_clk signal by an integral factor, according to th f = pwm_clk / (2**n * m) -with n being in the range of 0..7, and m in the range of 2..65536. pmw_clk is 125Mhz +with n being in the range of 0..7, and m in the range of 2..65535. pmw_clk is 125Mhz for MIMXRT1010/1015/1020, 150 MHz for MIMXRT1050/1060/1064 and 160MHz for MIMXRT1170. The lowest frequency is pwm_clk/2**23 (15, 18, 20Hz). The highest frequency with U16 resolution is pwm_clk/2**16 (1907, 2288, 2441 Hz), the highest frequency @@ -255,7 +255,7 @@ Use the :ref:`machine.ADC ` class:: from machine import ADC adc = ADC(Pin('A2')) # create ADC object on ADC pin - adc.read_u16() # read value, 0-65536 across voltage range 0.0v - 3.3v + adc.read_u16() # read value, 0-65535 across voltage range 0.0v - 3.3v The resolution of the ADC is 12 bit with 10 to 11 bit accuracy, irrespective of the value returned by read_u16(). If you need a higher resolution or better accuracy, use diff --git a/docs/pyboard/tutorial/reset.rst b/docs/pyboard/tutorial/reset.rst index 59a3cd82ae1a..ad56995c60dd 100644 --- a/docs/pyboard/tutorial/reset.rst +++ b/docs/pyboard/tutorial/reset.rst @@ -10,6 +10,8 @@ execution of ``boot.py`` and ``main.py`` and gives default USB settings. If you have problems with the filesystem you can do a factory reset, which restores the filesystem to its original state. +For more information, see :doc:`/reference/reset_boot`. + Safe mode --------- diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 98f6b6573769..1558c0fdfa9b 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -21,6 +21,7 @@ implementation and the best practices to use them. glossary.rst repl.rst + reset_boot.rst mpremote.rst mpyfiles.rst isr_rules.rst diff --git a/docs/reference/mpremote.rst b/docs/reference/mpremote.rst index 4d86f0080b11..d4c6983595fe 100644 --- a/docs/reference/mpremote.rst +++ b/docs/reference/mpremote.rst @@ -477,7 +477,7 @@ An example ``config.py`` might look like: """,], # Print out nearby WiFi networks. "wl_ipconfig": [ "exec", - "import network; sta_if = network.WLAN(network.STA_IF); print(sta_if.ipconfig('addr4'))", + "import network; sta_if = network.WLAN(network.WLAN.IF_STA); print(sta_if.ipconfig('addr4'))", """,], # 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. @@ -547,9 +547,9 @@ device at ``/dev/ttyACM1``, printing each result. 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. +Connect to the device without triggering a :ref:`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 diff --git a/docs/reference/repl.rst b/docs/reference/repl.rst index 55f76ee1a03b..be02ddebde8e 100644 --- a/docs/reference/repl.rst +++ b/docs/reference/repl.rst @@ -143,10 +143,12 @@ the auto-indent feature, and changes the prompt from ``>>>`` to ``===``. For exa Paste Mode allows blank lines to be pasted. The pasted text is compiled as if it were a file. Pressing Ctrl-D exits paste mode and initiates the compilation. +.. _repl_soft_reset: + Soft reset ---------- -A soft reset will reset the python interpreter, but tries not to reset the +A :ref:`soft_reset` will reset the python interpreter, but tries not to reset the method by which you're connected to the MicroPython board (USB-serial, or Wifi). You can perform a soft reset from the REPL by pressing Ctrl-D, or from your python @@ -182,6 +184,9 @@ variables no longer exist: ['__name__', 'pyb'] >>> +For more information about reset types and the startup process, see +:doc:`/reference/reset_boot`. + The special variable _ (underscore) ----------------------------------- @@ -196,6 +201,8 @@ So you can use the underscore to save the result in a variable. For example: 15 >>> +.. _raw_repl: + Raw mode and raw-paste mode --------------------------- diff --git a/docs/reference/reset_boot.rst b/docs/reference/reset_boot.rst new file mode 100644 index 000000000000..4772d02dd1e3 --- /dev/null +++ b/docs/reference/reset_boot.rst @@ -0,0 +1,262 @@ +Reset and Boot Sequence +======================= + +A device running MicroPython follows a particular boot sequence to start up and +initialise itself after a reset. + +.. _hard_reset: + +Hard reset +---------- + +Booting from hard reset is what happens when a board is first powered up, a cold +boot. This is a complete reset of the MCU hardware. + +The MicroPython port code initialises all essential hardware (including embedded +clocks and power regulators, internal serial UART, etc), and then starts the +MicroPython environment. Existing :doc:`RTC ` +configuration may be retained after a hard reset, but all other hardware state +is cleared. + +The same hard reset boot sequence can be triggered by a number of events such as: + +- Python code executing :func:`machine.reset()`. +- User presses a physical Reset button on the board (where applicable). +- Waking from deep sleep (on most ports). +- MCU hardware watchdog reset. +- MCU hardware brown out detector. + +The details of hardware-specific reset triggers depend on the port and +associated hardware. The :func:`machine.reset_cause()` function can be used to +further determine the cause of a reset. + +.. _soft_reset: + +Soft Reset +---------- + +When MicroPython is already running, it's possible to trigger a soft reset by +:ref:`typing Ctrl-D in the REPL ` or executing +:func:`machine.soft_reset()`. + +A soft reset clears the Python interpreter, frees all Python memory, and starts +the MicroPython environment again. + +State which is cleared by a soft reset includes: + +- All Python variables, objects, imported modules, etc. +- Most peripherals configured using the :doc:`machine module + `. There are very limited exceptions, for example + :doc:`machine.Pin ` modes (i.e. if a pin is input or + output, high or low) are not reset on most ports. More advanced configuration + such as :func:`Pin.irq()` is always reset. +- Bluetooth. +- Network sockets. Open TCP sockets are closed cleanly with respect to the other party. +- Open files. The filesystem is left in a valid state. + +Some system state remains the same after a soft reset, including: + +- Any existing network connections (Ethernet, Wi-Fi, etc) remain active at the + IP Network layer. Querying the :doc:`network interface from code + ` may indicate the network interface is still active with a + configured IP address, etc. +- An active :doc:`REPL ` appears continuous before and after soft reset, + except in some unusual cases: + + * If the :ref:`machine.USBDevice ` class has been used to + create a custom USB interface then any built-in USB serial device will + appear to disconnect and reconnect as the custom USB interface must be + cleared during reset. + * A serial UART REPL will restore its default hardware configuration (baud + rate, etc). + +- CPU clock speed is usually not changed by a soft reset. +- :doc:`RTC ` configuration (i.e. setting of the current + time) is not changed by soft reset. + +.. _boot_sequence: + +Boot Sequence +------------- + +When MicroPython boots following either a hard or soft reset, it follows this +boot sequence in order: + +_boot.py +^^^^^^^^ + +This is an internal script :doc:`frozen into the MicroPython firmware +`. It is provided by MicroPython on many ports to do essential +initialisation. + +For example, on most ports ``_boot.py`` will detect the first boot of a new +device and format the :doc:`internal flash filesystem ` ready for +use. + +Unless you're creating a custom MicroPython build or adding a new port then you +probably don't need to worry about ``_boot.py``. It's best not to change the +contents unless you really know what you're doing. + +.. _boot.py: + +boot.py +^^^^^^^ + +A file named ``boot.py`` can be copied to the board's internal :ref:`filesystem +` using :doc:`mpremote `. + +If ``boot.py`` is found then it is executed. You can add code in ``boot.py`` to +perform custom one-off initialisation (for example, to configure the board's +hardware). + +A common practice is to configure a board's network connection in ``boot.py`` so +that it's always available after reset for use with the :doc:`REPL `, +:doc:`mpremote `, etc. + +.. warning:: boot.py should always exit and not run indefinitely. + + Depending on the port, some hardware initialisation is delayed until after + ``boot.py`` exits. This includes initialising USB on the stm32 port and all + ports which support :ref:`machine.USBDevice `. On these + ports, output printed from ``boot.py`` may not be visible on the built-in USB + serial port until after ``boot.py`` finishes running. + + The purpose of this late initialisation is so that it's possible to + pre-configure particular hardware in ``boot.py``, and then have it start with + the correct configuration. + +.. note:: It is sometimes simpler to not have a ``boot.py`` file and place any + initialisation code at the top of ``main.py`` instead. + +.. _main.py: + +main.py +^^^^^^^ + +Similar to ``boot.py``, a file named ``main.py`` can be copied to the board's +internal :ref:`filesystem `. If found then it is executed next in the +startup process. + +``main.py`` is for any Python code that you want to run each time your device +starts. + +Some tips for ``main.py`` usage: + +- ``main.py`` doesn't have to exit, feel free to put an infinite ``while + True`` loop in there. +- For complex Python applications then you don't need to put all your + code in ``main.py``. ``main.py`` can be a simple entry point that + imports your application and starts execution:: + + import my_app + my_app.main() + + This can help keep the structure of your application clear. It also makes + it easy to install multiple applications on a board and switch among them. +- It's good practice when writing robust apps to wrap code in ``main.py`` with an + exception handler to take appropriate action if the code crashes. For example:: + + import machine, sys + import my_app + try: + my_app.main() + except Exception as e: + print("Fatal error in main:") + sys.print_exception(e) + + # Following a normal Exception or main() exiting, reset the board. + # Following a non-Exception error such as KeyboardInterrupt (Ctrl-C), + # this code will drop to a REPL. Place machine.reset() in a finally + # block to always reset, instead. + machine.reset() + + Otherwise MicroPython will drop to the REPL following any crash or if main + exits (see below). + +- Any global variables that were set in ``boot.py`` will still be set in the + global context of ``main.py``. + +- To fully optimise flash usage and memory consumption, you can copy + :doc:`pre-compiled ` ``main.mpy`` and/or ``boot.mpy`` files to the + filesystem, or even :doc:`freeze ` them into the firmware build + instead. +- ``main.py`` execution is skipped when a soft reset is initiated from :ref:`raw + REPL mode ` (for example, when :doc:`mpremote ` or another + program is interacting directly with MicroPython). + +Interactive Interpreter (REPL) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If ``main.py`` is not found, or if ``main.py`` exits, then :doc:`repl` +will start immediately. + +.. note:: Even if ``main.py`` contains an infinite loop, typing Ctrl-C on the + REPL serial port will inject a `KeyboardInterrupt`. If no exception + handler catches it then ``main.py`` will exit and the REPL will start. + +Any global variables that were set in ``boot.py`` and ``main.py`` will still be +set in the global context of the REPL. + +The REPL continues executing until Python code triggers a hard or soft reset. + +.. _soft_bricking: + +Soft Bricking (failure to boot) +--------------------------------- + +It is rare but possible for MicroPython to become unresponsive during startup, a +state sometimes called "soft bricked". For example: + +- If ``boot.py`` execution gets stuck and the native USB serial port + never initialises. +- If Python code reconfigures the REPL interface, making it inaccessible. + +Rest assured, recovery is possible! + +KeyboardInterrupt +^^^^^^^^^^^^^^^^^ + +In many cases, opening the REPL serial port and typing ``Ctrl-C`` will inject +`KeyboardInterrupt` and may cause the running script to exit and a REPL to +start. From the REPL, you can use :func:`os.remove()` to remove the misbehaving +Python file:: + + import os + os.remove('main.py') + +To confirm which files are still present in the internal filesystem:: + + import os + os.listdir() + +Safe Mode and Factory Reset +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you're unable to easily access the REPL then you may need to perform one of +two processes: + +1. "Safe mode" boot, which skips ``boot.py`` and ``main.py`` and immediately + starts a REPL, allowing you to clean up. This is only supported on some ports. +2. Factory Reset to erase the entire contents of the flash filesystem. This may + also be necessary if the internal flash filesystem has become corrupted + somehow. + +The specific process(es) are different on each port: + +- :doc:`pyboard and stm32 port instructions ` +- :doc:`esp32 port instructions ` +- :doc:`renesas-ra port instructions ` +- :doc:`rp2 port instructions ` +- :doc:`wipy port instructions ` + +For ports without specific instructions linked above, the factory reset process +involves erasing the board's entire flash and then flashing MicroPython again +from scratch. Usually this will involve the same tool(s) that were originally +used to install MicroPython. Consult the installation docs for your board, or +ask on the `GitHub Discussions`_ if you're not sure. + +.. warning:: Re-flashing the MicroPython firmware without erasing the entire + flash first will usually not recover from soft bricking, as a + firmware update usually preserves the contents of the filesystem. + +.. _GitHub Discussions: https://github.com/orgs/micropython/discussions diff --git a/docs/renesas-ra/tutorial/program_in_flash.rst b/docs/renesas-ra/tutorial/program_in_flash.rst index 99a7a3839dd4..985ce6c7fe66 100644 --- a/docs/renesas-ra/tutorial/program_in_flash.rst +++ b/docs/renesas-ra/tutorial/program_in_flash.rst @@ -28,6 +28,8 @@ As the factory setting, following 2 files are created in the file system: * boot.py : executed first when the system starts * main.py : executed after boot.py completes +See :doc:`/reference/reset_boot` for more information. + Write a program in the internal file system ------------------------------------------- diff --git a/docs/renesas-ra/tutorial/reset.rst b/docs/renesas-ra/tutorial/reset.rst index de5f4db91b3e..879979647956 100644 --- a/docs/renesas-ra/tutorial/reset.rst +++ b/docs/renesas-ra/tutorial/reset.rst @@ -20,6 +20,8 @@ If that isn't working you can perform a hard reset (turn-it-off-and-on-again) by pressing the RESET button. This will end your session, disconnecting whatever program (PuTTY, screen, etc) that you used to connect to the board. +For more details, see :doc:`/reference/reset_boot`. + boot mode --------- @@ -29,7 +31,9 @@ There are 3 boot modes: * safe boot mode * factory filesystem boot mode -boot.py and main.py are executed on "normal boot mode". +boot.py and main.py are executed on "normal boot mode". See :ref:`boot_sequence`. + +The other modes can be used to recover from :ref:`soft_bricking`: boot.py and main.py are *NOT* executed on "safe boot mode". @@ -46,16 +50,4 @@ on the board: You have created the main.py which executes LED1 blinking in the previous part. If you change the boot mode to safe boot mode, the MicroPython starts without -the execution of main.py. Then you can remove the main.py by following -command or change the boot mode to factory file system boot mode.:: - - import os - os.remove('main.py') - -or change the boot mode to factory file system boot mode. - -You can confirm that the initialized file system that there are only boot.py and main.py files.:: - - import os - os.listdir() - +the execution of main.py. diff --git a/docs/rp2/tutorial/intro.rst b/docs/rp2/tutorial/intro.rst index 69c3e6b0a54a..2d2990105dba 100644 --- a/docs/rp2/tutorial/intro.rst +++ b/docs/rp2/tutorial/intro.rst @@ -8,4 +8,5 @@ Let's get started! .. toctree:: :maxdepth: 1 + reset.rst pio.rst diff --git a/docs/rp2/tutorial/reset.rst b/docs/rp2/tutorial/reset.rst new file mode 100644 index 000000000000..3c296ec46ee1 --- /dev/null +++ b/docs/rp2/tutorial/reset.rst @@ -0,0 +1,18 @@ +Factory reset +============= + +If something unexpected happens and your RP2xxx-based board no longer boots +MicroPython, then you may have to factory reset it. For more details, see +:ref:`soft_bricking`. + +Factory resetting the MicroPython rp2 port involves fully erasing the flash and +resetting the flash memory, so you will need to re-flash the MicroPython +firmware afterwards and copy any Python files to the filesystem again. + +1. Follow the instructions on the Raspberry Pi website for `resetting flash + memory`_. +2. Copy the MicroPython .uf2 firmware file to your board. If needed, this file + can be found on the `MicroPython downloads page`_. + +.. _resetting flash memory: https://www.raspberrypi.com/documentation/microcontrollers/pico-series.html#resetting-flash-memory +.. _MicroPython downloads page: https://micropython.org/download/?port=rp2 diff --git a/docs/samd/quickref.rst b/docs/samd/quickref.rst index 25b5a8fc8ab2..d57dc6790839 100644 --- a/docs/samd/quickref.rst +++ b/docs/samd/quickref.rst @@ -215,7 +215,7 @@ 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``. + - *duty_u16* sets the duty cycle as a ratio ``duty_u16 / 65535``. - *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. @@ -246,7 +246,7 @@ Use the :ref:`machine.ADC ` class:: from machine import ADC 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 + adc0.read_u16() # read value, 0-65535 across voltage range 0.0v - 3.3v 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 diff --git a/docs/wipy/tutorial/reset.rst b/docs/wipy/tutorial/reset.rst index 1715d3e29788..fc56429b81a3 100644 --- a/docs/wipy/tutorial/reset.rst +++ b/docs/wipy/tutorial/reset.rst @@ -16,6 +16,8 @@ There are soft resets and hard resets. import machine machine.reset() +For more information, see :doc:`/reference/reset_boot`. + Safe boot --------- diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index cd0f50d10442..b718a66cc62f 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -536,6 +536,10 @@ static mp_obj_t framebuf_ellipse(size_t n_args, const mp_obj_t *args_in) { } else { mask |= ELLIPSE_MASK_ALL; } + if (args[2] == 0 && args[3] == 0) { + setpixel_checked(self, args[0], args[1], args[4], mask & ELLIPSE_MASK_ALL); + return mp_const_none; + } mp_int_t two_asquare = 2 * args[2] * args[2]; mp_int_t two_bsquare = 2 * args[3] * args[3]; mp_int_t x = args[2]; diff --git a/extmod/modlwip.c b/extmod/modlwip.c index 0d38bf41b205..ce70627c31c6 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -701,7 +701,12 @@ static mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte *buf, mp_ui MICROPY_PY_LWIP_ENTER - u16_t available = tcp_sndbuf(socket->pcb.tcp); + // If the socket is still connecting then don't let data be written to it. + // Otherwise, get the number of available bytes in the output buffer. + u16_t available = 0; + if (socket->state != STATE_CONNECTING) { + available = tcp_sndbuf(socket->pcb.tcp); + } if (available == 0) { // Non-blocking socket @@ -718,7 +723,8 @@ static mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte *buf, mp_ui // If peer fully closed socket, we would have socket->state set to ERR_RST (connection // reset) by error callback. // Avoid sending too small packets, so wait until at least 16 bytes available - while (socket->state >= STATE_CONNECTED && (available = tcp_sndbuf(socket->pcb.tcp)) < 16) { + while (socket->state == STATE_CONNECTING + || (socket->state >= STATE_CONNECTED && (available = tcp_sndbuf(socket->pcb.tcp)) < 16)) { MICROPY_PY_LWIP_EXIT if (socket->timeout != -1 && mp_hal_ticks_ms() - start > socket->timeout) { *_errno = MP_ETIMEDOUT; @@ -1548,7 +1554,7 @@ static mp_uint_t lwip_socket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_ // raw socket is writable ret |= MP_STREAM_POLL_WR; #endif - } else if (socket->pcb.tcp != NULL && tcp_sndbuf(socket->pcb.tcp) > 0) { + } else if (socket->state != STATE_CONNECTING && socket->pcb.tcp != NULL && tcp_sndbuf(socket->pcb.tcp) > 0) { // TCP socket is writable // Note: pcb.tcp==NULL if state<0, and in this case we can't call tcp_sndbuf ret |= MP_STREAM_POLL_WR; diff --git a/extmod/network_cyw43.c b/extmod/network_cyw43.c index 3066cac75d3b..02b022cb8132 100644 --- a/extmod/network_cyw43.c +++ b/extmod/network_cyw43.c @@ -60,6 +60,10 @@ typedef struct _network_cyw43_obj_t { static const network_cyw43_obj_t network_cyw43_wl_sta = { { &mp_network_cyw43_type }, &cyw43_state, CYW43_ITF_STA }; static const network_cyw43_obj_t network_cyw43_wl_ap = { { &mp_network_cyw43_type }, &cyw43_state, CYW43_ITF_AP }; +// Avoid race conditions with callbacks by tracking the last up or down request +// we have made for each interface. +static bool if_active[2]; + static void network_cyw43_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { network_cyw43_obj_t *self = MP_OBJ_TO_PTR(self_in); struct netif *netif = &self->cyw->netif[self->itf]; @@ -122,6 +126,10 @@ static MP_DEFINE_CONST_FUN_OBJ_3(network_cyw43_ioctl_obj, network_cyw43_ioctl); /*******************************************************************************/ // network API +static uint32_t get_country_code(void) { + return CYW43_COUNTRY(mod_network_country_code[0], mod_network_country_code[1], 0); +} + static mp_obj_t network_cyw43_deinit(mp_obj_t self_in) { network_cyw43_obj_t *self = MP_OBJ_TO_PTR(self_in); cyw43_deinit(self->cyw); @@ -132,10 +140,11 @@ static MP_DEFINE_CONST_FUN_OBJ_1(network_cyw43_deinit_obj, network_cyw43_deinit) static mp_obj_t network_cyw43_active(size_t n_args, const mp_obj_t *args) { network_cyw43_obj_t *self = MP_OBJ_TO_PTR(args[0]); if (n_args == 1) { - return mp_obj_new_bool(cyw43_tcpip_link_status(self->cyw, self->itf)); + return mp_obj_new_bool(if_active[self->itf]); } else { - uint32_t country = CYW43_COUNTRY(mod_network_country_code[0], mod_network_country_code[1], 0); - cyw43_wifi_set_up(self->cyw, self->itf, mp_obj_is_true(args[1]), country); + bool value = mp_obj_is_true(args[1]); + cyw43_wifi_set_up(self->cyw, self->itf, value, get_country_code()); + if_active[self->itf] = value; return mp_const_none; } } @@ -311,7 +320,17 @@ static MP_DEFINE_CONST_FUN_OBJ_1(network_cyw43_disconnect_obj, network_cyw43_dis static mp_obj_t network_cyw43_isconnected(mp_obj_t self_in) { network_cyw43_obj_t *self = MP_OBJ_TO_PTR(self_in); - return mp_obj_new_bool(cyw43_tcpip_link_status(self->cyw, self->itf) == 3); + bool result = (cyw43_tcpip_link_status(self->cyw, self->itf) == CYW43_LINK_UP); + + if (result && self->itf == CYW43_ITF_AP) { + // For AP we need to not only know if the link is up, but also if any stations + // have associated. + uint8_t mac_buf[6]; + int num_stas = 1; + cyw43_wifi_ap_get_stas(self->cyw, &num_stas, mac_buf); + result = num_stas > 0; + } + return mp_obj_new_bool(result); } static MP_DEFINE_CONST_FUN_OBJ_1(network_cyw43_isconnected_obj, network_cyw43_isconnected); @@ -351,13 +370,15 @@ static mp_obj_t network_cyw43_status(size_t n_args, const mp_obj_t *args) { if (self->itf != CYW43_ITF_AP) { mp_raise_ValueError(MP_ERROR_TEXT("AP required")); } - int num_stas; - uint8_t macs[32 * 6]; + static const unsigned mac_len = 6; + static const unsigned max_stas = 32; + int num_stas = max_stas; + uint8_t macs[max_stas * mac_len]; cyw43_wifi_ap_get_stas(self->cyw, &num_stas, macs); mp_obj_t list = mp_obj_new_list(num_stas, NULL); for (int i = 0; i < num_stas; ++i) { mp_obj_t tuple[1] = { - mp_obj_new_bytes(&macs[i * 6], 6), + mp_obj_new_bytes(&macs[i * mac_len], mac_len), }; ((mp_obj_list_t *)MP_OBJ_TO_PTR(list))->items[i] = mp_obj_new_tuple(1, tuple); } @@ -445,6 +466,10 @@ static mp_obj_t network_cyw43_config(size_t n_args, const mp_obj_t *args, mp_map mp_raise_TypeError(MP_ERROR_TEXT("can't specify pos and kw args")); } + // A number of these options only update buffers in memory, and + // won't do anything until the interface is cycled down and back up + bool cycle_active = false; + for (size_t i = 0; i < kwargs->alloc; ++i) { if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) { mp_map_elem_t *e = &kwargs->table[i]; @@ -457,6 +482,7 @@ static mp_obj_t network_cyw43_config(size_t n_args, const mp_obj_t *args, mp_map } case MP_QSTR_channel: { cyw43_wifi_ap_set_channel(self->cyw, mp_obj_get_int(e->value)); + cycle_active = true; break; } case MP_QSTR_ssid: @@ -464,6 +490,7 @@ static mp_obj_t network_cyw43_config(size_t n_args, const mp_obj_t *args, mp_map size_t len; const char *str = mp_obj_str_get_data(e->value, &len); cyw43_wifi_ap_set_ssid(self->cyw, len, (const uint8_t *)str); + cycle_active = true; break; } case MP_QSTR_monitor: { @@ -483,6 +510,7 @@ static mp_obj_t network_cyw43_config(size_t n_args, const mp_obj_t *args, mp_map } case MP_QSTR_security: { cyw43_wifi_ap_set_auth(self->cyw, mp_obj_get_int(e->value)); + cycle_active = true; break; } case MP_QSTR_key: @@ -490,6 +518,7 @@ static mp_obj_t network_cyw43_config(size_t n_args, const mp_obj_t *args, mp_map size_t len; const char *str = mp_obj_str_get_data(e->value, &len); cyw43_wifi_ap_set_password(self->cyw, len, (const uint8_t *)str); + cycle_active = true; break; } case MP_QSTR_pm: { @@ -519,6 +548,13 @@ static mp_obj_t network_cyw43_config(size_t n_args, const mp_obj_t *args, mp_map } } + // If the interface is already active, cycle it down and up + if (cycle_active && if_active[self->itf]) { + uint32_t country = get_country_code(); + cyw43_wifi_set_up(self->cyw, self->itf, false, country); + cyw43_wifi_set_up(self->cyw, self->itf, true, country); + } + return mp_const_none; } } diff --git a/extmod/network_ppp_lwip.c b/extmod/network_ppp_lwip.c index 2b77662a24f2..8eb90ea4a652 100644 --- a/extmod/network_ppp_lwip.c +++ b/extmod/network_ppp_lwip.c @@ -60,6 +60,18 @@ const mp_obj_type_t mp_network_ppp_lwip_type; static mp_obj_t network_ppp___del__(mp_obj_t self_in); +static void network_ppp_stream_uart_irq_disable(network_ppp_obj_t *self) { + if (self->stream == mp_const_none) { + return; + } + + // Disable UART IRQ. + mp_obj_t dest[3]; + mp_load_method(self->stream, MP_QSTR_irq, dest); + dest[2] = mp_const_none; + mp_call_method_n_kw(1, 0, dest); +} + static void network_ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) { network_ppp_obj_t *self = ctx; switch (err_code) { @@ -68,12 +80,9 @@ static void network_ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) { break; case PPPERR_USER: if (self->state >= STATE_ERROR) { - // Disable UART IRQ. - mp_obj_t dest[3]; - mp_load_method(self->stream, MP_QSTR_irq, dest); - dest[2] = mp_const_none; - mp_call_method_n_kw(1, 0, dest); - // Indicate that the IRQ is disabled. + network_ppp_stream_uart_irq_disable(self); + // Indicate that we are no longer connected and thus + // only need to free the PPP PCB, not close it. self->state = STATE_ACTIVE; } // Clean up the PPP PCB. @@ -91,7 +100,9 @@ static mp_obj_t network_ppp_make_new(const mp_obj_type_t *type, size_t n_args, s mp_obj_t stream = all_args[0]; - mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + if (stream != mp_const_none) { + mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + } network_ppp_obj_t *self = mp_obj_malloc_with_finaliser(network_ppp_obj_t, type); self->state = STATE_INACTIVE; @@ -105,7 +116,7 @@ static mp_obj_t network_ppp___del__(mp_obj_t self_in) { network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in); if (self->state >= STATE_ACTIVE) { if (self->state >= STATE_ERROR) { - // Still connected over the UART stream. + // Still connected over the stream. // Force the connection to close, with nocarrier=1. self->state = STATE_INACTIVE; ppp_close(self->pcb, 1); @@ -127,10 +138,11 @@ static mp_obj_t network_ppp_poll(size_t n_args, const mp_obj_t *args) { } mp_int_t total_len = 0; - for (;;) { + mp_obj_t stream = self->stream; + while (stream != mp_const_none) { uint8_t buf[256]; int err; - mp_uint_t len = mp_stream_rw(self->stream, buf, sizeof(buf), &err, 0); + mp_uint_t len = mp_stream_rw(stream, buf, sizeof(buf), &err, 0); if (len == 0) { break; } @@ -149,16 +161,42 @@ static mp_obj_t network_ppp_poll(size_t n_args, const mp_obj_t *args) { } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ppp_poll_obj, 1, 2, network_ppp_poll); +static void network_ppp_stream_uart_irq_enable(network_ppp_obj_t *self) { + if (self->stream == mp_const_none) { + return; + } + + // Enable UART IRQ to call PPP.poll() when incoming data is ready. + mp_obj_t dest[4]; + mp_load_method(self->stream, MP_QSTR_irq, dest); + dest[2] = mp_obj_new_bound_meth(MP_OBJ_FROM_PTR(&network_ppp_poll_obj), MP_OBJ_FROM_PTR(self)); + dest[3] = mp_load_attr(self->stream, MP_QSTR_IRQ_RXIDLE); + mp_call_method_n_kw(2, 0, dest); +} + static mp_obj_t network_ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { if (n_args != 1 && kwargs->used != 0) { mp_raise_TypeError(MP_ERROR_TEXT("either pos or kw args are allowed")); } - // network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); if (kwargs->used != 0) { for (size_t i = 0; i < kwargs->alloc; i++) { if (mp_map_slot_is_filled(kwargs, i)) { switch (mp_obj_str_get_qstr(kwargs->table[i].key)) { + case MP_QSTR_stream: { + if (kwargs->table[i].value != mp_const_none) { + mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + } + if (self->state >= STATE_ACTIVE) { + network_ppp_stream_uart_irq_disable(self); + } + self->stream = kwargs->table[i].value; + if (self->state >= STATE_ACTIVE) { + network_ppp_stream_uart_irq_enable(self); + } + break; + } default: break; } @@ -174,6 +212,10 @@ static mp_obj_t network_ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t mp_obj_t val = mp_const_none; switch (mp_obj_str_get_qstr(args[1])) { + case MP_QSTR_stream: { + val = self->stream; + break; + } default: mp_raise_ValueError(MP_ERROR_TEXT("unknown config param")); } @@ -201,10 +243,14 @@ static u32_t network_ppp_output_callback(ppp_pcb *pcb, const void *data, u32_t l } mp_printf(&mp_plat_print, ")\n"); #endif + mp_obj_t stream = self->stream; + if (stream == mp_const_none) { + return 0; + } int err; // The return value from this output callback is the number of bytes written out. // If it's less than the requested number of bytes then lwIP will propagate out an error. - return mp_stream_rw(self->stream, (void *)data, len, &err, MP_STREAM_RW_WRITE); + return mp_stream_rw(stream, (void *)data, len, &err, MP_STREAM_RW_WRITE); } static mp_obj_t network_ppp_connect(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { @@ -227,12 +273,7 @@ static mp_obj_t network_ppp_connect(size_t n_args, const mp_obj_t *args, mp_map_ } self->state = STATE_ACTIVE; - // Enable UART IRQ to call PPP.poll() when incoming data is ready. - mp_obj_t dest[4]; - mp_load_method(self->stream, MP_QSTR_irq, dest); - dest[2] = mp_obj_new_bound_meth(MP_OBJ_FROM_PTR(&network_ppp_poll_obj), MP_OBJ_FROM_PTR(self)); - dest[3] = mp_load_attr(self->stream, MP_QSTR_IRQ_RXIDLE); - mp_call_method_n_kw(2, 0, dest); + network_ppp_stream_uart_irq_enable(self); } if (self->state == STATE_CONNECTING || self->state == STATE_CONNECTED) { diff --git a/extmod/vfs_blockdev.c b/extmod/vfs_blockdev.c index a7c14b76ee36..d43c96b08f32 100644 --- a/extmod/vfs_blockdev.c +++ b/extmod/vfs_blockdev.c @@ -58,6 +58,13 @@ static int mp_vfs_blockdev_call_rw(mp_obj_t *args, size_t block_num, size_t bloc if (ret == mp_const_none) { return 0; } else { + // Some block devices return a bool indicating success, so + // convert those to an errno integer code. + if (ret == mp_const_true) { + return 0; + } else if (ret == mp_const_false) { + return -MP_EIO; + } // Block device functions are expected to return 0 on success // and negative integer on errors. Check for positive integer // results as some callers (i.e. littlefs) will produce corrupt diff --git a/ports/esp32/README.md b/ports/esp32/README.md index a04ad0c6eea1..4eb791389380 100644 --- a/ports/esp32/README.md +++ b/ports/esp32/README.md @@ -195,7 +195,7 @@ quickly call `wlan_connect()` and it just works): ```python def wlan_connect(ssid='MYSSID', password='MYPASS'): import network - wlan = network.WLAN(network.STA_IF) + wlan = network.WLAN(network.WLAN.IF_STA) if not wlan.active() or not wlan.isconnected(): wlan.active(True) print('connecting to:', ssid) diff --git a/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lilygo_oled.py b/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lilygo_oled.py index bfe02c35765b..98ea0adcbf86 100644 --- a/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lilygo_oled.py +++ b/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lilygo_oled.py @@ -30,7 +30,7 @@ def display_wifi(self): self.text("Scan...", 0, 0, 1) self.show() - sta_if = network.WLAN(network.STA_IF) + sta_if = network.WLAN(network.WLAN.IF_STA) sta_if.active(True) _wifi = sta_if.scan() diff --git a/ports/esp32/boards/LOLIN_S2_PICO/modules/s2pico_oled.py b/ports/esp32/boards/LOLIN_S2_PICO/modules/s2pico_oled.py index 37dc5a340fe4..58120d44adf7 100644 --- a/ports/esp32/boards/LOLIN_S2_PICO/modules/s2pico_oled.py +++ b/ports/esp32/boards/LOLIN_S2_PICO/modules/s2pico_oled.py @@ -37,7 +37,7 @@ def display_wifi(self): self.text("Scan...", 0, 0, 1) self.show() - sta_if = network.WLAN(network.STA_IF) + sta_if = network.WLAN(network.WLAN.IF_STA) sta_if.active(True) _wifi = sta_if.scan() diff --git a/ports/esp32/help.c b/ports/esp32/help.c index 2351d7dc7394..62639a0b408c 100644 --- a/ports/esp32/help.c +++ b/ports/esp32/help.c @@ -48,7 +48,7 @@ const char esp32_help_text[] = "Basic WiFi configuration:\n" "\n" "import network\n" - "sta_if = network.WLAN(network.STA_IF); sta_if.active(True)\n" + "sta_if = network.WLAN(network.WLAN.IF_STA); sta_if.active(True)\n" "sta_if.scan() # Scan for available access points\n" "sta_if.connect(\"\", \"\") # Connect to an AP\n" "sta_if.isconnected() # Check for successful connection\n" diff --git a/ports/esp32/machine_touchpad.c b/ports/esp32/machine_touchpad.c index 7612063e51d4..48250280bada 100644 --- a/ports/esp32/machine_touchpad.c +++ b/ports/esp32/machine_touchpad.c @@ -29,12 +29,14 @@ #include "modmachine.h" #include "driver/gpio.h" -#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 +#if SOC_TOUCH_SENSOR_SUPPORTED -#if CONFIG_IDF_TARGET_ESP32 +#if SOC_TOUCH_VERSION_1 // ESP32 only #include "driver/touch_pad.h" -#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 +#elif SOC_TOUCH_VERSION_2 // All other SoCs with touch, to date #include "driver/touch_sensor.h" +#else +#error "Unknown touch hardware version" #endif typedef struct _mtp_obj_t { @@ -70,6 +72,8 @@ static const mtp_obj_t touchpad_obj[] = { {{&machine_touchpad_type}, GPIO_NUM_12, TOUCH_PAD_NUM12}, {{&machine_touchpad_type}, GPIO_NUM_13, TOUCH_PAD_NUM13}, {{&machine_touchpad_type}, GPIO_NUM_14, TOUCH_PAD_NUM14}, + #else + #error "Please add GPIO mapping for this SoC" #endif }; @@ -92,17 +96,18 @@ static mp_obj_t mtp_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ if (!initialized) { touch_pad_init(); touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER); - #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 - touch_pad_fsm_start(); - #endif initialized = 1; } - #if CONFIG_IDF_TARGET_ESP32 + #if SOC_TOUCH_VERSION_1 esp_err_t err = touch_pad_config(self->touchpad_id, 0); - #elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + #elif SOC_TOUCH_VERSION_2 esp_err_t err = touch_pad_config(self->touchpad_id); #endif if (err == ESP_OK) { + #if SOC_TOUCH_VERSION_2 + touch_pad_fsm_start(); + #endif + return MP_OBJ_FROM_PTR(self); } mp_raise_ValueError(MP_ERROR_TEXT("Touch pad error")); @@ -110,10 +115,10 @@ static mp_obj_t mtp_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ static mp_obj_t mtp_config(mp_obj_t self_in, mp_obj_t value_in) { mtp_obj_t *self = self_in; - #if CONFIG_IDF_TARGET_ESP32 + #if SOC_TOUCH_VERSION_1 uint16_t value = mp_obj_get_int(value_in); esp_err_t err = touch_pad_config(self->touchpad_id, value); - #elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + #elif SOC_TOUCH_VERSION_2 esp_err_t err = touch_pad_config(self->touchpad_id); #endif if (err == ESP_OK) { @@ -125,10 +130,10 @@ MP_DEFINE_CONST_FUN_OBJ_2(mtp_config_obj, mtp_config); static mp_obj_t mtp_read(mp_obj_t self_in) { mtp_obj_t *self = self_in; - #if CONFIG_IDF_TARGET_ESP32 + #if SOC_TOUCH_VERSION_1 uint16_t value; esp_err_t err = touch_pad_read(self->touchpad_id, &value); - #elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + #elif SOC_TOUCH_VERSION_2 uint32_t value; esp_err_t err = touch_pad_read_raw_data(self->touchpad_id, &value); #endif @@ -155,4 +160,4 @@ MP_DEFINE_CONST_OBJ_TYPE( locals_dict, &mtp_locals_dict ); -#endif +#endif // SOC_TOUCH_SENSOR_SUPPORTED diff --git a/ports/esp32/main.c b/ports/esp32/main.c index 03dc0807a025..18ef9d735479 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -39,6 +39,7 @@ #include "esp_task.h" #include "esp_event.h" #include "esp_log.h" +#include "esp_memory_utils.h" #include "esp_psram.h" #include "py/cstack.h" @@ -237,6 +238,13 @@ void *esp_native_code_commit(void *buf, size_t len, void *reloc) { len = (len + 3) & ~3; size_t len_node = sizeof(native_code_node_t) + len; native_code_node_t *node = heap_caps_malloc(len_node, MALLOC_CAP_EXEC); + #if CONFIG_IDF_TARGET_ESP32S2 + // Workaround for ESP-IDF bug https://github.com/espressif/esp-idf/issues/14835 + if (node != NULL && !esp_ptr_executable(node)) { + free(node); + node = NULL; + } + #endif // CONFIG_IDF_TARGET_ESP32S2 if (node == NULL) { m_malloc_fail(len_node); } diff --git a/ports/esp32/modsocket.c b/ports/esp32/modsocket.c index 916eb79bd926..7ea5e855d325 100644 --- a/ports/esp32/modsocket.c +++ b/ports/esp32/modsocket.c @@ -213,7 +213,7 @@ static int mdns_getaddrinfo(const char *host_str, const char *port_str, #endif // MICROPY_HW_ENABLE_MDNS_QUERIES static void _getaddrinfo_inner(const mp_obj_t host, const mp_obj_t portx, - const struct addrinfo *hints, struct addrinfo **res) { + struct addrinfo *hints, struct addrinfo **res) { int retval = 0; *res = NULL; @@ -235,6 +235,9 @@ static void _getaddrinfo_inner(const mp_obj_t host, const mp_obj_t portx, MP_THREAD_GIL_EXIT(); + // The ai_canonname field is used below, so set the hint. + hints->ai_flags |= AI_CANONNAME; + #if MICROPY_HW_ENABLE_MDNS_QUERIES retval = mdns_getaddrinfo(host_str, port_str, hints, res); #endif @@ -264,7 +267,8 @@ static void _getaddrinfo_inner(const mp_obj_t host, const mp_obj_t portx, static void _socket_getaddrinfo(const mp_obj_t addrtuple, struct addrinfo **resp) { mp_obj_t *elem; mp_obj_get_array_fixed_n(addrtuple, 2, &elem); - _getaddrinfo_inner(elem[0], elem[1], NULL, resp); + struct addrinfo hints = { 0 }; + _getaddrinfo_inner(elem[0], elem[1], &hints, resp); } static mp_obj_t socket_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { diff --git a/ports/esp32/network_ppp.c b/ports/esp32/network_ppp.c index f2813f430d48..5c41cf948c1e 100644 --- a/ports/esp32/network_ppp.c +++ b/ports/esp32/network_ppp.c @@ -85,7 +85,9 @@ static void ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) { } static mp_obj_t ppp_make_new(mp_obj_t stream) { - mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + if (stream != mp_const_none) { + mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + } ppp_if_obj_t *self = mp_obj_malloc_with_finaliser(ppp_if_obj_t, &ppp_if_type); self->stream = stream; @@ -100,8 +102,14 @@ MP_DEFINE_CONST_FUN_OBJ_1(esp_network_ppp_make_new_obj, ppp_make_new); static u32_t ppp_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) { ppp_if_obj_t *self = ctx; + + mp_obj_t stream = self->stream; + if (stream == mp_const_none) { + return 0; + } + int err; - return mp_stream_rw(self->stream, data, len, &err, MP_STREAM_RW_WRITE); + return mp_stream_rw(stream, data, len, &err, MP_STREAM_RW_WRITE); } static void pppos_client_task(void *self_in) { @@ -110,10 +118,15 @@ static void pppos_client_task(void *self_in) { int len = 0; while (ulTaskNotifyTake(pdTRUE, len <= 0) == 0) { - int err; - len = mp_stream_rw(self->stream, buf, sizeof(buf), &err, 0); - if (len > 0) { - pppos_input_tcpip(self->pcb, (u8_t *)buf, len); + mp_obj_t stream = self->stream; + if (stream == mp_const_none) { + len = 0; + } else { + int err; + len = mp_stream_rw(stream, buf, sizeof(buf), &err, 0); + if (len > 0) { + pppos_input_tcpip(self->pcb, (u8_t *)buf, len); + } } } @@ -323,6 +336,13 @@ static mp_obj_t ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs for (size_t i = 0; i < kwargs->alloc; i++) { if (mp_map_slot_is_filled(kwargs, i)) { switch (mp_obj_str_get_qstr(kwargs->table[i].key)) { + case MP_QSTR_stream: { + if (kwargs->table[i].value != mp_const_none) { + mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + } + self->stream = kwargs->table[i].value; + break; + } default: break; } @@ -338,6 +358,10 @@ static mp_obj_t ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs mp_obj_t val = mp_const_none; switch (mp_obj_str_get_qstr(args[1])) { + case MP_QSTR_stream: { + val = self->stream; + break; + } case MP_QSTR_ifname: { if (self->pcb != NULL) { struct netif *pppif = ppp_netif(self->pcb); diff --git a/ports/esp32/network_wlan.c b/ports/esp32/network_wlan.c index d415c56d3217..6509b4fc6aae 100644 --- a/ports/esp32/network_wlan.c +++ b/ports/esp32/network_wlan.c @@ -549,21 +549,38 @@ static mp_obj_t network_wlan_config(size_t n_args, const mp_obj_t *args, mp_map_ break; } case MP_QSTR_channel: { - uint8_t primary; - wifi_second_chan_t secondary; - // Get the current value of secondary - esp_exceptions(esp_wifi_get_channel(&primary, &secondary)); - primary = mp_obj_get_int(kwargs->table[i].value); - esp_err_t err = esp_wifi_set_channel(primary, secondary); - if (err == ESP_ERR_INVALID_ARG) { - // May need to swap secondary channel above to below or below to above - secondary = ( - (secondary == WIFI_SECOND_CHAN_ABOVE) - ? WIFI_SECOND_CHAN_BELOW - : (secondary == WIFI_SECOND_CHAN_BELOW) + uint8_t channel = mp_obj_get_int(kwargs->table[i].value); + if (self->if_id == ESP_IF_WIFI_AP) { + cfg.ap.channel = channel; + } else { + // This setting is only used to determine the + // starting channel for a scan, so it can result in + // slightly faster connection times. + cfg.sta.channel = channel; + + // This additional code to directly set the channel + // on the STA interface is only relevant for ESP-NOW + // (when there is no STA connection attempt.) + uint8_t old_primary; + wifi_second_chan_t secondary; + // Get the current value of secondary + esp_exceptions(esp_wifi_get_channel(&old_primary, &secondary)); + esp_err_t err = esp_wifi_set_channel(channel, secondary); + if (err == ESP_ERR_INVALID_ARG) { + // May need to swap secondary channel above to below or below to above + secondary = ( + (secondary == WIFI_SECOND_CHAN_ABOVE) + ? WIFI_SECOND_CHAN_BELOW + : (secondary == WIFI_SECOND_CHAN_BELOW) ? WIFI_SECOND_CHAN_ABOVE : WIFI_SECOND_CHAN_NONE); - esp_exceptions(esp_wifi_set_channel(primary, secondary)); + err = esp_wifi_set_channel(channel, secondary); + } + esp_exceptions(err); + if (channel != old_primary) { + // Workaround the ESP-IDF Wi-Fi stack sometimes taking a moment to change channels + mp_hal_delay_ms(1); + } } break; } diff --git a/ports/esp8266/help.c b/ports/esp8266/help.c index a9cb27bad51c..694cd90b951d 100644 --- a/ports/esp8266/help.c +++ b/ports/esp8266/help.c @@ -36,12 +36,12 @@ const char esp_help_text[] = "Basic WiFi configuration:\n" "\n" "import network\n" - "sta_if = network.WLAN(network.STA_IF); sta_if.active(True)\n" + "sta_if = network.WLAN(network.WLAN.IF_STA); sta_if.active(True)\n" "sta_if.scan() # Scan for available access points\n" "sta_if.connect(\"\", \"\") # Connect to an AP\n" "sta_if.isconnected() # Check for successful connection\n" "# Change name/password of ESP8266's AP:\n" - "ap_if = network.WLAN(network.AP_IF)\n" + "ap_if = network.WLAN(network.WLAN.IF_AP)\n" "ap_if.config(ssid=\"\", security=network.AUTH_WPA_WPA2_PSK, key=\"\")\n" "\n" "Control commands:\n" diff --git a/ports/esp8266/machine_pwm.c b/ports/esp8266/machine_pwm.c index 25a2d6898f7c..0d5ed595428e 100644 --- a/ports/esp8266/machine_pwm.c +++ b/ports/esp8266/machine_pwm.c @@ -80,7 +80,7 @@ static void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, size_t n_args, c 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); + pwm_set_duty(args[ARG_duty_u16].u_int * 1000 / 65535, self->channel); } if (args[ARG_duty_ns].u_int != -1) { uint32_t freq = pwm_get_freq(0); @@ -164,13 +164,13 @@ static void mp_machine_pwm_duty_set(machine_pwm_obj_t *self, mp_int_t duty) { 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); + return MP_OBJ_NEW_SMALL_INT(pwm_get_duty(self->channel) * 65535 / 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_set_duty(duty * 1024 / 65535, self->channel); pwm_start(); } diff --git a/ports/esp8266/modules/inisetup.py b/ports/esp8266/modules/inisetup.py index 20bb28e80b06..f97c23852002 100644 --- a/ports/esp8266/modules/inisetup.py +++ b/ports/esp8266/modules/inisetup.py @@ -6,7 +6,7 @@ def wifi(): import binascii - ap_if = network.WLAN(network.AP_IF) + ap_if = network.WLAN(network.WLAN.IF_AP) ssid = b"MicroPython-%s" % binascii.hexlify(ap_if.config("mac")[-3:]) ap_if.config(ssid=ssid, security=network.AUTH_WPA_WPA2_PSK, key=b"micropythoN") diff --git a/ports/esp8266/modules/port_diag.py b/ports/esp8266/modules/port_diag.py index 4eea6a6d90d3..bbd6225a616f 100644 --- a/ports/esp8266/modules/port_diag.py +++ b/ports/esp8266/modules/port_diag.py @@ -23,8 +23,8 @@ def main(): print(esp.check_fw()) print("\nNetworking:") - print("STA ifconfig:", network.WLAN(network.STA_IF).ifconfig()) - print("AP ifconfig:", network.WLAN(network.AP_IF).ifconfig()) + print("STA ifconfig:", network.WLAN(network.WLAN.IF_STA).ifconfig()) + print("AP ifconfig:", network.WLAN(network.WLAN.IF_AP).ifconfig()) print("Free WiFi driver buffers of type:") for i, comm in enumerate( ("1,2 TX", "4 Mngmt TX(len: 0x41-0x100)", "5 Mngmt TX (len: 0-0x40)", "7", "8 RX") diff --git a/ports/mimxrt/hal/pwm_backport.c b/ports/mimxrt/hal/pwm_backport.c index 7732e0e8167c..7815fd1dff57 100644 --- a/ports/mimxrt/hal/pwm_backport.c +++ b/ports/mimxrt/hal/pwm_backport.c @@ -36,7 +36,7 @@ void PWM_UpdatePwmDutycycle_u16( // Setup the PWM dutycycle of channel A or B if (pwmSignal == kPWM_PwmA) { - if (dutyCycle >= 65536) { + if (dutyCycle >= PWM_FULL_SCALE) { base->SM[subModule].VAL2 = 0; base->SM[subModule].VAL3 = pulseCnt; } else { @@ -44,7 +44,7 @@ void PWM_UpdatePwmDutycycle_u16( base->SM[subModule].VAL3 = base->SM[subModule].VAL2 + pwmHighPulse; } } else { - if (dutyCycle >= 65536) { + if (dutyCycle >= PWM_FULL_SCALE) { base->SM[subModule].VAL4 = 0; base->SM[subModule].VAL5 = pulseCnt; } else { @@ -110,12 +110,8 @@ void PWM_SetupPwmx_u16(PWM_Type *base, pwm_submodule_t subModule, base->SM[subModule].OCTRL = (base->SM[subModule].OCTRL & ~PWM_OCTRL_POLX_MASK) | PWM_OCTRL_POLX(!invert); - // Switch the output on or off. - if (duty_cycle == 0) { - base->OUTEN &= ~(1U << subModule); - } else { - base->OUTEN |= (1U << subModule); - } + // Enable PWM output + base->OUTEN |= (1U << subModule); } #ifdef FSL_FEATURE_SOC_TMR_COUNT @@ -160,7 +156,7 @@ status_t QTMR_SetupPwm_u16(TMR_Type *base, qtmr_channel_selection_t channel, uin if (dutyCycleU16 == 0) { // Clear the output at the next compare reg |= (TMR_CTRL_LENGTH_MASK | TMR_CTRL_OUTMODE(kQTMR_ClearOnCompare)); - } else if (dutyCycleU16 >= 65536) { + } else if (dutyCycleU16 >= PWM_FULL_SCALE) { // Set the output at the next compare reg |= (TMR_CTRL_LENGTH_MASK | TMR_CTRL_OUTMODE(kQTMR_SetOnCompare)); } else { diff --git a/ports/mimxrt/hal/pwm_backport.h b/ports/mimxrt/hal/pwm_backport.h index 04899173e20b..9c9f6811add2 100644 --- a/ports/mimxrt/hal/pwm_backport.h +++ b/ports/mimxrt/hal/pwm_backport.h @@ -24,7 +24,7 @@ typedef struct _pwm_signal_param_u16 uint16_t deadtimeValue; // The deadtime value; only used if channel pair is operating in complementary mode } pwm_signal_param_u16_t; -#define PWM_FULL_SCALE (65536UL) +#define PWM_FULL_SCALE (65535UL) void PWM_UpdatePwmDutycycle_u16(PWM_Type *base, pwm_submodule_t subModule, pwm_channels_t pwmSignal, uint32_t dutyCycle, uint16_t center); diff --git a/ports/mimxrt/machine_pwm.c b/ports/mimxrt/machine_pwm.c index f9ae5e22edcc..b68521281ae6 100644 --- a/ports/mimxrt/machine_pwm.c +++ b/ports/mimxrt/machine_pwm.c @@ -45,6 +45,8 @@ typedef struct _machine_pwm_obj_t { mp_obj_base_t base; PWM_Type *instance; + const machine_pin_obj_t *pwm_pin; + const machine_pin_af_obj_t *pwm_pin_af_obj; bool is_flexpwm; uint8_t complementary; uint8_t module; @@ -255,6 +257,8 @@ static void configure_flexpwm(machine_pwm_obj_t *self) { PWM_SetupFaultDisableMap(self->instance, self->submodule, self->channel2, kPWM_faultchannel_1, 0); } + // clear the load okay bit for the submodules in case there is a pending load + PWM_SetPwmLdok(self->instance, 1 << self->submodule, false); if (self->channel1 != kPWM_PwmX) { // Only for A/B channels // Initialize the channel parameters pwmSignal.pwmChannel = self->channel1; @@ -283,6 +287,17 @@ static void configure_flexpwm(machine_pwm_obj_t *self) { self->instance->SM[self->submodule].CTRL &= ~(PWM_CTRL_DBLEN_MASK | PWM_CTRL_SPLIT_MASK); } } else { + if (self->duty_u16 == 0) { + // For duty_u16 == 0 just set the output to GPIO mode + if (self->invert) { + mp_hal_pin_high(self->pwm_pin); + } else { + mp_hal_pin_low(self->pwm_pin); + } + IOMUXC_SetPinMux(self->pwm_pin->muxRegister, PIN_AF_MODE_ALT5, 0, 0, 0, 0U); + } else { + IOMUXC_SetPinMux(self->pwm_pin->muxRegister, self->pwm_pin_af_obj->af_mode, 0, 0, 0, 0U); + } PWM_SetupPwmx_u16(self->instance, self->submodule, self->freq, self->duty_u16, self->invert, pwmSourceClockInHz); if (self->xor) { @@ -408,12 +423,16 @@ static void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, } self->center = center; } else { // Use alignment setting shortcut - if (args[ARG_align].u_int >= 0) { + uint32_t duty = self->duty_u16; + if (duty == VALUE_NOT_SET && self->duty_ns != VALUE_NOT_SET) { + duty = duty_ns_to_duty_u16(self->freq, self->duty_ns); + } + if (args[ARG_align].u_int >= 0 && duty != VALUE_NOT_SET && self->freq != VALUE_NOT_SET) { uint8_t align = args[ARG_align].u_int & 3; // limit to 0..3 if (align == PWM_BEGIN) { - self->center = self->duty_u16 / 2; + self->center = duty / 2; } else if (align == PWM_END) { - self->center = PWM_FULL_SCALE - self->duty_u16 / 2; + self->center = PWM_FULL_SCALE - duty / 2; } else { self->center = 32768; // Default value: mid. } @@ -515,6 +534,8 @@ static mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args // Create and populate the PWM object. machine_pwm_obj_t *self = mp_obj_malloc(machine_pwm_obj_t, &machine_pwm_type); + self->pwm_pin = pin1; + self->pwm_pin_af_obj = af_obj1; self->is_flexpwm = is_flexpwm; self->instance = af_obj1->instance; self->module = module; @@ -534,6 +555,8 @@ static mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args // Initialize the Pin(s). CLOCK_EnableClock(kCLOCK_Iomuxc); // just in case it was not set yet + // Configure PWMX channels to pin output mode to be prepared for duty_u16 == 0. + mp_hal_pin_output(pin1); IOMUXC_SetPinMux(pin1->muxRegister, af_obj1->af_mode, af_obj1->input_register, af_obj1->input_daisy, pin1->configRegister, 0U); IOMUXC_SetPinConfig(pin1->muxRegister, af_obj1->af_mode, af_obj1->input_register, af_obj1->input_daisy, diff --git a/ports/nrf/drivers/ticker.c b/ports/nrf/drivers/ticker.c index c2fa31e7c207..e9c07c842ecd 100644 --- a/ports/nrf/drivers/ticker.c +++ b/ports/nrf/drivers/ticker.c @@ -62,6 +62,7 @@ void ticker_init0(void) { #else NRFX_IRQ_PRIORITY_SET(FastTicker_IRQn, 2); #endif + m_num_of_slow_tickers = 0; NRFX_IRQ_PRIORITY_SET(SlowTicker_IRQn, 3); diff --git a/ports/nrf/modules/machine/pwm.c b/ports/nrf/modules/machine/pwm.c index 8145509c7628..13d824e86674 100644 --- a/ports/nrf/modules/machine/pwm.c +++ b/ports/nrf/modules/machine/pwm.c @@ -285,7 +285,7 @@ static mp_obj_t mp_machine_pwm_duty_get(machine_pwm_obj_t *self) { if (self->p_config->duty_mode[self->channel] == DUTY_PERCENT) { return MP_OBJ_NEW_SMALL_INT(self->p_config->duty[self->channel]); } else if (self->p_config->duty_mode[self->channel] == DUTY_U16) { - return MP_OBJ_NEW_SMALL_INT(self->p_config->duty[self->channel] * 100 / 65536); + return MP_OBJ_NEW_SMALL_INT(self->p_config->duty[self->channel] * 100 / 65535); } else { return MP_OBJ_NEW_SMALL_INT(-1); } @@ -301,7 +301,7 @@ static mp_obj_t mp_machine_pwm_duty_get_u16(machine_pwm_obj_t *self) { if (self->p_config->duty_mode[self->channel] == DUTY_U16) { return MP_OBJ_NEW_SMALL_INT(self->p_config->duty[self->channel]); } else if (self->p_config->duty_mode[self->channel] == DUTY_PERCENT) { - return MP_OBJ_NEW_SMALL_INT(self->p_config->duty[self->channel] * 65536 / 100); + return MP_OBJ_NEW_SMALL_INT(self->p_config->duty[self->channel] * 65535 / 100); } else { return MP_OBJ_NEW_SMALL_INT(-1); } @@ -365,7 +365,7 @@ static void machine_hard_pwm_start(const machine_pwm_obj_t *self) { if (self->p_config->duty_mode[i] == DUTY_PERCENT) { pulse_width = ((period * self->p_config->duty[i]) / 100); } else if (self->p_config->duty_mode[i] == DUTY_U16) { - pulse_width = ((period * self->p_config->duty[i]) / 65536); + pulse_width = ((period * self->p_config->duty[i]) / 65535); } else if (self->p_config->duty_mode[i] == DUTY_NS) { pulse_width = (uint64_t)self->p_config->duty[i] * tick_freq / 1000000000ULL; } diff --git a/ports/samd/boards/SAMD21_XPLAINED_PRO/board.json b/ports/samd/boards/SAMD21_XPLAINED_PRO/board.json index c59ebc1b7b7b..899e56e905a6 100644 --- a/ports/samd/boards/SAMD21_XPLAINED_PRO/board.json +++ b/ports/samd/boards/SAMD21_XPLAINED_PRO/board.json @@ -1,6 +1,6 @@ { "deploy": [ - "../deploy.md" + "deploy_xplained_pro.md" ], "docs": "", "features": [ diff --git a/ports/samd/boards/SAMD21_XPLAINED_PRO/deploy_xplained_pro.md b/ports/samd/boards/SAMD21_XPLAINED_PRO/deploy_xplained_pro.md new file mode 100644 index 000000000000..d95ab5061a30 --- /dev/null +++ b/ports/samd/boards/SAMD21_XPLAINED_PRO/deploy_xplained_pro.md @@ -0,0 +1,24 @@ +The SAMD21 Xplained Pro board is shipped without a bootloader. For use +with MicroPyhton a suitable bootloader has be be installed first. The +following procedure has been found to work and be simple: + +1. Get the bootloader from https://micropython.org/resources/firmware/bootloader-xplained-pro-v3.16.0-15-gaa52b22.hex. +2. Connect your board to the debug port. A drive with the name XPLAINED +shall appear. +3. Copy the Intel hex file of the bootloader to that drive. +4. Connect your board to the target port. A drive with the name SAMD21XPL should +appear. If not, push reset twice. Then it should appear. If that does not +happen, the bootloader was not properly installed or is not compatible. +5. Copy the MicroPython firmware, a .uf2 file, to the SAMD21 drive. When the SAMD21 +drive disappears, MicroPython is installed. + +From now on only steps 4 and 5 are needed to update MicroPython. You can use the +usual methods to invoke the bootloader, namely: + +- Push Reset twice. +- Call machine.bootloader(). +- Use the touch 1200 procedure by switching the USB baud rate to 1200 baud and back. + +At the above link above there are as well .uf2 versions of the bootloader +which one can install using steps 5. and 6. above once a .uf2 capable +bootloader is installed. diff --git a/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.h b/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.h index 064d1ecc0d03..09bad81bb79a 100644 --- a/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.h +++ b/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.h @@ -2,3 +2,7 @@ #define MICROPY_HW_MCU_NAME "SAMD21J18A" #define MICROPY_HW_XOSC32K (1) + +#define MICROPY_HW_SPIFLASH (1) +#define MICROPY_HW_SPIFLASH_ID (5) +#define MICROPY_HW_SPIFLASH_BAUDRATE (12000000) diff --git a/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.mk b/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.mk index f95c6549381b..cc43c22cea58 100644 --- a/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.mk +++ b/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.mk @@ -2,3 +2,5 @@ MCU_SERIES = SAMD21 CMSIS_MCU = SAMD21J18A LD_FILES = boards/samd21x18a.ld sections.ld TEXT0 = 0x2000 + +MICROPY_HW_CODESIZE ?= 248K diff --git a/ports/samd/boards/SAMD21_XPLAINED_PRO/pins.csv b/ports/samd/boards/SAMD21_XPLAINED_PRO/pins.csv index 69d0c136d08e..e5327e7916bf 100644 --- a/ports/samd/boards/SAMD21_XPLAINED_PRO/pins.csv +++ b/ports/samd/boards/SAMD21_XPLAINED_PRO/pins.csv @@ -53,3 +53,7 @@ USB_DP,PA25 SWCLK,PA30 SWDIO,PA31 +FLASH_MOSI,PB22 +FLASH_MISO,PB16 +FLASH_SCK,PB23 +FLASH_CS,PA13 diff --git a/ports/samd/machine_pwm.c b/ports/samd/machine_pwm.c index b2a383c21cba..468e34a1653d 100644 --- a/ports/samd/machine_pwm.c +++ b/ports/samd/machine_pwm.c @@ -54,7 +54,7 @@ typedef struct _machine_pwm_obj_t { #define PWM_CLK_READY (1) #define PWM_TCC_ENABLED (2) #define PWM_MASTER_CLK (get_peripheral_freq()) -#define PWM_FULL_SCALE (65536) +#define PWM_FULL_SCALE (65535) #define PWM_UPDATE_TIMEOUT (2000) #define VALUE_NOT_SET (-1) diff --git a/ports/samd/machine_uart.c b/ports/samd/machine_uart.c index b0dc4c5768d1..6e9a5538647b 100644 --- a/ports/samd/machine_uart.c +++ b/ports/samd/machine_uart.c @@ -107,10 +107,17 @@ static const char *_parity_name[] = {"None", "", "0", "1"}; // Is defined as 0, // take all bytes from the fifo and store them in the buffer static void uart_drain_rx_fifo(machine_uart_obj_t *self, Sercom *uart) { + uint8_t bits = self->bits; while (uart->USART.INTFLAG.bit.RXC != 0) { - if (ringbuf_free(&self->read_buffer) > 0) { - // get a byte from uart and put into the buffer - ringbuf_put(&(self->read_buffer), uart->USART.DATA.bit.DATA); + if (ringbuf_free(&self->read_buffer) >= (bits <= 8 ? 1 : 2)) { + // get a word from uart and put into the buffer + if (bits <= 8) { + ringbuf_put(&(self->read_buffer), uart->USART.DATA.bit.DATA); + } else { + uint16_t data = uart->USART.DATA.bit.DATA; + ringbuf_put(&(self->read_buffer), data); + ringbuf_put(&(self->read_buffer), data >> 8); + } } else { // if the buffer is full, disable the RX interrupt // allowing RTS to come up. It will be re-enabled by the next read @@ -150,7 +157,12 @@ void common_uart_irq_handler(int uart_id) { #if MICROPY_HW_UART_TXBUF // handle the outgoing data if (ringbuf_avail(&self->write_buffer) > 0) { - uart->USART.DATA.bit.DATA = ringbuf_get(&self->write_buffer); + if (self->bits <= 8) { + uart->USART.DATA.bit.DATA = ringbuf_get(&self->write_buffer); + } else { + uart->USART.DATA.bit.DATA = + ringbuf_get(&self->write_buffer) | (ringbuf_get(&self->write_buffer) << 8); + } } else { #if MICROPY_PY_MACHINE_UART_IRQ // Set the TXIDLE flag @@ -274,6 +286,17 @@ void machine_uart_set_baudrate(mp_obj_t self_in, uint32_t baudrate) { static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + size_t rxbuf_len = self->read_buffer.size - 1; + #if MICROPY_HW_UART_TXBUF + size_t txbuf_len = self->write_buffer.size - 1; + #endif + if (self->bits > 8) { + rxbuf_len /= 2; + #if MICROPY_HW_UART_TXBUF + txbuf_len /= 2; + #endif + } + mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=%s, stop=%u, " "timeout=%u, timeout_char=%u, rxbuf=%d" #if MICROPY_HW_UART_TXBUF @@ -287,9 +310,9 @@ static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_ #endif ")", self->id, self->baudrate, self->bits, _parity_name[self->parity], - self->stop + 1, self->timeout, self->timeout_char, self->read_buffer.size - 1 + self->stop + 1, self->timeout, self->timeout_char, rxbuf_len #if MICROPY_HW_UART_TXBUF - , self->write_buffer.size - 1 + , txbuf_len #endif #if MICROPY_HW_UART_RTSCTS , self->rts != 0xff ? pin_find_by_id(self->rts)->name : MP_QSTR_None @@ -411,6 +434,14 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, } } #endif + + // Double the buffer lengths for 9 bit transfer + if (self->bits > 8) { + rxbuf_len *= 2; + #if MICROPY_HW_UART_TXBUF + txbuf_len *= 2; + #endif + } // Initialise the UART peripheral if any arguments given, or it was not initialised previously. if (n_args > 0 || kw_args->used > 0 || self->new) { self->new = false; @@ -619,7 +650,12 @@ static mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t uint64_t timeout_char = self->timeout_char; uint8_t *dest = buf_in; - for (size_t i = 0; i < size; i++) { + // Check that size is even for 9 bit transfers. + if ((self->bits >= 9) && (size & 1)) { + *errcode = MP_EIO; + return MP_STREAM_ERROR; + } + for (size_t i = 0; i < size;) { // Wait for the first/next character while (ringbuf_avail(&self->read_buffer) == 0) { if (mp_hal_ticks_ms_64() > t) { // timed out @@ -633,6 +669,11 @@ static mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t MICROPY_EVENT_POLL_HOOK } *dest++ = ringbuf_get(&(self->read_buffer)); + i++; + if (self->bits >= 9 && i < size) { + *dest++ = ringbuf_get(&(self->read_buffer)); + i++; + } t = mp_hal_ticks_ms_64() + timeout_char; // (Re-)Enable RXC interrupt if ((uart->USART.INTENSET.reg & SERCOM_USART_INTENSET_RXC) == 0) { @@ -647,12 +688,19 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_ size_t i = 0; const uint8_t *src = buf_in; Sercom *uart = sercom_instance[self->id]; - uint64_t t = mp_hal_ticks_ms_64() + self->timeout; + uint8_t bits = self->bits; + // Check that size is even for 9 bit transfers. + if ((bits >= 9) && (size & 1)) { + *errcode = MP_EIO; + return MP_STREAM_ERROR; + } + #if MICROPY_HW_UART_TXBUF #if MICROPY_PY_MACHINE_UART_IRQ // Prefill the FIFO to get rid of the initial IRQ_TXIDLE event + // Do not care for 9 Bit transfer here since the UART is not yet started. while (i < size && ringbuf_free(&(self->write_buffer)) > 0) { ringbuf_put(&(self->write_buffer), *src++); i++; @@ -672,8 +720,16 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_ } MICROPY_EVENT_POLL_HOOK } - ringbuf_put(&(self->write_buffer), *src++); - i++; + if (bits >= 9) { + mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + ringbuf_put(&(self->write_buffer), *src++); + ringbuf_put(&(self->write_buffer), *src++); + i += 2; + MICROPY_END_ATOMIC_SECTION(atomic_state); + } else { + ringbuf_put(&(self->write_buffer), *src++); + i += 1; + } uart->USART.INTENSET.reg = SERCOM_USART_INTENSET_DRE; // kick off the IRQ } @@ -691,7 +747,13 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_ } MICROPY_EVENT_POLL_HOOK } - uart->USART.DATA.bit.DATA = *src++; + if (self->bits > 8 && i < (size - 1)) { + uart->USART.DATA.bit.DATA = *(uint16_t *)src; + i++; + src += 2; + } else { + uart->USART.DATA.bit.DATA = *src++; + } i++; } #endif diff --git a/ports/samd/mboot/LICENSE b/ports/samd/mboot/LICENSE new file mode 100644 index 000000000000..7fffbab21e0e --- /dev/null +++ b/ports/samd/mboot/LICENSE @@ -0,0 +1,60 @@ +The MIT License (MIT) + +Copyright (c) Microsoft + +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. + +Third Party Programs: The software may include third party programs that +Microsoft, not the third party, licenses to you under this agreement. +Notices, if any, for the third party programs are included for your +information only. + + + +Otherwise, where noted: + +/* ---------------------------------------------------------------------------- + * SAM Software Package License + * ---------------------------------------------------------------------------- + * Copyright (c) 2011-2014, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following condition is met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + diff --git a/ports/samd/mboot/README.md b/ports/samd/mboot/README.md new file mode 100644 index 000000000000..1695f0a89112 --- /dev/null +++ b/ports/samd/mboot/README.md @@ -0,0 +1,5 @@ +The SAMD Xplained Pro bootloader is built using the files from the +repository https://github.com/adafruit/uf2-samdx1. The repository +includes the LICENSE file https://github.com/adafruit/uf2-samdx1/LICENSE +and a README https://github.com/adafruit/uf2-samdx1/README.md with +build instructions. diff --git a/ports/samd/mboot/bootloader-xplained-pro-v3.16.0-15-gaa52b22.hex b/ports/samd/mboot/bootloader-xplained-pro-v3.16.0-15-gaa52b22.hex new file mode 100644 index 000000000000..df8d5afd19c2 --- /dev/null +++ b/ports/samd/mboot/bootloader-xplained-pro-v3.16.0-15-gaa52b22.hex @@ -0,0 +1,513 @@ +:10000000E02D002069020000650200006702000088 +:1000100000000000000000000000000000000000E0 +:100020000000000000000000000000006502000069 +:100030000000000000000000650200005D020000FA +:100040006502000065020000650200006502000014 +:100050006502000065020000650200006502000004 +:1000600065020000650200006502000065020000F4 +:1000700065020000650200006502000065020000E4 +:1000800065020000650200006502000065020000D4 +:1000900065020000650200006502000065020000C4 +:1000A00065020000650200006502000065020000B4 +:1000B00000000000074B1A7DD207FCD52022FF323A +:1000C0001A83054A4008D8611A801A7DD207FCD5E8 +:1000D0007047C0460040004102A5FFFF70B5802573 +:1000E0000400ED02AC4200D370BD20000134FFF7E4 +:1000F000E1FFFF34F6E7002330B59A4200D130BD6E +:100100009C000D5901330551F7E70000F0B580253B +:10011000104C114E6368114FAB436360002A00D14D +:10012000F0BD1300102A00D91023D21A2680257D95 +:10013000ED07FCD59B009C460023CD58C5500433E9 +:100140006345FAD1C018C9182780237DDB07FCD589 +:10015000E4E7C0460040004144A5FFFF04A5FFFFBF +:10016000802270B5002304000D005200E958E058C9 +:10017000814203D104339342F8D170BD2000FFF7D0 +:1001800099FF402229002000FFF7C0FFF5E700009B +:1001900010230249CA681A42FCD070470008004088 +:1001A0001E2270B52149224C4B6893431C3A1343DD +:1001B0004B60A284FFF7ECFF1E4B1B689B0E3F2B8E +:1001C00000D1203B00251C4A9B0213431B4AE262DC +:1001D000A362A584FFF7DCFF194BA384FFF7D8FFC8 +:1001E0000223A28C1343A384FFF7D2FF154B9D601B +:1001F0005A7852B2002AFBDB134A5A605A7852B23C +:10020000002AFBDBC021114A114B12485360036ADC +:1002100009061B021B0A0B4303620023936007338A +:10022000136030220C4B1A6070BDC0460040004184 +:100230000008004024608000FF01000080BB0A1C11 +:1002400024050000000C00400007030010E000E05F +:10025000E703000000ED00E00000002010B500F012 +:1002600007F910BDFEE7FEE72449254870B5814235 +:100270000AD0244BC41E0022A34203D303331A1A0C +:100280009208920001F058FC1F48204BC11E00222A +:10029000994203D803331A1A92089200002101F000 +:1002A00054FCFF221A4B032193431A4A0C259360F6 +:1002B000022208243026184B18485A62C3788B4310 +:1002C0001343C370C378AB432343C370144B987B71 +:1002D000B0430600202030439873987BA843044322 +:1002E0009C73987B884302439A7380230D4A51681C +:1002F0000B43536000F07AFEFEE7C0466C1D000021 +:1003000000000020D0010020D0010020DC0D0020E2 +:100310000000000000ED00E0FC70004100500041D2 +:100320000048004100400041F7B5134D01906B8833 +:10033000002B18D1082738001A0292B214B2A44632 +:1003400066465400A2B2002E02DA0C4A5440A2B211 +:10035000013880B20028F1D158002A52802201339E +:1003600052009342E7D1019A0B0A53405800285A91 +:100370000902484080B2FEBDD001002021100000DB +:10038000074B1B685843074B1A789523002A00D166 +:100390001233012243439A4200D370470132FAE7F5 +:1003A00000000020D003002070B505001C240020B0 +:1003B0000F260B00E3403340002A09D1002B07D160 +:1003C000002C0BD0002809D1043C231DF1D170BDB5 +:1003D000092B03DD37332B540130F5E73033FAE7CF +:1003E000054B064A1A60BFF34F8F054B054ADA608A +:1003F000BFF34F8FFEE7C046FC7F0020EF6926F079 +:1004000000ED00E00400FA0510B50B4A1368002B5C +:100410000ED10A4911600A490A6801320A60094985 +:100420000868002806D0824204D30B60FFF7D8FF8B +:10043000013B136010BDC046E4030020DC05000052 +:10044000E0030020D8030020054B064A1A60BFF3E2 +:100450004F8F054B054ADA60BFF34F8FFEE7C0466A +:10046000FC7F0020EF6916F000ED00E00400FA05C3 +:10047000012210B5194B1A491A701A4A1368013330 +:1004800013600A68002A0CD017481018834203D161 +:1004900080241648E4050460934201D100230B60D8 +:1004A00010BD1348DBB20278002B10D180210F4B16 +:1004B000C905196011000A39C9B20E4BF02902D9D9 +:1004C0001978494219701B78D2180270E8E79342F4 +:1004D000E6D18022084BD2051A60E1E7D003002064 +:1004E000DC030020D403002018FCFFFF98440041E7 +:1004F000050000200400002094440041074B084AF6 +:100500001B681168994207D2FA21C9005B18136071 +:100510008022044BD2051A607047C046D4030020E5 +:10052000DC03002094440041044B80221900D205D2 +:10053000883198330A601A607047C0460044004111 +:1005400070470000202370B5354A0F20D1690B4356 +:10055000D361012233490B7813430B70324B197866 +:100560008143197006211C78214319702F490C789A +:1005700022430A701A7802401A70602219780A43DE +:100580001A702B4A2B4B53805378DB09FCD1012284 +:10059000294B19780A431A709A78D207FCD41F2085 +:1005A00026490A68520B0240824200D105221C8D66 +:1005B000234D024092012C4022431A850A68920C76 +:1005C00002401F2A00D1023A1F24188D2240A04366 +:1005D000024307201A850A68D20D0240824200D1E8 +:1005E0000322198D02401748120301400A437F215C +:1005F0001A851A7814480A401A70042219780A4396 +:100600000C211A7058621A898A431A811A890B3987 +:100610008A431A8180220021520001F096FA70BDAF +:1006200000040040584400413C440041594400410A +:10063000000C004006400000005000412460800093 +:100640003FF8FFFFFF8FFFFF74060020F7B51C0087 +:10065000314B06005B6A0F0015000193002C05D199 +:10066000443454432D4BE418FFF7CEFE63782278D0 +:10067000934210D2D51ABD4200D93D00002E08D0B9 +:10068000211DC9182A00300001F056FA63785B1961 +:1006900063702800FEBD019BA1786A01D318002970 +:1006A00011D1211D196059681D48890B89035960B2 +:1006B0005968014059601B49551840212879014368 +:1006C00029710121A1701449002551180191164981 +:1006D0005218127AD207DCD55D68A278EDB2257087 +:1006E000022A04D12A001968201D01F025FABD4212 +:1006F00000D93D00002E0CD02A0030006570211D6D +:1007000001F01AFA0122019BFF331A720023A37031 +:10071000BFE76670F6E7C046005000413004002095 +:10072000FF3F00F000510041FF50004110B5002391 +:10073000FFF78CFF10BDF8B505000C0016001F0078 +:10074000002C00D1F8BD210028003B003200FFF74B +:100750007DFF241A2D18F3E7F8B50D001F49560147 +:100760004C6A3419002B1BD063695B005B08636122 +:1007700020616369AA049B0B920C9B031343636182 +:100780006369174A174813406361321802238021B6 +:10079000160013729171327A1A42FCD02800F8BD0B +:1007A000012163695B005B0F03339940A94209D8BB +:1007B000043A531E9A416369D1075B005A080A4301 +:1007C0006261D5E744275743074A0437BF18010041 +:1007D0002A00380001F0B0F93800C9E700500041A4 +:1007E000FF3F00F0FF5000413004002010B500230F +:1007F000FFF7B2FF10BD000010B5054A0B001188CD +:10080000994200D919000022FFF7F0FF10BDC04641 +:100810007407002007B500216B460A00D81D01703F +:10082000FFF7E4FF07BD0000F0B5B94C9BB0FFF740 +:10083000EBFDA38B08211A000A400392B54A0B4234 +:100840002ED080234020A183A372C024093151708F +:1008500090715371B04BB14D5968A40529402143A3 +:100860005960596929400C43AD495C6104311960F4 +:10087000AC49AD4C196159680C4080218902214373 +:1008800059605968890B8903596050710022A74B40 +:100890001A70A64B1878431E9841C0B21BB0F0BD29 +:1008A0001020137A0342F4D09D4D10726D899C483C +:1008B000AC46664680799E4D02909948984BC78811 +:1008C000C0792E80402501909B889548994E0089DB +:1008D0005571B34200D1B0E136DC81246400A342FB +:1008E00000D1BFE115DC822B00D190E106DC803B1A +:1008F000012B00D886E120239371CAE780214900AB +:100900008B42F8D00221FF318B42F4D1FFF782FFF6 +:10091000BFE7B02189008B4200D16DE10EDC0139C7 +:10092000FF398B42E7D0A23B8349FF3B0B42E2D128 +:10093000002308210893099308A8C4E0C021890076 +:100940008B42D8D07D49DFE77D488342DED000DD91 +:1009500096E07C498B4212DC01398B4200DBACE033 +:1009600079498B42E4D06031FF318B42C3D1FFF72C +:1009700051FF80235B423B43DBB2A37289E78821AE +:100980006A4809018B4200D195E080318B42B2D197 +:100990000770FFF73FFFA02303225B00E254C02251 +:1009A0005D4B5E49586C92050840104358644620E0 +:1009B000FF302554654830271864902040002754A4 +:1009C000586B8026084010435863922040002654FC +:1009D0005F481863B02040002554586F084058679E +:1009E000B220400026543C307D3E2654FC388446DC +:1009F0009C44604606680E4016430660A626FF36F5 +:100A0000A5551E005348A0363060C0267600A75575 +:100A10001F0094373E6880200E4016433E60C22679 +:100A20007600A0551E004C4F90363760E0264427D4 +:100A30007600A7550436A555A055C01984469C4498 +:100A4000604606680E4016430660D42084469C44E7 +:100A5000604606680E4016430660F0268020760049 +:100A6000A7550436A555A0551C00E4342068F4337E +:100A7000084010432060186801400A431A6008E7E4 +:100A80003648834200D122E10ADC35498B4200D14D +:100A90003CE7344934480B40834200D136E72AE72B +:100AA00032498B4200D131E731498B4200D022E7F5 +:100AB000002308A80380012105E080235B009F42FA +:100AC00004D112212B48FFF797FEE2E680239B001A +:100AD0009F4202D199212848F5E7019B032B78D149 +:100AE000029B032B00D906E708AC48220021200016 +:100AF00001F02BF8019B6370029B002B3ED1092271 +:100B000004332370A270E37020002178DBE7C04635 +:100B100000500041FF50004174060020FFFFFF8F8E +:100B2000E803002034040020FF3F00F02C040020E4 +:100B30007407002002030000FFFE00000103000014 +:100B40002109000081060000A1030000BC04002070 +:100B5000780400208805002044050020A121000021 +:100B600021200000FFFEFFFF210A000021220000DB +:100B7000A1FE0000981B00004400002004AE3200DB +:100B8000544B23CB23C200252F001B681360AB00FE +:100B9000514AF358D01919680122FFF705FC0135B5 +:100BA0003F18042DF3D100234B4AD3554B4B4C4AED +:100BB0009B799B009D58280000F0CFFF2300013057 +:100BC000400020702A7802330135002A9CD01A7028 +:100BD000F8E7019B0F2B02D13921424873E7019BB3 +:100BE000212B02D1092140486DE7019B222B00D027 +:100BF00081E621213D4866E7072800D07BE6AA214F +:100C00003B4860E7039B08A8022103805BE708429A +:100C100000D070E60F235022034008335B011042DE +:100C200006D0344A9B189B799B06DB0F08A8EBE79C +:100C3000304A9B189B79DB06F7E70F24030023401B +:100C4000204200D157E60140394300D053E6294AFB +:100C500008335B019B18020602D520225A7155E623 +:100C60001022FBE70F2403002340204200D142E67C +:100C70000140394300D03EE65B01020613D51E4910 +:100C80001C4A9A185B182021D879084200D13DE609 +:100C900059711B7A2B4200D138E613004022FF33F2 +:100CA0001A723E3ADAE710201349124A9A185B1872 +:100CB000D979014200D129E658711B7A01180B42FB +:100CC00000D123E61300FF3301221972C6E70B4857 +:100CD000F9E6C046881B000050060020E80300200B +:100CE000D01B000008000020E0000020AC1B00002A +:100CF000F400002000500041FF500041EC000020B3 +:100D000010B5FFF71FFC0022034B1A700223034AA1 +:100D100011780B43137010BD2C04002000500041CB +:100D200010B50C000122FFF761FD200010BD10B5C9 +:100D30000C000122FFF75AFD200010BD70B5040021 +:100D40000D00FFF771FD03000020834204D0022252 +:100D500029002000FFF7EAFC70BD70B505000C000B +:100D6000FFF762FD002807D000230222210028009F +:100D7000FFF7E1FC200070BD0400FBE730B5002365 +:100D80002025934200DB30BD0C78002C03D00131CC +:100D9000C4540133F5E72C00FAE70000F8B58022CF +:100DA0000C00050000212000920000F0CEFE002D76 +:100DB0000CD13E2220004A4900F0BEFEFF235522FE +:100DC0005B00E254474B9218E254F8BD7E2D23D8C5 +:100DD0006E1E3E2E01D9403D2E00002E12D08025E1 +:100DE0003302581C404EFF30ED001A1F591CAA4216 +:100DF00003D2B3420ED08BB223800B000234814267 +:100E0000F3D1E2E7F0230922FF212370601C00F0F8 +:100E10009CFEE4E7354BEFE7822D31D87F2DD4D10E +:100E20002F490B2220002B31FFF7A8FF8027282312 +:100E30002F4EE3727D3D3F03F0683B0020340028D5 +:100E400002D000F08AFE03001A0A237762771A0C98 +:100E50001B0EE3772B0A3100A277A5760B22E376EF +:100E60002000FFF78BFF67224D2301355242ADB2C0 +:100E700022746374227663761036052DDCD1A4E7E4 +:100E80002B00833B012B0CD8194A1B01D318DD68BA +:100E9000280000F062FE29000200200000F04CFE55 +:100EA00093E78023853D2902DB02994200D38CE73A +:100EB000104B114A2360114B20006360FE235B003E +:100EC000E2508023DB00A361802380229B01A3608A +:100ED0000B4B52006561E1602261E3612030DDE788 +:100EE000E01B0000FF01000003040000FFFF000002 +:100EF000881C00005546320A306FB10A57515D9E7A +:100F0000882BED6870B516001D000A682F4B0C0089 +:100F10009A4252D12E4B4A689A424ED1FE2252003A +:100F20002C4B8A589A4248D18B689A0403D52A4A96 +:100F3000C969914241D1DB0712D4802322695B0049 +:100F40009A420DD1E068C3B2002B09D1F822234B9D +:100F50009202C318934203D221002031FFF700F917 +:100F6000002D2BD0A369002B28D029681C4A8B4266 +:100F700006D0934201D8002901D001235B422B60A7 +:100F80006369934219D8072201211A409140DB0876 +:100F9000EB181A7AC8B2114204D1696802430131D0 +:100FA0001A7269606A682B689A4206D3002E04D1CF +:100FB0000C4B1B681E330C4A136070BD002EFCD115 +:100FC000084B1B682D33FF33F5E7C0465546320A00 +:100FD00057515D9E306FB10A882BED6800E0FFFF2E +:100FE00063040000E0030020D8030020F8B5624D40 +:100FF000AB68002B00D0FEE70221604B5A6B8A439E +:101000005A63DA681205FCD55D4A5A630222596BAD +:101010000A435A63DA689205FCD5DA685205FCD4B3 +:1010200002215A6B8A435A63DA681205FCD50822FA +:10103000596B0A435A630222596B0A4351495A6356 +:101040000B68013321D120224F4BFF32188B024312 +:101050001A8358684D4A02435A604D4ADA614D4A34 +:101060001A801A7DD207FCD54B4A1A801A7DD20706 +:10107000FCD54A4A0A604A4A4A4911604A4A1A80DB +:101080001A7DD207FCD5FFF7DFF90023474A13701A +:10109000D379DB09FCD1FFF747FA454B1E68454B76 +:1010A000F218F8239B029A4213D8434B434A19681B +:1010B000434C444B914232D11978434AC90702D478 +:1010C000216891422BD0414B22601B68404A323349 +:1010D0001360FFF765F8BFF35F8F62B6FFF710FE8E +:1010E0004020FFF72DFA80270A230126394DFF0102 +:1010F0002B70FFF799FB384C00280AD02378DAB21E +:10110000002B05D1324B38001A60FFF719FA2E7008 +:1011100026702378002B25D000F0D6FAFCE71B7848 +:10112000DB07DA0F002B0DD00023802223602B4B2E +:10113000D2051A6080239B011A6882F30888AB608D +:101140003047C6E72168264B994201D12260C0E7AB +:1011500021681D4A9142E7D0FA2023604000FFF742 +:101160000FF9E1E72378002BC3D1FF33C046013BE1 +:10117000002BFBD1BDE7C04600ED00E000080040B9 +:10118000040027000040800000400041800004006F +:101190000020400005A5FFFF44A5FFFFFAC7E0D8E7 +:1011A000044080005DFCFFFF06A5FFFF001000402B +:1011B0000420000000E0FFFFB42000007CB0EE87B8 +:1011C000FC7F002038040040EF6926F0E003002097 +:1011D000D80300200400002076070020944400413A +:1011E000EF6916F010B5054C12220021200000F026 +:1011F000ACFCF0232370E63BE37110BDB309002083 +:1012000010B5FFF7EFFF0022014B1A7310BDC04667 +:101210009E010020F7B580270400160000250191EB +:10122000BF00A368AB4200D8F7BD3900A278019B8C +:10123000E068FFF780FA80235B01E81801223300A1 +:10124000E168FFF75FFE0135EBE770B513000600BC +:101250000C00150000200A000121FFF7F7F9002318 +:10126000984206D02B0022001F213000FFF7EEF934 +:101270000123180070BD000010B504220D210248A2 +:10128000FFF7B4FA10BDC0469E01002010B50B4C0C +:101290000B4861792379A27909021943E379120491 +:1012A00011431B060B431A0A037142711A0C1B0EE1 +:1012B0008271C371FFF7E0FF10BDC04694090020A2 +:1012C0009E01002070B504220D00FFF78FFA154C27 +:1012D00085420FD0FFF786FF012304222373124BB0 +:1012E0009A700022DA701A715A719A711A735A73CD +:1012F000FFF7CCFFFFF784FF637A217A1B020B43D1 +:10130000A17AE27A0904120619431143491B0B0A18 +:10131000217263720B0C090EA372E172FFF7B6FF24 +:1013200070BDC0469E010020B309002070B5184C66 +:10133000142205000021200000F007FC154A002DB2 +:101340001FD021000823D07D08313F26527C324037 +:101350001C2A01D0B24205D10F4A0C330A80052263 +:10136000DBB2CA70191C834200D9011CC9B2002D1E +:101370000BD0023B9BB21B0223802000FFF7A2FF91 +:1013800070BD0423D07C211DDFE7013B2370F4E70F +:1013900078090020940900201C0A0000F0B5040020 +:1013A00000252A4B85B0597C03AAD170997C917095 +:1013B000D97C5170197D1170997D02AAD170DB7DA5 +:1013C000937053880193019BAB4205D8FFF718FF38 +:1013D000FFF75CFF05B0F0BDFFF726FA0028F9D053 +:1013E000039B1B4FEE18002C1FD039003000FFF775 +:1013F000D5FC8021042238008900FFF7F7F9154A4F +:101400000135507A137A917A00021843D37A09048D +:1014100008431B06104903435B18190A13725172E3 +:10142000190C1B0E9172D372CDE780212300380076 +:1014300005228900FFF77FF9220039003000074BB1 +:10144000FFF760FDFFF75AF8D9E7C046940900207E +:10145000780700209E01002000FEFFFFC809002041 +:101460007FB500F02FFB444E002205213000FFF72E +:10147000ECFE002849D0737A327AB57A1B021A43FF +:10148000F37A2D041B0615433C4C1D432B0A637253 +:101490002B0CA3722B0E2572E372F07B2F2827D81A +:1014A000192815D8032827D012282FD0002840D07B +:1014B000FFF798FE012305222373314B9A70002217 +:1014C000DA701A715A719A711A7320325A7332E0B3 +:1014D0001A381528ECD800F01BFB282CEBEB2CEB72 +:1014E000EBEBEB3FEB31EBEB39EB3DEBEBEBEB2CD1 +:1014F0005A28DDD1012019E0F37C191C122B00D9E8 +:1015000012211F48C9B2FFF7DDFE7FBD1D4C1E49E9 +:10151000200018220830FFF731FCF37C191C242B23 +:1015200000D924212000C9B2EDE70020FFF7FEFE1C +:10153000EBE7FFF765FEFFF7A9FEE6E78023134818 +:101540009B024360124B08210360DCE70120FFF798 +:1015500025FFDAE70020FAE70C260E49320001A841 +:1015600000F0EAFAB54204D9002326726372A3722E +:10157000E3720C2101A8C6E7940900209E01002017 +:10158000B3090020AC010020721B00008C09002070 +:1015900000003E7F231D000072B6BFF35F8F044B37 +:1015A000044A9A829A830022034B9A607047C0468D +:1015B00000500041FF03000000ED00E0F0B5C5B0B1 +:1015C0000DAF0400FFF7E8FF44220021380000F0CF +:1015D000BCFA982200211EA800F0B7FA02230F21BE +:1015E000BB70A37862780B40A370264B0A4005932A +:1015F000636805AD06930023E16807932248627093 +:101600002B7301221EABFFF77DFC390020001EAAC0 +:10161000FFF700FE280001230D216278FFF79CF8F8 +:10162000002505AE3A003000A178FFF70EFE002835 +:101630001ED0144B01AD0193069B6B60079BAB6002 +:1016400000232B73F37B002BE4D02A2B17D16B469E +:10165000B27D3900DA70F27D20009A705E881EAA91 +:10166000A660FFF7D7FDAB6876029E1BAE60D1E7A0 +:10167000064B9D4201D9FEF7B3FE0135D1E7FEF7D7 +:10168000E3FEC7E753425355FF0F0000F824010063 +:1016900030B5EFF30883054C2360036883F30888B3 +:1016A0004568A847236883F3088830BDC00A002036 +:1016B00007B5010001226846FEF776FE082168465C +:1016C000FFF72EFB07BD0000F8B500237A227F4C00 +:1016D00023607F4B1A70FFF7C3FE7E4D4021280028 +:1016E000FFF72CFB7C4B186000232B54984201D051 +:1016F000FEF704FF0022794B1D60794B1A60784891 +:10170000754B02681F68BA42E5D2744E31680B7897 +:10171000FF2B37D0734D232B00D0B4E06C4B1B78DC +:10172000532B34D12B680132013102603160BA1A77 +:101730009A4200D91A006C4D20682A6000F0FCF92A +:10174000674829680368654ACB18013B0360106845 +:101750004B1EC3181360FF23644D29701940614B61 +:101760001B688B4203D92068591AFFF7F6FAC04666 +:101770007A22574B1A7000225A4B1A60574A136844 +:1017800001331360564A136801331360B7E7522BD5 +:1017900004D129682068FFF7CAFAE9E74F2B03D183 +:1017A0002B6822681370E3E7482B03D12B6822686B +:1017B0001380DDE7572B0AD14D4B22689A4202D1A4 +:1017C0000020FEF7BDFE23682A681A60D0E76F2B61 +:1017D00004D101212068FFF7A3FAC9E7682B05D1DE +:1017E000022123681B882B602800F4E7772B04D1A3 +:1017F000236804211B682B60F6E7472B09D1286872 +:10180000FFF746FF3B4B1B78002BB1D001213A4834 +:10181000E1E7542BACD04E2BAAD0562B02D12A2173 +:101820003648D8E7582B05D12868FEF757FC032126 +:101830003348D0E7592B0DD12A682068314B002A54 +:1018400003D1186003213048C5E719689208FEF7F4 +:101850005DFCF7E75A2B8BD12F6800252668F71916 +:10186000B74209D101212948FFF75AFA2800FFF7AA +:101870001FFF03212648AEE729003078FEF754FD0C +:1018800001360500ECE71A00303AD1B2092904D834 +:101890002B681B0113432B6070E71A00413A052A9D +:1018A00003D82A68373B1201F4E71A00613A052A87 +:1018B00003D82A68573B1201ECE72C2B03D12B6885 +:1018C00023600023E7E7024A1370FAE7BC0A00200E +:1018D000610A0020680A0020B40A0020B80A00202B +:1018E000AC0A0020640A0020C80A0020B00A0020C8 +:1018F0000CED00E0600A00202F1D00003F1D0000DD +:10190000311D0000C40A0020351D0000391D0000F3 +:101910003B1D00000300F7B5473304001A7840214F +:1019200003000020FEF792FE002801D10020FEBD3A +:1019300000232370237901930423E356002BF5DB66 +:101940003F262000019FA51DEB8F37404830C0186F +:101950003A00611D00F0F0F8E88F019BC01980B2D9 +:10196000B34301D1E887E1E70023EB87DFE7F0B578 +:1019700006000C001F0093B001923F25AC4203DC2F +:101980002500002F00D140373B0002AA2B431370E3 +:1019900002AB31002A00581C00F0CEF86B46402103 +:1019A0001A7902A80123FEF7D7FE7619641BE4D149 +:1019B00013B0F0BD030010B547331A78043100238B +:1019C0004830FFF7D4FF10BDF7B50400FFF7A2FFC2 +:1019D000002842D0230048339A88A06C1A80002245 +:1019E00001385A80082865D800F09CF8150523278F +:1019F000252A483F3800314E300000F0AEF805008F +:101A0000200031002A004C3000F096F82900200018 +:101A1000FFF7D0FF21E00123E364FF332365802338 +:101A2000DB006365A0235B00A365254B1421E36500 +:101A3000EDE7FEF7D5FC0021E9E7FEF705FDFAE743 +:101A400020000021FFF7B6FF8023206D9B01984204 +:101A500003D321005431FEF783FBF7BD2100626DF3 +:101A6000206D5831FEF747FBE5E72000656D216DDD +:101A70002A004C30FEF73FFBA900C8E70026636D43 +:101A8000276D0193019BB34201DC5900BFE73D0084 +:101A900000212B001878FEF747FC7B1C0135FF3333 +:101AA00001009D42F5D1230072004C332F00985263 +:101AB0000136E7E701225A80BDE7C046B81C0000A6 +:101AC000882BED6807480622030010B547331A70CB +:101AD000FFF77AFF04480722030047331A70FFF725 +:101AE00073FF10BDCC0A0020540C002010B5E2B0EA +:101AF0000400FFF751FDC42200216846520000F0A7 +:101B000024F847236B441C706846FFF75DFFFBE732 +:101B100002B4714649084900095649008E4402BC86 +:101B20007047C04602B4714649084900095C490043 +:101B30008E4402BC7047C046002310B59A4200D1C3 +:101B400010BDCC5CC4540133F8E703008218934203 +:101B500000D1704719700133F9E70023C25C0133EB +:101B6000002AFBD1581E70474D6963726F63686924 +:101B7000700053414D4432312058706C61696E657C +:101B8000642050726F0000000CA0800040A0800014 +:101B900044A0800048A0800012011002EF02014022 +:101BA000D804022401420102030100000697FF0944 +:101BB00001A101150026FF00750895400901810269 +:101BC00095400901910295010901B102C000000090 +:101BD00000000000681B0000721B0000500600207F +:101BE000EB3C905546322055463220000201010060 +:101BF0000240007E3EF83F000100010000000000AE +:101C0000000000008000294200420053414D443250 +:101C10003158504C000046415431362020203C21A0 +:101C2000646F63747970652068746D6C3E0A3C68FB +:101C3000746D6C3E3C626F64793E3C736372697094 +:101C4000743E0A6C6F636174696F6E2E7265706C9E +:101C5000616365282268747470733A2F2F777777E1 +:101C60002E7078742E696F2F22293B0A3C2F7363E4 +:101C7000726970743E3C2F626F64793E3C2F6874C9 +:101C80006D6C3E0A00000000494E464F5F554632DB +:101C900054585400B81C0000494E44455820202098 +:101CA00048544D001E1C000043555252454E5420CE +:101CB000554632000000000055463220426F6F74D6 +:101CC0006C6F616465722076332E31362E302D3183 +:101CD000352D6761613532623232205346485752A2 +:101CE0004F0D0A4D6F64656C3A2053414D443231BB +:101CF0002058706C61696E65642050726F0D0A42E5 +:101D00006F6172642D49443A2053414D4432314A47 +:101D10003138412D58706C61696E65642D50726F59 +:101D20000D0A000000000800003E800200020006CC +:101D300000580A0D00590A0D005A00230A0D0076BA +:101D4000312E31205B41726475696E6F3A58595A71 +:101D50005D205365702032382032303234203135E6 +:101D60003A35353A34340A0D000000000100000015 +:101D700001C80000050F3900021810050038B60828 +:101D800034A909A0478BFDA0768815B6650001012E +:101D9000001C100500DF60DDD88945C74C9CD2656A +:101DA0009D9E648A9F00000306AA000200000000B6 +:101DB000090299000501008032080B0002020201AD +:101DC00000090400000102020100052400100104C2 +:101DD00024020605240600010524010301070583EA +:101DE000030800FF09040100020A00000007058142 +:101DF0000240000007050202400000090402000240 +:101E0000080650000705840240000007050502404F +:101E10000000090403000203000000092100010082 +:101E20000122210007058603400001070506034043 +:101E300000010904040002FF2A010007058703408E +:101E4000000107050703400001000000090403002A +:101E5000020300000300000000C2010000000800AF +:101E60000A00000000000306AA00080002000400A7 +:101E7000A0001400030057494E55534200000000D3 +:101E80000000000000008400040007002A00440055 +:101E90006500760069006300650049006E0074000B +:101EA0006500720066006100630065004700550030 +:101EB000490044007300000050007B0039003200EC +:101EC0004300450036003400360032002D00390052 +:101ED0004300370037002D0034003600460045002F +:101EE0002D0039003300330042002D003300310053 +:101EF00043004200390043003500420042003300F5 +:101F0000420039007D00000000005342535500009C +:101F1000000000000000000000800202200000001D +:101F200000000000000000000000000000000000B1 +:101F30000000000000000000312E303000000000E2 +:101F40000000000000000000000000000000000091 +:101F50000000000000000000000000000000000081 +:101F60000000000000000000000000000000000071 +:101F70000000000000000000000000000000000061 +:101F80000000000000000000000000000000000051 +:101F90000000000000000000000000000000000041 +:101FA0000000000000000000000000000000000031 +:101FB0000000000000000000000000000000000021 +:101FC0000000000000000000000000000000000011 +:101FD0000000000000000000000000000000000001 +:101FE00000000000000000000000000000000000F1 +:101FF00000000000ED1A0000BD150000B81C000034 +:00000001FF diff --git a/ports/samd/samd_flash.c b/ports/samd/samd_flash.c index ef0de7b6fb58..f68bdf140f49 100644 --- a/ports/samd/samd_flash.c +++ b/ports/samd/samd_flash.c @@ -103,7 +103,8 @@ static mp_obj_t samd_flash_version(void) { static MP_DEFINE_CONST_FUN_OBJ_0(samd_flash_version_obj, samd_flash_version); static mp_obj_t samd_flash_readblocks(size_t n_args, const mp_obj_t *args) { - uint32_t offset = (mp_obj_get_int(args[1]) * BLOCK_SIZE) + samd_flash_obj.flash_base; + samd_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t offset = (mp_obj_get_int(args[1]) * BLOCK_SIZE) + self->flash_base; mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE); if (n_args == 4) { @@ -118,7 +119,8 @@ static mp_obj_t samd_flash_readblocks(size_t n_args, const mp_obj_t *args) { static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(samd_flash_readblocks_obj, 3, 4, samd_flash_readblocks); static mp_obj_t samd_flash_writeblocks(size_t n_args, const mp_obj_t *args) { - uint32_t offset = (mp_obj_get_int(args[1]) * BLOCK_SIZE) + samd_flash_obj.flash_base; + samd_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t offset = (mp_obj_get_int(args[1]) * BLOCK_SIZE) + self->flash_base; mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); if (n_args == 3) { diff --git a/ports/samd/samd_spiflash.c b/ports/samd/samd_spiflash.c index cd4f10640dbf..8ada7e96092c 100644 --- a/ports/samd/samd_spiflash.c +++ b/ports/samd/samd_spiflash.c @@ -45,6 +45,7 @@ const uint8_t _COMMANDS_32BIT[] = {0x13, 0x12, 0x21}; // READ, PROGRAM_PAGE, ER #define COMMAND_JEDEC_ID (0x9F) #define COMMAND_READ_STATUS (0x05) +#define COMMAND_WRITE_SR1 (0x01) #define COMMAND_WRITE_ENABLE (0x06) #define COMMAND_READ_SFDP (0x5A) #define PAGE_SIZE (256) @@ -88,7 +89,7 @@ static void wait(spiflash_obj_t *self) { mp_hal_pin_write(self->cs, 0); spi_transfer((mp_obj_base_t *)self->spi, 2, msg, msg); mp_hal_pin_write(self->cs, 1); - } while (msg[1] != 0 && timeout-- > 0); + } while ((msg[1] & 1) != 0 && timeout-- > 0); } static void get_id(spiflash_obj_t *self, uint8_t id[3]) { @@ -123,6 +124,17 @@ static void write_enable(spiflash_obj_t *self) { mp_hal_pin_write(self->cs, 1); } +// Write status register 1 +static void write_sr1(spiflash_obj_t *self, uint8_t value) { + uint8_t msg[2]; + msg[0] = COMMAND_WRITE_SR1; + msg[1] = value; + + mp_hal_pin_write(self->cs, 0); + spi_transfer(self->spi, 2, msg, NULL); + mp_hal_pin_write(self->cs, 1); +} + static void get_sfdp(spiflash_obj_t *self, uint32_t addr, uint8_t *buffer, int size) { uint8_t dummy[1]; write_addr(self, COMMAND_READ_SFDP, addr); @@ -155,27 +167,36 @@ static mp_obj_t spiflash_make_new(const mp_obj_type_t *type, size_t n_args, size mp_hal_pin_write(self->cs, 1); wait(self); - // Get the flash size from the device ID (default) uint8_t id[3]; get_id(self, id); + bool read_sfdp = true; + if (id[1] == 0x84 && id[2] == 1) { // Adesto self->size = 512 * 1024; - } else if (id[1] == 0x1f && id[2] == 1) { // Atmel / Renesas + } else if (id[0] == 0x1f && id[1] == 0x45 && id[2] == 1) { // Adesto/Renesas 8 MBit self->size = 1024 * 1024; + read_sfdp = false; + self->sectorsize = 4096; + self->addr_is_32bit = false; + // Globally unlock the sectors, which are locked after power on. + write_enable(self); + write_sr1(self, 0); } else { self->size = 1 << id[2]; } // Get the addr_is_32bit flag and the sector size - uint8_t buffer[128]; - get_sfdp(self, 0, buffer, 16); // get the header - int len = MIN(buffer[11] * 4, sizeof(buffer)); - if (len >= 29) { - int addr = buffer[12] + (buffer[13] << 8) + (buffer[14] << 16); - get_sfdp(self, addr, buffer, len); // Get the JEDEC mandatory table - self->sectorsize = 1 << buffer[28]; - self->addr_is_32bit = ((buffer[2] >> 1) & 0x03) != 0; + if (read_sfdp) { + uint8_t buffer[128]; + get_sfdp(self, 0, buffer, 16); // get the header + int len = MIN(buffer[11] * 4, sizeof(buffer)); + if (len >= 29) { + int addr = buffer[12] + (buffer[13] << 8) + (buffer[14] << 16); + get_sfdp(self, addr, buffer, len); // Get the JEDEC mandatory table + self->sectorsize = 1 << buffer[28]; + self->addr_is_32bit = ((buffer[2] >> 1) & 0x03) != 0; + } } self->commands = self->addr_is_32bit ? _COMMANDS_32BIT : _COMMANDS_24BIT; diff --git a/py/objfloat.c b/py/objfloat.c index 5c90b1491ceb..0728fce3151f 100644 --- a/py/objfloat.c +++ b/py/objfloat.c @@ -47,6 +47,13 @@ #define M_PI (3.14159265358979323846) #endif +// Workaround a bug in recent MSVC where NAN is no longer constant. +// (By redefining back to the previous MSVC definition of NAN) +#if defined(_MSC_VER) && _MSC_VER >= 1942 +#undef NAN +#define NAN (-(float)(((float)(1e+300 * 1e+300)) * 0.0F)) +#endif + typedef struct _mp_obj_float_t { mp_obj_base_t base; mp_float_t value; diff --git a/py/py.mk b/py/py.mk index 2e7ffe9905e0..9592fbb9170e 100644 --- a/py/py.mk +++ b/py/py.mk @@ -33,6 +33,9 @@ ifneq ($(USER_C_MODULES),) # pre-define USERMOD variables as expanded so that variables are immediate # expanded as they're added to them +# Confirm the provided path exists, show abspath if not to make it clearer to fix. +$(if $(wildcard $(USER_C_MODULES)/.),,$(error USER_C_MODULES doesn't exist: $(abspath $(USER_C_MODULES)))) + # C/C++ files that are included in the QSTR/module build SRC_USERMOD_C := SRC_USERMOD_CXX := diff --git a/py/usermod.cmake b/py/usermod.cmake index c814369a4865..4a8b99ff31b4 100644 --- a/py/usermod.cmake +++ b/py/usermod.cmake @@ -42,6 +42,16 @@ endfunction() # Include CMake files for user modules. if (USER_C_MODULES) foreach(USER_C_MODULE_PATH ${USER_C_MODULES}) + # If a directory is given, append the micropython.cmake to it. + if (IS_DIRECTORY ${USER_C_MODULE_PATH}) + set(USER_C_MODULE_PATH "${USER_C_MODULE_PATH}/micropython.cmake") + endif() + # Confirm the provided path exists, show abspath if not to make it clearer to fix. + if (NOT EXISTS ${USER_C_MODULE_PATH}) + get_filename_component(USER_C_MODULES_ABS "${USER_C_MODULE_PATH}" ABSOLUTE) + message(FATAL_ERROR "USER_C_MODULES doesn't exist: ${USER_C_MODULES_ABS}") + endif() + message("Including User C Module(s) from ${USER_C_MODULE_PATH}") include(${USER_C_MODULE_PATH}) endforeach() diff --git a/tests/cpydiff/modules_json_nonserializable.py b/tests/cpydiff/modules_json_nonserializable.py index ffe523786f50..d6a5660cadfd 100644 --- a/tests/cpydiff/modules_json_nonserializable.py +++ b/tests/cpydiff/modules_json_nonserializable.py @@ -6,10 +6,7 @@ """ import json -a = bytes(x for x in range(256)) try: - z = json.dumps(a) - x = json.loads(z) - print("Should not get here") + print(json.dumps(b"shouldn't be able to serialise bytes")) except TypeError: print("TypeError") diff --git a/tests/extmod/asyncio_new_event_loop.py b/tests/extmod/asyncio_new_event_loop.py index bebc3bf70cc5..3f05ffdd551d 100644 --- a/tests/extmod/asyncio_new_event_loop.py +++ b/tests/extmod/asyncio_new_event_loop.py @@ -12,6 +12,16 @@ asyncio.set_event_loop(asyncio.new_event_loop()) +def exception_handler(loop, context): + # This is a workaround for a difference between CPython and MicroPython: if + # a CPython event loop is closed while there are tasks pending (i.e. not finished) + # on it, then the task will log an error. MicroPython does not log this error. + if context.get("message", "") == "Task was destroyed but it is pending!": + pass + else: + loop.default_exception_handler(context) + + async def task(): for i in range(4): print("task", i) @@ -22,17 +32,21 @@ async def task(): async def main(): print("start") loop.create_task(task()) - await asyncio.sleep(0) + await asyncio.sleep(0) # yields, meaning new task will run once print("stop") loop.stop() # Use default event loop to run some tasks loop = asyncio.get_event_loop() +loop.set_exception_handler(exception_handler) loop.create_task(main()) loop.run_forever() +loop.close() # Create new event loop, old one should not keep running loop = asyncio.new_event_loop() +loop.set_exception_handler(exception_handler) loop.create_task(main()) loop.run_forever() +loop.close() diff --git a/tests/extmod/framebuf_ellipse.py b/tests/extmod/framebuf_ellipse.py index a4c784aff876..ec0461e66ca0 100644 --- a/tests/extmod/framebuf_ellipse.py +++ b/tests/extmod/framebuf_ellipse.py @@ -63,3 +63,18 @@ def printbuf(): fbuf.fill(0) fbuf.ellipse(x, y, 6, 12, 0xAA, True) printbuf() + +# Draw an ellipse with both radius 0 +fbuf.fill(0) +fbuf.ellipse(15, 15, 0, 0, 0xFF, True) +printbuf() + +# Draw an ellipse with both radius 0 out of bounds +fbuf.fill(0) +fbuf.ellipse(45, 45, 0, 0, 0xFF, True) +printbuf() + +# Draw an ellipse with radius 0 and all sectors masked out +fbuf.fill(0) +fbuf.ellipse(15, 15, 0, 0, 0xFF, True, 0) +printbuf() diff --git a/tests/extmod/framebuf_ellipse.py.exp b/tests/extmod/framebuf_ellipse.py.exp index ae6ad1ee7e48..96a60c24da1f 100644 --- a/tests/extmod/framebuf_ellipse.py.exp +++ b/tests/extmod/framebuf_ellipse.py.exp @@ -702,3 +702,99 @@ aaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000 aaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000 aaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000 -->8-- +--8<-- +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000ff0000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +-->8-- +--8<-- +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +-->8-- +--8<-- +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +-->8-- diff --git a/tests/misc/sys_settrace_features.py b/tests/misc/sys_settrace_features.py index 8ca6b382e3c6..6eeb2b900f18 100644 --- a/tests/misc/sys_settrace_features.py +++ b/tests/misc/sys_settrace_features.py @@ -6,6 +6,10 @@ print("SKIP") raise SystemExit +if sys.version.startswith("3.12"): + # There is a CPython change in settrace that is reverted in 3.13! + print("WARNING: this test will fail when compared to CPython 3.12.x behaviour") + def print_stacktrace(frame, level=0): # Ignore CPython specific helpers. diff --git a/tests/multi_espnow/10_simple_data.py b/tests/multi_espnow/10_simple_data.py index 1d218fe98130..00d69f30d915 100644 --- a/tests/multi_espnow/10_simple_data.py +++ b/tests/multi_espnow/10_simple_data.py @@ -14,7 +14,7 @@ def init(sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e = espnow.ESPNow() e.active(True) e.set_pmk(default_pmk) diff --git a/tests/multi_espnow/20_send_echo.py b/tests/multi_espnow/20_send_echo.py index 4a1d1624d843..4c325bf68c51 100644 --- a/tests/multi_espnow/20_send_echo.py +++ b/tests/multi_espnow/20_send_echo.py @@ -61,7 +61,7 @@ def echo_client(e, peer, msglens): def init(sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e = espnow.ESPNow() e.active(True) e.set_pmk(default_pmk) diff --git a/tests/multi_espnow/30_lmk_echo.py b/tests/multi_espnow/30_lmk_echo.py index ac8908049250..2a6c77c6331d 100644 --- a/tests/multi_espnow/30_lmk_echo.py +++ b/tests/multi_espnow/30_lmk_echo.py @@ -92,7 +92,7 @@ def echo_client(e, peer, msglens): # Initialise the wifi and espnow hardware and software def init(sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e = espnow.ESPNow() e.active(True) e.set_pmk(default_pmk) diff --git a/tests/multi_espnow/40_recv_test.py b/tests/multi_espnow/40_recv_test.py index 46f4f78df489..554db16ac171 100644 --- a/tests/multi_espnow/40_recv_test.py +++ b/tests/multi_espnow/40_recv_test.py @@ -48,7 +48,7 @@ def client_send(e, peer, msg, sync): def init(sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e = espnow.ESPNow() e.active(True) e.set_pmk(default_pmk) diff --git a/tests/multi_espnow/50_esp32_rssi_test.py b/tests/multi_espnow/50_esp32_rssi_test.py index 6a47b540d35e..8aded1c09940 100644 --- a/tests/multi_espnow/50_esp32_rssi_test.py +++ b/tests/multi_espnow/50_esp32_rssi_test.py @@ -49,7 +49,7 @@ def client_send(e, peer, msg, sync): def init(sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e = espnow.ESPNow() e.active(True) e.set_pmk(default_pmk) diff --git a/tests/multi_espnow/60_irq_test.py b/tests/multi_espnow/60_irq_test.py index 37fc57ce4ae7..db8b8168690e 100644 --- a/tests/multi_espnow/60_irq_test.py +++ b/tests/multi_espnow/60_irq_test.py @@ -49,7 +49,7 @@ def client_send(e, peer, msg, sync): def init(sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e = espnow.ESPNow() e.active(True) e.set_pmk(default_pmk) diff --git a/tests/multi_espnow/70_channel.py b/tests/multi_espnow/70_channel.py new file mode 100644 index 000000000000..f3e8b947b123 --- /dev/null +++ b/tests/multi_espnow/70_channel.py @@ -0,0 +1,89 @@ +# Test that ESP-NOW picks up the channel configuration for STA +# mode on ESP32. +# +# Note that setting the channel on a peer in ESP-NOW on modern ESP-IDF only +# checks it against the configured channel, it doesn't ever change the radio +# channel +import sys +import time + +try: + import network + import espnow +except ImportError: + print("SKIP") + raise SystemExit + +# ESP8266 doesn't support config('channel') on the STA interface, +# and the channel parameter to add_peer doesn't appear to set the +# channel either. +if sys.platform == "esp8266": + print("SKIP") + raise SystemExit + + +timeout_ms = 1000 +default_pmk = b"MicroPyth0nRules" + +CHANNEL = 3 +WRONG_CHANNEL = 8 + + +def init_sta(): + sta = network.WLAN(network.WLAN.IF_STA) + e = espnow.ESPNow() + e.active(True) + sta.active(True) + sta.disconnect() # Force AP disconnect for any saved config, important so the channel doesn't change + sta.config(channel=CHANNEL) + e.set_pmk(default_pmk) + return sta, e + + +# Receiver +def instance0(): + sta, e = init_sta() + multitest.globals(PEER=sta.config("mac")) + multitest.next() + print(sta.config("channel")) + while True: + peer, msg = e.recv(timeout_ms) + if peer is None: + print("Timeout") + break + print(msg) + e.active(False) + + +# Sender +def instance1(): + sta, e = init_sta() + multitest.next() + peer = PEER + + # both instances set channel via sta.config(), above + msg = b"sent to right channel 1" + e.add_peer(peer, channel=CHANNEL) + for _ in range(3): + e.send(peer, msg) + e.del_peer(peer) + print(sta.config("channel")) + + sta.config(channel=WRONG_CHANNEL) + msg = b"sent to wrong channel" + e.add_peer(peer, channel=WRONG_CHANNEL) + for _ in range(3): + e.send(peer, msg) + e.del_peer(peer) + print(sta.config("channel")) + + # switching back to the correct channel should also work + sta.config(channel=CHANNEL) + msg = b"sent to right channel 2" + e.add_peer(peer, channel=CHANNEL) + for _ in range(3): + e.send(peer, msg) + e.del_peer(peer) + print(sta.config("channel")) + + e.active(False) diff --git a/tests/multi_espnow/70_channel.py.exp b/tests/multi_espnow/70_channel.py.exp new file mode 100644 index 000000000000..d8553966032c --- /dev/null +++ b/tests/multi_espnow/70_channel.py.exp @@ -0,0 +1,13 @@ +--- instance0 --- +3 +b'sent to right channel 1' +b'sent to right channel 1' +b'sent to right channel 1' +b'sent to right channel 2' +b'sent to right channel 2' +b'sent to right channel 2' +Timeout +--- instance1 --- +3 +8 +3 diff --git a/tests/multi_espnow/80_asyncio_client.py b/tests/multi_espnow/80_asyncio_client.py index 4d58a451c1c0..8c34b98a3c4a 100644 --- a/tests/multi_espnow/80_asyncio_client.py +++ b/tests/multi_espnow/80_asyncio_client.py @@ -50,7 +50,7 @@ def client_send(e, peer, msg, sync): def init(e, sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e.active(True) e.set_pmk(default_pmk) wlans[0].active(sta_active) diff --git a/tests/multi_espnow/81_asyncio_server.py b/tests/multi_espnow/81_asyncio_server.py index df1dbb2ac8aa..e6002582c085 100644 --- a/tests/multi_espnow/81_asyncio_server.py +++ b/tests/multi_espnow/81_asyncio_server.py @@ -30,7 +30,7 @@ def client_send(e, peer, msg, sync): def init(e, sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e.active(True) e.set_pmk(default_pmk) wlans[0].active(sta_active) diff --git a/tests/multi_espnow/90_memory_test.py b/tests/multi_espnow/90_memory_test.py index 5e80eb0fdf66..b59ff61b594a 100644 --- a/tests/multi_espnow/90_memory_test.py +++ b/tests/multi_espnow/90_memory_test.py @@ -65,7 +65,7 @@ def echo_client(e, peer, msglens): def init(sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e = espnow.ESPNow() e.active(True) e.set_pmk(default_pmk) diff --git a/tests/multi_wlan/01_ap_sta.py b/tests/multi_wlan/01_ap_sta.py new file mode 100644 index 000000000000..1a8e80cd33aa --- /dev/null +++ b/tests/multi_wlan/01_ap_sta.py @@ -0,0 +1,117 @@ +# Basic Wi-Fi MAC layer test where one device creates an Access Point and the +# other device connects to it as a Station. Also tests channel assignment (where +# possible) and disconnection. + +try: + import network + + network.WLAN +except (ImportError, NameError): + print("SKIP") + raise SystemExit + +import os +import sys +import time + +CHANNEL = 8 + +# Note that on slower Wi-Fi stacks this bumps up against the run-multitests.py +# timeout which expects <10s between lines of output. We work around this by +# logging something half way through the wait_for loop... +CONNECT_TIMEOUT = 15000 + + +def wait_for(test_func): + has_printed = False + start = time.ticks_ms() + while not test_func(): + time.sleep(0.1) + delta = time.ticks_diff(time.ticks_ms(), start) + if not has_printed and delta > CONNECT_TIMEOUT / 2: + print("...") + has_printed = True + elif delta > CONNECT_TIMEOUT: + break + + if not has_printed: + print("...") # keep the output consistent + + return test_func() + + +# AP +def instance0(): + ap = network.WLAN(network.WLAN.IF_AP) + ssid = "MP-test-" + os.urandom(6).hex() + psk = "Secret-" + os.urandom(6).hex() + + # stop any previous activity + network.WLAN(network.WLAN.IF_STA).active(False) + ap.active(False) + + ap.active(True) + ap.config(ssid=ssid, key=psk, channel=CHANNEL, security=network.WLAN.SEC_WPA_WPA2) + + # print("AP setup", ssid, psk) + print("AP started") + + multitest.globals(SSID=ssid, PSK=psk) + multitest.next() + + # Wait for station + if not wait_for(ap.isconnected): + raise RuntimeError("Timed out waiting for station, status ", ap.status()) + + print("AP got station") + time.sleep( + 3 + ) # depending on port, may still need to negotiate DHCP lease for STA to see connection + + print("AP disabling...") + ap.active(False) + + +# STA +def instance1(): + sta = network.WLAN(network.WLAN.IF_STA) + + # stop any previous activity + network.WLAN(network.WLAN.IF_AP).active(False) + sta.active(False) + + multitest.next() + ssid = SSID + psk = PSK + + # print("STA setup", ssid, psk) + + sta.active(True) + sta.connect(ssid, psk) + + print("STA connecting...") + + if not wait_for(sta.isconnected): + raise RuntimeError("Timed out waiting to connect, status ", sta.status()) + + print("STA connected") + + # Print the current channel, if the port support this + try: + print("channel", sta.config("channel")) + except OSError as e: + if "AP" in str(e): + # ESP8266 only supports reading channel on the AP interface, so fake this result + print("channel", CHANNEL) + else: + raise + + print("STA waiting for disconnect...") + + # Expect the AP to disconnect us immediately + if not wait_for(lambda: not sta.isconnected()): + raise RuntimeError("Timed out waiting for AP to disconnect us, status ", sta.status()) + + print("STA disconnected") + + sta.active(False) diff --git a/tests/multi_wlan/01_ap_sta.py.exp b/tests/multi_wlan/01_ap_sta.py.exp new file mode 100644 index 000000000000..8fc023a39805 --- /dev/null +++ b/tests/multi_wlan/01_ap_sta.py.exp @@ -0,0 +1,13 @@ +--- instance0 --- +AP started +... +AP got station +AP disabling... +--- instance1 --- +STA connecting... +... +STA connected +channel 8 +STA waiting for disconnect... +... +STA disconnected diff --git a/tests/net_hosted/connect_nonblock_xfer.py b/tests/net_hosted/connect_nonblock_xfer.py index dc4693cea6a8..23620908afbc 100644 --- a/tests/net_hosted/connect_nonblock_xfer.py +++ b/tests/net_hosted/connect_nonblock_xfer.py @@ -1,15 +1,24 @@ # test that socket.connect() on a non-blocking socket raises EINPROGRESS # and that an immediate write/send/read/recv does the right thing -import sys, time, socket, errno, ssl +import errno +import select +import socket +import ssl -isMP = sys.implementation.name == "micropython" +# only mbedTLS supports non-blocking mode +if not hasattr(ssl, "MBEDTLS_VERSION"): + print("SKIP") + raise SystemExit -def dp(e): - # uncomment next line for development and testing, to print the actual exceptions - # print(repr(e)) - pass +# get the name of an errno error code +def errno_name(er): + if er == errno.EAGAIN: + return "EAGAIN" + if er == errno.EINPROGRESS: + return "EINPROGRESS" + return er # do_connect establishes the socket and wraps it if tls is True. @@ -22,112 +31,75 @@ def do_connect(peer_addr, tls, handshake): # print("Connecting to", peer_addr) s.connect(peer_addr) except OSError as er: - print("connect:", er.errno == errno.EINPROGRESS) - if er.errno != errno.EINPROGRESS: - print(" got", er.errno) + print("connect:", errno_name(er.errno)) # wrap with ssl/tls if desired if tls: ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) - if hasattr(ssl_context, "check_hostname"): - ssl_context.check_hostname = False - try: s = ssl_context.wrap_socket(s, do_handshake_on_connect=handshake) - print("wrap: True") + print("wrap ok: True") except Exception as e: - dp(e) - print("wrap:", e) - elif handshake: - # just sleep a little bit, this allows any connect() errors to happen - time.sleep(0.2) + print("wrap er:", e) return s +# poll a socket and print out the result +def poll(s): + poller = select.poll() + poller.register(s) + print("poll: ", poller.poll(0)) + + # test runs the test against a specific peer address. -def test(peer_addr, tls=False, handshake=False): - # MicroPython plain sockets have read/write, but CPython's don't - # MicroPython TLS sockets and CPython's have read/write - # hasRW captures this wonderful state of affairs - hasRW = isMP or tls +def test(peer_addr, tls, handshake): + # MicroPython plain and TLS sockets have read/write + hasRW = True - # MicroPython plain sockets and CPython's have send/recv - # MicroPython TLS sockets don't have send/recv, but CPython's do - # hasSR captures this wonderful state of affairs - hasSR = not (isMP and tls) + # MicroPython plain sockets have send/recv + # MicroPython TLS sockets don't have send/recv + hasSR = not tls # connect + send + # non-blocking send should raise EAGAIN if hasSR: s = do_connect(peer_addr, tls, handshake) - # send -> 4 or EAGAIN + poll(s) try: ret = s.send(b"1234") - print("send:", handshake and ret == 4) + print("send ok:", ret) # shouldn't get here except OSError as er: - # - dp(er) - print("send:", er.errno in (errno.EAGAIN, errno.EINPROGRESS)) + print("send er:", errno_name(er.errno)) s.close() - else: # fake it... - print("connect:", True) - if tls: - print("wrap:", True) - print("send:", True) # connect + write + # non-blocking write should return None if hasRW: s = do_connect(peer_addr, tls, handshake) - # write -> None - try: - ret = s.write(b"1234") - print("write:", ret in (4, None)) # SSL may accept 4 into buffer - except OSError as er: - dp(er) - print("write:", False) # should not raise - except ValueError as er: # CPython - dp(er) - print("write:", er.args[0] == "Write on closed or unwrapped SSL socket.") + poll(s) + ret = s.write(b"1234") + print("write: ", ret) s.close() - else: # fake it... - print("connect:", True) - if tls: - print("wrap:", True) - print("write:", True) + # connect + recv + # non-blocking recv should raise EAGAIN if hasSR: - # connect + recv s = do_connect(peer_addr, tls, handshake) - # recv -> EAGAIN + poll(s) try: - print("recv:", s.recv(10)) + ret = s.recv(10) + print("recv ok:", ret) # shouldn't get here except OSError as er: - dp(er) - print("recv:", er.errno == errno.EAGAIN) + print("recv er:", errno_name(er.errno)) s.close() - else: # fake it... - print("connect:", True) - if tls: - print("wrap:", True) - print("recv:", True) # connect + read + # non-blocking read should return None if hasRW: s = do_connect(peer_addr, tls, handshake) - # read -> None - try: - ret = s.read(10) - print("read:", ret is None) - except OSError as er: - dp(er) - print("read:", False) # should not raise - except ValueError as er: # CPython - dp(er) - print("read:", er.args[0] == "Read on closed or unwrapped SSL socket.") + poll(s) + ret = s.read(10) + print("read: ", ret) s.close() - else: # fake it... - print("connect:", True) - if tls: - print("wrap:", True) - print("read:", True) if __name__ == "__main__": @@ -136,10 +108,8 @@ def test(peer_addr, tls=False, handshake=False): print("--- Plain sockets to nowhere ---") test(socket.getaddrinfo("192.0.2.1", 80)[0][-1], False, False) print("--- SSL sockets to nowhere ---") - # this test fails with AXTLS because do_handshake=False blocks on first read/write and - # there it times out until the connect is aborted test(socket.getaddrinfo("192.0.2.1", 443)[0][-1], True, False) print("--- Plain sockets ---") - test(socket.getaddrinfo("micropython.org", 80)[0][-1], False, True) + test(socket.getaddrinfo("micropython.org", 80)[0][-1], False, False) print("--- SSL sockets ---") test(socket.getaddrinfo("micropython.org", 443)[0][-1], True, True) diff --git a/tests/net_hosted/connect_nonblock_xfer.py.exp b/tests/net_hosted/connect_nonblock_xfer.py.exp new file mode 100644 index 000000000000..c5498a03873a --- /dev/null +++ b/tests/net_hosted/connect_nonblock_xfer.py.exp @@ -0,0 +1,44 @@ +--- Plain sockets to nowhere --- +connect: EINPROGRESS +poll: [] +send er: EAGAIN +connect: EINPROGRESS +poll: [] +write: None +connect: EINPROGRESS +poll: [] +recv er: EAGAIN +connect: EINPROGRESS +poll: [] +read: None +--- SSL sockets to nowhere --- +connect: EINPROGRESS +wrap ok: True +poll: [] +write: None +connect: EINPROGRESS +wrap ok: True +poll: [] +read: None +--- Plain sockets --- +connect: EINPROGRESS +poll: [] +send er: EAGAIN +connect: EINPROGRESS +poll: [] +write: None +connect: EINPROGRESS +poll: [] +recv er: EAGAIN +connect: EINPROGRESS +poll: [] +read: None +--- SSL sockets --- +connect: EINPROGRESS +wrap ok: True +poll: [(, 4)] +write: 4 +connect: EINPROGRESS +wrap ok: True +poll: [(, 4)] +read: None diff --git a/tests/run-multitests.py b/tests/run-multitests.py index 93a6d3844d23..387eec7018bb 100755 --- a/tests/run-multitests.py +++ b/tests/run-multitests.py @@ -105,15 +105,14 @@ def output_metric(data): multitest.flush() """ -# The btstack implementation on Unix generates some spurious output that we -# can't control. Also other platforms may output certain warnings/errors that -# can be safely ignored. +# Some ports generate output we can't control, and that can be safely ignored. IGNORE_OUTPUT_MATCHES = ( - "libusb: error ", # It tries to open devices that it doesn't have access to (libusb prints unconditionally). + "libusb: error ", # unix btstack tries to open devices that it doesn't have access to (libusb prints unconditionally). "hci_transport_h2_libusb.c", # Same issue. We enable LOG_ERROR in btstack. - "USB Path: ", # Hardcoded in btstack's libusb transport. - "hci_number_completed_packet", # Warning from btstack. + "USB Path: ", # Hardcoded in unix btstack's libusb transport. + "hci_number_completed_packet", # Warning from unix btstack. "lld_pdu_get_tx_flush_nb HCI packet count mismatch (", # From ESP-IDF, see https://github.com/espressif/esp-idf/issues/5105 + " ets_task(", # ESP8266 port debug output ) diff --git a/tools/mpremote/mpremote/commands.py b/tools/mpremote/mpremote/commands.py index 2b1acea43808..f86befd08034 100644 --- a/tools/mpremote/mpremote/commands.py +++ b/tools/mpremote/mpremote/commands.py @@ -129,8 +129,15 @@ def _remote_path_basename(a): def do_filesystem_cp(state, src, dest, multiple, check_hash=False): if dest.startswith(":"): - dest_exists = state.transport.fs_exists(dest[1:]) - dest_isdir = dest_exists and state.transport.fs_isdir(dest[1:]) + dest_no_slash = dest.rstrip("/" + os.path.sep + (os.path.altsep or "")) + dest_exists = state.transport.fs_exists(dest_no_slash[1:]) + dest_isdir = dest_exists and state.transport.fs_isdir(dest_no_slash[1:]) + + # A trailing / on dest forces it to be a directory. + if dest != dest_no_slash: + if not dest_isdir: + raise CommandError("cp: destination is not a directory") + dest = dest_no_slash else: dest_exists = os.path.exists(dest) dest_isdir = dest_exists and os.path.isdir(dest) diff --git a/tools/mpremote/mpremote/main.py b/tools/mpremote/mpremote/main.py index 8c0a6cd224c3..e6e397020fe8 100644 --- a/tools/mpremote/mpremote/main.py +++ b/tools/mpremote/mpremote/main.py @@ -547,7 +547,10 @@ def main(): return 0 except CommandError as e: + # Make sure existing stdout appears before the error message on stderr. + sys.stdout.flush() print(f"{_PROG}: {e}", file=sys.stderr) + sys.stderr.flush() return 1 finally: do_disconnect(state) diff --git a/tools/mpremote/tests/run-mpremote-tests.sh b/tools/mpremote/tests/run-mpremote-tests.sh index 11d82c9bb380..b25b5fd7d296 100755 --- a/tools/mpremote/tests/run-mpremote-tests.sh +++ b/tools/mpremote/tests/run-mpremote-tests.sh @@ -16,7 +16,7 @@ for t in $TESTS; do TMP=$(mktemp -d) echo -n "${t}: " # Strip CR and replace the random temp dir with a token. - if env MPREMOTE=${MPREMOTE} TMP="${TMP}" "${t}" | tr -d '\r' | sed "s,${TMP},"'${TMP},g' > "${t}.out"; then + if env MPREMOTE=${MPREMOTE} TMP="${TMP}" "${t}" 2>&1 | tr -d '\r' | sed "s,${TMP},"'${TMP},g' > "${t}.out"; then if diff "${t}.out" "${t}.exp" > /dev/null; then echo "OK" else diff --git a/tools/mpremote/tests/test_filesystem.sh b/tools/mpremote/tests/test_filesystem.sh index b6d5a7febcb8..afeb7c91da8d 100755 --- a/tools/mpremote/tests/test_filesystem.sh +++ b/tools/mpremote/tests/test_filesystem.sh @@ -66,6 +66,16 @@ $MPREMOTE resume cp "${TMP}/a.py" :aaa $MPREMOTE resume cp "${TMP}/a.py" :bbb/b.py $MPREMOTE resume cat :aaa/a.py bbb/b.py +# Test cp -f (force copy). +echo ----- +$MPREMOTE resume cp -f "${TMP}/a.py" :aaa +$MPREMOTE resume cat :aaa/a.py + +# Test cp where the destination has a trailing /. +echo ----- +$MPREMOTE resume cp "${TMP}/a.py" :aaa/ +$MPREMOTE resume cp "${TMP}/a.py" :aaa/a.py/ || echo "expect error" + echo ----- $MPREMOTE resume rm :b.py c.py $MPREMOTE resume ls diff --git a/tools/mpremote/tests/test_filesystem.sh.exp b/tools/mpremote/tests/test_filesystem.sh.exp index 7220dc41e472..82fe7d6bf78c 100644 --- a/tools/mpremote/tests/test_filesystem.sh.exp +++ b/tools/mpremote/tests/test_filesystem.sh.exp @@ -38,6 +38,16 @@ print("World") print("Hello") print("World") ----- +cp ${TMP}/a.py :aaa +print("Hello") +print("World") +----- +cp ${TMP}/a.py :aaa/ +Up to date: aaa/a.py +cp ${TMP}/a.py :aaa/a.py/ +mpremote: cp: destination is not a directory +expect error +----- rm :b.py rm :c.py ls :