diff --git a/docs/esp32/general.rst b/docs/esp32/general.rst
new file mode 100644
index 0000000000000000000000000000000000000000..51918d4e183ea078b87751eb21cac72d4f182550
--- /dev/null
+++ b/docs/esp32/general.rst
@@ -0,0 +1,63 @@
+.. _esp32_general:
+
+General information about the ESP32 port
+========================================
+
+The ESP32 is a popular WiFi and Bluetooth enabled System-on-Chip (SoC) by
+Espressif Systems.
+
+Multitude of boards
+-------------------
+
+There is a multitude of modules and boards from different sources which carry
+the ESP32 chip. MicroPython tries to provide a generic port which would run on
+as many boards/modules as possible, but there may be limitations. Espressif
+development boards are taken as reference for the port (for example, testing is
+performed on them).  For any board you are using please make sure you have a
+datasheet, schematics and other reference materials so you can look up any
+board-specific functions.
+
+To make a generic ESP32 port and support as many boards as possible the
+following design and implementation decision were made:
+
+* GPIO pin numbering is based on ESP32 chip numbering.  Please have the manual/pin
+  diagram of your board at hand to find correspondence between your board pins and
+  actual ESP32 pins.
+* All pins are supported by MicroPython but not all are usable on any given board.
+  For example pins that are connected to external SPI flash should not be used,
+  and a board may only expose a certain selection of pins.
+
+
+Technical specifications and SoC datasheets
+-------------------------------------------
+
+The datasheets and other reference material for ESP32 chip are available
+from the vendor site: https://www.espressif.com/en/support/download/documents?keys=esp32 .
+They are the primary reference for the chip technical specifications, capabilities,
+operating modes, internal functioning, etc.
+
+For your convenience, some of technical specifications are provided below:
+
+* Architecture: Xtensa Dual-Core 32-bit LX6
+* CPU frequency: up to 240MHz
+* Total RAM available: 528KB (part of it reserved for system)
+* BootROM: 448KB
+* Internal FlashROM: none
+* External FlashROM: code and data, via SPI Flash; usual size 4MB
+* GPIO: 34 (GPIOs are multiplexed with other functions, including
+  external FlashROM, UART, etc.)
+* UART: 3 RX/TX UART (no hardware handshaking), one TX-only UART
+* SPI: 4 SPI interfaces (one used for FlashROM)
+* I2C: 2 I2C (bitbang implementation available on any pins)
+* I2S: 2
+* ADC: 12-bit SAR ADC up to 18 channels
+* DAC: 2 8-bit DACs
+* Programming: using BootROM bootloader from UART - due to external FlashROM
+  and always-available BootROM bootloader, the ESP32 is not brickable
+
+For more information see the ESP32 datasheet: https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf
+
+MicroPython is implemented on top of the ESP-IDF, Espressif's development
+framework for the ESP32.  This is a FreeRTOS based system.  See the
+`ESP-IDF Programming Guide <https://docs.espressif.com/projects/esp-idf/en/latest/index.html>`_
+for details.
diff --git a/docs/esp32/img/esp32.jpg b/docs/esp32/img/esp32.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..a96ddcbe6a39a74bded948698db956998f9d6c5e
Binary files /dev/null and b/docs/esp32/img/esp32.jpg differ
diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst
new file mode 100644
index 0000000000000000000000000000000000000000..81b2a53b39b0d23fb0c0116e53dcf463bd8b62ad
--- /dev/null
+++ b/docs/esp32/quickref.rst
@@ -0,0 +1,478 @@
+.. _esp32_quickref:
+
+Quick reference for the ESP32
+=============================
+
+.. image:: img/esp32.jpg
+    :alt: ESP32 board
+    :width: 640px
+
+The Espressif ESP32 Development Board (image attribution: Adafruit).
+
+Below is a quick reference for ESP32-based boards.  If it is your first time
+working with this board it may be useful to get an overview of the microcontroller:
+
+.. toctree::
+   :maxdepth: 1
+
+   general.rst
+   tutorial/intro.rst
+
+Installing MicroPython
+----------------------
+
+See the corresponding section of tutorial: :ref:`esp32_intro`. It also includes
+a troubleshooting subsection.
+
+General board control
+---------------------
+
+The MicroPython REPL is on UART0 (GPIO1=TX, GPIO3=RX) at baudrate 115200.
+Tab-completion is useful to find out what methods an object has.
+Paste mode (ctrl-E) is useful to paste a large slab of Python code into
+the REPL.
+
+The :mod:`machine` module::
+
+    import machine
+
+    machine.freq()          # get the current frequency of the CPU
+    machine.freq(240000000) # set the CPU frequency to 240 MHz
+
+The :mod:`esp` module::
+
+    import esp
+
+    esp.osdebug(None)       # turn off vendor O/S debugging messages
+    esp.osdebug(0)          # redirect vendor O/S debugging messages to UART(0)
+
+    # low level methods to interact with flash storage
+    esp.flash_size()
+    esp.flash_user_start()
+    esp.flash_erase(sector_no)
+    esp.flash_write(byte_offset, buffer)
+    esp.flash_read(byte_offset, buffer)
+
+The :mod:`esp32` module::
+
+    import esp32
+
+    esp32.hall_sensor()     # read the internal hall sensor
+    esp32.raw_temperature() # read the internal temperature of the MCU, in Farenheit
+    esp32.ULP()             # access to the Ultra-Low-Power Co-processor
+
+Note that the temperature sensor in the ESP32 will typically read higher than
+ambient due to the IC getting warm while it runs.  This effect can be minimised
+by reading the temperature sensor immediately after waking up from sleep.
+
+Networking
+----------
+
+The :mod:`network` module::
+
+    import network
+
+    wlan = network.WLAN(network.STA_IF) # 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
+    wlan.connect('essid', 'password') # connect to an AP
+    wlan.config('mac')      # get the interface's MAC adddress
+    wlan.ifconfig()         # get the interface's IP/netmask/gw/DNS addresses
+
+    ap = network.WLAN(network.AP_IF) # create access-point interface
+    ap.config(essid='ESP-AP') # set the ESSID of the access point
+    ap.active(True)         # activate the interface
+
+A useful function for connecting to your local WiFi network is::
+
+    def do_connect():
+        import network
+        wlan = network.WLAN(network.STA_IF)
+        wlan.active(True)
+        if not wlan.isconnected():
+            print('connecting to network...')
+            wlan.connect('essid', 'password')
+            while not wlan.isconnected():
+                pass
+        print('network config:', wlan.ifconfig())
+
+Once the network is established the :mod:`socket <usocket>` module can be used
+to create and use TCP/UDP sockets as usual, and the ``urequests`` module for
+convenient HTTP requests.
+
+Delay and timing
+----------------
+
+Use the :mod:`time <utime>` module::
+
+    import time
+
+    time.sleep(1)           # sleep for 1 second
+    time.sleep_ms(500)      # sleep for 500 milliseconds
+    time.sleep_us(10)       # sleep for 10 microseconds
+    start = time.ticks_ms() # get millisecond counter
+    delta = time.ticks_diff(time.ticks_ms(), start) # compute time difference
+
+Timers
+------
+
+Virtual (RTOS-based) timers are supported. Use the :ref:`machine.Timer <machine.Timer>` class
+with timer ID of -1::
+
+    from machine import Timer
+
+    tim = Timer(-1)
+    tim.init(period=5000, mode=Timer.ONE_SHOT, callback=lambda t:print(1))
+    tim.init(period=2000, mode=Timer.PERIODIC, callback=lambda t:print(2))
+
+The period is in milliseconds.
+
+Pins and GPIO
+-------------
+
+Use the :ref:`machine.Pin <machine.Pin>` class::
+
+    from machine import Pin
+
+    p0 = Pin(0, Pin.OUT)    # create output pin on GPIO0
+    p0.on()                 # set pin to "on" (high) level
+    p0.off()                # set pin to "off" (low) level
+    p0.value(1)             # set pin to on/high
+
+    p2 = Pin(2, Pin.IN)     # create input pin on GPIO2
+    print(p2.value())       # get value, 0 or 1
+
+    p4 = Pin(4, Pin.IN, Pin.PULL_UP) # enable internal pull-up resistor
+    p5 = Pin(5, Pin.OUT, value=1) # set pin high on creation
+
+Available Pins are from the following ranges (inclusive): 0-19, 21-23, 25-27, 32-39.
+These correspond to the actual GPIO pin numbers of ESP32 chip.  Note that many
+end-user boards use their own adhoc pin numbering (marked e.g. D0, D1, ...).
+For mapping between board logical pins and physical chip pins consult your board
+documentation.
+
+Notes:
+
+* Pins 1 and 3 are REPL UART TX and RX respectively
+
+* Pins 6, 7, 8, 11, 16, and 17 are used for connecting the embedded flash,
+  and are not recommended for other uses
+
+* Pins 34-39 are input only, and also do not have internal pull-up resistors
+
+PWM (pulse width modulation)
+----------------------------
+
+PWM can be enabled on all output-enabled pins. The base frequency can
+range from 1Hz to 40MHz but there is a tradeoff; as the base frequency
+*increases* the duty resolution *decreases*. See 
+`LED Control <https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/ledc.html>`_
+for more details.
+
+Use the ``machine.PWM`` class::
+
+    from machine import Pin, PWM
+
+    pwm0 = PWM(Pin(0))      # create PWM object from a pin
+    pwm0.freq()             # get current frequency
+    pwm0.freq(1000)         # set frequency
+    pwm0.duty()             # get current duty cycle
+    pwm0.duty(200)          # set duty cycle
+    pwm0.deinit()           # turn off PWM on the pin
+
+    pwm2 = PWM(Pin(2), freq=20000, duty=512) # create and configure in one go
+
+ADC (analog to digital conversion)
+----------------------------------
+
+On the ESP32 ADC functionality is available on Pins 32-39. Note that, when
+using the default configuration, input voltages on the ADC pin must be between
+0.0v and 1.0v (anything above 1.0v will just read as 4095).  Attenuation must
+be applied in order to increase this usable voltage range.
+
+Use the :ref:`machine.ADC <machine.ADC>` class::
+
+    from machine import ADC
+
+    adc = ADC(Pin(32))          # create ADC object on ADC pin
+    adc.read()                  # read value, 0-4095 across voltage range 0.0v - 1.0v
+
+    adc.atten(ADC.ATTN_11DB)    # set 11dB input attentuation (voltage range roughly 0.0v - 3.6v)
+    adc.width(ADC.WIDTH_9BIT)   # set 9 bit return values (returned range 0-511)
+    adc.read()                  # read value using the newly configured attenuation and width
+
+ESP32 specific ADC class method reference:
+
+.. method:: ADC.atten(attenuation)
+
+    This method allows for the setting of the amount of attenuation on the
+    input of the ADC. This allows for a wider possible input voltage range,
+    at the cost of accuracy (the same number of bits now represents a wider
+    range). The possible attenuation options are:
+
+      - ``ADC.ATTN_0DB``: 0dB attenuation, gives a maximum input voltage
+        of 1.00v - this is the default configuration
+      - ``ADC.ATTN_2_5DB``: 2.5dB attenuation, gives a maximum input voltage
+        of approximately 1.34v
+      - ``ADC.ATTN_6DB``: 6dB attenuation, gives a maximum input voltage
+        of approximately 2.00v
+      - ``ADC.ATTN_11DB``: 11dB attenuation, gives a maximum input voltage
+        of approximately 3.6v
+
+.. Warning::
+   Despite 11dB attenuation allowing for up to a 3.6v range, note that the
+   absolute maximum voltage rating for the input pins is 3.6v, and so going
+   near this boundary may be damaging to the IC!
+
+.. method:: ADC.width(width)
+
+    This method allows for the setting of the number of bits to be utilised
+    and returned during ADC reads. Possible width options are:
+
+      - ``ADC.WIDTH_9BIT``: 9 bit data
+      - ``ADC.WIDTH_10BIT``: 10 bit data
+      - ``ADC.WIDTH_11BIT``: 11 bit data
+      - ``ADC.WIDTH_12BIT``: 12 bit data - this is the default configuration
+
+Software SPI bus
+----------------
+
+There are two SPI drivers. One is implemented in software (bit-banging)
+and works on all pins, and is accessed via the :ref:`machine.SPI <machine.SPI>`
+class::
+
+    from machine import Pin, SPI
+
+    # construct an SPI bus on the given pins
+    # polarity is the idle state of SCK
+    # phase=0 means sample on the first edge of SCK, phase=1 means the second
+    spi = SPI(baudrate=100000, polarity=1, phase=0, sck=Pin(0), mosi=Pin(2), miso=Pin(4))
+
+    spi.init(baudrate=200000) # set the baudrate
+
+    spi.read(10)            # read 10 bytes on MISO
+    spi.read(10, 0xff)      # read 10 bytes while outputing 0xff on MOSI
+
+    buf = bytearray(50)     # create a buffer
+    spi.readinto(buf)       # read into the given buffer (reads 50 bytes in this case)
+    spi.readinto(buf, 0xff) # read into the given buffer and output 0xff on MOSI
+
+    spi.write(b'12345')     # write 5 bytes on MOSI
+
+    buf = bytearray(4)      # create a buffer
+    spi.write_readinto(b'1234', buf) # write to MOSI and read from MISO into the buffer
+    spi.write_readinto(buf, buf) # write buf to MOSI and read MISO back into buf
+
+.. Warning::
+   Currently *all* of ``sck``, ``mosi`` and ``miso`` *must* be specified when
+   initialising Software SPI. 
+
+Hardware SPI bus
+----------------
+
+There are two hardware SPI channels that allow faster (up to 80Mhz)
+transmission rates, but are only supported on a subset of pins.
+
+=====  ===========  ============
+\      HSPI (id=1)   VSPI (id=2)
+=====  ===========  ============
+sck    14           18
+mosi   13           23
+miso   12           19
+=====  ===========  ============
+
+Hardware SPI has the same methods as Software SPI above::
+
+    from machine import Pin, SPI
+
+    hspi = SPI(1, 10000000, sck=Pin(14), mosi=Pin(13), miso=Pin(12))
+    vspi = SPI(2, baudrate=80000000, polarity=0, phase=0, bits=8, firstbit=0, sck=Pin(18), mosi=Pin(23), miso=Pin(19))
+
+
+I2C bus
+-------
+
+The I2C driver is implemented in software and works on all pins,
+and is accessed via the :ref:`machine.I2C <machine.I2C>` class::
+
+    from machine import Pin, I2C
+
+    # construct an I2C bus
+    i2c = I2C(scl=Pin(5), sda=Pin(4), freq=100000)
+
+    i2c.readfrom(0x3a, 4)   # read 4 bytes from slave device with address 0x3a
+    i2c.writeto(0x3a, '12') # write '12' to slave device with address 0x3a
+
+    buf = bytearray(10)     # create a buffer with 10 bytes
+    i2c.writeto(0x3a, buf)  # write the given buffer to the slave
+
+Real time clock (RTC)
+---------------------
+
+See :ref:`machine.RTC <machine.RTC>` ::
+
+    from machine import RTC
+
+    rtc = RTC()
+    rtc.datetime((2017, 8, 23, 1, 12, 48, 0, 0)) # set a specific date and time
+    rtc.datetime() # get date and time
+
+Deep-sleep mode
+---------------
+
+The following code can be used to sleep, wake and check the reset cause::
+
+    import machine
+
+    # check if the device woke from a deep sleep
+    if machine.reset_cause() == machine.DEEPSLEEP_RESET:
+        print('woke from a deep sleep')
+
+    # put the device to sleep for 10 seconds
+    machine.deepsleep(10000)
+
+Notes:
+
+* Calling ``deepsleep()`` without an argument will put the device to sleep
+  indefinitely
+* A software reset does not change the reset cause
+
+OneWire driver
+--------------
+
+The OneWire driver is implemented in software and works on all pins::
+
+    from machine import Pin
+    import onewire
+
+    ow = onewire.OneWire(Pin(12)) # create a OneWire bus on GPIO12
+    ow.scan()               # return a list of devices on the bus
+    ow.reset()              # reset the bus
+    ow.readbyte()           # read a byte
+    ow.writebyte(0x12)      # write a byte on the bus
+    ow.write('123')         # write bytes on the bus
+    ow.select_rom(b'12345678') # select a specific device by its ROM code
+
+There is a specific driver for DS18S20 and DS18B20 devices::
+
+    import time, ds18x20
+    ds = ds18x20.DS18X20(ow)
+    roms = ds.scan()
+    ds.convert_temp()
+    time.sleep_ms(750)
+    for rom in roms:
+        print(ds.read_temp(rom))
+
+Be sure to put a 4.7k pull-up resistor on the data line.  Note that
+the ``convert_temp()`` method must be called each time you want to
+sample the temperature.
+
+NeoPixel driver
+---------------
+
+Use the ``neopixel`` module::
+
+    from machine import Pin
+    from neopixel import NeoPixel
+
+    pin = Pin(0, Pin.OUT)   # set GPIO0 to output to drive NeoPixels
+    np = NeoPixel(pin, 8)   # create NeoPixel driver on GPIO0 for 8 pixels
+    np[0] = (255, 255, 255) # set the first pixel to white
+    np.write()              # write data to all pixels
+    r, g, b = np[0]         # get first pixel colour
+
+For low-level driving of a NeoPixel::
+
+    import esp
+    esp.neopixel_write(pin, grb_buf, is800khz)
+
+.. Warning::
+   By default ``NeoPixel`` is configured to control the more popular *800kHz*
+   units. It is possible to use alternative timing to control other (typically
+   400kHz) devices by passing ``timing=0`` when constructing the
+   ``NeoPixel`` object.
+
+
+Capacitive Touch
+----------------
+
+Use 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``.
+
+Note that TouchPads can be used to wake an ESP32 from sleep::
+
+    import machine
+    from machine import TouchPad, Pin
+    import esp32
+
+    t = TouchPad(Pin(14))
+    t.config(500)               # configure the threshold at which the pin is considered touched
+    esp32.wake_on_touch(True)
+    machine.sleep()             # put the MCU to sleep until a touchpad is touched
+
+For more details on touchpads refer to `Espressif Touch Sensor
+<https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/touch_pad.html>`_.
+
+
+DHT driver
+----------
+
+The DHT driver is implemented in software and works on all pins::
+
+    import dht
+    import machine
+
+    d = dht.DHT11(machine.Pin(4))
+    d.measure()
+    d.temperature() # eg. 23 (°C)
+    d.humidity()    # eg. 41 (% RH)
+
+    d = dht.DHT22(machine.Pin(4))
+    d.measure()
+    d.temperature() # eg. 23.6 (°C)
+    d.humidity()    # eg. 41.3 (% RH)
+
+WebREPL (web browser interactive prompt)
+----------------------------------------
+
+WebREPL (REPL over WebSockets, accessible via a web browser) is an
+experimental feature available in ESP32 port. Download web client
+from https://github.com/micropython/webrepl (hosted version available
+at http://micropython.org/webrepl), and configure it by executing::
+
+    import webrepl_setup
+
+and following on-screen instructions. After reboot, it will be available
+for connection. If you disabled automatic start-up on boot, you may
+run configured daemon on demand using::
+
+    import webrepl
+    webrepl.start()
+
+    # or, start with a specific password
+    webrepl.start(password='mypass')
+
+The WebREPL daemon listens on all active interfaces, which can be STA or
+AP.  This allows you to connect to the ESP32 via a router (the STA
+interface) or directly when connected to its access point.
+
+In addition to terminal/command prompt access, WebREPL also has provision
+for file transfer (both upload and download).  The web client has buttons for
+the corresponding functions, or you can use the command-line client
+``webrepl_cli.py`` from the repository above.
+
+See the MicroPython forum for other community-supported alternatives
+to transfer files to an ESP32 board.
diff --git a/docs/esp32/tutorial/intro.rst b/docs/esp32/tutorial/intro.rst
new file mode 100644
index 0000000000000000000000000000000000000000..4c567383476f954863cfb2f47ec1fe20e3334d97
--- /dev/null
+++ b/docs/esp32/tutorial/intro.rst
@@ -0,0 +1,139 @@
+.. _esp32_intro:
+
+Getting started with MicroPython on the ESP32
+=============================================
+
+Using MicroPython is a great way to get the most of your ESP32 board.  And
+vice versa, the ESP32 chip is a great platform for using MicroPython.  This
+tutorial will guide you through setting up MicroPython, getting a prompt, using
+WebREPL, connecting to the network and communicating with the Internet, using
+the hardware peripherals, and controlling some external components.
+
+Let's get started!
+
+Requirements
+------------
+
+The first thing you need is a board with an ESP32 chip.  The MicroPython
+software supports the ESP32 chip itself and any board should work.  The main
+characteristic of a board is how the GPIO pins are connected to the outside
+world, and whether it includes a built-in USB-serial convertor to make the
+UART available to your PC.
+
+Names of pins will be given in this tutorial using the chip names (eg GPIO2)
+and it should be straightforward to find which pin this corresponds to on your
+particular board.
+
+Powering the board
+------------------
+
+If your board has a USB connector on it then most likely it is powered through
+this when connected to your PC.  Otherwise you will need to power it directly.
+Please refer to the documentation for your board for further details.
+
+Getting the firmware
+--------------------
+
+The first thing you need to do is download the most recent MicroPython firmware 
+.bin file to load onto your ESP32 device. You can download it from the  
+`MicroPython downloads page <https://micropython.org/download#esp32>`_.
+From here, you have 3 main choices:
+
+* Stable firmware builds
+* Daily firmware builds
+* Daily firmware builds with SPIRAM support
+
+If you are just starting with MicroPython, the best bet is to go for the Stable
+firmware builds. If you are an advanced, experienced MicroPython ESP32 user
+who would like to follow development closely and help with testing new
+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.
+
+Deploying the firmware
+----------------------
+
+Once you have the MicroPython firmware you need to load it onto your ESP32 device.
+There are two main steps to do this: first you need to put your device in
+bootloader mode, and second you need to copy across the firmware.  The exact
+procedure for these steps is highly dependent on the particular board and you will
+need to refer to its documentation for details.
+
+Fortunately, most boards have a USB connector, a USB-serial convertor, and the DTR
+and RTS pins wired in a special way then deploying the firmware should be easy as
+all steps can be done automatically.  Boards that have such features
+include the Adafruit Feather HUZZAH32, M5Stack, Wemos LOLIN32, and TinyPICO
+boards, along with the Espressif DevKitC, PICO-KIT, WROVER-KIT dev-kits.
+
+For best results it is recommended to first erase the entire flash of your
+device before putting on new MicroPython firmware.
+
+Currently we only support esptool.py to copy across the firmware.  You can find
+this tool here: `<https://github.com/espressif/esptool/>`__, or install it
+using pip::
+
+    pip install esptool
+
+Versions starting with 1.3 support both Python 2.7 and Python 3.4 (or newer).
+An older version (at least 1.2.1 is needed) works fine but will require Python
+2.7.
+
+Using esptool.py you can erase the flash with the command::
+
+    esptool.py --port /dev/ttyUSB0 erase_flash
+
+And then deploy the new firmware using::
+
+    esptool.py --chip esp32 --port /dev/ttyUSB0 write_flash -z 0x1000 esp32-20180511-v1.9.4.bin
+
+Notes:
+
+* You might need to change the "port" setting to something else relevant for your
+  PC
+* You may need to reduce the baudrate if you get errors when flashing
+  (eg down to 115200 by adding ``--baud 115200`` into the command)
+* For some boards with a particular FlashROM configuration you may need to
+  change the flash mode (eg by adding ``-fm dio`` into the command)
+* The filename of the firmware should match the file that you have
+
+If the above commands run without error then MicroPython should be installed on
+your board!
+
+Serial prompt
+-------------
+
+Once you have the firmware on the device you can access the REPL (Python prompt)
+over UART0 (GPIO1=TX, GPIO3=RX), which might be connected to a USB-serial
+convertor, depending on your board.  The baudrate is 115200.
+
+From here you can now follow the ESP8266 tutorial, because these two Espressif chips
+are very similar when it comes to using MicroPython on them.  The ESP8266 tutorial
+is found at :ref:`esp8266_tutorial` (but skip the Introduction section).
+
+Troubleshooting installation problems
+-------------------------------------
+
+If you experience problems during flashing or with running firmware immediately
+after it, here are troubleshooting recommendations:
+
+* Be aware of and try to exclude hardware problems.  There are 2 common
+  problems: bad power source quality, and worn-out/defective FlashROM.
+  Speaking of power source, not just raw amperage is important, but also low
+  ripple and noise/EMI in general.  The most reliable and convenient power
+  source is a USB port.
+
+* The flashing instructions above use flashing speed of 460800 baud, which is
+  good compromise between speed and stability. However, depending on your
+  module/board, USB-UART convertor, cables, host OS, etc., the above baud
+  rate may be too high and lead to errors. Try a more common 115200 baud
+  rate instead in such cases.
+
+* To catch incorrect flash content (e.g. from a defective sector on a chip),
+  add ``--verify`` switch to the commands above.
+
+* If you still experience problems with flashing the firmware please
+  refer to esptool.py project page, https://github.com/espressif/esptool
+  for additional documentation and a bug tracker where you can report problems.
+
+* If you are able to flash the firmware but the ``--verify`` option returns
+  errors even after multiple retries the you may have a defective FlashROM chip.
diff --git a/docs/index.rst b/docs/index.rst
index af5ffb885a7d84b78f9d6d1f4d3bafb7f907b782..235185b6c2992bd1de165eaf6283d81724d8b98c 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -9,4 +9,5 @@ MicroPython documentation and references
     license.rst
     pyboard/quickref.rst
     esp8266/quickref.rst
+    esp32/quickref.rst
     wipy/quickref.rst
diff --git a/docs/library/esp.rst b/docs/library/esp.rst
index 121a80d42e78c35a0fdd5e6c3f7aabe25d867747..867182be9945c0dc5c9496424edd36f5e6075dd3 100644
--- a/docs/library/esp.rst
+++ b/docs/library/esp.rst
@@ -1,10 +1,12 @@
-:mod:`esp` --- functions related to the ESP8266
-===============================================
+:mod:`esp` --- functions related to the ESP8266 and ESP32
+=========================================================
 
 .. module:: esp
-    :synopsis: functions related to the ESP8266
+    :synopsis: functions related to the ESP8266 and ESP32
 
-The ``esp`` module contains specific functions related to the ESP8266 module.
+The ``esp`` module contains specific functions related to both the ESP8266 and 
+ESP32 modules.  Some functions are only available on one or the other of these
+ports.
 
 
 Functions
@@ -12,6 +14,8 @@ Functions
 
 .. function:: sleep_type([sleep_type])
 
+    **Note**: ESP8266 only
+
     Get or set the sleep type.
 
     If the *sleep_type* parameter is provided, sets the sleep type to its
@@ -29,6 +33,8 @@ Functions
 
 .. function:: deepsleep(time=0)
 
+    **Note**: ESP8266 only - use `machine.deepsleep()` on ESP32
+
     Enter deep sleep.
 
     The whole module powers down, except for the RTC clock circuit, which can
@@ -38,8 +44,18 @@ Functions
 
 .. function:: flash_id()
 
+    **Note**: ESP8266 only
+
     Read the device ID of the flash memory.
 
+.. function:: flash_size()
+
+    Read the total size of the flash memory.
+
+.. function:: flash_user_start()
+
+    Read the memory offset at which the user flash space begins.
+
 .. function:: flash_read(byte_offset, length_or_buffer)
 
 .. function:: flash_write(byte_offset, bytes)
@@ -48,6 +64,8 @@ Functions
 
 .. function:: set_native_code_location(start, length)
 
+    **Note**: ESP8266 only
+
     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
diff --git a/docs/library/index.rst b/docs/library/index.rst
index e37f1d625611a5f1394230d0f039c3753cf872e2..290192fa08b8f01e51c84dbe0d019fae7a29d337 100644
--- a/docs/library/index.rst
+++ b/docs/library/index.rst
@@ -139,10 +139,10 @@ The following libraries and classes are specific to the WiPy.
   machine.TimerWiPy.rst
 
 
-Libraries specific to the ESP8266
----------------------------------
+Libraries specific to the ESP8266 and ESP32
+-------------------------------------------
 
-The following libraries are specific to the ESP8266.
+The following libraries are specific to the ESP8266 and ESP32.
 
 .. toctree::
   :maxdepth: 2
diff --git a/docs/templates/topindex.html b/docs/templates/topindex.html
index 675fae29fa3a9390db012ce3516b936e874a1f73..08bf4c73e7923a7a39bd9618a6147d6075680095 100644
--- a/docs/templates/topindex.html
+++ b/docs/templates/topindex.html
@@ -53,6 +53,10 @@
         <a class="biglink" href="{{ pathto("esp8266/quickref") }}">Quick reference for the ESP8266</a><br/>
         <span class="linkdescr">pinout for ESP8266-based boards, snippets of useful code, and a tutorial</span>
       </p>
+      <p class="biglink">
+        <a class="biglink" href="{{ pathto("esp32/quickref") }}">Quick reference for the ESP32</a><br/>
+        <span class="linkdescr">pinout for ESP32-based boards, snippets of useful code, and a tutorial</span>
+      </p>
       <p class="biglink">
         <a class="biglink" href="{{ pathto("wipy/quickref") }}">Quick reference for the WiPy/CC3200</a><br/>
         <span class="linkdescr">pinout for the WiPy/CC3200, snippets of useful code, and a tutorial</span>