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).
| The Pin objects are ready to be used, initialized as inputs, and can be found under ``inputs.Pin``.
.. group-tab:: omega:ONE
| omega:ONE provides 20 digital inputs, compatible with 9-36V input voltage.
| The digital inputs are isolated using optocouplers, 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.
| omega:ONE also supports quadrature encoders on the digital inputs, see next paragraph.
| The Pin objects are ready to be used, initialized as inputs, and can be found under ``inputs.Pin``.
.. 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
inputs.Pin.input1.value() # returns the input logic state, either 0 or 1
.. admonition:: official MycroPython documentation
:class: note
``pyb.Pin`` `class `_
Quadrature encoder Inputs
~~~~~~~~~~~~~~~~~~~~~~~~~
.. tabs::
.. group-tab:: alpha:ONE
| alpha:ONE does not support quadrature encoders on the digital inputs.
.. group-tab:: omega:ONE
| omega:ONE supports up to four quadrature encoders on the digital inputs.
.. list-table::
:widths: 11 20
:header-rows: 1
:align: center
* - encoder
- digital inputs
* - ``ENC1``
- 9 and 10
* - ``ENC2``
- 11 and 12
* - ``ENC3``
- 13 and 14
* - ``ENC4``
- 15 and 16
| The corresponding objects are located under ``inputs.Encoder``.
| To enable encoder mode use the method ``mode()`` with arguments:
.. list-table::
:widths: 11 20
:align: center
* - ``'AB'`` or ``'on'``
- counts both on input A and B flanks, default mode
* - ``'A'``
- only counts on input A flanks
* - ``'B'``
- only counts on input B flanks
* - ``'off'``
- disables encoder mode and returns to basic digital input mode
| ``mode()`` invoked without arguments returns the current mode.
For instance:
.. code-block:: python
inputs.Encoder.ENC1.mode('AB') # enables encoder using mode AB, default mode
The encoder count value can be read using the method ``value()``:
.. code-block:: python
inputs.Encoder.ENC1.value()
.. admonition:: Note
:class: note
| Encoder modes ``'A'`` and ``'B'`` still require both A and B valid input quadrature signals.
| This is a physical necessity to be able to detect the direction and count up or down accordingly.
| The different modes only affect the number of counts per electrical cycle.
.. group-tab:: omega:TWO
| omega:TWO does not support quadrature encoders on the digital inputs.
.. admonition:: official MycroPython documentation
:class: note
``pyb.Timer`` `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.
| At high current levels it is recommended to monitor the board temperature. Cooling may be required in some applications.
| The outputs are high-side switches, meaning they switch the supply voltage. The low side of the load is wired to ground.
| The outputs can be switched on and off by calling the methods ``on()`` and ``off()``.
| Each output has a yellow LED that lights up to show when the output has been switched on.
| This example switches on output 1 for one second:
.. code-block:: python
outputs.output1.on() # output_1 is switched on
pyb.delay(1000)
outputs.output1.off() # output_1 is switched off
| The outputs are short-circuit protected. A red LED will light up if there is a fault.
| The method ``is_in_fault()`` returns ``True`` if the protection has tripped.
| The proteciton resets automatically once the fault has been removed.
| The method ``OC_threshold()`` returns the current threshold in Amps for the overcurrent protection.
| The outputs can also be used in PWM mode to modulate the power output to the load.
| ``mode('PWM')`` is used to enable PWM mode and ``pulse_width_percent()`` sets the percentage of ON time:
.. code-block:: python
outputs.output1.mode('PWM') # output1 is configured to PWM mode
outputs.output1.pulse_width_percent(20) # output1 set to 20% duty cycle
pyb.delay(1000)
outputs.output1.off() # output_1 is switched off
| Note that ``on()`` and ``off()`` still work also in PWM mode, and set the duty cycle to 100% and 0% respectively.
| ``pwm_freq()`` changes the PWM frequency:
.. code-block:: python
outputs.output1.pwm_freq(500) # PWM frequency set to 500 Hz (default is 1000 Hz)
| A specific PWM frequency can also be set when enabling PWM mode using the keyword ``pwm_freq``:
.. code-block:: python
outputs.output1.mode('PWM', pwm_freq=2000) # PWM mode with 2000 Hz PWM frequency
| Outputs 1 to 4 use timer5, while outputs 5 and 6 use timer8. Changing the PWM frequency of one channel affects also the others using the same timer.
| To go back to GPIO mode (the default mode) simply call ``mode('GPIO')``.
| Each power output has two diagnostic functions, accessible only when the output is off:
| The method ``load_connected()`` returns ``True`` if a low-impedance load is connected to the output.
| The method ``Vin()`` returns the actual supply voltage measured from the power output.
| These diagnostic functions require the installation of voltage divider resistors on the board. Contact us for further assistance.
.. 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.
| At high current levels it is recommended to monitor the board temperature. Cooling may be required in some applications.
| 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 wired to ground.
| This example switches on output 1 for one second:
.. code-block:: python
outputs.output1.high() # output_1 is switched on
pyb.delay(1000)
outputs.output1.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.
| This is even more necessary if you use the outputs in PWM mode so, that the current can recirculate through the load during the off-phase.
.. 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, connected between the terminals A1 and A2 or B1 and B2 of each of the two H-Bridge ICs.
| The four H-Bridges can also be used to control eight independent, non-reversible loads such as unidirectional solenoids, lamps or relays. In this case the eight outputs can be used as high-side or low-side switches, as needed. The other side of the load is then connected to ground or 24V respectively.
| 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.
| At high current levels it is recommended to monitor the board temperature. Cooling may be required in some applications.
| The H-Bridge ICs provides Overcurrent and Overtemperature protection.
| The method ``is_in_fault()`` returns ``True`` if any of the H-Bridges outputs have tripped the protection.
| Each H-Bridge IC has an enable pin to activate the 24V outputs. It is controlled using the ``on()`` and ``off()`` methods.
| The H-Bridges can be configured either in ``'GPIO'`` or in ``'PWM'`` mode, using the method ``mode()``.
| ``mode()`` invoked without arguments returns the current operating mode.
| Regardless of the operating mode, the objects to control the outputs are always found under ``Pin``.
| The (HTL) logic state of the H-Bridge output pins corresponds to the (CMOS) logic state of the control pins.
| GPIO mode example (default operating mode):
.. code-block:: python
outputs.HB1.mode('GPIO')
outputs.HB1.on() # outputs are enabled, but both are low (connected to GND)
outputs.HB1.Pin.A1.high() # output A1 high -> move forward
pyb.delay(1000)
outputs.HB1.Pin.A1.low() # stop
pyb.delay(1000)
outputs.HB1.Pin.A2.high() # output A2 high -> move backwards
pyb.delay(1000)
outputs.HB1.Pin.A2.low() # stop
| PWM mode example:
.. code-block:: python
outputs.HB1.mode('PWM')
outputs.HB1.on()
outputs.HB1.Pin.A1.pulse_width_percent(30) # move forward with 30% speed
pyb.delay(1000)
outputs.HB1.Pin.A1.pulse_width_percent(0) # stop
| When operating in PWM mode, the frequency can be set using the method ``pwm_freq()``:
| ``pwm_freq()`` invoked without arguments returns the current value.
.. code-block:: python
outputs.HB1.pwm_freq(500) # sets the pwm frequency to 500 Hz
| The PWM frequency can also be set when configuring PWM mode using the keyword ``pwm_freq``:
.. code-block:: python
outputs.HB1.mode('PWM', pwm_freq=1200) # sets the pwm frequency to 1200 Hz
| The current recirculation mode or current decay mode can be configured using the method ``decay()``:
| ``decay()`` invoked without arguments returns the current mode.
.. list-table::
:widths: 11 20
:align: center
* - ``'fast'``
- | In the off-time the current quickly decays to zero.
| A spinning motor will coast in this phase.
* - ``'slow'``
- | In the off-time the current recirculates longer through the load.
| As a consequence of its back-emf, a spinning motor will experience a braking torque and slow down quicker.
| For solenoids, slow decay is usually preferred for a more constant current throughout the PWM cycle.
* - ``'mixed'``
- | Fast decay for a fixed amount of time, subsequently slow decay mode for the remaining period of time.
| Mainly used with stepper motors, particularly for microstepping.
.. group-tab:: omega:TWO
omega:TWO does not provide H-Bridge outputs.
.. seealso::
For more detailed information on the current decay modes, read Texas Instruments' `Application Report Current Recirculation and Decay Modes `_.
as well as the H-Bridge IC, `DRV8841 datasheet `_.
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)``
- blue
* - ``LED(2)``
- green
* - ``LED(3)``
- red
.. 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
.. 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
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 self-explanatory variable:
.. code-block:: python
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
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 object ``LEDs``, automatically instantiated at boot.
| To set one of the LEDs to a specific color you can use the method ``set_color(LED_nr, color, brightness)``
.. list-table::
:widths: 5 20
:header-rows: 1
:align: center
* - keyword
- valid values
* - ``LED_nr``
- 1 to 3
* - ``color``
- 'red', 'green', 'blue', 'yellow', 'orange', 'turquoise', 'aqua', 'pink', 'white', 'off'
* - ``brightness``
- 0 to 31
.. code-block:: python
LEDs.set_color(LED_nr=1,color='red') # with default brightness = 1
LEDs.set_color(LED_nr=2,color='green', brightness=5)
LEDs.set_color(3, 'blue') # shorter, using positional arguments
| The colors available can be listed from the dictionary object ``colors``:
.. code-block:: python
LEDs.colors.keys() # returns all colors
| The ``colors`` dictionary can also be modified to include special, user-defined colors, if needed.
| You can also directly set the LEDs to any RGB color using the method ``set_rgb(LED_nr, red, green, blue, brightness)``
.. list-table::
:widths: 5 5
:header-rows: 1
:align: center
* - keyword
- valid values
* - ``LED_nr``
- 1 to 3
* - ``red``
- 0 to 255
* - ``green``
- 0 to 255
* - ``blue``
- 0 to 255
* - ``brightness``
- 0 to 31
.. code-block:: python
LEDs.set_rgb(LED_nr=1, red=167, green=23, blue=255, brightness=3)
To turn it off you can set ``color = 'off'``:
.. code-block:: python
LEDs.set_color(LED_nr=1, color='off')
LEDs.set_color(2, 'off')
| The method ``all_off()`` is also provided, to turn all LEDs off at once.
.. 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
pyb.delay(100) # wait for 100 milliseconds
pyb.udelay(10) # wait for 10 microsenconds
Measure the duration of a program in milliseconds
.. code-block:: python
start_ms_count = pyb.millis() # store the starting time
pyb.delay(100) # the program or code block you want to measure
delta_t = pyb.elapsed_millis(start_ms_count)
print("elapsed time:", delta_t, "ms")
Measure the duration of a program in microseconds
.. code-block:: python
start_us_count = pyb.micros() # store the starting time
pyb.delay(100) # the program or code block you want to measure
delta_t = pyb.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
pyb.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
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 = pyb.ExtInt(input_1, pyb.ExtInt.IRQ_RISING, pyb.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
# 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
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 ``RTC`` class provides access to the microcontroller's Real Time Clock, which keeps track of time and date also when the board is not supplied with power, thanks to the CR2032 backup battery. The object ``rtc`` is automatically instantiated on boot.
| 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
rtc.datetime((2013, 4, 29, 1, 0, 0, 0, 0)) # set a fairly specific date and time
pyb.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 use the method ``datetime_str()``:
.. code-block:: python
rtc.datetime_str()
| You can also call ``datetime_print()`` to print it out.
| If a different format is needed, the format string ``_formatstring`` and/or the method ``datetime_str()`` can be overridden.
| 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 interface object ``eth`` is automatically instantiated on boot.
.. group-tab:: omega:ONE
| omega:ONE provides an ethernet interface using the MAC controller integrated in the microcontroller.
| The network interface object ``eth`` is automatically instantiated on boot.
.. 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 bundled with the firmware.
| It can 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 `_
| `uping module `_ by Github user `Shawwwn `_ (not part of the official MicroPython project)
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.
CAN Bus test in loopback mode (to be able to test without actual bus nodes connected)
.. code-block:: python
can.init(mode=can.LOOPBACK, baudrate=250_000)
can.setfilter(0, can.LIST16, 0, (123, 124, 125, 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, otherwise the message will keep retransmitting indefinitely)
.. code-block:: python
can.init(mode=can.NORMAL, baudrate=250_000)
can.setfilter(0, can.LIST16, 0, (123, 124, 125, 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
.. 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
CAN Bus test in loopback mode (to be able to test without actual bus nodes connected)
.. code-block:: python
can.init(mode=can.LOOPBACK, baudrate=250_000)
can.setfilter(0, 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, otherwise the message will keep retransmitting indefinitely)
.. code-block:: python
can.init(mode=can.NORMAL, baudrate=250_000)
can.setfilter(0, 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
.. 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
can.init(mode=can.LOOPBACK, baudrate=250_000)
can.setfilter(0, 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, otherwise the message will keep retransmitting indefinitely)
.. code-block:: python
can.init(mode=can.NORMAL, baudrate=250_000)
can.setfilter(0, 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, using objects ``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.
| A low-EMI mode up to 250 kbit/s is software selectable using the method ``slew_rate('limited')`` (default).
| To use the interface in 4-wire, full-duplex mode, connect the AB (rx pair) and YZ (tx pair) and use the following code:
.. code-block:: python
rs485_1.mode('4-Wire') # default mode
rs485_1.init(baudrate=9600)
rs485_1.write('Test RS485 full-duplex 9.6 kbaud')
| In this mode the receiver is always enabled, while the transmitter is only enabled while sending.
| The methods ``write()``, ``writechar()`` and ``sendbreak()`` automatically toggle the driver enable pin on and off when transmitting.
| To use the interface in half-duplex mode (2-wire), jumper A-Y and B-Z, connect AB and use the following code:
.. code-block:: python
rs485_1.mode('2-Wire')
rs485_1.init(baudrate=9600)
rs485_1.write('Test RS485 half-duplex 9.6 kbaud')
| In this mode the receiver is automatically disabled when the transmitter is enabled to suppress local echo.
| If you wish to receive an echo of the transmitted data, this can be enabled using ``echo('on')``:
.. code-block:: python
rs485_1.echo('on')
| This can also be configured toghether with the mode by calling
.. code-block:: python
rs485_1.mode('2-Wire echo')
|``mode()`` returns the current mode:
.. code-block:: python
rs485_1.mode()
For baud rates above 250 kbit/s the driver must be configured in fast mode using ``slew_rate('max')``:
.. code-block:: python
rs485_1.init(baudrate=500000)
rs485_1.slew_rate('max')
rs485_1.write('Test RS485 500 kbaud')
.. 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).
| Verify SJ8 is open, solder SJ4 for GND so, that the receiver is always enabled.
| Close SJ6 and SJ7 if terminators are needed.
| Then use the following code:
.. code-block:: python
rs485.init(baudrate=9600)
rs485.write('TEST RS485 full-duplex')
| To use the interface in half-duplex (2-wire) mode connect just the AB pair.
| Solder SJ8 close, verify SJ4 is soldered for DE. Close SJ6 if a terminator is needed.
| Then use the following code:
.. code-block:: python
rs485.init(baudrate=9600)
rs485.write('TEST RS485 half-duplex')
| If baudrates above 250 kbit/s are used, configure SJ9 accordingly.
.. 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 object is named ``rs232_485`` and can be used as follows:
| For RS232 mode, connect Tx, Rx and GND to the device and use following code:
.. code-block:: python
# set up interface in RS232 mode
rs232_485.init(baudrate=9600)
rs232_485.mode('RS232')
rs232_485.slew_rate('slow') # enables low-EMI mode for <= 250 kBit/s (dafault)
# send a test message
rs232_485.write('Test RS232')
| For baudrates higher than 250 kbit/s select ``slew_rate('max')``.
| ``termination()`` does not apply to RS232 mode.
| For RS485 half-duplex mode, connect A, B and GND to the device and use following code:
.. code-block:: python
# set up interface in RS485 half-duplex mode
rs232_485.init(baudrate=9600)
rs232_485.mode('RS485')
rs232_485.termination('on') # enable 120 Ohm termination
# send a test message
rs232_485.write('Test RS485 half-duplex')
| The bus termination should only be enabled with ``termination('on')`` if the bus is otherwise unterminated.
| Only one termination at each end of the bus is required for correct operation.
| ``slew_rate()`` does not apply to RS485 mode.
.. 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 found under ``gpios.Pin``, named ``gpio1`` to ``gpio21``.
| Refer to the `pinout documentation `_ to see what hardware capabilities are available on each pin.
| alpha:ONE also provides on the gpio terminal a 5V output (always on), protected by a 350 mA resettable fuse, as well as a 3.3V from a dedicated LDO regulator, which can be controlled using ``AUX_3V3()`` as follows:
.. code-block:: python
gpios.AUX_3V3('on') # turns on the 3V3 regulator
| Alternatively, the ``gpios.Pin.EN_3V3`` regulator enable pin can also be manipulated directly.
.. 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.
| Never connect voltages higher than 3.3 V or reverse voltages, as they may cause permanent damage to the board!
GPIO output
~~~~~~~~~~~
| These pins can be used as 3.3 V digital outputs, providing up to a few mA.
| Current-limiting resistors may be needed, for example if you connect an LED.
.. tabs::
.. group-tab:: alpha:ONE
.. code-block:: python
gpios.Pin.gpio1.init(pyb.Pin.OUT)
# turn on the output for 1s
gpios.Pin.gpio1.high()
pyb.delay(1000)
gpios.Pin.gpio1.low()
.. group-tab:: omega:ONE
.. code-block:: python
# initialize the output by instantiating the Pin object
pin_out = pyb.Pin(pyb.Pin.cpu.A4, pyb.Pin.OUT)
# turn on the output for 1s
pin_out.high()
pyb.delay(1000)
pin_out.low()
.. group-tab:: omega:TWO
.. code-block:: python
# initialize the output by instantiating the Pin object
pin_out = pyb.Pin(pyb.Pin.cpu.A4, pyb.Pin.OUT)
# turn on the output for 1s
pin_out.high()
pyb.delay(1000)
pin_out.low()
.. admonition:: official MycroPython documentation
:class: note
| ``pyb.Pin`` `class `_
GPIO input
~~~~~~~~~~
| GPIOs can also be used as digital inputs with a maximum voltage of 3.3 V.
| You also have to specify if / what kind of pull you want to enable:
.. 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. Use this only if the circuit you connect already has a pull resistor installed.
.. tabs::
.. group-tab:: alpha:ONE
.. code-block:: python
gpios.Pin.gpio1.init(pyb.Pin.IN, pyb.Pin.PULL_UP)
gpios.Pin.gpio1.value()
.. group-tab:: omega:ONE
.. code-block:: python
pin_in = pyb.Pin(GPIO_2, pyb.Pin.IN, pyb.Pin.PULL_UP)
pin_in.value() # returns value, either 0 or 1
.. group-tab:: omega:TWO
.. code-block:: python
pin_in = pyb.Pin(GPIO_2, pyb.Pin.IN, pyb.Pin.PULL_UP)
pin_in.value() # returns value, either 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.
.. tabs::
.. group-tab:: alpha:ONE
| This example uses Timer2, Channel1 on GPIO9
.. code-block:: python
# instantiate the timer with a frequency of 1 kHz
tim = pyb.Timer(2, freq=1000) # the frequency will be the PWM frequency
# then create the pwm channel on the pin
ch = tim.channel(1, pyb.Timer.PWM, gpios.Pin.gpio9)
# you can then set or change the pulse width in percent
ch.pulse_width_percent(50)
.. group-tab:: omega:ONE
| This example uses Timer13, Channel1 on CPU pin A6
.. code-block:: python
# instantiate the timer with a frequency of 1 kHz
tim = pyb.Timer(13, freq=1000) # the frequency will be the PWM frequency
# then create the pwm channel on the pin
ch = tim.channel(1, pyb.Timer.PWM, pyb.Pin.cpu.A6)
# you can then set or change the pulse width in percent
ch.pulse_width_percent(50)
.. group-tab:: omega:TWO
| This example uses Timer13, Channel1 on CPU pin A6
.. code-block:: python
# instantiate the timer with a frequency of 1 kHz
tim = pyb.Timer(13, freq=1000) # the frequency will be the PWM frequency
# then create the pwm channel on the pin
ch = tim.channel(1, pyb.Timer.PWM, pyb.Pin.cpu.A6)
# you can then set or change the pulse width in percent
ch.pulse_width_percent(50)
.. admonition:: official MycroPython documentation
:class: note
| ``pyb.Pin`` `class `_
| ``pyb.Timer`` `class `_
ADC
~~~
| The Analog to Digital Converter ADC 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 for a voltage between 0 and 3.3 V.
.. tabs::
.. group-tab:: alpha:ONE
.. code-block:: python
adc = pyb.ADC(gpios.Pin.gpio8) # GPIO8 or cpu pin A4
adc.read() # read value, 0 to 4095 corresponding to a voltage of 0 to 3.3V
.. group-tab:: omega:ONE
.. code-block:: python
adc = pyb.ADC(pyb.Pin.cpu.C0) # cpu pin C0
adc.read() # read value, 0 to 4095 corresponding to a voltage of 0 to 3.3V
.. group-tab:: omega:TWO
.. code-block:: python
adc = pyb.ADC(pyb.Pin.cpu.C0) # cpu pin C0
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 DAC 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.
| 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).
.. tabs::
.. group-tab:: alpha:ONE
.. code-block:: python
dac = pyb.DAC(gpios.Pin.gpio8, buffering=True) # GPIO8 or cpu pin A4
dac.write(123) # output between 0 and 255
.. group-tab:: omega:ONE
.. code-block:: python
dac = pyb.DAC(pyb.Pin.cpu.A4, buffering=True) # cpu pin A4
dac.write(123) # output between 0 and 255
.. group-tab:: omega:TWO
.. code-block:: python
dac = pyb.DAC(pyb.Pin.cpu.A4, buffering=True) # cpu pin A4
dac.write(123) # output between 0 and 255
.. 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, TX and RX, 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 level devices. An external transceiver is needed for other levels.
| Hardware UARTS are available on selected pins using ``pyb.UART()``
.. tabs::
.. group-tab:: alpha:ONE
| This example uses UART4 on GPIO19 (TX) and GPIO20 (RX)
| Note that UART4 is assigned to these pins in the Firmware, so Alternate Function configuration is not necessary.
.. code-block:: python
uart = pyb.UART(4, 9600)
uart.write('UART test')
uart.read() # read what has been received in the UART buffer
.. group-tab:: omega:ONE
| This example uses UART1 on cpu pins B14 (TX) and B15 (RX)
.. code-block:: python
uart = pyb.UART(1, 9600)
uart.write('UART test')
uart.read() # read what has been received in the UART buffer
.. group-tab:: omega:TWO
| This example uses UART1 on cpu pins B14 (TX) and B15 (RX)
.. code-block:: python
uart = pyb.UART(1, 9600)
uart.write('UART test')
uart.read() # read what has been received in the UART buffer
.. 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 pulled low to enable communication with a slave device
| Hardware SPI buses are available on selected pins using ``pyb.SPI()``
.. tabs::
.. group-tab:: alpha:ONE
| This example uses SPI4 on pins E5 (MISO), E6 (MOSI), E12 (SCK) and E4 (CS)
.. code-block:: python
# configure SPI4 as a master/controller
spi = pyb.SPI(4, mode=pyb.SPI.MASTER, baudrate=1000000, polarity=1, phase=0)
cs = pyb.Pin(pyb.Pin.cpu.E4, pyb.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
.. group-tab:: omega:ONE
| This example uses SPI2 on pins B14 (MISO), B15 (MOSI), B10 (SCK) and E10 (CS)
.. code-block:: python
# configure SPI2 as a master/controller
spi = pyb.SPI(2, mode=pyb.SPI.MASTER, baudrate=1000000, polarity=1, phase=0)
cs = pyb.Pin(pyb.Pin.cpu.E10, pyb.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
.. group-tab:: omega:TWO
| This example uses SPI2 on pins B14 (MISO), B15 (MOSI), B10 (SCK) and E10 (CS)
.. code-block:: python
# configure SPI2 as a master/controller
spi = pyb.SPI(2, mode=pyb.SPI.MASTER, baudrate=1000000, polarity=1, phase=0)
cs = pyb.Pin(pyb.Pin.cpu.E10, pyb.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
| Whenever possible use hardware SPI peripherals, as they offer the best performance. However, if this is not possible, a software SPI interface can also be created on any pin using ``machine.SoftSPI()``:
.. tabs::
.. group-tab:: alpha:ONE
| This example uses a SoftSPI on GPIO17 (MISO), GPIO18 (MOSI), GPIO19 (SCK) and GPIO20 (CS)
.. code-block:: python
from machine import SoftSPI
# 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=gpios.Pin.gpio19, mosi=gpios.Pin.gpio18, miso=gpios.Pin.gpio17)
cs = pyb.Pin(gpios.Pin.gpio20, pyb.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
.. group-tab:: omega:ONE
| This example uses SoftSPI on pins E7 (MISO), E8 (MOSI), E9 (SCK) and E10 (CS)
.. code-block:: python
from machine import SoftSPI
# 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=pyb.Pin.cpu.E9, mosi=pyb.Pin.cpu.E8, miso=pyb.Pin.cpu.E7)
cs = pyb.Pin(pyb.Pin.cpu.E10, pyb.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
.. group-tab:: omega:TWO
| This example uses SoftSPI on pins E7 (MISO), E8 (MOSI), E9 (SCK) and E10 (CS)
.. code-block:: python
from machine import SoftSPI
# 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=pyb.Pin.cpu.E9, mosi=pyb.Pin.cpu.E8, miso=pyb.Pin.cpu.E7)
cs = pyb.Pin(pyb.Pin.cpu.E10, pyb.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.SoftSPI`` `class `_
I2C bus
~~~~~~~
| *Inter-Integrated Circuit Bus*
| It uses two pins, SDA and SCL, 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()``
.. tabs::
.. group-tab:: alpha:ONE
| This example uses I2C2 on GPIO14 (SDA) and GPIO13 (SCL)
.. code-block:: python
# create hardware I2C1 object
i2c = pyb.I2C(2, pyb.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
.. group-tab:: omega:ONE
| This example uses I2C1 on pins B7 (SDA) and B8 (SCL)
.. code-block:: python
# create hardware I2C1 object
i2c = pyb.I2C(1, pyb.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
.. group-tab:: omega:TWO
| This example uses I2C1 on pins B7 (SDA) and B8 (SCL)
.. code-block:: python
# create hardware I2C1 object
i2c = pyb.I2C(1, pyb.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
| Whenever possible use hardware I2C peripherals, as they offer the best performance. However, if this is not possible, a software I2C interface can also be created on any avilable pin using ``machine.SoftI2C()``:
.. tabs::
.. group-tab:: alpha:ONE
| This example uses SoftI2C on GPIO19 (SDA) and GPIO20 (SCL)
.. code-block:: python
from machine import SoftI2C
# create Software I2C bus as a controller
i2c = SoftI2C(scl=gpios.Pin.gpio20, sda=gpios.Pin.gpio19, freq=400000)
i2c.writeto(0x42, 'hello') # write 5 bytes to slave with address 0x42
i2c.readfrom(0x42, 5) # read 5 bytes from slave
.. group-tab:: omega:ONE
| This example uses I2C on pins E8 (SDA) and E7 (SCL)
.. code-block:: python
from machine import SoftI2C
# create Software I2C bus as a controller
i2c = SoftI2C(scl=pyb.Pin.cpu.E7, sda=pyb.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
.. group-tab:: omega:TWO
| This example uses I2C on pins E8 (SDA) and E7 (SCL)
.. code-block:: python
from machine import SoftI2C
# create Software I2C bus as a controller
i2c = SoftI2C(scl=pyb.Pin.cpu.E7, sda=pyb.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.SoftI2C`` `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 EXFAT, which supports card sizes >32 Gb
To use the microSD card, insert it into the slot and mount it using:
.. code-block:: python
import 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
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
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
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
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
pyb.freq(120000000) # set CPU freq to 120MHz
.. 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
pyb.repl_uart(pyb.UART(1, 9600)) # duplicate REPL on UART(1)
``info()`` prints lots of information about the board
.. code-block:: python
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
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 os, micropython
micropython.alloc_emergency_exception_buf(100)
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):
global last_event
if pyb.elapsed_millis(last_event) > DEBOUNCING_THRESHOLD:
last_event = pyb.millis()
with open(FOLDER+FILENAME, 'a') as f: # 'a' appends data to the file
f.write(rtc.datetime_str()) # appends the 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 os, micropython
micropython.alloc_emergency_exception_buf(100)
# 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):
value = str(adc.read())
with open(FOLDER+FILENAME, 'a') as f: # 'a' appends data to the file
f.write(rtc.datetime_str() + ';' + 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.