Photo by on Regina Valetova Unsplash I’m going to show you how to t̶u̶r̶n̶ ̶o̶n̶ ̶y̶o̶u̶r̶ ̶f̶u̶n̶k̶ ̶m̶o̶t̶o̶r get started with on an development board. In this part of this tutorial, I’ll show you how to: MicroPython Espressif ESP32 first Get up & running with on the ESP32 MicroPython Connect to WiFi Upload scripts to the board Read the ambient temperature (everyone loves that, right?) In the forthcoming second part of this tutorial, I’ll show you publish the data you’ve collected with . MQTT This guide expects you to possess: …familiarity with the command-line …basic experience interfacing with development boards (like Arduino) …a basic understanding of programming in Python If I’ve glossed over something I shouldn’t have, please ! let me know Before we begin, you will need some stuff. Bill of Stuff You need Stuff in the following categories. Hardware Not necessarily stuff, but same idea. Photo by this Alexandra Cárdenas One (1) ESP32 development board such as the (any kind will do; they are all roughly the same) SparkFun ESP32 Thing One (1) digital thermometer ( ) in its TO-92 package DS18B20 datasheet One (1) 4.7kꭥ resistor Four (4) jumper wires One (1) 400-point or larger breadboard One (1) USB Micro-B cable do so. If you need to solder header pins on your dev board: these typically have the resistor built-in, so you won’t need it. You need to figure out which pin is which, however. If you have a DS18B20 “breakout board”: will Software You will need to download and install some software. Some of these things you may have installed already. Other things may need to be upgraded. This guide assumes you . ain’t got jack squat I apologize that I don’t have much information for Windows users! However, I assure you that none of this is impossible. VCP Driver If you’re running macOS or Windows, you may need to download and install a Virtual COM Port (VCP) driver, if you haven’t done so already. Typically, the USB-to-serial chip on these boards is a or ; check the datasheet for your specific board or just squint at the IC near the USB port. CP210x FT232RL Newer Linux kernels have support for these chips baked-in, so driver installation is unnecessary. Here’s an example of a CP2104 on an ESP32 dev board of mine: A SiLabs CP2104. Thanks, macro lens! To assert the driver is working, plug your dev board into your computer. If you’re on Linux, check for : /dev/ttyUSB0 $ ls -l /dev/ttyUSB0crw-rw---- 1 root dialout 188, 0 Dec 19 17:04 /dev/ttyUSB0 Or on macOS: /dev/tty.SLAB_USBtoUART $ ls -l /dev/tty.SLAB_USBtoUARTcrw-rw-rw- 1 root wheel 21, 20 Dec 19 17:10 /dev/tty.SLAB_USBtoUART Serial Terminal A free, cross-platform, GUI terminal is . Linux & macOS users can get away with using on the command line. More purpose-built solutions include , which ships with Python 3, and can be launched via , and . CoolTerm screen miniterm python3 -m serial.tools.miniterm minicom Python, Etc. You will also need: Python v3.6.x For extra libraries, a clone or archive of ( micropython/micropython-lib git clone [https://github.com/micropython/micropython-lib](https://github.com/micropython/micropython-lib%29) ) How you install these will vary per your installation of Python: To flash the board, (version 2.2 or newer) esptool To manage files on the board, adafruit-ampy You could try . This worked for me on macOS with ; YMMV. You might need to preface that with if not using Homebrew. pip3 install esptool adafruit-ampy Homebrew sudo MicroPython Firmware Finally, you’ll need to download the . latest MicroPython firmware for ESP32 Now that our tools are at hand, we can begin by flashing the ESP32 board with MicroPython. Flashing MicroPython & First Steps Unless MicroPython is installed on your ESP32, you will want to start by connecting it to your computer via USB, and erasing its flash: already In the below examples, replace with the appropriate device or COM port for your system. /dev/tty.SLAB_USBtoUART $ esptool.py --chip esp32 -p /dev/tty.SLAB_USBtoUART erase_flashesptool.py v2.2Connecting........___Chip is ESP32D0WDQ6 (revision 1)Uploading stub...Running stub...Stub running...Erasing flash (this may take a while)...Chip erase completed successfully in 4.6sHard resetting... Now, we can flash it with the we downloaded earlier: firmware $ esptool.py --chip esp32 -p /dev/tty.SLAB_USBtoUART write_flash \ -z 0x1000 ~/Downloads/esp32-20171219-v1.9.2-445-g84035f0f.binesptool.py v2.2Connecting........_Chip is ESP32D0WDQ6 (revision 1)Uploading stub...Running stub...Stub running...Configuring flash size...Auto-detected Flash size: 4MBCompressed 936288 bytes to 587495...Wrote 936288 bytes (587495 compressed) at 0x00001000 in 51.7 seconds (effective 144.8 kbit/s)...Hash of data verified. Leaving...Hard resetting... If you’re feeling dangerous, you can increase the baud rate when flashing by using the option. --baud If that worked, you should be able to enter a MicroPython REPL by opening up the port: # 115200 is the baud rate at which the REPL communicates$ screen /dev/tty.SLAB_USBtoUART 115200 >>> Congratulations, is your REPL prompt. This works similarly to a normal Python REPL (e.g. just running with no arguments). Try the function: >>> python3 help() >>> help()Welcome to MicroPython on the ESP32! For generic online docs please visit http://docs.micropython.org/ For access to the hardware use the 'machine' module: import machinepin12 = machine.Pin(12, machine.Pin.OUT)pin12.value(1)pin13 = machine.Pin(13, machine.Pin.IN, machine.Pin.PULL_UP)print(pin13.value())i2c = machine.I2C(scl=machine.Pin(21), sda=machine.Pin(22))i2c.scan()i2c.writeto(addr, b'1234')i2c.readfrom(addr, 4) Basic WiFi configuration: import networksta_if = network.WLAN(network.STA_IF); sta_if.active(True)sta_if.scan() # Scan for available access pointssta_if.connect("<AP_name>", "<password>") # Connect to an APsta_if.isconnected() # Check for successful connection Control commands: CTRL-A -- on a blank line, enter raw REPL mode CTRL-B -- on a blank line, enter normal REPL mode CTRL-C -- interrupt a running program CTRL-D -- on a blank line, do a soft reset of the board CTRL-E -- on a blank line, enter paste mode For further help on a specific object, type help(obj)For a list of available modules, type help('modules') If you’ve never seen this before on an MCU: , crazy, right? I know You can type in the commands from “Basic WiFi configuration” to connect. You will see a good deal of debugging information from the ESP32 (this can be suppressed, as you’ll see): >>> import network>>> sta_if = network.WLAN(network.STA_IF)I (323563) wifi: wifi firmware version: 111e74dI (323563) wifi: config NVS flash: enabledI (323563) wifi: config nano formating: disabledI (323563) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSEI (323573) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSEI (323593) wifi: Init dynamic tx buffer num: 32I (323593) wifi: Init data frame dynamic rx buffer num: 64I (323593) wifi: Init management frame dynamic rx buffer num: 64I (323603) wifi: wifi driver task: 3ffe1584, prio:23, stack:4096I (323603) wifi: Init static rx buffer num: 10I (323613) wifi: Init dynamic rx buffer num: 0I (323613) wifi: Init rx ampdu len mblock:7I (323623) wifi: Init lldesc rx ampdu entry mblock:4I (323623) wifi: wifi power manager task: 0x3ffe84b0 prio: 21 stack: 2560W (323633) phy_init: failed to load RF calibration data (0x1102), falling back to full calibrationI (323793) phy: phy_version: 362.0, 61e8d92, Sep 8 2017, 18:48:11, 0, 2I (323803) wifi: mode : null>>> sta_if.active(True)I (328553) wifi: mode : sta (30:ae:a4:27:d4:88)I (328553) wifi: STA_STARTTrue>>> sta_if.scan()I (389423) network: event 1[(b'SON OF ZOLTAR', b"`\xe3'\xcf\xf4\xf5", 1, -57, 4, False), (b'CenturyLink6105', b'`1\x97%\xd9t', 1, -96, 4, False)]>>> sta_if.connect('SON OF ZOLTAR', '<REDACTED>')>>> I (689573) wifi: n:1 0, o:1 0, ap:255 255, sta:1 0, prof:1I (690133) wifi: state: init -> auth (b0)I (690133) wifi: state: auth -> assoc (0)I (690143) wifi: state: assoc -> run (10)I (690163) wifi: connected with SON OF ZOLTAR, channel 1I (690173) network: event 4I (691723) event: sta ip: 10.0.0.26, mask: 255.255.255.0, gw: 10.0.0.1I (691723) network: GOT_IPI (693143) wifi: pm start, type:0 >>> sta_if.isconnected()True Cool, huh? Now that we know we can connect to WiFi, let’s have the board connect every time it powers up. Creating a MicroPython Module To perform tasks upon boot, MicroPython wants you to put code in a file named , which is a MicroPython module. boot.py Let’s create with code modified from , replacing where indicated: boot.py the MicroPython ESP8266 docs def connect(): import network sta_if = network.WLAN(network.STA_IF) if not sta_if.isconnected(): print('connecting to network...') sta_if.active(True) sta_if.connect('<YOUR WIFI SSID>', '<YOUR WIFI PASS>') while not sta_if.isconnected(): pass print('network config:', sta_if.ifconfig()) We can also create a function to disable debugging output. Append to : boot.py def no_debug(): import esp # this can be run from the REPL as well esp.osdebug(None) These functions will be at boot, but not called automatically. Let’s test them before making them automatically execute. defined To do this, we can upload . You’ll need to close the connection to the serial port. If you’re using , type , then to confirm; otherwise disconnect or just quit your terminal program. boot.py screen Ctrl-A Ctrl-\ y Uploading a MicroPython Module Though there are other ways to do this, I’ve found the most straightforward for the ESP32 is to use , a general-purpose tool by . Here’s what it can do: ampy Adafruit $ ampy --help Usage: ampy [OPTIONS] COMMAND [ARGS]... ampy - Adafruit MicroPython Tool Ampy is a tool to control MicroPython boards over a serial connection. Using ampy you can manipulate files on the board's internal filesystem and even run scripts. Options: -p, --port PORT Name of serial port for connected board. Can optionally specify with AMPY_PORT environemnt variable. [required] -b, --baud BAUD Baud rate for the serial connection (default 115200). Can optionally specify with AMPY_BAUD environment variable. --version Show the version and exit. --help Show this message and exit. Commands: get Retrieve a file from the board. ls List contents of a directory on the board. mkdir Create a directory on the board. put Put a file or folder and its contents on the... reset Perform soft reset/reboot of the board. rm Remove a file from the board. rmdir Forcefully remove a folder and all its... run Run a script and print its output. MicroPython stores files (scripts or anything else that fits) in a very basic filesystem. By default, an empty should exist already. To list the files on your board, execute: boot.py $ ampy -p /dev/tty.SLAB_USBtoUART lsboot.py Using the command will echo a file’s contents to your shell (which could be piped to a file, if you wish): get $ ampy -p /dev/tty.SLAB_USBtoUART get boot.py# This file is executed on every boot (including wake-boot from deepsleep) We can overwrite it with our own : boot.py $ ampy -p /dev/tty.SLAB_USBtoUART put boot.py And retrieve it to see that it overwrote the default : boot.py $ ampy -p /dev/tty.SLAB_USBtoUART get boot.pydef connect(): import network sta_if = network.WLAN(network.STA_IF) if not sta_if.isconnected(): print('connecting to network...') sta_if.active(True) sta_if.connect('<YOUR WIFI SSID>', '<YOUR WIFI PASS>') while not sta_if.isconnected(): pass print('network config:', sta_if.ifconfig()) def no_debug(): import esp # this can be run from the REPL as well esp.osdebug(None) Success! This is the gist of uploading files with . You can also upload entire folders, as we’ll see later. ampy From here, we can open our REPL again, and run our code. No need to restart the board! Running a MicroPython Module In following examples, I will eliminate the command prompt ( **>>>** ) from code run in a REPL, for ease of copying & pasting. Re-connect to the REPL. $ screen /dev/tty.SLAB_USBtoUART 115200 First, we’ll disconnect from WiFi: import networksta_if = network.WLAN(network.STA_IF)sta_if.disconnect() Debug output follows: I (3299583) wifi: state: run -> init (0)I (3299583) wifi: n:1 0, o:1 0, ap:255 255, sta:1 0, prof:1I (3299583) wifi: pm stop, total sleep time: 0/-1688526567I (3299583) wifi: STA_DISCONNECTED, reason:8 Then, we can the module. This will make our and functions available. import boot connect no_debug import bootconnect() Output: connecting to network...I (87841) wifi: n:1 0, o:1 0, ap:255 255, sta:1 0, prof:1I (88401) wifi: state: init -> auth (b0)I (88401) wifi: state: auth -> assoc (0)I (88411) wifi: state: assoc -> run (10)I (88441) wifi: connected with SON OF ZOLTAR, channel 1I (88441) network: event 4I (90081) event: sta ip: 10.0.0.26, mask: 255.255.255.0, gw: 10.0.0.1I (90081) network: GOT_IPnetwork config: ('10.0.0.26', '255.255.255.0', '10.0.0.1', '10.0.0.1')I (91411) wifi: pm start, type:0 Super. Let’s silence the noise, and try again: no_debug()sta_if.disconnect()connect() Output: connecting to network...network config: ('10.0.0.26', '255.255.255.0', '10.0.0.1', '10.0.0.1') LGTM. The IP addresses above depend upon your local network configuration, and will likely be different. Disconnect from the port (if using : , ) and append these lines to : screen Ctrl-A Ctrl-\ y boot.py no_debug()connect() Upload it again via , which will overwrite the existing . Hard reset (“push the button”) or otherwise power -cycle the board. Reconnect to the REPL and execute to assert connectivity: ampy put boot.py boot.py connect() connect() Output: network config: ('10.0.0.26', '255.255.255.0', '10.0.0.1', '10.0.0.1') You’ll notice “connecting to network…” was not printed to the console; if already connected, the function prints the configuration and returns. If you’ve gotten this far, then your board is successfully connecting to Wifi at boot. Good job! connect() We now have two more items to check off our list, unless you forgot what we were trying to do: We need to read the ambient temperature on an interval. We need to publish this information to an broker. MQTT Next, we’ll knock out that temperature reading. Temperature Readings in MicroPython As we write our code, we can use the REPL to experiment. I’m using the example . You’ll need to import three (3) modules, , and (note the ): found here machine onewire ds18x20 x import machine, onewire, ds18x20 I’ve connected my sensor to pin 12 on my ESP32. Your breadboard should look something like this: Example breadboard wiring for ESP32 dev board and DS18B20 To read temperature, we will create a -like object by passing a instance into a constructor (read about ) and finally into a constructor: Matryoshka-doll Pin OneWire 1-Wire DS18X20 pin = machine.Pin(12)wire = onewire.OneWire(pin)ds = ds18x20.DS18X20(wire) Note that if the output of the following command is an empty list ( ), the sensor couldn't be found. Check your wiring! [] Now, we can ask to scan for connected devices, and return their addresses: ds ds.scan() Output: [bytearray(b'(\xee3\x0c"\x15\x004')] returns a of device addresses in format. Yours may look slightly different. Since we only have one, we can save its address to a variable. To read temperature data, we tell the 1-Wire bus to reset via , take a short pause of 750ms (in case you're pasting this): ds.scan() list bytearray ds.convert_temp() import timeaddr = ds.scan().pop()ds.convert_temp()time.sleep_ms(750)temp = ds.read_temp(addr)temp Output: 19.875 This reading is in Celsius. If you’re like me, you don’t speak Celsius, so maybe you want to convert it to Fahrenheit: (temp * 1.8) + 32 Output: 67.775 …which is right around what I expected! Let’s take what we’ve done and create a new file, : temperature.py import timefrom machine import Pinfrom onewire import OneWirefrom ds18x20 import DS18X20 class TemperatureSensor: """ Represents a Temperature sensor """ def __init__(self, pin): """ Finds address of single DS18B20 on bus specified by `pin` :param pin: 1-Wire bus pin :type pin: int """ self.ds = DS18X20(OneWire(Pin(pin))) addrs = self.ds.scan() if not addrs: raise Exception('no DS18B20 found at bus on pin %d' % pin) # save what should be the only address found self.addr = addrs.pop() def read_temp(self, fahrenheit=True): """ Reads temperature from a single DS18X20 :param fahrenheit: Whether or not to return value in Fahrenheit :type fahrenheit: bool :return: Temperature :rtype: float """ self.ds.convert_temp() time.sleep_ms(750) temp = self.ds.read_temp(self.addr) if fahrenheit: return self.c_to_f(temp) return temp @staticmethod def c_to_f(c): """ Converts Celsius to Fahrenheit :param c: Temperature in Celsius :type c: float :return: Temperature in Fahrenheit :rtype: float """ return (c * 1.8) + 32 Disconnect from the REPL. Upload via : temperature.py ampy $ ampy -p /dev/tty.SLAB_USBtoUART put temperature.py Then we can open our REPL once again, and try it: from temperature import TemperatureSensort = TemperatureSensor(12)t.read_temp() # use t.read_temp(False) to return Celsius Seems to have warmed up a bit. Output: 68.7875 Good work! Conclusion of Part One (1) In the first part of this tutorial, we’ve learned how to: Flash an ESP32 dev board with MicroPython Use MicroPython’s REPL to experiment Connect the ESP32 to WiFi Upload and execute MicroPython scripts Read the temperature with a 1-Wire DS18B20 sensor In the forthcoming part of this tutorial, we’ll learn about MQTT, how to publish our temperature data to an MQTT broker, and likewise interface with an MQTT-based cloud “IoT platform”. second This article originally appeared January 8, 2018 on boneskull.com .
Share Your Thoughts