In this tutorial, we will see what exceptions are and how to handle them in MicroPython to manage errors in our code.
An exception is an event that occurs during the execution of a program and that interrupts the normal flow of instructions.
These situations can be caused by errors in the code, unexpected hardware conditions, or problems with input data.
In MicroPython, exceptions are objects that represent these errors. When an exception occurs, the program stops unless the exception is caught and handled.
Basic Structure of Exception Handling
In MicroPython, exception handling is done using try and except blocks. The basic structure is as follows:
try:
# Code that may raise an exception
result = divide(10, 0)
except ZeroDivisionError:
# Code that runs if a ZeroDivisionError occurs
print("Error: Cannot divide by zero.")
Here we place the code that may generate an exception. If an exception occurs inside this block, the program flow is diverted to the except block.
Here we handle the exception. We can specify the type of exception we want to catch (e.g., ZeroDivisionError) or use a generic except to catch any exception.
This block executes if no exception is raised in the try block.
This block always executes, regardless of whether an exception was raised or not. It is useful for releasing resources or performing cleanup tasks.
Let’s see it with a complete example that includes try, except, else, and finally blocks.
try:
result = divide(10, 2) # This will not raise an exception
except ZeroDivisionError:
print("Error: Cannot divide by zero.")
else:
print(f"The result is: {result}")
finally:
print("End of the try-except block.")
In this example, since no exception is raised, the else and finally blocks are executed.
We can also catch multiple types of exceptions in a single try-except block:
try:
value = int("not a number") # This will raise a ValueError
except ValueError:
print("Error: Invalid value.")
except TypeError:
print("Error: Incorrect data type.")
Common Types of Exceptions in MicroPython
MicroPython defines several types of exceptions that we can catch and handle. Some of the most common ones are:
| Error | Description |
|---|---|
| ZeroDivisionError | Occurs when trying to divide by zero. |
| TypeError | Occurs when performing an operation with incompatible data types. |
| ValueError | Occurs when an inappropriate value is passed to a function. |
| IndexError | Occurs when trying to access an index outside the bounds of a list or array. |
| NameError | Occurs when trying to use a variable or function that is not defined. |
| OSError | Occurs in filesystem or I/O related operations (e.g., trying to open a file that does not exist). |
Practical Examples
User Input Validation
This example shows how to handle invalid user input:
try:
age = int(input("Enter your age: "))
except ValueError:
print("Error: You must enter a valid number.")
else:
if age >= 18:
print("You are an adult.")
else:
print("You are a minor.")
File Management
In this example, the file is always closed, regardless of whether an exception occurred or not.
try:
file = open("data.txt", "r")
content = file.read()
except OSError as e:
print(f"Error opening the file: {e}")
else:
print("File read successfully.")
finally:
if 'file' in locals():
file.close()
print("File closed.")
Handling Errors in Hardware Operations
In embedded devices, it’s common to work with sensors and actuators. Here’s how to handle errors when reading a sensor:
from machine import Pin, ADC
sensor = ADC(Pin(34))
try:
value = sensor.read()
except OSError as e:
print(f"Error reading the sensor: {e}")
else:
print(f"Sensor value: {value}")
Handling Exceptions in I2C Communications
When working with I2C devices, communication errors may occur. Here’s how to handle them:
from machine import I2C, Pin
i2c = I2C(scl=Pin(22), sda=Pin(21))
try:
devices = i2c.scan()
except OSError as e:
print(f"I2C communication error: {e}")
else:
print(f"Devices found: {devices}")
