MicroPython API
With MicroPython, you can rapidly prototype and develop applications without delving into low-level programming. Just a few lines of Python code allow you to draw on the display, access the camera, and leverage the FPGA for processing. Plus, you enjoy all the advantages of Python. The best part? It’s completely wireless, and you can access the Python REPL seamlessly over Bluetooth.
The MicroPython firmware for Monocle is a customized version based on the upstream MicroPython project. Thanks to the thriving MicroPython community, we continuously update our firmware to incorporate new features from the upstream project.
Currently, a subset of the standard MicroPython libraries is supported, with periodic additions. We have also included additional modules that facilitate easy interaction with the Monocle hardware. To familiarize yourself with all the features, make sure to explore the MicroPython docs site and our own documentation.
- The introduction video is using the former version of the API.
- Before trying anything, you might want to update the firmware and FPGA by visiting https://repl.brilliant.xyz and following the indications or manually.
The REPL enables you to explore the hardware capabilities and prototype your applications line by line before final implementation.
As you embark on building your applications, you can conveniently save them directly onto your Monocle device. Upon booting up, Monocle will automatically attempt to execute a main.py
file if you have created one. Using the Brilliant AR Studio, you can easily create, save, and edit files directly on your Monocle.
Typing help('modules')
lists all the modules contained on the device:
>>> help('modules')
__main__ asyncio/core display os
_asyncio asyncio/event errno random
_camera asyncio/funcs fpga re
_display asyncio/lock gc select
_mountfs asyncio/stream hashlib struct
_rtt binascii io sys
_splashscreen bluetooth json time
_test builtins led touch
_update camera math uasyncio
array collections microphone update
asyncio/__init__ device micropython vgr2d
Plus any modules on the filesystem
To explore a specific module, import it, and call help(module_name)
:
>>> import device
>>> help(device)
object <module ''> is of type module
NAME -- monocle
mac_address -- <function>
VERSION -- v23.181.0720
GIT_TAG -- 813d559bc
GIT_REPO -- https://github.com/brilliantlabsAR/monocle-micropython
battery_level -- <function>
reset -- <function>
reset_cause -- <function>
prevent_sleep -- <function>
force_sleep -- <function>
is_charging -- <function>
Storage -- <class 'Storage'>
To gain a clear understanding of the purpose and return values of each function or class, refer to the MicroPython API reference provided below.
Library reference
builtins
device
– Monocle specificdisplay
– Monocle specificcamera
– Monocle specificmicrophone
– Monocle specifictouch
– Monocle specificled
– Monocle specificfpga
– Monocle specificbluetooth
- Monocle specifictime
– Monocle specificupdate
- Monocle specificgc
math
micropython
uarray
uasyncio
ubinascii
ucollections
uerrno
uhashlib
uio
ujson
uos
urandom
ure
uselect
ustruct
usys
builtins
Standard MicroPython built in functions are supported.
Example
import builtins
help(builtins) # Lists all of the built-in MicroPython functions
device
– Monocle specific
The device class contains general information about the Monocle’s hardware and firmware.
Members | Description |
---|---|
NAME constant | Constant which holds 'monocle' as a string. |
mac_address() function | Returns the 48-bit MAC address as a 17 character string in the format 'xx:xx:xx:xx:xx:xx' , where each xx is a byte in lowercase hex format. |
VERSION constant | Constant containing the firmware version as a 12 character string. E.g. 'v22.342.1252' . |
GIT_TAG constant | Constant containing the build git tag as a 9 character string. |
GIT_REPO constant | Constant containing the project git repo as a string. |
battery_level() function | Returns the battery level percentage as an integer. |
reset() function | Resets the device. |
reset_cause() function | Returns the reason for the previous reset or startup state. These can be either: - 'POWERED_ON' if the device was powered on normally- 'SOFTWARE_RESET' if device.reset() was called- 'CRASHED' if the device had crashed. |
prevent_sleep(enable) function | Enables or disables sleeping of the device when put back into the charging case. Sleeping is enabled by default. If no argument is given. The currently set value is returned. WARNING: Running monocle for prolonged periods may result in display burn in, as well as reduced lifetime of components. |
force_sleep() function | Puts the device to sleep. All hardware components are shut down, and Bluetooth is disconnected. Upon touching either of the touch pads, the device will wake up, and reset. |
is_charging() function | Returns True if the Monocle is currently charging. Useful only when prevent_sleep() is enabled, otherwise the Monocle automatically turns off when placed on charge. |
Storage(start, length) class | The Storage class is used internally for initializing and accessing the file system. This class shouldn’t be accessed, unless you want to reformat the internal storage. To learn more about how MicroPython handles user files, have a look at the documentation here and here. |
Example
import device
device.VERSION # Prints the device firmware version
device.battery_level() # Returns the current battery level as a percentage
display
– Monocle specific
The display module allows for drawing to the micro OLED display.
Members | Description |
---|---|
Text(string, x, y, color, justify=TOP_LEFT) class | Creates a text object at the coordinate x, y which can be passed to display.show() . string can be any string, and color should be an RGB hex value with the format 0xRRGGBB . If the justify parameter is given, the text will be justified accordingly from the x, y coordinate. |
Rectangle(x1, y1, x2, y2, color) class | Creates a rectangle object which can be passed to display.show() . x1, y1 and x2, y2 define each corner of the rectangle. color should be an RGB hex value with the format 0xRRGGBB . |
Fill(color) class | Creates a colored box object which fills the entire screen, and can be passed to display.show() . color should be an RGB hex value with the format 0xRRGGBB . |
Line(x1, y1, x2, y2, color, thickness=1) class | Creates a line object from x1, y1 to x2, y2 which can be passed to display.show() . color should be an RGB hex value with the format 0xRRGGBB , and thickness can optionally be provided to override the default line thickness in pixels. |
VLine(x, y, height, color, thickness=1) class | Creates a vertical line object from position x, y with a length of height which can be passed to display.show() . color should be an RGB hex value with the format 0xRRGGBB , and thickness can optionally be provided to override the default line thickness in pixels. |
HLine(x, y, width, color, thickness=1) class | Creates an horizontal line object from position x, y with a length of width which can be passed to display.show() . color should be an RGB hex value with the format 0xRRGGBB , and thickness can optionally be provided to override the default line thickness in pixels. |
Polygon([x1, y1, ... xn, yn], color) class | Creates a polygon object which can be passed to display.show() . The first parameter should be a list of coordinates, and color should be an RGB hex value with the format 0xRRGGBB . Polygons are always closed shapes, therefore if the last coordinate does not close the shape, it will be closed automatically. |
Polyline([x1,y1,...xn,yn],color,thickness=1) class | Similar to the Polygon object, Polyline creates a shape based on a list of coordinates. Unlike Polygon, Polyline does not need to be a closed shape. color should be an RGB hex value with the format 0xRRGGBB , and thickness can optionally be provided to override the default line thickness in pixels. |
show(object_1, ... object_n) function | Prints to the display. The passed arguments can be any number of Text, Line, Rectangle, Polygon, or Polyline objects, or any number of lists containing such objects. Objects are layered front to back, i.e. object_1 is shown on top of object_n . |
move(x, y) function | move() can be called as a class method on any printable object to translate its position. x and y will move the object relative to its current position. |
move([objects], x, y) function | Additionally, move() can be called as a standard function to move a list of objects together. This is useful for grouping printable objects together and moving them as layers. |
color(color) function | color() can be called as a class method on any printable object to change its color. color should be an RGB hex value with the format 0xRRGGBB . |
color([objects], color) function | Additionally, color() can be called as a standard function to change the color on a whole list of objects. color should be an RGB hex value with the format 0xRRGGBB . |
brightness(level) function | Sets the display’s brightness. level can be 0 (dimmest), 1, 2, 3, or 4 (brightest). Level 3 is the default. |
clear() function | Clears the display. |
CLEAR constant | Equal to 0x000000 . |
RED constant | Equal to 0xad2323 . |
GREEN constant | Equal to 0x1d6914 . |
BLUE constant | Equal to 0x2a4bd7 . |
CYAN constant | Equal to 0x29d0d0 . |
MAGENTA constant | Equal to 0x8126c0 . |
YELLOW constant | Equal to 0xffee33 . |
WHITE constant | Equal to 0xffffff . |
GRAY1 constant | Equal to 0x1c1c1c . |
GRAY2 constant | Equal to 0x383838 . |
GRAY3 constant | Equal to 0x555555 . |
GRAY4 constant | Equal to 0x717171 . |
GRAY5 constant | Equal to 0x8d8d8d . |
GRAY6 constant | Equal to 0xaaaaaa . |
GRAY7 constant | Equal to 0xc6c6c6 . |
GRAY8 constant | Equal to 0xe2e2e2 . |
TOP_LEFT constant | Justifies a text object on its x, y coordinate to the top left. |
TOP_CENTER constant | Justifies a text object on its x, y coordinate to the top center |
TOP_RIGHT constant | Justifies a text object on its x, y coordinate to the bottom center |
MIDDLE_LEFT constant | Justifies a text object on its x, y coordinate to the middle left |
MIDDLE_CENTER constant | Justifies a text object on its x, y coordinate to the top right |
MIDDLE_RIGHT constant | Justifies a text object on its x, y coordinate to the middle right |
BOTTOM_LEFT constant | Justifies a text object on its x, y coordinate to the bottom left |
BOTTOM_CENTER constant | Justifies a text object on its x, y coordinate to the middle center |
BOTTOM_RIGHT constant | Justifies a text object on its x, y coordinate to the bottom right |
WIDTH constant | The display width in pixels. Equal to 640. |
HEIGHT constant | The display height in pixels. Equal to 400. |
FONT_WIDTH constant | The font width in pixels. Equal to 24. |
FONT_HEIGHT constant | The font height in pixels. Equal to 48. |
This shows, for a given coordinate (x,y)
used as reference, where the text will be placed:
import display
# Place some text in the middle of the screen with a line underneath
text = display.Text('Hello world', 320, 200, display.WHITE, justify=display.MIDDLE_CENTER)
line = display.Line(175, 230, 465, 230, display.WHITE)
display.show(text, line)
# Group the line and the text together to change the color of both at the same time
group = [text, line]
display.color(group, display.GREEN)
display.show(group)
# Move the line above the text, and print everything again
line.move(0, -75)
display.show(group)
# Create a white polygon with a red outline and print everything again
poly = display.Polygon([0, 0, 639, 399, 0, 399], display.WHITE)
outline = display.Polyline([0, 0, 639, 399, 0, 399, 0, 0], display.RED)
display.show(text, line, outline, poly)
camera
– Monocle specific
The camera module allows for capturing images and transferring them to another device over Bluetooth.
Members | Description |
---|---|
capture() function | Issues an instruction to the FPGA to start capturing a single image. Always overwrites any previously captured image. Once capture() returns, the image data can be read out using read() . |
read(bytes=254) function | Reads out a number of bytes from the image frame buffer. bytes can be overridden to read out less than 254 bytes. A maximum of 254 bytes may be read out at a time. Multiple reads are required to read out an entire image. Once an entire image is read, and no bytes remain, read() will return None . If the output mode is set to RGB , a known number of bytes can be read out, however in JPEG mode, the total number of bytes will vary. |
RGB constant | String constant which represents a RGB565 output format. |
JPEG constant | String constant which represents a JPEG output format. |
Example
import camera
import bluetooth
import ubinascii
# Capture a JPEG image and transfer it over the Bluetooth raw data service
camera.capture()
while data := camera.read(bluetooth.max_length()):
bluetooth.send(data)
# Capture a JPEG image and shows it as base64 in the REPL (less efficient)
camera.capture()
while data := camera.read(bluetooth.max_length()):
print(binascii.b2a_base64(data))
microphone
– Monocle specific
The microphone module allows for capturing audio and streaming it to another device over Bluetooth.
Members | Description |
---|---|
record(seconds=5.0,sample_rate=16000,bit_depth=16) function | Issues an instruction to the FPGA to start recording audio for a number of seconds . Always clears previously recorded audio. While recording is ongoing, data can be read out using read() . sample_rate can be either 16000 or 8000. bit_depth can be either 16 or 8. |
read(samples) function | Reads out a number of recorded audio samples from the FPGA as a bytearray. Samples are either signed 16bit values, or signed 8bit values, depending on what bit_depth was previously set to during recording. Up to 127 samples can be read at a time. Note if using 16bit mode, the number of bytes returned are always twice the sample value. |
Example
import microphone
# Starts a new 4 second recording with 8khz sample rate, and signed 8bit output
microphone.record(seconds=4.0, bit_depth=8, sample_rate=8000)
time.sleep(0.5) ## A short time is needed to let the FPGA prepare the buffer
samples = bluetooth.max_length() // 2
while True:
chunk1 = microphone.read(samples)
chunk2 = microphone.read(samples)
if chunk1 == None:
break
elif chunk2 == None:
bluetooth_send_message(chunk1)
else:
bluetooth_send_message(chunk1 + chunk2)
touch
– Monocle specific
The touch module allows for reacting to touch events from the capacitive touch pads on Monocle.
Members | Description |
---|---|
callback(pad,callback) function | Attaches a callback handler to one, both or either of the touch pads. If pad is given as 'BOTH' , then both pads must be touched to trigger the callback. If pad is given as 'EITHER' , then touching either of the pads will trigger the callback. Otherwise 'A' or 'B' can be used to assign separate callback functions to each pad individually. callback should be a predefined function taking one argument. This argument will equal the pad which triggered the callback. To unassign a callback, issue callback(pad, None) . To view the currently assigned callback for a particular pad, issue callback(pad) without the second argument. |
state(pad) function | Returns the current touch state of the touch pads. If pad is not specified, either 'A' , 'B' , 'BOTH' or None will be returned depending on which pads are currently touched. If pad is specified, then True or False will be returned for the touch state of that pad. |
A constant | String constant which represents Pad A. |
B constant | String constant which represents Pad B. |
BOTH constant | String constant which represents both pads. |
EITHER constant | String constant which represents either pads. |
Example
import touch
# Define the touch callback function which is triggered upon a touch event
def fn(arg):
if arg == touch.A:
print("button A pressed!")
if arg == touch.B:
print("button B pressed!")
touch.callback(touch.EITHER, fn) # Attaches the same callback to either of the touch pads
led
– Monocle specific
The LED module contains functions to control the red and green LED on the front of Monocle.
Members | Description |
---|---|
on(color) function | Illuminates an led. color can be either led.RED or led.GREEN . |
off(color) function | Turns off an led. color can be either led.RED or led.GREEN . |
RED constant | String constant which represents the red led. |
GREEN constant | String constant which represents the green led. |
Example
import led
led.on(led.GREEN) # Turns on the green LED
led.on('GREEN') # Also turns on the green LED
led.off(led.GREEN) # Turns off the green LED
led.on(led.RED) # Turns on the red LED
led.off(led.RED) # Turns off the red LED
fpga
– Monocle specific
The FPGA module allows for communicating with the FPGA over the internal SPI bus.
Members | Description |
---|---|
read(addr, n) function | Reads n number of bytes from the 16-bit address addr , and returns a bytes object. |
write(addr,bytes[]) function | Writes all bytes from a given bytes object bytes[] to the 16-bit address addr . |
run(state) function | run(False) stops and powers down the FPGA. run(True) powers up the FPGA and restarts operation. This function should only be using during the FPGA application update process. Once stopped, the camera and display will no longer be configured, and a device.reset() would be required to reinitialize them. Calling run() without any argument will return the current run state of the FPGA. |
Example
import fpga
fpga.read(0x1234, 5) # Reads 5 bytes from some FPGA register (0x1234 in this case)
fpga.write(0x1234, b'\x01\x02\x03') # Writes three bytes (1, 2, 3) to the FPGA register 0x1234
fpga.write(0x1234, b'hello fpga') # Writes the byte string 'hello fpga' to the FPGA register
fpga.write(0x1234, b'') # Doesn't write anything specific, but acts as a command or trigger
bluetooth
- Monocle specific
The
bluetooth
module can be used to transfer byte data between Monocle and the host Bluetooth device. Bytes can contain any arbitrary data, and avoids having to pollute the REPL interface with printing data out as strings. The raw data service is used for all communications under thebluetooth
module.
Members | Description |
---|---|
send(bytes[]) function | Sends data from a bytes object bytes[] over Bluetooth using the raw data service. The length of bytes[] must be equal to or less than the value returned by the bluetooth.max_length() function. |
receive_callback(callback) function | Assigns a callback to receive data over the raw data service. callback must be a predefined function taking one argument. The value of the argument will be a bytes object bytes[] containing the received data. To unbind the callback, issue this function with callback set as None . If callback isn’t given when issuing this function, the currently set callback will be returned if it is set. |
connected() function | Returns True if the raw data service is connected, otherwise returns False . |
max_length() function | Returns the maximum data size the Bluetooth host allows for single transfers. |
Example
import bluetooth
bluetooth.connected() # Returns True if the Bluetooth is connected. Always true when using the REPL, but useful for saved scripts
len = bluetooth.max_length() # Returns the maximum payload size we can send in one go
str = "hello world"
bluetooth.send(str[:len]) # Sends the string 'hello world' to the host. Limited by the maximum payload size
# Define a callback function which is triggered upon reception of Bluetooth data from the host
def fn(bytes):
print(bytes)
bluetooth.receive_callback(fn) # Attaches the above function to the receive callback
time
– Monocle specific
The time module allows for getting/setting the time and date as well as adding delays into your programs.
Members | Description |
---|---|
time(secs) function | Sets or gets the current system time in seconds. If secs is not provided, the current time is returned, otherwise the time is set according to the value of secs . secs should be referenced from midnight on the 1st of January 1970. |
now(epoch) function | Returns a dictionary containing a human readable date and time. If no argument is provided, the current system time is used. If epoch is provided, that value will be used to generate the dictionary. The epoch should be referenced from midnight on the 1st of January 1970. |
zone(offset) function | Sets or gets the time zone offset from GMT as a string. If offset is provided, the timezone is set to the new value. offset must be provided as a string, eg “8:00”, or “-06:30”. |
mktime(dict) function | The inverse of now() . Converts a dictionary provided into an epoch timestamp. The returned epoch value will be referenced from midnight on the 1st of January 1970. |
sleep(secs) function | Sleep for a given number of seconds. |
sleep_ms(msecs) function | Sleep for a given number of milliseconds. |
ticks_ms() function | Returns a timestamp in milliseconds since power on. |
ticks_add(ticks, delta) function | Offsets a timestamp value by a given number. Can be positive or negative. |
ticks_diff(ticks1,ticks2) function | Returns the difference between two timestamps. i.e. ticks1 - ticks2 , but takes into consideration if the numbers wrap around. |
Example
import utime
time.sleep(1.5) # Sleeps for 1.5 seconds
time.sleep_ms(100) # Sleeps for 500ms
time.time(1681236168) # Sets the device system time using UTC timestamp
time.time() # Returns the current time (as a UTC timestamp), based on the system time
time.now() # Returns the current time as a human readable dictionary
update
- Monocle specific
The update module allows for firmware upgrades of the MicroPython firmware, as well as the FPGA application over bluetooth. This library use used automatically by the Brilliant REPL for firmware updates. You generally shouldn’t need to call these manually, unless you’re implementing your own update process.
Members | Description |
---|---|
micropython() function | Puts the device into over-the-air device firmware update mode. The device will stay in DFU mode for 5 minutes or until an update is finished being performed. The device will then restart with an updated MicroPython firmware. |
Fpga class | Fpga contains three low level functions which are used to update the FPGA. Fpga.erase() erases the existing application, Fpga.write(bytes[]) sequentially writes in the new application, and Fpga.read(address, length) can be used to read back and verify the application. For details on how the FPGA application is updated, check out the FPGA application updates section. |
Example
import update
update.micropython() # Reboots the device into update mode for updating the MicroPython firmware
gc
Standard MicroPython garbage collection is supported.
Example
import gc
# Shows the current memory usage as a percentage
mem_used = gc.mem_alloc() / (gc.mem_alloc() + gc.mem_free())
print('{:d}%'.format(round(mem_used * 100)))
# Manually runs the garbage collector
gc.collect()
math
Standard MicroPython math functions are supported.
micropython
Standard MicroPython internals are supported.
uarray
Standard MicroPython arrays are supported.
uasyncio
Standard MicroPython asynchronous scheduling is supported.
ubinascii
Standard MicroPython binary/ASCII conversions are supported.
ucollections
Standard MicroPython collection and container types are supported.
uerrno
Standard MicroPython system error codes are supported.
uhashlib
Standard MicroPython hashing algorithms are supported.
uio
Standard MicroPython IO streams are supported.
ujson
Standard MicroPython JSON handling is supported.
uos
Standard MicroPython operating system services for file handling are supported.
urandom
Standard MicroPython random number generation is supported.
ure
Standard MicroPython regular expressions are supported.
uselect
Standard MicroPython stream event waiting is supported.
ustruct
Standard MicroPython struct primitives are supported.
usys
Standard MicroPython system specific functions are supported.
Under the hood
This section describes how data is transferred to and from Monocle over Bluetooth.
For testing everything Bluetooth related, try our the nRF Connect App, for desktop, Android, or iOS.
Communication
All MicroPython communication, i.e. the REPL interface, is accessed via a single Bluetooth Service containing two Characteristics.
- Serial service UUID: 6e400001-b5a3-f393-e0a9-e50e24dcca9e
- Serial RX characteristic UUID: 6e400002-b5a3-f393-e0a9-e50e24dcca9e
- Serial TX characteristic UUID: 6e400003-b5a3-f393-e0a9-e50e24dcca9e
The RX characteristic is write only, and transports serial string data from the central BLE device to Monocle. The TX characteristic is notification only and delivers messages back from Monocle, to the central BLE device.
Each characteristic transports string data of any length up to the negotiated MTU size - 3 bytes. Longer strings must be chopped up and will be automatically rejoined on the receiving side by Monocle. Likewise if Monocle wishes to send longer responses than can fit into a single MTU payload, the data will arrive sequentially, and can be concatenated by the central Bluetooth app. The diagram below describes how communication operates between Monocle and a central device.
A secondary Bluetooth Service, again containing two Characteristics, is used to transport raw data such as image, audio and firmware update data. The mechanism for the raw data service is similar to the serial data service, aside from the fact that null
or 0
characters may also be included within the payload.
- Raw data service UUID: e5700001-7bac-429a-b4ce-57ff900f479d
- Raw data RX Characteristic UUID: e5700002-7bac-429a-b4ce-57ff900f479d
- Raw data TX Characteristic UUID: e5700003-7bac-429a-b4ce-57ff900f479d
Raw data transfer may often be bigger than a single MTU payload. Similarly to the serial data, it may need to be broken up into pieces. To help reconstruct the packets sent or received by Monocle, a flag is present at the start of each payload to determine if it’s either a starting payload, middle payload, end payload, or a small single buffer payload. The exact mechanisms for different types of data transfer are outlined below.
Downloading media
Media files such as audio and images may be downloaded from the Monocle using the raw data service mentioned in the Communication section. Files may also be sent asynchronously by Monocle when they are ready.
Due to the small payload size of Bluetooth packets, the file may be spit into many chunks and need to be recombined by the receiving central device. A flag at the start of each payload indicates if it is the START, MIDDLE or END of the file. A very small file may also be transferred using the SMALL flag.
The first payload includes metadata about the file such as the filename (with a relevant file extension) and the file size. The sequence diagram below describes how a file is broken into several parts and the data can be recombined to construct the full file:
Firmware updates
Firmware updates are handled in two parts:
MicroPython firmware which runs on the Nordic nRF52 Bluetooth IC.
The nRF52 contains a bootloader which is able to wirelessly update the firmware. To enter the bootloader mode, the following command can be used:
import update update.micropython()
Monocle will then reboot, and show as a new Bluetooth device named
DFUTarg
. The Nordic DFU protocol can then be used to perform an update.If you’re using the AR Studio for VSCode, firmware updates are handled automatically, however if you wish to implement your own. The WebREPL project serves as an example of how to implement your own firmware update protocol.
FPGA binary image which is used for display, camera and microphone processing.
FPGA images can also be loaded wirelessly using Python commands. For simplicity, the FPGA binary is represented here as base64 encoded data, however the
bluetooth.receive_callback()
function can also be used to send bytes more efficiently over the air.Again, the WebREPL project serves as an example of how to implement your own FPGA update protocol.
import device import update import ubinascii # Erases the entire application update.Fpga.erase() # Send exactly 444430 bytes. Each write appends to the already written data # There's no limitation on how many bytes you can send, but it should be # tuned to match the host MTU length for a faster upload speed update.Fpga.write(ubinascii.a2b_base64(b'TWFueSBoYW5kcyBtYWt...lIGxpZ2h0IHdvcmsu')) ... update.Fpga.write(ubinascii.a2b_base64(b'yBtYWtTWFueSBoYW5kc...cmslIGxpZIHd2h0vu')) # Write the special "done" flag at the end of the file update.Fpga.write(b'done') device.reset()