Programming reference
=====================
| Start coding in MicroPython on systematica products using code snippets from this programming reference!
| Both alpha and omega boards come with `MicroPython `_ preinstalled, ready to use.
.. seealso::
| Check out the official `MicroPython documentation `_ for more in-depth information on MicroPython itself.
| See also the official website `MicroPython.org `_ and the official `MicroPython Github repository `_.
.. admonition:: official MycroPython documentation
:class: note
Boxes like this throughout the page provide direct links to specific relevant pages in the official MicroPython documentation.
Digital IOs
___________
Digital Inputs
~~~~~~~~~~~~~~
.. tabs::
.. group-tab:: alpha:ONE
| alpha:ONE provides 12 digital inputs, compatible with 11-36V input voltage.
| The inputs are isolated, therefore at least one reference ground has to be connected to the input ground on one of the pins labeled `GND` between the inputs (by default they are all connected together).
.. group-tab:: omega:ONE
| omega:ONE provides 20 digital inputs, compatible with 9-36V input voltage.
| The digital inputs are isolated, however by default their ground is connected together with the system ground for ease of use. It is therefore not necessary to connect the ground on the input terminals.
| If you need to isolate the input ground from the system ground, please remove the jumper resistor R35.
.. group-tab:: omega:TWO
| omega:TWO provides 20 digital inputs
Each digital input has a green LED that lights up when voltage is applied to the input (meaning the input state is then logic high).
The state of the input can be read simply by calling the method ``value()`` from the corresponding ``Pin`` object:
.. code-block:: python
input_1.value() # returns the input logic state, either 0 or 1
.. admonition:: official MycroPython documentation
:class: note
``pyb.Pin`` `class `_
Digital outputs
~~~~~~~~~~~~~~~
.. tabs::
.. group-tab:: alpha:ONE
| alpha:ONE provides six power outputs compatible with a supply voltage between 9 and 48V and delivering up to 32A.
| The outputs are short-circuit protected. A red LED will light up if there is a fault.
| At high current levels it is recommended to monitor the board temperature. Cooling may be required in some applications.
.. group-tab:: omega:ONE
omega:ONE has H-Bridges instead of discrete power outputs, see the next paragraph.
.. group-tab:: omega:TWO
| omega:TWO provides 20 power outputs
| The outputs can be switched on and off by calling the methods ``high()`` and ``low()``, respectively.
| Each output has a yellow LED that lights up to show when the output has been switched on.
| The outputs are high-side switches, meaning they switch the supply voltage.
| The low side of the load is permanently connected to ground.
| This example switches on output 1 for one second:
.. code-block:: python
from pyb import delay
output_1.high() # output_1 is switched on
pyb.delay(1000)
output_1.low() # output_1 is switched off
.. attention::
| Make sure inductive loads such as solenoids and relays have a free-wheeling diode installed anti-parallel to the load. This is critical to avoid voltage spikes when the load is turned off.
.. admonition:: official MycroPython documentation
:class: note
| ``pyb.Pin`` `class `_
H-Bridges
~~~~~~~~~
.. tabs::
.. group-tab:: alpha:ONE
alpha:ONE does not provide H-Bridge outputs.
.. group-tab:: omega:ONE
| omega:ONE provides four H-Bridges able to control four reversible loads, such as DC-motors or bidirectional solenoids.
| Each H-Bridge supports a supply voltage between 9 and 45V, with a maximum output current of 1.75 Arms and 2.5 A peak.
| Note: the four H-Bridges can also be used to control eight non-reversible loads such as unidirectional solenoids, lamps or relays. In this case the eight outputs can be wired either as high-side or low-side switches, as needed. The other side of the load is connected permanently to ground or 24V respectively.
| At high current levels it is recommended to monitor the board temperature. Cooling may be required in some applications.
| Code example coming soon.
.. group-tab:: omega:TWO
omega:TWO does not provide H-Bridge outputs.
LEDs
____
MicroPython LEDs
~~~~~~~~~~~~~~~~
.. tabs::
.. group-tab:: alpha:ONE
| alpha:ONE has one RGB LED, accessible through the module ``pyb``, where each color is controlled by a different LED object
.. list-table::
:widths: 11 20
:align: center
* - ``LED(1)``
- red
* - ``LED(2)``
- blue
* - ``LED(3)``
- green
The LEDs can be turned on and off simply using the ``on()`` and ``off()`` methods:
.. code-block:: python
import pyb
pyb.LED(1).on() # red LED is switched on
pyb.delay(1000)
pyb.LED(1).off() # red LED is switched off
For better code readability, you can also use a more explanatory variable:
.. code-block:: python
import pyb
LED_red = pyb.LED(1)
LED_red.on() # red LED is switched on
pyb.delay(1000)
LED_red.off() # red LED is switched off
Finally, the ``toggle()`` method also exists:
.. code-block:: python
import pyb
LED_red = pyb.LED(1)
LED_red.toggle() # red LED is toggled
.. group-tab:: omega:ONE
| omega:ONE only has a single user-controllable blue LED, accessible through the module ``pyb``.
.. list-table::
:widths: 11 20
:align: center
* - ``LED(1)``
- blue
The LEDs can be turned on and off simply using the ``on()`` and ``off()`` methods:
.. code-block:: python
import pyb
pyb.LED(1).on() # blue LED is switched on
pyb.delay(1000)
pyb.LED(1).off() # blue LED is switched off
For better code readability, you can also use a more explanatory variable:
.. code-block:: python
import pyb
LED_blue = pyb.LED(1)
LED_blue.on() # blue LED is switched on
pyb.delay(1000)
LED_blue.off() # blue LED is switched off
Finally, the ``toggle()`` method also exists:
.. code-block:: python
import pyb
LED_blue = pyb.LED(1)
LED_blue.toggle() # blue LED is toggled
.. group-tab:: omega:TWO
| omega:TWO only has a single user-controllable blue LED, accessible through the module ``pyb``.
.. list-table::
:widths: 11 20
:align: center
* - ``LED(1)``
- blue
The LEDs can be turned on and off simply using the ``on()`` and ``off()`` methods:
.. code-block:: python
import pyb
pyb.LED(1).on() # blue LED is switched on
pyb.delay(1000)
pyb.LED(1).off() # blue LED is switched off
For better code readability, you can also use a more explanatory variable:
.. code-block:: python
import pyb
LED_blue = pyb.LED(1)
LED_blue.on() # blue LED is switched on
pyb.delay(1000)
LED_blue.off() # blue LED is switched off
Finally, the ``toggle()`` method also exists:
.. code-block:: python
import pyb
LED_blue = pyb.LED(1)
LED_blue.toggle() # blue LED is toggled
.. note::
| MicroPython LEDs are also directly accessed by MicroPython:
| LED(1) is used to signal when data is being written to the flash memory.
.. admonition:: official MycroPython documentation
:class: note
| ``pyb.LED`` `class `_
RGB Status LEDs
~~~~~~~~~~~~~~~
.. tabs::
.. group-tab:: alpha:ONE
| alpha:ONE provides three additional user-controllable RGB status LEDs, accessed using the module ``status_LEDs``.
The object ``status_LEDs`` has to be instantiated first:
.. code-block:: python
import status_LEDs
status_LEDs = status_LEDs.status_LEDs() # instantiate the object status_LEDs
| To set one of the LEDs to a specific color you can use the method ``set_LED_color(LED_nr, color, brightness)``
.. list-table::
:widths: 5 20
:align: center
* - ``LED_nr``
- 1 to 3
* - ``color``
- 'red', 'green', 'blue', 'yellow', 'orange', 'turquoise', 'aqua', 'pink', 'white', 'off'
* - ``brightness``
- 0 to 31
.. code-block:: python
status_LEDs.set_LED_color(LED_nr=1,color='red') # with default brightness = 1
status_LEDs.set_LED_color(LED_nr=2,color='green', brightness=5)
status_LEDs.set_LED_color(3, 'blue') # shorter, using positional arguments
You can also set the LEDs to any RGB color using the method ``set_LED_rgb(LED_nr, red, green, blue, brightness)``
.. list-table::
:widths: 5 5
:align: center
* - ``LED_nr``
- 1 to 3
* - ``red``
- 0 to 255
* - ``green``
- 0 to 255
* - ``blue``
- 0 to 255
* - ``brightness``
- 0 to 31
.. code-block:: python
status_LEDs.set_LED_rgb(LED_nr=1, red=167, green=23, blue=255, brightness=3)
To turn it off you can set ``color = 'off'``:
.. code-block:: python
status_LEDs.set_LED_color(LED_nr=1, color='off')
Though setting ``brightness = 0`` would also lead to the same result.
.. group-tab:: omega:ONE
| omega:ONE does not have RGB status LEDs
.. group-tab:: omega:TWO
| omega:TWO does not have RGB status LEDs
Timing and interrupts
_____________________
Delay and timing
~~~~~~~~~~~~~~~~
Insert a delay in the execution of a program, using the module ``pyb``
.. code-block:: python
import pyb
pyb.delay(100) # wait for 100 milliseconds
pyb.udelay(10) # wait for 10 microsenconds
Insert a delay in the execution of a program, using the module ``time``
.. code-block:: python
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
Measure the duration of a program in milliseconds
.. code-block:: python
from pyb import delay, millis, elapsed_millis
start_ms_count = millis() # store the starting time
delay(100) # the program or code block you want to measure
delta_t = elapsed_millis(start_ms_count)
print("elapsed time:", delta_t, "ms")
Measure the duration of a program in microseconds
.. code-block:: python
from pyb import delay, micros, elapsed_micros
start_us_count = micros() # store the starting time
delay(100) # the program or code block you want to measure
delta_t = elapsed_micros(start_us_count)
print("elapsed time:", delta_t, "us")
alternatively you can also use the ``time`` module
.. code-block:: python
import time
start_ms_count = time.ticks_ms() # store the start time in milliseconds
delay(100) # the program or code block you want to measure
delta_t = time.ticks_diff(time.ticks_ms(), start_ms_count) # compute time difference
print("elapsed time:", delta_t, "ms")
.. admonition:: official MycroPython documentation
:class: note
| ``pyb`` `time related functions `_
| ``time`` `time related functions `_
External interrupts
~~~~~~~~~~~~~~~~~~~
| External interrupts automatically invoke a user-provided callback function each time a digital input changes value.
| They can be enabled on the individual digital inputs using the class ``pyb.ExtInt(pin, mode, pull, callback)``,
| where ``mode`` can have following values:
.. list-table::
:widths: 5 10
:align: center
* - ``ExtInt.IRQ_RISING``
- The interrupt will be triggered only by rising edges
* - ``ExtInt.IRQ_FALLING``
- The interrupt will be triggered only by falling edges
* - ``ExtInt.IRQ_RISING_FALLING``
- The interrupt will be triggered both by rising and falling edges
| ``pull`` is defined like with any `GPIO input pin `_.
| The following example demonstrates toggling the LED each time the digital input goes from low to high (flip-flop logic):
.. code-block:: python
from pyb import Pin, ExtInt
led = pyb.LED(1)
# first define the callback function
def cb_func():
led.toggle()
# this will call the callback function cb_func() each time the pin object input_1 goes high, toggling the LED
ext_input_1 = ExtInt(input_1, ExtInt.IRQ_RISING, Pin.PULL_NONE, cb_func)
.. important::
Special care has to be given when writing callback functions (also called interrupt handlers) since they can be invoked at any time during the execution of the code and could therefore have unwanted consequences. For more information on this topic, see the detailed information in the official MicroPython documentation: `Writing interrupt handlers `_.
.. attention::
| Be careful with mecanical switches as they generally bounce when opening and closing, firing off multiple interrupts rather than just one. Hardware debouncing, such as a simple RC circuit, is recommended in this case.
| See `A Guide to Debouncing `_ if you're interested in a deep-dive into the topic.
| Note that the isolation circuit present on the inputs will already provide some filtering of the fastest bounces.
| As a minimum, at least software debouncing should be implemented, see the `external interrupt datalogger example `_.
| Software debouncing is not ideal, since multiple interrupts will still fire off, and need to be processed, but it should at least ensure that the software generally behaves as expected.
.. admonition:: official MycroPython documentation
:class: note
| ``pyb.ExtInt`` `class `_
| ``pyb.Pin`` `class `_
| `Writing interrupt handlers `_
Timers
~~~~~~
Timers are hardware peripherals that can be used for a variety of tasks, from frequency and pulse counting to generating PWM signals.
See the `official Micropython documentation `_ for all the supported features.
| Here is an example where it is used to repeatedly trigger a callback function at a precise frequency given in Hz:
.. code-block:: python
import pyb
# first define the callback function
def cb_func(t):
pyb.LED(1).toggle()
# then create the timer and assign the callback function
tim = pyb.Timer(1, freq=1, callback=cb_func) # Timer1 set to a frequency of 1 Hz
Alternatively, the callback function can also be defined or changed after creating the timer, here demonstrated using a lambda function
.. code-block:: python
import pyb
tim = pyb.Timer(1, freq=1) # Timer1 set to a frequency of 1 Hz
tim.callback(lambda t: pyb.LED(1).toggle())
The callback function can be disabled by setting it to ``None``
.. code-block:: python
tim.callback(None)
Using the ``machine`` module, a timer can also be configured to trigger a callback function just once after a delay specified in milliseconds:
.. code-block:: python
import machine
def cb_function(t):
print('test')
tim = machine.Timer()
tim.init(mode=machine.Timer.ONE_SHOT, period=2000, callback=cb_function)
.. admonition:: official MycroPython documentation
:class: note
| ``pyb.Timer`` `class `_
| ``machine.Timer`` `class `_
| `pyboard timer tutorial `_
RTC
~~~
| The ``pyb.RTC`` class provides access to the microcontroller's Real Time Clock, which keeps track of time and date also when unpowered thanks to the CR2032 backup battery.
| The method ``datetime()`` is used both to set and to read the current datetime and uses a tuple with format
| ``(year, month, day, weekday, hours, minutes, seconds, microseconds)``:
.. code-block:: python
from pyb import RTC, delay
rtc = RTC() # instantiate the RTC object
rtc.datetime((2017, 8, 23, 1, 12, 48, 0, 0)) # set a specific date and time
delay(1000) # 1 s delay so you see a difference in the current time
rtc.datetime() # returns a tuple corresponding to the current datetime
If you need a datetime string, such as for a timestamp, you can format it like this:
.. code-block:: python
import pyb
rtc = pyb.RTC() # instantiate the RTC object
t = rtc.datetime()
datetime_string = '{:02d}-{:02d}-{:04d} {:02d}:{:02d}:{:02d}'.format(t[2], t[1], t[0], t[4], t[5], t[6])
print(datetime_string)
Alternatively, the ``time`` module provides access to different time formats, such as the Epoch (in seconds since Jan 1, 2000)
.. code-block:: python
import time
print(time.mktime(time.localtime())) # seconds since Jan 1, 2000
.. admonition:: official MycroPython documentation
:class: note
| ``pyb.RTC`` `class `_
| ``time`` `time related functions `_
Communication interfaces
________________________
Ethernet
~~~~~~~~
.. tabs::
.. group-tab:: alpha:ONE
| alpha:ONE provides an ethernet interface through a Wiznet W5500 ethernet controller.
| The network object can be instantiated as follows:
.. code-block:: python
import network, pyb
W5500_pwr.high() # enable power to the W5500 controller
pyb.delay(10)
eth = network.WIZNET5K(pyb.SPI('W5500'), W5500_cs, W5500_rst)
.. group-tab:: omega:ONE
| omega:ONE provides an ethernet interface using the MAC controller integrated in the microcontroller.
| The network object can be instantiated as follows:
.. code-block:: python
import network
eth = network.LAN()
.. group-tab:: omega:TWO
| omega:TWO does not provide an ethernet interface.
The network interface then needs to be activated and configured, e.g. using DHCP:
.. code-block:: python
eth.active(True)
eth.ifconfig('dhcp')
You can also configure a static IP by passing a tuple ``(ip_address, subnet_mask, gateway, dns_server)``
.. code-block:: python
eth.active(True)
eth.ifconfig(('192.168.1.88','255.255.255.0','192.168.1.1','192.168.1.1'))
This snippet prints out information on the state of the ethernet interface, so you can verify it is connected:
.. code-block:: python
print('eth.status =', eth.status())
print('eth.active =', eth.active())
print('eth.isconnected =', eth.isconnected())
print('eth.ifconfig =', eth.ifconfig())
Look-up the IP address of a host using ``socket`` (it will automatically use the network interface available):
.. code-block:: python
import usocket as socket
ip_addr = socket.getaddrinfo('www.systematica.ch',80)[0][-1][0]
print('www.systematica.ch has address', ip_addr)
| To ping a host / ip address you can use the ``uping`` utility. `Download uping `_, and copy ``uping.py`` to PYBFLASH.
| It can then be imported and used as follows:
.. code-block:: python
from uping import ping
ping('www.systematica.ch', count=4, interval=1000) # ping www.systematica.ch four times with an interval of 1000 ms
Use a ``socket`` to receive data from a server:
.. code-block:: python
import usocket as socket
addr_info = socket.getaddrinfo("towel.blinkenlights.nl", 23)
addr = addr_info[0][-1]
s = socket.socket()
s.connect(addr)
while True:
data = s.recv(500)
print(str(data, 'utf8'), end='')
You can interrupt the program execution with ``Ctrl + C`` as usual
.. admonition:: official MycroPython documentation
:class: note
| ``network`` `module `_
| ``network.WIZNET5K`` `class `_
| ``network.LAN`` `class `_
| ``socket`` `module `_
| `TCP sockets tutorial `_
CAN bus
~~~~~~~
.. tabs::
.. group-tab:: alpha:ONE
| alpha:ONE has a single CAN Bus interface named ``CAN``
| It supports baudrates up to 1 Mbit/s and tolerates bus fault voltages up to +/- 70V.
| The 120 Ohm bus terminator is permanently connected.
.. group-tab:: omega:ONE
| omega:ONE has a single CAN Bus interface named ``CAN``, supporting baudrates up to 1 Mbit/s.
| Solder jumper settings:
.. list-table::
:widths: 3 5 6 10
:header-rows: 1
:align: center
* - jumper
- function
- setting
- result
* - SJ1
- bus terminator
- open
- bus unterminated
* -
-
- closed
- bus terminated with 120 Ohm resistor
* - SJ5
- slew-rate limit
- open
- max 250 kbit/s
* -
-
- lower pads closed
- max 500 kbit/s
* -
-
- upper pads closed
- max 1 Mbit/s
.. group-tab:: omega:TWO
| omega:TWO has a single CAN Bus interface named ``CAN``
CAN Bus test in loopback mode (to be able to test without actual bus nodes connected)
.. code-block:: python
from pyb import CAN
can = CAN('CAN', mode=CAN.LOOPBACK, baudrate=250000)
can.setfilter(0, pyb.CAN.RANGE, 0, (123, 126)) # set a filter to receive messages with id=123, 124, 125, 126
can.send('message!', 123) # send a message with id 123
can.recv(0) # receive message on FIFO 0
CAN Bus test in normal mode (at least one more node has to be present on the bus so, that the messages are acknowledged)
.. code-block:: python
from pyb import CAN
can = CAN('CAN', mode=CAN.NORMAL, baudrate=250000)
can.setfilter(0, pyb.CAN.RANGE, 0, (123, 126)) # set a filter to receive messages with id=123, 124, 125, 126
can.send('message!', 123) # send a message with id 123
can.recv(0) # receive message on FIFO 0
.. admonition:: official MycroPython documentation
:class: note
| ``pyb.CAN`` `class `_
RS422/485
~~~~~~~~~
.. tabs::
.. group-tab:: alpha:ONE
| alpha:ONE provides two RS422/485 Full-duplex interfaces, named ``RS485_1`` and ``RS485_2``.
| They support baudrates up to 20 Mbit/s and tolerate line fault voltages up to +/- 60V.
| The 120 Ohm terminators are permanently connected.
| The low-EMI mode up to 250 kbit/s is software selectable using the ``RS485_x_nSLOW`` pin.
| To use the interface in 4-wire, full-duplex mode, connect AB and YZ and use the following code:
.. code-block:: python
import pyb
RS485_1 = pyb.UART('RS485_1', baudrate=9600) # to use RS485 2 set 'RS485_2'
RS485_1_nRE.low() # pin low enables the receiver
RS485_1_DE.high() # pin high enables the driver permanently, only ok in full-duplex mode (4 wires)
RS485_1_nSLOW.low() # pin low enables low-EMI mode for <= 250 kBit/s
RS485_1.write('Test RS485 full-duplex')
.. group-tab:: omega:ONE
| omega:ONE provides one RS422/485 Full-duplex capable interface, named ``RS485``.
| It supports baudrates up to 16 Mbit/s and the 120 Ohm terminators and slew-rate limits are solder-jumper selectable:
.. list-table::
:widths: 3 5 6 10
:header-rows: 1
:align: center
* - jumper
- function
- setting
- result
* - SJ4
- nRE
- upper pads closed (default)
- nRE connected to DE
* -
-
- lower pads closed
- nRE connected to GND (receiver always on)
* - SJ6
- AB terminator
- open
- rx line unterminated
* -
-
- closed (default)
- rx line terminated with 120 Ohm resistor
* - SJ7
- YZ terminator
- open
- tx line unterminated
* -
-
- closed (default)
- tx line terminated with 120 Ohm resistor
* - SJ8
- half-duplex
- open (default)
- 4-wire full-duplex mode
* -
-
- closed
- 2-wire half-duplex mode (use AB)
* - SJ9
- slew-rate limit
- open (default)
- max 250 kbit/s
* -
-
- lower pads closed
- max 500 kbit/s
* -
-
- upper pads closed
- max 16 Mbit/s
To use the interface in full-duplex (4-wire) mode, connect AB (rx pair) and YZ (tx pair) and use the following code:
.. code-block:: python
import pyb
RS485 = pyb.UART('RS485', baudrate=9600)
RS485_DE.high() # the driver can be left enabled, as the interface is full-duplex
RS485.write('TEST RS485 full-duplex')
To use the interface in half-duplex (2-wire) mode, close SJ8, connect AB and use the following code:
.. code-block:: python
import pyb
RS485 = pyb.UART('RS485', baudrate=9600)
RS485_DE.high() # enable driver before transmitting
RS485.write('TEST RS485 half-duplex')
RS485_DE.low() # the driver needs to be disabled right after each transmission in order to receive
Note that it is necessary to turn off the driver enable pin right after transmitting each message in order to be able to receive the response, as the bus is half-duplex only.
.. group-tab:: omega:TWO
| omega:TWO provides one RS422/485 Full-duplex interface.
.. admonition:: official MycroPython documentation
:class: note
| ``pyb.UART`` `class `_
RS232/485
~~~~~~~~~
.. tabs::
.. group-tab:: alpha:ONE
| alpha:ONE provides a serial interface switchable between RS232 and RS485 half duplex.
| The interface is named ``RS232_485`` can be instantiated and used as follows:
| For RS232 mode, connect Tx, Rx and GND to the device and use following code
.. code-block:: python
import pyb
# set up interface in RS232 mode
RS232_485 = pyb.UART('RS232_485', baudrate=9600)
RS232_485_n232.low() # pin low enables RS232 mode
RS232_485_DE485_F232.low() # pin low enables low-EMI mode for <= 250 kBit/s
# send a test message
RS232_485.write('Test RS232')
| For RS485 half-duplex mode, connect A, B and GND to the device and use following code
.. code-block:: python
import pyb
# set up interface in RS485 half-duplex mode
RS232_485 = pyb.UART('RS232_485', baudrate=9600)
RS232_485_n232.high() # pin high enables RS485 mode
# send a test message
RS232_485_DE485_F232.high() # enable the driver to transmit
RS232_485.write('Test RS485 half-duplex')
RS232_485_DE485_F232.low() # disable the driver right after each transmission in order to receive
Note that it is necessary to turn off the driver enable pin right after transmitting each message in order to be able to receive the response, as the bus is half-duplex only.
.. group-tab:: omega:ONE
omega:ONE does not provide an RS232/485 interface.
.. group-tab:: omega:TWO
omega:TWO does not provide an RS232/485 interface.
.. admonition:: official MycroPython documentation
:class: note
| ``pyb.UART`` `class `_
Multifunction pins
__________________
.. tabs::
.. group-tab:: alpha:ONE
| alpha:ONE provides 21 multifunction pins, which can be used to expand the functionality of the board and connect to expansion boards.
| These pins can be used freely as they are dedicated exclusively to this function and are named ``GPIO_1`` to ``GPIO_21``.
.. group-tab:: omega:ONE
| omega:ONE provides 46 multifunction pins, which can be used to expand the functionality of the board and connect to expansion boards. These pins are however partially shared with other resources on the board and can be accessed with the CPU pin names printed on the board, e.g. using ``pyb.Pin.cpu.A4``.
| Refer to the `pinout documentation `_ to identify which pins are free to be used in your specific application.
.. group-tab:: omega:TWO
| omega:TWO provides 46 multifunction pins, which can be used to expand the functionality of the board and connect to expansion boards. These pins are however partially shared with other resources on the board and can be accessed with the CPU pin names printed on the board, e.g. using ``pyb.Pin.cpu.A4``.
| Refer to the `pinout documentation `_ to identify which pins are free to be used in your specific application.
.. attention::
| These pins are 3.3 V level and may need additional circuitry to interface to third party hardware with other voltage levels.
| Voltages higher than 3.3 V and reverse voltage may cause permanent damage to the board!
GPIO output
~~~~~~~~~~~
| These pins can be used as 3.3 V digital output, providing up to a few mA.
| Current-limiting resistors may be needed, for example if you connect an LED.
.. code-block:: python
from pyb import Pin, delay
# initialize the output by instantiating the Pin object
p_out = Pin(Pin.cpu.A4, Pin.OUT_PP)
# turn on the output for 1s
p_out.high()
delay(1000)
p_out.low()
.. admonition:: official MycroPython documentation
:class: note
| ``pyb.Pin`` `class `_
GPIO input
~~~~~~~~~~
| They can also be used as inputs with a maximum voltage of 3.3 V.
| You also have to specify what kind of pull you need:
.. list-table::
:widths: 5 20
:align: center
* - ``Pin.PULL_UP``
- enables a pull up of about 40 kOhm to 3.3 V
* - ``Pin.PULL_DOWN``
- enables a pull down of about 40 kOhm to ground
* - ``Pin.PULL_NONE``
- leaves it floating if the circuit you connect already has a pull resistor installed
.. code-block:: python
from pyb import Pin
p_in = Pin(GPIO_2, Pin.IN, Pin.PULL_UP)
p_in.value() # get value, 0 or 1
.. admonition:: official MycroPython documentation
:class: note
| ``pyb.Pin`` `class `_
PWM
~~~
| Pulse-width modulation is also supported on selected pins, using timer hardware.
| Verify which timer and channel correspond to which pin first.
.. code-block:: python
from pyb import Pin, Timer
# instantiate the timer with a frequency of 1 kHz
tim = Timer(13, freq=1000) # the frequency will be the PWM carrier frequency
# then create the pwm channel on the pin
ch = tim.channel(1, Timer.PWM, Pin.cpu.F8)
# you can then set or change the pulse width in percent to be output using
ch.pulse_width_percent(50)
.. admonition:: official MycroPython documentation
:class: note
| ``pyb.Pin`` `class `_
| ``pyb.Timer`` `class `_
ADC
~~~
| The Analog to Digital Converter allows you to convert an analog voltage to a digital value. (available on selected pins)
| The ADC has a resolution of 12 bit, meaning the measured value will be between 0 and 4095.
.. code-block:: python
from pyb import Pin, ADC
adc = ADC(Pin.cpu.A4) # 'A4' or GPIO_8
adc.read() # read value, 0 to 4095 corresponding to a voltage of 0 to 3.3V
.. admonition:: official MycroPython documentation
:class: note
| ``pyb.Pin`` `class `_
| ``pyb.ADC`` `class `_
DAC
~~~
| The Digital to Analog Converter generates an analog voltage proportional to a digital value. (available on selected pins)
| The DAC default resolution is 8 bit, meaning the value will have to be between 0 and 255 for an output of 0 to 3.3V.
.. code-block:: python
from pyb import Pin, DAC
dac = DAC(Pin.cpu.A4, buffering=True) # 'A4' or GPIO_8
dac.write(120) # output between 0 and 255
| A higher resolution of 12 bits can be selected by passing the argument ``bits=12``
| It is recommended to enable output buffering using ``buffering=True``, unless your load has a very high impedance (MOhm range).
.. admonition:: official MycroPython documentation
:class: note
| ``pyb.Pin`` `class `_
| ``pyb.DAC`` `class `_
UART
~~~~
| *Universal Asynchronous Receiver-Transmitter*, a.k.a. serial ports
| An UART uses two pins and is full-duplex capable:
.. list-table::
:widths: 5 15
:align: center
* - TX
- transmit data
* - RX
- receive data
| UARTS on multifunctioni pins are compatible with 3.3V TTL level. An external transceiver is needed for other levels.
| Hardware UARTS are available on selected pins using ``pyb.UART()``
.. code-block:: python
from pyb import UART
uart = UART(4, 9600)
uart.write('hello')
uart.read(5) # read up to 5 bytes
.. admonition:: official MycroPython documentation
:class: note
| ``pyb.UART`` `class `_
SPI bus
~~~~~~~
*Serial Peripheral Interface Bus*
| It uses 4 pins and is full-duplex capable:
.. list-table::
:widths: 5 25
:align: center
* - SCK
- clock signal
* - MISO
- Master-in, Slave-out data
* - MOSI
- Master-out, Slave-in data
* - CS
- Chip Select, must be pull low to enable communication with a slave
| Hardware SPI buses are available on selected pins using ``pyb.SPI()``
.. code-block:: python
from pyb import SPI, Pin
# configure SPI2 as a master/controller (pinout for omega)
spi = SPI(2, mode=SPI.MASTER, baudrate=1000000, polarity=1, phase=0)
cs = Pin(Pin.cpu.E10, Pin.OUT, value=1)
cs.low() # enable chip select
spi.send(b'\x01\x02\x03\x04') # send bytes on the bus
spi.recv(5) # receive 5 bytes on the bus
spi.send_recv(b'\x01\x02\x03\x04') # send message and simultaneously receive 4 bytes
cs.high() # disable chip select
A softwre SPI bus can be created on any pin using ``machine.SoftSPI()``
.. code-block:: python
from machine import SoftSPI
from pyb import Pin
# create Software SPI bus as a master/controller (pinout for omega)
spi = SoftSPI(baudrate=500000, polarity=1, phase=0, bits=8, firstbit=SoftSPI.MSB, sck=Pin.cpu.E7, mosi=Pin.cpu.E8, miso=Pin.cpu.E9)
cs = Pin(Pin.cpu.E10, Pin.OUT, value=1)
cs.low() # enable chip select
spi.write(b'\x01\x02\x03\x04') # send bytes on the bus
spi.read(4) # receive 4 bytes on the bus
cs.high() # disable chip select
.. admonition:: official MycroPython documentation
:class: note
| ``pyb.SPI`` `class `_
| ``machine.SPI`` `class `_
I2C bus
~~~~~~~
| *Inter-Integrated Circuit Bus*
| It uses two pins and is only half-duplex capable
.. list-table::
:widths: 5 15
:align: center
* - SCL
- clock signal
* - SDA
- transmit and receive data
| Hardware I2C buses are available on selected pins using ``pyb.I2C()``
.. code-block:: python
from pyb import I2C, Pin
# create hardware I2C1 object
i2c = I2C(1, I2C.CONTROLLER, baudrate=400000)
addresses = i2c.scan() # returns list of slave addresses
i2c.send('123', addr=0x42) # send 3 bytes to slave with address 0x42
data = i2c.recv(2, addr=0x42) # read 2 bytes from bus
i2c.mem_write(b'\x01\x02', addr=0x42, memaddr=0x10) # write 2 bytes to slave 0x42, memory address 0x10
data = i2c.mem_read(2, addr=0x42, memaddr=0x10) # read 2 bytes from slave 0x42, starting from memory address 0x10
A software I2C bus can be created on any avilable pin using ``machine.SoftI2C()``
.. code-block:: python
from machine import SoftI2C
from pyb import Pin
# create Software I2C bus as a controller (pinout example for omega)
i2c = SoftI2C(scl=Pin.cpu.E7, sda=Pin.cpu.E8, freq=400000) # create software I2C object
i2c.writeto(0x42, 'hello') # write 5 bytes to slave with address 0x42
i2c.readfrom(0x42, 5) # read 5 bytes from slave
.. admonition:: official MycroPython documentation
:class: note
| ``pyb.I2C`` `class `_
| ``machine.I2C`` `class `_
Storage
_______
flash memory
~~~~~~~~~~~~
| All boards provide onboard, non-removable flash storage of 8 Mb, which is automatically mounted in the file system under ``/flash`` and can be used to store small files in addition to ``boot.py`` and ``main.py``.
basic file system operations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| You can list the ``/flash`` directory content using:
.. code-block:: python
import os
print(os.listdir('/flash'))
To create a file and/or write data to it use the ``open()`` function with ``mode='w'``:
.. code-block:: python
FILENAME = '/flash/test.txt' # any file extension can be used
# create file
file = open(FILENAME, mode='w')
file.write('First line\n') # write the first line to the file
file.close() # always close the file when it is not used anymore
To append data to a file use ``mode='a'``:
.. code-block:: python
# append data to file
with open(FILENAME, mode='a') as file: # 'w' would over write the data in the file
file.write('Second line\n') # '\n' is the escape sequence for the new line character
To print the content of a file use ``mode='r'`` to open it with read-only access, so you don't accedentally modify it:
.. code-block:: python
# read data from file
with open(FILENAME, mode='r') as f: # using 'with' the file will be automatically closed afterwards
for line in f.readlines():
print(line)
| Note: the statement ``with open() as f:`` makes sure the file is automatically closed after the operation.
To delete a file use ``os.remove()``
.. code-block:: python
import os
os.remove('/flash/test.txt')
To create and remove directories you can use ``os.mkdir(path)`` and ``os.rmdir(path)``, see the documentation below.
For examples on how to test for the presence or absence of files, see the `code in the advanced examples `_
.. admonition:: official MycroPython documentation
:class: note
| ``os`` `module `_
| ``open()`` / ``io.open()`` `function `_
| `local filesystem and SD card `_
| `working with filesystems `_
microSD Card
~~~~~~~~~~~~
| The microSD card slot adds the possibility to store larger amounts of data, such as for long-term logging.
.. important::
| Make sure the microSD card is formatted to FAT32.
| Note that FAT32 supports a maximum card size of 32 GB.
To use the microSD card, insert it into the slot and mount it using:
.. code-block:: python
import pyb, os
os.mount(pyb.SDCard(), '/sd')
# list the SD card content
print(os.listdir('/sd'))
.. attention::
| If an SD card is detected when booting, MicroPython will ignore the files under ``/flash`` , automatically mount the SD card and boot using the files under ``/sd``.
| If no files are found, nothing will be executed (i.e. it will not fall back to booting from ``/flash``)!
| While this can be useful in some cases, it is often source of unexpected behaviour and generally it seems preferrable to always boot from ``/flash``.
| To force booting from ``/flash``, create a folder or an empty file named ``SKIPSD`` under ``/flash``:
.. code-block:: python
import os
os.mkdir('/flash/SKIPSD')
print(os.listdir('/flash')) # list the current directory content to verify the SKIPSD folder has been created
.. admonition:: official MycroPython documentation
:class: note
| ``os`` `module `_
| `local filesystem and SD card `_
| `working with filesystems `_
Accessing storage over USB
~~~~~~~~~~~~~~~~~~~~~~~~~~
| By default, the memory currently in use by MicroPython (flash memory or microSD card) is exposed to the Host PC as Mass Storage Device, so you can read and write files to them. This is controlled by the command found in ``boot.py``:
.. code-block:: python
import pyb
pyb.usb_mode( 'VCP+MSC', msc=(pyb.Flash(),) ) # default setting
Meaning that both the Virtual Com Port for the REPL as well as the Mass Storage Device are enabled on the USB port.
.. attention::
| Always safely remove the USB drive(s) by clicking `Eject` before unplugging / power cycling / hard resetting the board!
| Failure to do so could cause memory corruption. You may lose the files on the device as a consequence.
| → frequently perform backup copies of important code while you develop it on the board!
| To simultaneously access both the microSD card and the board's internal flash memory from your computer as separate mass storage devices use the following command:
.. code-block:: python
import pyb
pyb.usb_mode( 'VCP+MSC', msc=(pyb.Flash(), pyb.SDCard()) )
| To make this setting permanent you can add this line to ``boot.py``.
.. admonition:: official MycroPython documentation
:class: note
``pyb.usb_mode()`` `function `_
General board control
_____________________
power-saving sleep mode
~~~~~~~~~~~~~~~~~~~~~~~
For power saving purposes in battery applications you can put the CPU in a low-power state. An external interrupt or an interrupt from the RTC is then needed to wake it up.
.. code-block:: python
import pyb
pyb.stop() # stop CPU, waiting for external or RTC interrupt
See ``rtc.wakeup()`` to configure a real-time-clock wakeup event, respectively ``pyb.ExtInt`` for external interrupts.
.. admonition:: official MycroPython documentation
:class: note
| ``pyb`` `power related functions `_
| ``pyb.RTC.wakeup()`` `method `_
| ``pyb.ExtInt`` `class `_
CPU frequency
~~~~~~~~~~~~~
You can read the CPU operating frequency, as well as the frequency of the different peripheral buses:
.. code-block:: python
import pyb
pyb.freq() # get CPU and bus frequencies
| In special cases, you can also change the CPU operating frequency, for instance to reduce the power consumption.
| For the majority of the applications, it is however recommended to leave the default settings.
.. code-block:: python
import pyb
pyb.freq(60000000) # set CPU freq to 60MHz
.. admonition:: official MycroPython documentation
:class: note
``pyb.freq()`` `method `_
miscellaneous functions
~~~~~~~~~~~~~~~~~~~~~~~
You can duplicate the REPL on an UART using ``pyb.repl_uart()``
.. code-block:: python
import pyb
pyb.repl_uart(pyb.UART(1, 9600)) # duplicate REPL on UART(1)
``info()`` prints lots of information about the board
.. code-block:: python
import pyb
pyb.info() # print out lots of information about the board
.. admonition:: official MycroPython documentation
:class: note
``pyb`` `miscellaneous functions `_
unique ID
~~~~~~~~~
Each microcontroller has a globally-unique ID you can read out to identify a specific board:
.. code-block:: python
import pyb
pyb.unique_id() # returns a 96 bit microcontroller unique identifier
.. admonition:: official MycroPython documentation
:class: note
``pyb.unique_id`` `method `_
Advanced examples
_________________
external interrupt datalogger
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| This example logs the current timestamp to a csv file located on the microSD card each time the input pin sees a rising edge (goes from low to high).
.. code-block:: python
FOLDER = '/sd/' # where the log file will be saved
FILENAME = 'datalog_INT.csv' # log filename, any other file extension such as .log or .txt can be used
INTERRUPT_PIN = input_1 # the input pin triggering the interrupt
DEBOUNCING_THRESHOLD = 20 # debouncing window in ms (determines minimum valid repetition rate)
import pyb, os, micropython
micropython.alloc_emergency_exception_buf(100)
rtc = pyb.RTC()
last_event = pyb.millis()
# mount sd card if it isn't already
if 'sd' not in os.listdir('/'):
os.mount(pyb.SDCard(), '/sd')
# create a new file to log to if it doesn't already exist
if FILENAME not in os.listdir(FOLDER):
with open(FOLDER+FILENAME, 'w') as f:
f.write('Timestamp;\n') # write column header
# define the callback function
def schedule_log(line):
micropython.schedule(log_event,line)
#define the logger function
def log_event(line):
t = rtc.datetime()
global last_event
if pyb.elapsed_millis(last_event) > DEBOUNCING_THRESHOLD:
last_event = pyb.millis()
timestamp = '{:02d}-{:02d}-{:04d} {:02d}:{:02d}:{:02d}.{:02d};\n'.format(t[2], t[1], t[0], t[4], t[5], t[6], t[7])
with open(FOLDER+FILENAME, 'a') as f: # 'a' appends data to the file
f.write(timestamp) #
# attach the external interrupt to input_1
extint_pin = pyb.ExtInt(INTERRUPT_PIN, pyb.ExtInt.IRQ_RISING, pyb.Pin.PULL_NONE, schedule_log)
# define function to print the log file content
def print_log():
with open(FOLDER+FILENAME, 'r') as f:
for line in f.readlines():
print(line, end='')
timed ADC datalogger
~~~~~~~~~~~~~~~~~~~~
| This example logs the timestamp and the value of an ADC pin to a csv file located on the microSD card at fixed intervals, controlled by a timer.
| The voltage to be measured has to be between 0 and 3.3V, corrisponding to ADC values between 0 and 4095.
.. code-block:: python
FOLDER = '/sd/' # where the log file will be saved
FILENAME = 'datalog_ADC.csv' # log filename, any other file extension such as .log or .txt can be used
ADC_PIN = 'A4' # the ADC pin to measure
LOGGING_FREQUENCY = 10 # logging frequency in Hz
import pyb, os, micropython
micropython.alloc_emergency_exception_buf(100)
rtc = pyb.RTC()
# create the ADC object on the specified pin
adc = pyb.ADC(eval('pyb.Pin.cpu.' + ADC_PIN))
# mount sd card if it isn't already
if 'sd' not in os.listdir('/'):
os.mount(pyb.SDCard(), '/sd')
# create a new file to log to if it doesn't already exist
if FILENAME not in os.listdir(FOLDER):
with open(FOLDER+FILENAME, 'w') as f:
f.write('Timestamp;ADC_value;\n') # write column header
# define the callback function
def schedule_log(tim):
micropython.schedule(log_ADC,tim)
#define the logger function
def log_ADC(tim):
t = rtc.datetime()
value = str(adc.read())
timestamp = '{:02d}-{:02d}-{:04d} {:02d}:{:02d}:{:02d}.{:02d};'.format(t[2], t[1], t[0], t[4], t[5], t[6], t[7])
with open(FOLDER+FILENAME, 'a') as f: # 'a' appends data to the file
f.write(timestamp + value + ';\n') #
# create timer with interrupt
timer_log = pyb.Timer(1, freq=LOGGING_FREQUENCY, callback=schedule_log)
# define function to print the log file content
def print_log():
with open(FOLDER+FILENAME, 'r') as f:
for line in f.readlines():
print(line, end='')
printing log files
~~~~~~~~~~~~~~~~~~
You can print the log file content by calling the function ``print_log()`` defined in both datalogger examples.