MicroPython is a software implementation of the Python 3 programming language for microcontrollers. Nearly all major microcontroller platforms are supported by MicroPython’s firmware.
MicroPython eases the development of embedded systems in a couple of ways. For one, it lets users program microcontrollers and microcomputers in a common programming language (such as Python, which is currently the most popular programming language for developing networked embedded applications in microcomputers). MicroPython brings the same syntax and programming features to the microcontroller platforms.
Secondly, Python is a high-level programming language with a clean weak-typed syntax, particularly compared to embedded C. This lets users access several high-level programming features with microcontrollers, making programming networked embedded applications possible.
Understanding MicroPython
As MicroPython is a subset of the Python 3 programming language, it shares the same syntax and programming style. However, MicroPython is intended for use with microcontrollers and low-level hardware platforms, so it has language-related limitations.
Its libraries include the rewritten standard libraries of Python 3, as well as others written from scratch to manage the hardware-specific functions and features. As a result, even a seasoned Python developer can run into syntax errors and programming bugs if they stick to the standard Python while scripting MicroPython codes.
To get the most out of this article, be sure to upload your MicroPython firmware to a supported microcontroller board such as ESP, Arduino, Raspberry Pi Pico, or Pyboard. Also, ensure your MicroPython programming environment includes uPyCraft or Thonny IDE.
Next, we’ll examine the basics of programming to support MicroPython and Python programmers to recognize key differences between the two.
Importing modules
The first statements in a MicroPython script are importing modules. Every MicroPython program uses one or more modules. Machine and/or time modules are used in all MicroPython-run embedded applications.
Modules/libraries that are already available are ideal to use to save time and focus on new developments. Modules or libraries are a collection of functions and classes written for specific tasks/applications and are excellent for organizing similar and related code blocks. Developers can also write their own modules for adding or supporting new functions and features.
In MicroPython scripts, modules are imported using this syntax:
import module_name
As MicroPython codes are typically uploaded to microcontrollers that have limited RAM and flash memory, it’s possible to import specific classes of a module using this syntax:
from module_name import class_name
In fact, several classes from a module can be imported in a single statement by using:
from module_name import class_name1, class_name2
Here are a few examples of importing modules in MicroPython:
import machine
from machine import Pin
from machine import Pin, PWM
from machine import, Pin, PWM, ADC, ADCBlock
Datatypes
Much like its name denotes, datatypes are the types of data supported in a programming language. Datatypes specify the type of value and what operations can be done on that type.
There are five datatypes supported in MicroPython:
1. Integers (int): stores positive and negative integer numbers, including zero
2. Float (float): stores positive and negative decimal numbers, including zero
3. String (str): stores a set of text characters enclosed between quotation marks
4. Boolean (bool): stores True or False — an expression resulting in a value other than zero is considered True and an expression resulting in a value equal to zero is considered False.
5. Object (obj): an instantiation of a class, which provides access to all f the properties and methods of a specified class
Much like Python, MicroPython is a weak-typed language. This means the variables must not be declared explicitly with specific data types. Rather, the variables can be instantiated anywhere in the code and the datatype is assumed, based on the value assigned to them.
However, the type of a variable can be confirmed by calling the type(variable) method, which is useful if data validation is required.
Variables
Variables are the placeholders for data values. The value stored in a variable can change during a program.
In MicroPython, the datatype for the variables does not have to be defined and is instantiated by assigning a value to an identifier.
Examples:
newPWMfreq = 10
defPWMfreq = 1000
msg = “Data logged to SD card”
Variables can be instantiated anywhere in the code. Their datatype is automatically assumed or changed based on the value assigned to it.
The variables for instantiating class objects are defined by assignment to the constructor function. For example, this statement instantiates a variable as a pin object:
led = Pin(5, Pin.OUT)
The identifiers used as variable names must start with a letter or underscore and can contain numbers, letters, and an underscore. Although special characters are not allowed in variable names. By convention, the functions and classes must have camel-cased identifiers and the variables must have all lower-case identifiers.
Examples of valid variable names:
led
led2
adc1_2
Examples of invalid variable names:
1led
led&1
Comments
Comments are an excellent way to document scripts. MicroPython allows shell-style comments, which are single-line comments starting with the hash (#) character. Everything from the hash character to the end of the line is considered a comment and is not a part of the executed script.
In MicroPython scripts, the comments should only be used minimally or if unavoidable. This is because a large number of comments in the code can significantly increase the size of the execution script and compromise the flash memory of the target port. (Note: the script files are uploaded to the flash memory of the target microcontroller.) They’re also not converted to an executable as occurs in other programming languages, such as embedded C.
Examples of MicroPython comments:
led = Pin(5, Pin.OUT) #LED connected to GPIO5
#function to read data from sensor over UART
Mathematical operators
MicroPython permits arithmetic operations. The mathematical operators that are supported include: addition (+), subtraction (-), multiplication (*), division (/), remainder after division (%), and division discarding decimal point (//).
Additional mathematical operations and constants can be accessed by importing the math module. The module provides two constants: math.e and math.pi.
The functions available in MicroPython’s math module include: math.acos(x), math.acosh(x), math.asin(x), math.asinh(x), math.atan(x), math.atan2(y, x), math.atanh(x), math.ceil(x), math.copysign(x, y), math.cos(x), math.cosh(x), math.degrees(x), math.erf(x), math.erfc(x), math.exp(x), math.expm1(x), math.fabs(x), math.floor(x), math.fmod(x, y), math.frexp(x), math.gamma(x), math.isfinite(x), math.isinf(x), math.isnan(x), math.ldexp(x, exp), math.lgamma(x), math.log(x), math.log10(x), math.log2(x), math.modf(x), math.pow(x, y), math.radians(x), math.sin(x), math.sinh(x), math.sqrt(x), math.tan(x), math.tanh(x), and math.trunc(x).
Examples of the mathematical operators:
5*8+9
>>>49
589/256
>>>2.3007815
2022%4
>>>2
Relational operators
The relational operators are used for the comparison of values. These operators are useful for writing expressions of conditional statements.
The relational operators supported in MicroPython include: equal to (==), not equal to (!=), greater than (>), less than (<), greater than or equal to (>=), and less than or equal to (<=).
The expressions using relational operators use only two operands, which are placed on either side of the relational operator. The result of an expression of a relational operator is always True or False.
Examples of relational operators:
5>3
>>> True
2/3 = 1.5
>>> True
5.3 >= 5.3
>>> True
Conditional statements
Conditional statements are used for evaluating the conditions based on the comparison of values. Two values are compared in an expression by using relational operators. The result of the expression is True or False. If the result of the relational expression is True, the body of the condition is executed — otherwise, it’s skipped throughout the program execution.
In MicroPython, the comparison can be between integer values and strings. The methods called for an object can also return True or False. The body of the conditional includes one or several program statements.
Unlike embedded C, the body of the conditional is not enclosed in braces. Rather, it’s indented to group a single block of code. Only the use of a colon is necessary after the conditional expression.
There are three conditional statements supported in MicroPython:
1. if statement: tests a single expression and either executes or skips a block of code, depending on the result.
A valid example of MicroPython when the “if” statement is conditional:
a = 1
b = 2
if (a == b):
print(“a and b are equal”)
2. else statement: tests a single expression and either executes or skips a block of code, depending on the result. If the expression is False, a block of code is defined after the “else” statement is executed.
A valid example of MicroPython when the “else” statement is conditional:
a = 1
b = 2
if (a == b):
print(“a and b are equal”)
else:
print(“a and b are not equal”)
3. elif statement: tests multiple conditions and, based on a match, executes one of the blocks of code. Once an expression is matched, a block of code for the given expression is executed and the program exits the condition.
A valid example of MicroPython when the “elif” statement is conditional.
a = 3
if (a == 0):
print(“a is equal to 0”)
elif (a == 1):
print(“a is equal to 1”)
elif (a == 2):
print(“a is equal to 2”)
elif (a == 3):
print(“a is equal to 3”)
else:
print(“a is not assigned any value”)
In MicroPython, an indentation requires only two spaces (an indentation in Python3.x uses four). This is done to minimize the size of the execution script as it must be uploaded in memory-constraint microcontrollers.
Classes and objects
As Python is an object-oriented programming language, so is MicroPython, which is a re-implementation of Python 3. In object-oriented programming, real-life entities are first modeled as objects. The definition of a model for certain objects is called a class.
The attributes of these entities are specified with the help of the variables associated with a given class. These variables are called properties or attributes. The computational operations associated with the entities are expressed as the function definitions associated with the given class. These functions are called methods.
In MicroPython, the definition of a class starts with the keyword “class,” followed by the class name and a pair of parenthesis. The class name is followed by a colon(:) to begin the class definition.
The prototype of a class in MicroPython:
class class_name():
class_definition
The definition of a class includes the instantiation of the associated properties/attributes, a constructor, and the definition of the class-specific methods. The properties or attributes are instantiated as variables with the body of a given class.
Example:
class DHT11():
pin = 0
temp = 0
hum = 0
In the above code, the pin, temp, and hum are the properties/attributes of the class DHT11. If the properties/attributes are to be referenced within the class definition, the keyword “self” is used. Outside of the class definition, the attributes are accessed by the object name, followed by a period (.) and the attribute name.
A valid example of calling an attribute within a class definition:
class DHT11():
pin = 0
temp = 0
hum = 0
def getTemp():
….
self.temp = temp()
print(self.temp)
A valid example of accessing the attributes of a class outside of its definition:
class DHT11():
pin = 0
temp = 0
hum = 0
dht = DHT11()
currentTemp = dht.temp
The definition of a class can include one or several methods. The methods are used for performing computational operations on class attributes and/or global variables. The methods are defined as functions within a class body.
Example of defining a method for a given class:
class DHT11():
pin = 0
temp = 0
hum = 0
def getTemp():
return self.temp
If a method must be accessed within a class definition, the “self” keyword is used. If it has to be accessed outside of the class definition, it must be called by its class name, followed by a period (.) and the method name.
Example of calling a method within a class definition:
class DHT11():
pin = 0
temp = 0
hum = 0
def getTemp():
return self.temp
def printTemp():
temperature = self.getTemp()
print(“Temperature: “)
print(temperature)
Example of calling a method outside of a class definition:
class DHT11():
pin = 0
temp = 0
hum = 0
def getTemp():
return self.temp
dht = DHT11()
temperature = dht.getTemp()
The values of the required attributes of a class can be assigned as soon as an object of a given class is instantiated. This first requires defining a constructor method. The constructor method explicitly sets the default values to the attributes or assigns the values of essential attributes by passing arguments.
The constructor method is defined using the __init__() function. If the value of the attributes is set by the user, the constructor definition takes one or more parameters.
Example of a constructor that explicitly sets the values of the attributes:
class login():
username = user
password = password
def __init__():
self.user = “root”
self.password = “raspberry”
logcredentials = login()
Example of a constructor that lets a user specify the value of the required class attributes:
class DHT11():
pin = 0
temp = 0
hum = 0
def __init__(pin):
pin = self.pin
dhtpin = Pin(5, Pin.IN)
dht11 = DHT11(dhtpin)
An object of a given class is instantiated by calling the constructor method. Outside of the class definition, the constructor method has the same name as the class name.
A valid example of defining a class, instantiating an object of the class, and using its attributes and methods:
class DHT11():
pin = 0
temp = 0
hum = 0
def __init__(pin):
pin = self.pin
def getTemp():
….
def getHumdity():
….
dhtpin = Pin(5, Pin.IN)
dht11 = DHT11(dhtpin)
dht11.getTemp()
dht11.getHumidity()
if dht11.temp > 25:
…
Conclusion
In this article, we covered the basics of MicroPython programming, a subset of Python 3 — meaning MicroPython shares much of the same syntax and object-oriented programming as Python 3. However, there are slight differences and Python developers might experience syntax errors and bugs if they only apply their Python knowledge to the MicroPython domain.
The fundamentals of the programming features of Pythonic syntax is applicable in MicroPython scripting, which are covered in this article. These language basics can serve as the backbone of your MicroPython scripts.
You may also like:
Filed Under: Arduino Projects, Featured, Python, Tech Articles, Tutorials
Questions related to this article?
👉Ask and discuss on EDAboard.com and Electro-Tech-Online.com forums.
Tell Us What You Think!!
You must be logged in to post a comment.