Methods
It often happens that you want to respond to different inputs with the same output.
Function
blocks (methods) in MakeCode help to break down large programs into small, easy-to-understand parts. You can use code written once several times, which saves time and reduces errors. Clear names for methods mean that everyone knows immediately what they do without having to read the whole code. Complicated processes become simpler because you only have to concentrate on the inputs and results.
without “Functions” block
In the following example, we react to the detection of an obstacle by both the left-hand obstacle sensor and the right-hand obstacle sensor by repeatedly switching the headlights on and off every second.
basic.forever(function () {
JoyCar.initJoyCar(RevisionMainboard.OnepThree)
if (JoyCar.obstacleavoidance(SensorLRSelection.Left)) {
JoyCar.light(ToggleSwitch.On)
basic.pause(1000)
JoyCar.light(ToggleSwitch.Off)
basic.pause(1000)
JoyCar.light(ToggleSwitch.On)
basic.pause(1000)
JoyCar.light(ToggleSwitch.Off)
basic.pause(1000)
} else if (JoyCar.obstacleavoidance(SensorLRSelection.Right)) {
JoyCar.light(ToggleSwitch.On)
basic.pause(1000)
JoyCar.light(ToggleSwitch.Off)
basic.pause(1000)
JoyCar.light(ToggleSwitch.On)
basic.pause(1000)
JoyCar.light(ToggleSwitch.Off)
basic.pause(1000)
}
})
The sample code in MicroPython is there because it is also available in MakeCode. This is super handy because if you're already comfortable with MakeCode, it's much easier to get started with MicroPython. By having sample code in both environments, you can continue learning without much interruption. This will help you understand how programming works and you can see how to do the same things in different programming languages.
# Import necessary libraries
from microbit import *
import neopixel
# Define your Joy-Car mainboard revision
joycar_rev = 1.3
# Define object for the lights
np = neopixel.NeoPixel(pin0, 8)
# Initialization of the I2C interface for the Joy-Car mainboard
i2c.init(freq=400000, sda=pin20, scl=pin19)
# Define values for the lights
# Values which LEDs are to be activated
headlights = (0, 3)
backlights = (5, 6)
indicator_left = (1, 4)
indicator_right = (2, 7)
indicator_warning = (1, 2, 4, 7)
# Values, which color should be displayed on the LEDs
led_white = (60, 60, 60)
led_red = (60, 0, 0)
led_off = (0, 0, 0)
led_red_br = (255, 0, 0)
led_orange = (100, 35, 0)
# method to activate/deactivate lights
def lights(on = True):
if on:
for x, y in zip(headlights, backlights):
# define white for the headlights
np[x] = led_white
# define dark red for the backlights
np[y] = led_red
else:
for x, y in zip(headlights, backlights):
# define black for the headlights and backlights
np[x] = led_off
np[y] = led_off
np.show()
# retrieve all sensor data
def fetchSensorData():
# Since the zfill function is not included in micro:bit Micropython,
# it must be inserted as a function
def zfill(s, width):
return '{:0>{w}}'.format(s, w=width)
# Read hexadecimal data and convert to binary data
data = "{0:b}".format(ord(i2c.read(0x38, 1)))
# Fill in the data to 8 digits if required
data = zfill(data, 8)
# declare bol_data_dict as dictionary
bol_data_dict = {}
# Counter for the loop that enters the data from data into bol_data_dict
bit_count = 7
# Transfer the data from data to bol_data_dict
for i in data:
if i == "0":
bol_data_dict[bit_count] = False
bit_count -= 1
else:
bol_data_dict[bit_count] = True
bit_count -= 1
# As of mainboard revision 1.3, the speed sensors are on separate pins
if joycar_rev >= 1.3:
bol_data_dict[8], bol_data_dict[9] = bol_data_dict[0], bol_data_dict[1]
bol_data_dict[0] = bool(pin14.read_digital())
bol_data_dict[1] = bool(pin15.read_digital())
# bit 0 = SpeedLeft, bit 1 = SpeedRight, bit 2 = LineTrackerLeft,
# bit 3 = LinetrackerCenter, bit 4 = LinetrackerRight,
# bit 5 = ObstacleLeft, bit 6 = ObstacleRight, bit 7 = free pin(7)
# (bit 8 = free (pin0) bit 9 = free (pin1)) - only with revision 1.3 or newer
return bol_data_dict
while True:
# Read sensor data from the mainboard
sensor_data = fetchSensorData()
# Check left-hand obstacle sensor
if not sensor_data[5]:
# Switch on low beam
lights()
# Wait for 1 second
sleep(1000)
# Switch off low beam
lights(on = False)
# Wait for 1 second
sleep(1000)
# Switch on low beam
lights()
# Wait for 1 second
sleep(1000)
# Switch off low beam
lights(on = False)
# Wait for 1 second
sleep(1000)
# Check right-hand obstacle sensor
elif not sensor_data[6]:
# Switch on low beam
lights()
# Wait for 1 second
sleep(1000)
# Switch off low beam
lights(on = False)
# Wait for 1 second
sleep(1000)
# Switch on low beam
lights()
# Wait for 1 second
sleep(1000)
# Switch off low beam
lights(on = False)
# Wait for 1 second
sleep(1000)
Advantage of functions
However, this can very quickly become confusing. If we need certain processes more than once, we can outsource them to so-called functions.
In principle, functions consist of two parts. The function header and the function body. The function name is first defined in the header. The function body then contains all the instruction blocks that are to be executed with this function.
The function can then be called in the main flow of the program. Each time it is called, all the instructions that we have previously added to the function body are executed.
In the following example, we have outsourced the light change to the light_sequence
function. This allows us to reuse the sequence both for detection by the left-hand obstacle sensor and by the right-hand obstacle sensor. This not only guarantees that the same blocks are executed in both cases, but also makes our forever
block much clearer!
function light_sequence () {
JoyCar.light(ToggleSwitch.On)
basic.pause(1000)
JoyCar.light(ToggleSwitch.Off)
basic.pause(1000)
JoyCar.light(ToggleSwitch.On)
basic.pause(1000)
JoyCar.light(ToggleSwitch.Off)
basic.pause(1000)
}
basic.forever(function () {
JoyCar.initJoyCar(RevisionMainboard.OnepThree)
if (JoyCar.obstacleavoidance(SensorLRSelection.Left)) {
light_sequence()
} else if (JoyCar.obstacleavoidance(SensorLRSelection.Right)) {
light_sequence()
}
})
The sample code in MicroPython is there because it is also available in MakeCode. This is super handy because if you're already comfortable with MakeCode, it's much easier to get started with MicroPython. By having sample code in both environments, you can continue learning without much interruption. This will help you understand how programming works and you can see how to do the same things in different programming languages.
# Import necessary libraries
from microbit import *
import neopixel
# Define your Joy-Car mainboard revision
joycar_rev = 1.3
# Define object for the lights
np = neopixel.NeoPixel(pin0, 8)
# Initialization of the I2C interface for the Joy-Car mainboard
i2c.init(freq=400000, sda=pin20, scl=pin19)
# Define values for the lights
# Values which LEDs are to be activated
headlights = (0, 3)
backlights = (5, 6)
indicator_left = (1, 4)
indicator_right = (2, 7)
indicator_warning = (1, 2, 4, 7)
# Values, which color should be displayed on the LEDs
led_white = (60, 60, 60)
led_red = (60, 0, 0)
led_off = (0, 0, 0)
led_red_br = (255, 0, 0)
led_orange = (100, 35, 0)
# method to activate/deactivate lights
def lights(on = True):
if on:
for x, y in zip(headlights, backlights):
# define white for the headlights
np[x] = led_white
# define dark red for the backlights
np[y] = led_red
else:
for x, y in zip(headlights, backlights):
# define black for the headlights and backlights
np[x] = led_off
np[y] = led_off
np.show()
# retrieve all sensor data
def fetchSensorData():
# Since the zfill function is not included in micro:bit Micropython,
# it must be inserted as a function
def zfill(s, width):
return '{:0>{w}}'.format(s, w=width)
# Read hexadecimal data and convert to binary data
data = "{0:b}".format(ord(i2c.read(0x38, 1)))
# Fill in the data to 8 digits if required
data = zfill(data, 8)
# declare bol_data_dict as dictionary
bol_data_dict = {}
# Counter for the loop that enters the data from data into bol_data_dict
bit_count = 7
# Transfer the data from data to bol_data_dict
for i in data:
if i == "0":
bol_data_dict[bit_count] = False
bit_count -= 1
else:
bol_data_dict[bit_count] = True
bit_count -= 1
# As of mainboard revision 1.3, the speed sensors are on separate pins
if joycar_rev >= 1.3:
bol_data_dict[8], bol_data_dict[9] = bol_data_dict[0], bol_data_dict[1]
bol_data_dict[0] = bool(pin14.read_digital())
bol_data_dict[1] = bool(pin15.read_digital())
# bit 0 = SpeedLeft, bit 1 = SpeedRight, bit 2 = LineTrackerLeft,
# bit 3 = LinetrackerCenter, bit 4 = LinetrackerRight,
# bit 5 = ObstacleLeft, bit 6 = ObstacleRight, bit 7 = free pin(7)
# (bit 8 = free (pin0) bit 9 = free (pin1)) - only with revision 1.3 or newer
return bol_data_dict
# Define your own method for activating/deactivating the lights
def light_sequence():
# Switch on low beam
lights()
# Wait for 1 second
sleep(1000)
# Switch off low beam
lights(on = False)
# Wait for 1 second
sleep(1000)
# Switch on low beam
lights()
# Wait for 1 second
sleep(1000)
# Switch off low beam
lights(on = False)
# Wait for 1 second
sleep(1000)
while True:
# Read sensor data from the mainboard
sensor_data = fetchSensorData()
# Check left-hand obstacle sensor
if not sensor_data[5]:
# Call the defined method
light_sequence()
# Check right-hand obstacle sensor
elif not sensor_data[6]:
# Call the defined method
light_sequence()
Returning values
Optionally, functions can also return so-called return values. In this case, the function sends back a response each time it is called. This return value, i.e. the response, can be determined within the function. For example, calculations can be performed here and the result returned to the main function.
In the next example, we create the sonar_sensor
function. As soon as it is called, it checks the ultrasonic sensor and returns either a 0, a 1 or a 2 to the main function, depending on the distance value returned by the sensor. Depending on the value returned, we switch the headlights and hazard warning lights of the Joy-Car to different states.
If the measured distance to the obstacle is 50 cm or greater, a 0 is returned, i.e. the lights are switched on and the hazard warning lights are switched off. If the measured distance is less than 50 cm, a 1 is returned, i.e. the hazard warning flashers are switched on and the lights are switched off. If an error occurs and the return value of the sensor is not within the defined ranges, a 2 is returned. In this case, both the light and the hazard warning lights are switched on.
function sonar_sensor () {
if (JoyCar.sonar() >= 50) {
return 0
} else if (JoyCar.sonar() < 50) {
return 1
} else {
return 2
}
}
basic.forever(function () {
JoyCar.initJoyCar(RevisionMainboard.OnepThree)
if (sonar_sensor() == 1) {
JoyCar.light(ToggleSwitch.Off)
JoyCar.hazardlights(ToggleSwitch.On)
} else if (sonar_sensor() == 0) {
JoyCar.light(ToggleSwitch.On)
JoyCar.hazardlights(ToggleSwitch.Off)
}
})
The sample code in MicroPython is there because it is also available in MakeCode. This is super handy because if you're already comfortable with MakeCode, it's much easier to get started with MicroPython. By having sample code in both environments, you can continue learning without much interruption. This will help you understand how programming works and you can see how to do the same things in different programming languages.
# Import necessary libraries
from microbit import *
import neopixel
import gc
from machine import time_pulse_us
# Define your Joy-Car mainboard revision
joycar_rev = 1.3
# Define object for the lights
np = neopixel.NeoPixel(pin0, 8)
# Initialization of the I2C interface for the Joy-Car mainboard
i2c.init(freq=400000, sda=pin20, scl=pin19)
# Define pins for ultrasonic sensor
trigger = pin8
echo = pin12
# Initialization of the pins for the ultrasonic sensor
trigger.write_digital(0)
echo.read_digital()
# Define values for the lights
# which LEDs are to be activated
headlights = (0, 3)
backlights = (5, 6)
indicator_left = (1, 4)
indicator_right = (2, 7)
indicator_warning = (1, 2, 4, 7)
# which color should be displayed on the LEDs
led_white = (60, 60, 60)
led_red = (60, 0, 0)
led_off = (0, 0, 0)
led_red_br = (255, 0, 0)
led_orange = (100, 35, 0)
# Variables for the lights
last_ind_act = 0
last_state_hazard = False
last_state_lights = False
# Method for calculating the distance from the ultrasonic sensor
def get_distance():
# Activate garbage collector
gc.collect()
# Set a short pulse on the trigger pin
trigger.write_digital(1)
trigger.write_digital(0)
# Measurement of the time until the echo pin becomes high
duration = time_pulse_us(echo, 1)
# Calculate distance
distance = ((duration / 1000000) * 34300) / 2
# Return the distance, rounded to 2 decimal places
return round(distance, 2)
# method to activate/deactivate lights
def lights(on = True):
if on:
for x, y in zip(headlights, backlights):
# define white for the headlights
np[x] = led_white
# define dark red for the backlights
np[y] = led_red
else:
for x, y in zip(headlights, backlights):
# define black for the headlights and backlights
np[x] = led_off
np[y] = led_off
np.show()
# Method for activating/deactivating the indicators.
# Variable for the method to compare when the lights were last active.
last_ind_act = running_time()
def lightsIndicator(direction, on = True):
# to be able to change the global variable
global last_ind_act
# Activate garbage collector
gc.collect()
# if you want to switch off the indicators
if on is False:
# Deactivate LEDs
for x in direction:
np[x] = led_off
np.show()
# Close the method
return
# Activation/deactivation of the indicators after 400 ms
if running_time() - last_ind_act >= 400:
# Activate LEDs when the LEDs are off
if np[direction[0]] == led_off:
for x in direction:
np[x] = led_orange
# Deactivate the LEDs when they are switched on
else:
for x in direction:
np[x] = led_off
np.show()
# Set global variable to current runtime
last_ind_act = running_time()
# Method for checking the distance
def sonar_sensor():
# Return 0 if the distance is greater than 50cm, otherwise return 1
distance = get_distance()
if distance >= 50:
return 0
else:
return 1
while True:
# Retrieve information from the sonar_sensor method
sonar = sonar_sensor()
# if distance < 50 cm
if sonar == 1:
# Switch off low beam (if it is still on)
lights(on = False)
# Switch on hazard warning lights
lightsIndicator(indicator_warning)
else:
# Deactivate the hazard warning lights (if they are still switched on)
if last_state_hazard is True:
# Deactivate hazard warning lights
lightsIndicator(indicator_warning, on = False)
# Switch on low beam (if it is still switched off)
if last_state_lights is False:
# Switch on low beam
lights()