Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ESP32 to supported devices with basic type hints #114

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.jetbrains.micropython.devices

import com.intellij.execution.configurations.CommandLineState
import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.execution.process.OSProcessHandler
import com.intellij.execution.runners.ExecutionEnvironment
import com.intellij.openapi.projectRoots.Sdk
import com.jetbrains.micropython.run.MicroPythonRunConfiguration
import com.jetbrains.micropython.run.getMicroUploadCommand
import com.jetbrains.micropython.settings.MicroPythonTypeHints
import com.jetbrains.micropython.settings.MicroPythonUsbId
import com.jetbrains.python.packaging.PyPackageManager
import com.jetbrains.python.packaging.PyRequirement

/**
* @author Gor-Ren
*/
class Esp32DeviceProvider : MicroPythonDeviceProvider {
override val persistentName: String
get() = "ESP32"

override val documentationURL: String
get() = "https://github.com/vlasovskikh/intellij-micropython/wiki/ESP32"

override val usbIds: List<MicroPythonUsbId>
get() = listOf(MicroPythonUsbId(0x1A86, 0x7523),
MicroPythonUsbId(0x10C4, 0xEA60),
MicroPythonUsbId(0x0403, 0x6001))
Comment on lines +25 to +28
Copy link
Author

@Gor-Ren Gor-Ren Jul 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are copied from the ESP8266.

To find the values for an ESP32, I connected MCUs by USB and ran $ cat /sys/bus/usb-serial/devices/ttyUSB0/../uevent. I tested with an ESP8266 and an ESP32 and both output PRODUCT=10c4/ea60/100. I was a bit surprised - I expected them to be different.

PRODUCT=10c4/ea60/100 corresponds to the second element in this list. Auto-detect of my ESP32 subsequently worked. Shall we keep all the values copied from ESP8266 the same, then?


override val typeHints: MicroPythonTypeHints by lazy {
MicroPythonTypeHints(listOf("stdlib", "micropython", "esp32"))
}

override fun getPackageRequirements(sdk: Sdk): List<PyRequirement> {
val manager = PyPackageManager.getInstance(sdk)
return manager.parseRequirements("""|pyserial>=3.3,<4.0
|docopt>=0.6.2,<0.7
|adafruit-ampy>=1.0.5,<1.1""".trimMargin())
}

override fun getRunCommandLineState(configuration: MicroPythonRunConfiguration,
environment: ExecutionEnvironment): CommandLineState? {
val module = configuration.module ?: return null
val command = getMicroUploadCommand(configuration.path, module) ?: return null

return object : CommandLineState(environment) {
override fun startProcess() =
OSProcessHandler(GeneralCommandLine(command))
}
}
}
1 change: 1 addition & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
<extensions defaultExtensionNs="com.jetbrains.micropython">
<deviceProvider implementation="com.jetbrains.micropython.devices.MicroBitDeviceProvider"/>
<deviceProvider implementation="com.jetbrains.micropython.devices.Esp8266DeviceProvider"/>
<deviceProvider implementation="com.jetbrains.micropython.devices.Esp32DeviceProvider"/>
<deviceProvider implementation="com.jetbrains.micropython.devices.PyboardDeviceProvider"/>
</extensions>

Expand Down
70 changes: 70 additions & 0 deletions typehints/esp32/esp.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@

from typing import Optional

def sleep_type(sleep_type: Optional[int]) -> Optional[int]:
"""
Get or set the sleep type.

If the sleep_type parameter is provided, sets the sleep type to its value.
If the function is called without parameters, returns the current sleep type.

The possible sleep types are defined as constants:

* ``SLEEP_NONE`` – all functions enabled,\n
* ``SLEEP_MODEM`` – modem sleep, shuts down the WiFi Modem circuit.\n
* ``SLEEP_LIGHT`` – light sleep, shuts down the WiFi Modem circuit and suspends the processor periodically.\n
The system enters the set sleep mode automatically when possible.

:param sleep_type: Sleep type.
:type sleep_type: int
:return: Current sleep type
:rtype: int
"""

def deepsleep(time: int = 0) -> None:
"""
Enter deep sleep.

The whole module powers down, except for the RTC clock circuit, which can
be used to restart the module after the specified time if the pin 16 is
connected to the reset pin. Otherwise the module will sleep until manually reset.

:param time: Amount of time in milliseconds to sleep.
"""

def set_native_code_location(start: Optional[int], length: Optional[int]) -> None:
"""
Set the location that native code will be placed for execution after it is
compiled. Native code is emitted when the ``@micropython.native``, ``@micropython.viper``
and ``@micropython.asm_xtensa`` decorators are applied to a function. The
ESP8266 must execute code from either iRAM or the lower 1MByte of flash

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a missed comment update. Still referring to ESP8266. Not ESP32.

(which is memory mapped), and this function controls the location.

If start and length are both **None** then the native code location is
set to the unused portion of memory at the end of the iRAM1 region. The
size of this unused portion depends on the firmware and is typically
quite small (around 500 bytes), and is enough to store a few very small
functions. The advantage of using this iRAM1 region is that it does not
get worn out by writing to it.

If neither start nor length are None then they should be integers. start
should specify the byte offset from the beginning of the flash at which
native code should be stored. length specifies how many bytes of flash
from start can be used to store native code. start and length should be
multiples of the sector size (being 4096 bytes). The flash will be
automatically erased before writing to it so be sure to use a region of
flash that is not otherwise used, for example by the firmware or the filesystem.

When using the flash to store native code *start*+*length* must be less
than or equal to 1MByte. Note that the flash can be worn out if repeated
erasures (and writes) are made so use this feature sparingly. In particular,
native code needs to be recompiled and rewritten to flash on each boot
(including wake from deepsleep).

In both cases above, using iRAM1 or flash, if there is no more room left in
the specified region then the use of a native decorator on a function will
lead to ``MemoryError`` exception being raised during compilation of that function.

:param start: Start of native code region.
:param length: End of native code region.
"""
203 changes: 203 additions & 0 deletions typehints/esp32/network.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
"""network configuration

This module provides network drivers and routing configuration. To use this
module, a MicroPython variant/build with network capabilities must be installed.
Network drivers for specific hardware are available within this module and are
used to configure hardware network interface(s). Network services provided
by configured interfaces are then available for use via the :mod:`usocket`
module.

For example::

# connect/ show IP config a specific network interface
# see below for examples of specific drivers
import network
import utime
nic = network.Driver(...)
if not nic.isconnected():
nic.connect()
print("Waiting for connection...")
while not nic.isconnected():
utime.sleep(1)
print(nic.ifconfig())

# now use usocket as usual
import usocket as socket
addr = socket.getaddrinfo('micropython.org', 80)[0][-1]
s = socket.socket()
s.connect(addr)
s.send(b'GET / HTTP/1.1\r\nHost: micropython.org\r\n\r\n')
data = s.recv(1000)
s.close()
"""

from typing import overload, Optional, List, Tuple, Union, Any


@overload
def phy_mode() -> int:
"""Get the PHY mode."""
...


@overload
def phy_mode(mode: int) -> None:
"""Set the PHY mode.

The possible modes are defined as constants:
* ``MODE_11B`` -- IEEE 802.11b,
* ``MODE_11G`` -- IEEE 802.11g,
* ``MODE_11N`` -- IEEE 802.11n.
"""
...


class WLAN:
def __init__(self, interface_id: int) -> None:
"""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
connect). Availability of the methods below depends on interface type.
For example, only STA interface may `connect()` to an access point.
"""
...

@overload
def active(self) -> bool:
"""Query current state of the interface."""
...

@overload
def active(self, is_active: bool) -> None:
"""Activate ("up") or deactivate ("down") network interface."""
...

def connect(self, ssid: Optional[Union[bytes, str]] = None,
password: Optional[Union[bytes, str]] = None, *,
bssid: Optional[Union[bytes, str]] = None) -> None:
"""Connect to the specified wireless network, using the specified password.
If *bssid* is given then the connection will be restricted to the
access-point with that MAC address (the *ssid* must also be specified
in this case).
"""
...

def disconnect(self) -> None:
"""Disconnect from the currently connected wireless network."""
...

def scan(self) -> List[Tuple[bytes, bytes, int, int, int, int]]:
"""Scan for the available wireless networks.

Scanning is only possible on STA interface. Returns list of tuples with
the information about WiFi access points:

(ssid, bssid, channel, RSSI, authmode, hidden)

*bssid* is hardware address of an access point, in binary form, returned as
bytes object. You can use `ubinascii.hexlify()` to convert it to ASCII form.

There are five values for authmode:

* 0 -- open
* 1 -- WEP
* 2 -- WPA-PSK
* 3 -- WPA2-PSK
* 4 -- WPA/WPA2-PSK

and two for hidden:

* 0 -- visible
* 1 -- hidden
"""
...

def status(self) -> int:
"""Return the current status of the wireless connection.

The possible statuses are defined as constants:

* ``STAT_IDLE`` -- no connection and no activity,
* ``STAT_CONNECTING`` -- connecting in progress,
* ``STAT_WRONG_PASSWORD`` -- failed due to incorrect password,
* ``STAT_NO_AP_FOUND`` -- failed because no access point replied,
* ``STAT_CONNECT_FAIL`` -- failed due to other problems,
* ``STAT_GOT_IP`` -- connection successful.
"""
...

def isconnected(self) -> bool:
"""In case of STA mode, returns ``True`` if connected to a WiFi access
point and has a valid IP address. In AP mode returns ``True`` when a
station is connected. Returns ``False`` otherwise.
"""
...

@overload
def ifconfig(self) -> Tuple[str, str, str, str]:
"""Get IP-level network interface parameters: IP address, subnet mask,
gateway and DNS server.
"""
...

@overload
def ifconfig(self, ip: str, subnet: str, gateway: str, dns: str) -> None:
"""Get/set IP-level network interface parameters: IP address, subnet mask,
gateway and DNS server.
"""
...

@overload
def config(self, param: str) -> Any:
"""Get general network interface parameters."""
...

@overload
def config(self, **kwargs: Any) -> None:
"""Get or set general network interface parameters. These methods allow to work
with additional parameters beyond standard IP configuration (as dealt with by
`wlan.ifconfig()`). These include network-specific and hardware-specific
parameters. For setting parameters, keyword argument syntax should be used,
multiple parameters can be set at once. For querying, parameters name should
be quoted as a string, and only one parameter can be queries at time::

# Set WiFi access point name (formally known as ESSID) and WiFi channel
ap.config(essid='My AP', channel=11)
# Query params one by one
print(ap.config('essid'))
print(ap.config('channel'))

Following are commonly supported parameters (availability of a specific parameter
depends on network technology type, driver, and `MicroPython port`).

============= ===========
Parameter Description
============= ===========
mac MAC address (bytes)
essid WiFi access point name (string)
channel WiFi channel (integer)
hidden Whether ESSID is hidden (boolean)
authmode Authentication mode supported (enumeration, see module constants)
password Access password (string)
dhcp_hostname The DHCP hostname to use
============= ===========
"""
...


STA_IF: int
AP_IF: int


STAT_IDLE: int
STAT_CONNECTING: int
STAT_WRONG_PASSWORD: int
STAT_NO_AP_FOUND: int
STAT_CONNECT_FAIL: int
STAT_GOT_IP: int


MODE_11B: int
MODE_11G: int
MODE_11N: int