Lesson 14 — Writing Your Own Functions
Estimated time: 60 minutes
- Learning Objectives
- Concepts
- Guided Walkthrough
- Challenges
- Common Mistakes & Debugging
- Key Vocabulary
Learning Objectives
By the end of this lesson you will be able to:
- Define a function using
def - Call a function to run it
- Explain why functions make code better
- Write a set of reusable NeoPixel control functions
Concepts
What is a Function?
A function is a named, reusable block of code. Think of it as a recipe — you write the recipe once, then follow it as many times as you want without rewriting it.
You’ve been using functions all along: print(), len(), input(), time.sleep() — these are all built-in functions. Now you’ll write your own.
Why functions?
- Avoid repetition — the DRY principle: “Don’t Repeat Yourself”. If you find yourself copy-pasting code, it should probably be a function.
- Readability —
flash_red()is much easier to understand than 6 lines of LED code. - Maintainability — fix a bug in one function and it’s fixed everywhere that function is used.
Defining a Function
def function_name():
# function body
# (indented code that runs when called)
defkeyword — tells Python you’re defining a functionfunction_name— the name you give it (same naming rules as variables)():— empty parentheses and colon (we’ll add parameters in Lesson 15)- Indented body — the code that runs when the function is called
Nothing happens until you call it! Defining a function just writes the recipe. Calling it actually executes it.
Calling a Function
# Define first:
def say_hello():
print("Hello!")
print("Welcome to my program.")
# Then call (can call multiple times):
say_hello() # Runs the function
say_hello() # Runs it again
say_hello() # And again — same code, no repetition
Docstrings
A docstring is a string immediately after the def line that describes what the function does. It’s optional but good practice:
def flash_led():
"""Flash the LED red once."""
# ... code here ...
Functions Calling Functions
Functions can call other functions — building up complexity from simple pieces:
def led_on():
np[0] = (255, 0, 0)
np.write()
def led_off():
np[0] = (0, 0, 0)
np.write()
def flash():
led_on()
time.sleep(0.3)
led_off()
time.sleep(0.3)
def triple_flash():
flash() # Calls flash(), which calls led_on() and led_off()
flash()
flash()
This is called composition — building complex behaviour from simple functions.
Guided Walkthrough
Step 1: Your First Function
def greet():
"""Print a friendly greeting."""
print("Hello!")
print("Welcome to MicroPython.")
print("Let's write some code!")
# Call it three times:
greet()
greet()
greet()
Notice how much cleaner this is than copying those 3 print statements three times.
Step 2: Refactoring Repetitive Code
Without functions (messy):
import machine, neopixel, time
pin = machine.Pin(48, machine.Pin.OUT)
np = neopixel.NeoPixel(pin, 1)
np[0] = (255, 0, 0); np.write(); time.sleep(0.5); np[0] = (0,0,0); np.write(); time.sleep(0.5)
np[0] = (255, 0, 0); np.write(); time.sleep(0.5); np[0] = (0,0,0); np.write(); time.sleep(0.5)
np[0] = (255, 0, 0); np.write(); time.sleep(0.5); np[0] = (0,0,0); np.write(); time.sleep(0.5)
With functions (clean):
import machine, neopixel, time
pin = machine.Pin(48, machine.Pin.OUT)
np = neopixel.NeoPixel(pin, 1)
def flash_red():
"""Flash the LED red once."""
np[0] = (255, 0, 0)
np.write()
time.sleep(0.5)
np[0] = (0, 0, 0)
np.write()
time.sleep(0.5)
flash_red()
flash_red()
flash_red()
If you want to change the flash timing, you only change it in one place.
Step 3: LED Toolkit — Functions for Common Actions
import machine, neopixel, time
pin = machine.Pin(48, machine.Pin.OUT)
np = neopixel.NeoPixel(pin, 1)
def led_on(r, g, b):
"""Turn LED on with RGB colour."""
np[0] = (r, g, b)
np.write()
def led_off():
"""Turn LED off."""
np[0] = (0, 0, 0)
np.write()
def flash(r, g, b, times=3, delay=0.3):
"""Flash LED a number of times. times and delay have defaults."""
for _ in range(times):
led_on(r, g, b)
time.sleep(delay)
led_off()
time.sleep(delay)
# Using the toolkit:
flash(255, 0, 0) # Flash red 3 times (defaults)
time.sleep(0.5)
flash(0, 255, 0, times=5) # Flash green 5 times
time.sleep(0.5)
flash(0, 0, 255, times=2, delay=0.1) # Fast blue flashes
Default parameters: times=3 means “if no times is given, use 3”. You’ll learn more about this in Lesson 15.
Step 4: Functions Calling Functions
def alert():
"""Urgent alert pattern."""
flash(255, 0, 0, times=5, delay=0.1) # Rapid red flashes
led_on(255, 0, 0) # Hold red
time.sleep(1)
led_off()
def standby():
"""Calm standby pattern — slow green pulse."""
for brightness in range(0, 100, 5):
led_on(0, brightness, 0)
time.sleep(0.04)
for brightness in range(100, -1, -5):
led_on(0, brightness, 0)
time.sleep(0.04)
led_off()
def startup_sequence():
"""Run startup: breathe green, then alert."""
print("Starting up...")
standby()
time.sleep(0.5)
print("Alert!")
alert()
print("Done.")
startup_sequence()
Challenges
⭐ Core
Write three functions: show_red(), show_green(), show_blue() — each shows that colour for 1 second and turns off. Then write a show_rainbow() function that calls them in order with short pauses. Call show_rainbow() twice.
⭐⭐ Extension
Write morse_dot() (short flash — 0.1s) and morse_dash() (long flash — 0.3s) functions. Use them to spell SOS in Morse code: ... --- ... (S=dot dot dot, O=dash dash dash).
⭐⭐⭐ Stretch
Write these functions: police_lights(duration) — alternating rapid red/blue flashes for duration seconds; ambulance_lights(duration) — rapid white flashes; traffic_light_cycle(cycles) — full red/amber/green sequence repeated cycles times. Create a demo() function that calls all three in a dramatic sequence. Comment each function with a docstring.
Common Mistakes & Debugging
Defining a function but never calling it Nothing happens! def my_function(): just defines it. You must call my_function() to run it.
Calling before defining Python reads top to bottom. If you call my_function() on line 5 but define def my_function(): on line 10, you’ll get a NameError. Define functions before calling them.
Forgetting the parentheses when calling my_function (no brackets) refers to the function object itself. my_function() (with brackets) runs it.
Indentation errors in the function body All code inside the function must be indented the same amount.
Key Vocabulary
| Term | Definition |
|---|---|
| function | A named, reusable block of code |
| def | The keyword used to define a function |
| call | Executing a function by writing its name followed by () |
| DRY | “Don’t Repeat Yourself” — use functions instead of copying code |
| function body | The indented code inside the function that runs when called |
| docstring | A string at the top of a function body describing what it does |
| default parameter | A parameter with a pre-set value used when no argument is provided |
| composition | Building complex functions by calling simpler functions |