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.