Programming Mechatronics

Combining mechanical engineering, electronics, and computer science to design and engineer autonomous, intelligent systems built from sensors, actuators, and microcontrollers.

Year 11 — Preliminary ~10 weeks Outcomes: SE-11-01 to 09
19Dot points
3Parts
9Outcomes
~10Weeks
Syllabus Part 1 Understanding mechatronic hardware and software

Applications of Mechatronic Systems

🎯 (SE-11-03)

📌 Outline applications of mechatronic systems in a variety of specialised fields

Mechatronics combines mechanical engineering, electronics and computing so that a machine can sense its surroundings, decide what to do and act on the decision. Every application below follows the same loop: sensors measure something, a controller running a program decides, and actuators change the physical world. Only the sensors, the actuators and the rules differ from field to field.

AerospaceUnmanned Aerial Vehicles (UAVs) use mechatronics for flight stabilisation, GPS navigation, and autonomous landing routines.
HealthcareAdvanced prosthetics use electromyography (EMG) sensors to detect muscle signals, allowing users to control bionic limbs with high precision.
AutomotiveModern vehicles incorporate mechatronic subsystems such as Anti-lock Braking Systems (ABS) and Electronic Stability Control (ESC) to enhance driver safety.
MiningAutonomous haul trucks and drills, used at some Australian mines, follow programmed routes using GPS and other sensors so that fewer people work near hazards.
ManufacturingIndustrial robot arms weld, paint and assemble parts, repeating the same motion accurately for a whole shift.
AgricultureAutomatic milking systems, irrigation controllers and guided tractors sense the animal, the soil or the paddock and adjust what they do.
▶ Watch & Learn

A short overview of how mechanical, electrical, and software engineering combine to create intelligent mechatronic systems.

Sense, decide, act in five fields

FieldMechatronic systemWhat it senses → what it does
AgricultureAutomated irrigationSoil moisture → switches a pump on and off
ManufacturingPick-and-place robot armCamera finds a part → gripper picks it up and places it
HomeRobot vacuumBump and distance sensors → steers around furniture
TransportAutomatic train doorsPlatform position and obstruction sensors → opens or holds the doors
HealthcareInsulin pump with a glucose sensorSensor under the skin reports glucose level → pump adjusts the insulin it delivers

Why use a mechatronic system, and what does it cost?

  • Benefits: consistent repeatable results, work in places that are dangerous or remote, continuous operation, and fine control that a person cannot match (a drone corrects its balance many times a second).
  • Trade-offs: higher up-front cost, specialist skills to design and repair, new failure modes (a sensor fault or software bug can make the machine act wrongly), safety risks where machines share space with people, and changes to the jobs people do.
Exam tip: "Outline" an application An outline answer is brief and specific. Name the field and the system, then say what it senses and what it does in response, for example: "In agriculture, an irrigation controller reads a soil moisture sensor and switches a pump on when the soil is dry." Naming a real sensor and a real actuator earns more than a general statement such as "robots make farming easier".

Hardware Requirements and Code Development

🎯 (SE-11-01, SE-11-03)

📌 Identify the hardware requirements to run a program and the effect on code development

  • assessing the relationship of microcontrollers and the central processing unit (CPU)
  • the influence of instruction set and opcodes
  • the use of address and data registers

Microcontrollers and the CPU

The central processing unit (CPU) is the part of a computer that fetches and executes instructions. A microcontroller is a whole small computer on one chip: a CPU core plus flash memory (holds the program), RAM (holds variables while running) and peripherals such as GPIO pins, analog-to-digital converters (ADCs) and PWM outputs. A microprocessor is mainly the CPU, so a computer built around one needs separate RAM, storage and input/output chips. Single-board computers such as the Raspberry Pi put a microprocessor, memory and ports on one board and run a full operating system. The CPU is therefore a part of a microcontroller, and the microcontroller is what most mechatronic systems are built around.

Microcontroller (Arduino Uno class, Raspberry Pi Pico class)Microprocessor board (Raspberry Pi class)
On the chipCPU, flash, RAM and peripherals togetherCPU cores; RAM and storage (an SD card) are separate
Typical scaleUno class: 8-bit chip, 2 KB of RAM, 16 MHz. Pico class: 32-bit dual-core Arm chip, 264 KB of RAM64-bit multi-core Arm chip, gigabytes of RAM, clock in the GHz range
Operating systemNone, or a small runtime; your program is all that runsA full OS such as Linux runs many programs at once
TimingPredictable: you can switch a pin within microsecondsThe OS can pause your program, so timing varies
PowerLow; can sleep on a small batteryHigher; needs a proper supply and a clean shutdown
Best forMotor control, sensors, simple loopsCameras, image processing, web servers, machine learning

Many systems use both: a microprocessor for vision and planning, and a microcontroller for the fast, safety-critical motor loop.

Effect on code development

  • Memory limits data choices. With 2 KB of RAM you cannot store a long list of readings or load a large library.
  • Word size affects arithmetic. On an Uno an int is 16 bits, so it overflows past 32 767; counting milliseconds needs a larger type. The 32-bit Pico does not have this problem at that scale.
  • Small chips may lack floating-point hardware, so decimal maths is done in software and is slow; integers are faster. Code is also built for one instruction set: a program compiled for AVR will not run on Arm.
  • Language trades speed for convenience. Compiled C++ is fast and compact; MicroPython is quicker to write but needs more memory and runs slower.
  • Voltage and pins matter. A Pico's pins are 3.3 V and must not be driven at 5 V; an Uno's pins are 5 V. The number of ADC and PWM pins limits how many sensors and motors you can connect.

Instruction set and opcodes

A CPU repeats the fetch, decode, execute cycle: fetch the next instruction from memory, decode which operation it asks for, then execute it. The instruction set is the complete list of operations a particular CPU understands. Each machine instruction holds an opcode (which operation) and its operands (which registers, addresses or values to use). A compiler or interpreter translates your Python or C++ into these instructions, so the instruction set decides what is fast, what is slow and what code can run at all. AVR and Arm are two instruction set families; RISC-V is a third.

The example below uses an invented 16-bit teaching instruction set (not a real chip) to show how an opcode and operands share one instruction word.

AssemblyOpcodeMeaningInstruction word
LOAD R1, [0x20]0001Copy the value at address 0x20 into register R10x1120
LOAD R2, [0x21]0001Copy the value at address 0x21 into R20x1221
ADD R1, R20010R1 ← R1 + R20x2120
STORE [0x22], R10011Copy R1 into address 0x220x3122

Address and data registers

Registers are the CPU's own tiny, very fast storage. Data registers hold the values being worked on, such as a sensor reading waiting to be compared. Address registers hold the address of a memory location to read or write (the memory address register, MAR). The program counter is a special register holding the address of the next instruction, and the instruction register holds the instruction being decoded. An address is the number that identifies one memory location; the address bus carries it and the data bus carries the value. A 16-bit address can identify 216 = 65 536 locations.

Trace of the four instructions above, with 7 stored at 0x20 and 5 at 0x21:

StepProgram counter after fetchInstruction executedR1R2Memory 0x22
Start0x00—???
10x01LOAD R1, [0x20]7??
20x02LOAD R2, [0x21]75?
30x03ADD R1, R2125?
40x04STORE [0x22], R112512

On a microcontroller, the peripherals are reached the same way: this is memory-mapped I/O. Reading a pin or setting a PWM duty cycle is a read or write of a special register at a fixed address, and Pin.value() in MicroPython hides that detail.

▶ Watch & Learn

A beginner-friendly walkthrough of a microcontroller (the Arduino) — how to read inputs, drive outputs, and upload code to control hardware.

Exam tip: "Identify" hardware requirements An identify question wants the requirement named and linked to its effect on the code. A full answer says, for example: "The controller needs a microcontroller with an ADC and PWM pins; because it has only 2 KB of RAM, the program must use small data types and integer maths." Do not swap the terms: registers sit inside the CPU, RAM sits outside it (or beside it, on the same microcontroller chip), and the opcode is the part of an instruction that names the operation.

Sensors, Actuators and End Effectors

🎯 (SE-11-06)

📌 Identify and describe a range of sensors, actuators and end effectors/manipulators within existing mechatronic systems

  • motion sensors
  • light level sensors
  • hydraulic actuators
  • robotic grippers

A sensor converts a physical quantity (light, heat, movement) into an electrical signal the controller can read. An actuator converts an electrical signal, plus a source of power, into physical action. An end effector is the tool at the end of a robot arm that touches the work; the arm and joints that position it are the manipulator.

Sensors (Inputs)

Devices that convert physical properties into electrical signals.

  • Motion: PIR (Passive Infrared) sensors detect the change in infrared radiation as a warm body moves across their field of view.
  • Light: LDRs (Light Dependent Resistors) change resistance based on ambient light levels.

Actuators (Outputs)

Components that convert electrical energy into physical motion.

  • Hydraulic: Utilise pressurised fluid to generate high force (e.g., in excavators).
  • Electric: DC motors or Servos that provide rotational movement.

Manipulators

The parts of the system that physically interact with objects.

  • Robotic Gripper: An end effector designed to grasp and release objects.
  • Degrees of Freedom: The number of independent movements a robot can make (see the next sections).

Motion sensors

"Motion" covers several measurements. A PIR sensor gives a digital HIGH when it sees a warm body moving, but cannot say how far away it is. An ultrasonic sensor times the echo of a sound pulse to find distance. An accelerometer or gyroscope (often together in an inertial measurement unit, IMU) reports acceleration and rotation, so a drone can tell it is tilting. A rotary encoder counts pulses as a shaft turns, giving speed and position.

Light level sensors

An LDR's resistance falls as light increases. A microcontroller cannot read resistance directly, so the LDR is wired with a fixed resistor as a voltage divider and the resulting voltage is read by an ADC. A photodiode responds faster, and a digital lux sensor sends a calibrated reading over a serial bus.

SensorMeasuresSignal to the controllerExample use
PIRMovement of a warm bodyDigital (HIGH or LOW)Automatic door, security light
UltrasonicDistance, from echo timeDigital pulse whose width is timedRobot obstacle avoidance
IMUAcceleration and rotationDigital data over I2C or SPIDrone or self-balancing robot
Rotary encoderShaft rotationDigital pulsesWheel speed, arm position
LDRLight levelAnalog voltage, read by an ADCAutomatic night light
Thermistor or TMP36TemperatureAnalog voltage, read by an ADCThermostat, greenhouse vent
MicroPython — Sensing and Moving
from machine import Pin, PWM
import utime

sensor = Pin(14, Pin.IN)         # PIR motion sensor: 1 = movement
servo = PWM(Pin(15), freq=50)    # Actuator: a servo needs a 50 Hz signal

while True:
    if sensor.value() == 1:
        servo.duty_u16(5000)     # about a 1.5 ms pulse: move to the centre position
    else:
        servo.duty_u16(2000)     # about a 0.6 ms pulse: return to the home position
    utime.sleep(0.1)
MicroPython — a light level sensor switching a lamp
from machine import ADC, Pin
import utime

ldr = ADC(26)                    # LDR voltage divider on an ADC pin
lamp = Pin(15, Pin.OUT)
DARK_BELOW = 20000               # found by measuring: readings run 0 (dark) to 65535 (bright)

while True:
    lamp.value(1 if ldr.read_u16() < DARK_BELOW else 0)
    utime.sleep(0.5)

Hydraulic actuators

A hydraulic actuator uses an electric pump to push an incompressible liquid (oil) through valves into a cylinder, where it pushes a piston. Pressure acts equally on every surface, so force = pressure × piston area. Oil at 10 MPa (10 000 000 Pa) on a piston of area 0.005 m² gives 10 000 000 × 0.005 = 50 000 N, enough to lift several tonnes. That is why excavators and press brakes use hydraulics. The costs are a pump and hoses, oil that can leak, heat and noise, and slower, less precise control than an electric motor.

ActuatorHow it is drivenStrengthLimitation
DC motorPWM for speed, an H-bridge for directionSimple, cheap, continuous rotationNo position feedback unless an encoder is added
ServoA pulse whose width sets the angleHolds a position; feedback is built inLimited range (often about 180°)
Stepper motorOne pulse per step through a driverPrecise positioning without a sensorCan lose steps if overloaded; draws current while holding
Hydraulic cylinderPump and valvesVery high forceLeaks, heat, complex plumbing
Pneumatic cylinderCompressed air and valvesFast and cleanAir compresses, so less force and precision

Robotic grippers

A robotic gripper is the most common end effector. A parallel-jaw gripper closes two flat fingers, an angular gripper pivots them, a vacuum gripper holds smooth flat objects with suction, and a soft gripper uses flexible fingers for fragile items such as fruit. Other end effectors include a welding torch, a paint nozzle or a 3D printer's extruder. A gripper usually has one degree of freedom (open or close), and the code limits the grip force so it does not crush the part.

Exam tip: "Identify and describe" devices For each device, give its name, what it measures or does, the kind of signal (digital or analog) or force it works with, and one system that uses it. For example: "A PIR sensor detects the infrared change caused by a moving person and outputs a digital signal; an automatic door uses it to open." Keep sensors (inputs), actuators (outputs) and end effectors (the tool) separate: a servo is an actuator, but the gripper it drives is the end effector.

Data in Mechatronic Systems

🎯 (SE-11-04)

📌 Use different types of data and understand how it is obtained and processed in a mechatronic system, including diagnostic data and data used for optimisation

Types of data

The word "data" covers several kinds of value, and each is obtained and processed differently.

TypeExampleHow it is obtainedHow it is processed
Analog sensor dataTemperature as a voltageSampled by an ADCConverted to a number, scaled to °C, filtered
Digital sensor dataPIR HIGH or LOW; a BooleanRead from a pin, or from a serial bus such as I2CCompared or counted; may need debouncing
Control dataSetpoint, gain, servo angleSet by the designer or the userCombined with sensor data to give an output
Diagnostic dataMotor current, battery voltage, error countSensors that watch the system itselfChecked against limits to raise an alarm
Optimisation dataLogged temperature and vent angle over a dayRecorded to a file or memory cardAnalysed to tune gains and save energy

Processing and diagnostic data

Mechatronic systems process raw analog signals (via ADCs) into digital data for decision-making. Diagnostic data involves monitoring internal states, such as motor temperature, battery voltage or current draw, to identify potential hardware failures before they occur. For example, a motor that suddenly draws much more current than usual may be stalled, so the program stops it before it overheats. Optimisation data is used to refine control algorithms, such as adjusting the duty cycle of a PWM signal to achieve maximum motor efficiency with minimal heat generation. Engineers use logged data to perform predictive maintenance, replacing components before a failure happens.

Analog-to-Digital Conversion (ADC)

Sensors output continuous analog voltages. Microcontrollers use Analog-to-Digital Converters to sample these voltages and convert them to digital numbers for processing. An ADC has a resolution (for example 10 or 12 bits), which sets how many levels it can represent: 2n levels for n bits.

ADC formula: count = (measured voltage ÷ reference voltage) × 2n, limited to a maximum of 2n − 1. Some textbooks use 2n − 1 as the multiplier, which changes the result by about one count.

A 10-bit ADC (1024 levels, counts 0 to 1023) with a 5 V reference measuring 2.5 V gives (2.5 ÷ 5) × 1024 = 512. One count is 5 V ÷ 1024 ≈ 4.9 mV, the smallest change the ADC can notice. Software then uses the count to make decisions, for example switching on a cooling fan when a temperature crosses a threshold.

Try it

Analog-to-digital converter explorer

See how the number of bits changes the count a program reads from a sensor. Enable JavaScript to use this tool. In short: an n-bit ADC has 2^n levels, so each step is the reference voltage divided by 2^n.

Worked example: from raw count to a decision

A TMP36 temperature sensor outputs 10 mV per °C with 0.5 V at 0 °C. A 10-bit ADC (5 V reference) reads a count of 158. The steps are: count → volts → °C → decision.

  1. Volts = 158 × 5 ÷ 1024 = 0.771 V.
  2. Temperature = (0.771 − 0.5) × 100 ≈ 27.1 °C.
  3. 27.1 °C is above the 26 °C setpoint, so the vent should open.

Because one count is about 4.9 mV, this sensor and ADC can only resolve steps of about 0.5 °C. Logging the readings gives optimisation data:

Python — converting counts and analysing a log
VREF = 5.0
STEPS = 1024                       # 10-bit ADC

def count_to_celsius(count):
    volts = count * VREF / STEPS   # counts to volts
    return (volts - 0.5) * 100     # TMP36: 10 mV per degree, 0.5 V at 0 degrees

print(round(count_to_celsius(158), 1))

log = [(0, 24.8), (10, 26.9), (20, 29.4), (30, 28.1), (40, 26.2), (50, 25.9)]   # (minute, deg C)
hottest = max(temp for minute, temp in log)
minutes_hot = sum(10 for minute, temp in log if temp > 28)    # samples are 10 min apart
print("Peak:", hottest, "C; above 28 C for about", minutes_hot, "min")
Expected output
27.1
Peak: 29.4 C; above 28 C for about 20 min

If the greenhouse spent 20 minutes above 28 °C, the designer might raise the proportional gain so the vent opens sooner. That is data used for optimisation.


Software to Control Interactions and Dependencies

🎯 (SE-11-02)

📌 Experiment with software to control interactions and dependencies within mechatronic systems

  • motion constraints
  • degrees of freedom
  • combination of subsystems
  • combination of sensors, actuators and end effectors to create viable subsystems

Motion Constraints and Degrees of Freedom

Motion Constraints are limitations on how a mechanical system can move. Some come from the physical design: a joint that can only turn through a set range, a gripper that can only open and close, a differential-drive robot that can turn on the spot but cannot slide sideways. Others are added in software: a maximum speed or acceleration, and keep-out zones that the arm must not enter. Software must respect these constraints when it generates movement commands, because exceeding them can damage the mechanism, the work or a person.

Python — enforcing joint limits in software
def clamp(value, low, high):
    return max(low, min(high, value))

SHOULDER_LIMITS = (10, 170)        # degrees the joint may move through
ELBOW_LIMITS = (0, 135)

def safe_angles(shoulder, elbow):
    return clamp(shoulder, *SHOULDER_LIMITS), clamp(elbow, *ELBOW_LIMITS)

print(safe_angles(90, 45))
print(safe_angles(200, -20))
Expected output
(90, 45)
(170, 0)

Degrees of Freedom (DOF) is the number of independent movements a mechanism can make, usually one for each joint that can move on its own. A joint may rotate (revolute) or slide along a line (prismatic). More degrees of freedom let the end effector reach more positions and orientations, but each one needs an actuator, a sensor and more code.

MechanismDegrees of freedomWhat the software must control
Gripper1Open or close, and the grip force
Pan-and-tilt camera mount2Turn left or right; tilt up or down
Two-joint planar arm2Shoulder and elbow angles, which together set where the hand is on a flat surface
Six-axis industrial arm6Six joint angles, giving any position and orientation in 3D space

Try it

Two-joint arm: degrees of freedom and limits

Move two joints, set a target and switch joint limits on or off. Enable JavaScript to use this tool. In short: each independent joint is one degree of freedom, and limits written in code restrict where the end effector can go.

Combination of Subsystems

Complex mechatronic systems are built by combining simpler subsystems. For example, an automated warehouse robot combines: (1) a vision subsystem to identify items, (2) a navigation subsystem to move through the warehouse, (3) a gripper subsystem to pick items, and (4) a communication subsystem to report status. The subsystems depend on each other, so software must coordinate them safely. In the code below, the gripper only opens when the robot is stationary, and the robot only drives when the gripper is closed, so it cannot drag an open gripper through a shelf.

Python — dependencies between two subsystems
class Rover:
    def __init__(self):
        self.speed = 0
        self.gripper_open = False

    def open_gripper(self):
        if self.speed != 0:                    # dependency: only when stationary
            return "REFUSED: rover is moving"
        self.gripper_open = True
        return "gripper open"

    def drive(self, speed):
        if self.gripper_open:                  # dependency: only with the gripper closed
            return "REFUSED: gripper is open"
        self.speed = speed
        return "driving at " + str(speed)

rover = Rover()
print(rover.drive(50))
print(rover.open_gripper())
print(rover.drive(0))
print(rover.open_gripper())
print(rover.drive(50))
Expected output
driving at 50
REFUSED: rover is moving
driving at 0
gripper open
REFUSED: gripper is open

Combination of Sensors, Actuators, and End Effectors

A viable subsystem has every part it needs to do one job: a sensor to know the state, an actuator to change it, and where relevant an end effector to do the work. A 3D printer's subsystems show this:

SubsystemSensorsActuatorsEnd effectorSoftware dependency
Hot endTemperature sensorHeater elementExtruder nozzleDo not extrude until the nozzle reaches temperature
Motion axesLimit switches (home position)Stepper motors on X, Y and ZThe print head they carryHome each axis before printing
Filament feedFilament run-out sensorExtruder stepperNozzlePause the job if the filament runs out

Software must synchronise these subsystems: heating the nozzle only when filament is loaded, coordinating several motors so the print head moves smoothly, and monitoring temperature feedback.

System diagram

Inside a mechatronic system

Sensors feed the microcontroller, the control algorithm decides, and actuators act. Power reaches every part.

What to notice
  • Data flows in one direction: sense, decide, act.
  • Different parts need different voltages, so the power system matters as much as the code.

Try thisRedraw this for a 3D printer. Which sensors and actuators does it need?

Experiment: finding a safe gripper speed

To experiment is to change one thing on purpose, measure the result and draw a conclusion. Suppose a gripper sometimes crushes a paper cup. A fair test changes only the closing speed and keeps everything else the same.

  1. Question and hypothesis: a slower closing speed will grip the cup without crushing it.
  2. Variables: change the closing speed (independent); measure whether the cup is held or crushed (dependent); keep the cup, the gripper and the force limit constant (controlled).
  3. Method: run each speed three times and record every result, so one odd trial does not mislead you.
  4. Conclusion: choose the fastest speed that never crushes the cup, then change the code and re-test.
Closing speedTrial 1Trial 2Trial 3
100%CrushedCrushedHeld
60%HeldCrushedHeld
30%HeldHeldHeld

These are sample results. They support 30% as the safe speed, and they show why three trials are used: at 60% the same setting gave different results.


Power, Battery and Material Requirements

🎯 (SE-11-03, SE-11-06)

📌 Determine power, battery and material requirements for components of a mechatronic system

Before you build, work out how much power the system needs, how long a battery will last, and what the parts should be made of. Getting this wrong leads to flat batteries, resets when motors start, or a frame that bends.

Power and current requirements

Two relationships do most of the work. Ohm's law, V = I × R, links voltage, current and resistance. Power is P = V × I. Add up the current every part draws, and find the worst case: a motor draws far more when it starts or stalls than when it runs freely.

Requirement Engineering Detail
Power & Battery Current Draw Calculation: Total system current is calculated by summing the individual component currents: Total Current (mA) = Σ(Component Current)If a microcontroller draws 50 mA, two DC motors draw 300 mA each, and an LED draws 20 mA, total = 50 + 300 + 300 + 20 = 670 mA. A 5V supply must provide at least 670 mA, and a margin of about 50% is sensible, so choose a supply rated for about 1 A.
Actuators often require separate power supplies from the controller to avoid "brownouts" (voltage drops) or component damage caused by the high current demands of motors.
Material Choice Choosing based on weight, strength, and environmental conditions.Using carbon fibre for lightweight drone frames vs steel for industrial grippers.

Worked example: the greenhouse power budget

The current figures below are typical example values; in a real design, read them from each part's datasheet.

PartSupplyCurrent (worst case)
Microcontroller board5 V rail60 mA
Temperature and soil sensors5 V rail10 mA
Vent servo (when it moves under load)5 V rail500 mA
5 V rail total570 mA, so with a 50% margin about 860 mA: use a 1 A supply
Water pumpSeparate 12 V supply400 mA (4.8 W)

Estimating battery life

Battery life (hours) ≈ battery capacity (mAh) ÷ average current (mA). A 2000 mAh battery powering the 670 mA system above lasts about 2000 ÷ 670 ≈ 3 hours. In practice, allow 20–30% less, because batteries lose capacity as they age and as they get cold: 2000 × 0.75 ÷ 670 ≈ 2.2 hours. Use the average current for battery life and the peak current to size the supply and wiring.

BatteryVoltage per cellNotes
Alkaline AA1.5 VCheap, not rechargeable, voltage sags under heavy load
NiMH AA1.2 VRechargeable; four in series give 4.8 V
Lithium-ion or LiPo3.7 V nominalHigh capacity for its weight; needs a matching charger and protection circuit

Try it

Power budget calculator

Add up the current of each part, size the supply and estimate battery life. Enable JavaScript to use this tool. In short: total current is the sum of each part's current, the supply must cover the worst case plus a margin, and battery life is capacity divided by average current.

Regulating voltage and limiting current

  • Linear regulator: steps voltage down by turning the excess into heat. Dropping 9 V to 5 V at 500 mA wastes (9 − 5) × 0.5 = 2 W, which needs a heatsink and wastes battery. A buck converter is far more efficient.
  • Current-limiting resistor: an LED that drops about 2 V on a 5 V supply at 15 mA needs R = (5 − 2) ÷ 0.015 = 200 Ω. Choose the next standard value, 220 Ω, which gives about 14 mA.
  • Safety: fit a fuse on the battery lead, check polarity before connecting (reversed power can destroy a board), keep lithium batteries away from punctures and heat, and never leave them charging unattended.

Material requirements

Choose materials against the job: how strong and stiff the part must be, how much it can weigh, how it will be made, what it costs and where it will be used (heat, water, sunlight).

MaterialStrengthsWeaknessesSuits
AluminiumStrong for its weight (about 2.7 g/cm³), easy to machine, does not rustCosts more than plastic; needs toolsRobot chassis, brackets, arm links
SteelVery strong and stiff, cheapHeavy (about 7.8 g/cm³), rusts unless coatedIndustrial grippers, fixed frames
3D-printed plastic (PLA or ABS)Cheap, quick, any shapeWeaker; PLA softens in a hot car or sunny greenhousePrototype parts, sensor mounts
Acrylic sheetLaser-cuts cleanly, light, clearBrittle, cracks at screw holesEnclosures, flat chassis plates
Carbon fibre compositeVery stiff and lightCostly; conducts electricity; splinters when cutDrone frames

A vent arm in a sunny greenhouse is outdoors, so PLA would sag on hot days. Aluminium or a heat-resistant plastic such as ABS is a better choice, even though a PLA part is quicker to print.

HSC writing sample "The greenhouse controller draws about 60 mA, but the pump draws 400 mA when it runs. The pump was therefore given its own 12 V supply through a relay, so its start-up current cannot cause the microcontroller to reset."
Exam tip: "Determine" A determine question expects a decision reached from evidence, so show your working. Write the formula, substitute the numbers with units, state the result, and say what it means for the design (for example, "670 mA, so a 1 A supply is needed, and the motors get their own supply"). Marks are lost for a bare number, for mixing mA with A, and for using peak current where the question asks about battery life.

Wiring Diagrams

🎯 (SE-11-06)

📌 Develop wiring diagrams for a mechatronic system, considering data and power supply requirements

A wiring diagram shows every connection in a mechatronic system: which pin connects to which component, and how power reaches each part. Draw it before you build, and update it whenever the circuit changes. A schematic uses standard electronic symbols; a pictorial or breadboard diagram shows the parts as they look. Either works, provided every connection is labelled.

  • Data connections: label each microcontroller pin and what it does, such as D9 → servo signal or A0 ← light sensor. Say whether each pin is a digital input, digital output, PWM output or analog input.
  • Power supply: show voltage and ground for every component, and the current each supply can deliver. Give motors their own supply so they don't cause the microcontroller to brown out.
  • Standard symbols and colours: red for positive, black for ground, and other colours for signals.
ComponentSignal pinPowerGround
Servo motorD9 (PWM)External 5 VCommon GND
Ultrasonic sensorD7 trigger, D8 echo5 V from boardGND
Light sensor (LDR)A0, at the joint between the LDR and a 10 kΩ resistorLDR from the 5 V pin10 kΩ resistor to GND

The LDR row is a voltage divider: the two resistors share the supply voltage, and the voltage at the joint changes as the LDR's resistance changes, which is what the analog pin reads.

Interface details a diagram must get right

  • Common ground. Every supply and the microcontroller must share a ground, or the signals have no reference and behave randomly.
  • Logic levels. A 3.3 V board such as the Pico must not be driven with a 5 V signal. Use a level shifter or a voltage divider on signals that come from 5 V parts.
  • Pin current limits. A microcontroller pin can supply only a few tens of milliamps at most (check the datasheet). Motors, pumps and relays need a driver, such as a transistor, MOSFET or H-bridge, with their own supply.
  • Flyback diode. Motors, relays and solenoids produce a voltage spike when switched off. A diode across the coil, pointing the right way, protects the driver.
  • Pull-up resistors. A button wired between a pin and ground leaves the pin floating when open. Enabling the pin's internal pull-up, as in Pin.PULL_UP, makes it read 1 when open and 0 when pressed.

Worked example: the greenhouse

ComponentPinTypePower
TMP36 temperature sensorGP26 (ADC0)Analog input3.3 V from the board
Soil moisture sensorGP27 (ADC1)Analog input3.3 V from the board
Emergency stop buttonGP14Digital input with pull-upNone: the other side goes to GND
Vent servoGP15PWM output, 50 HzSeparate 5 V supply
Pump driverGP16Digital outputPump on its own 12 V supply, with a flyback diode

Wiring diagram

Data and power in the greenhouse controller

Solid lines carry signals and dashed lines carry power. The 12 V pump never draws its current through the board.

What to notice
  • The servo and pump each have their own supply; only their signals come from the board.
  • Every pin is labelled with the same name the code uses.

Try thisAdd a second servo for a side vent. Which pin would you use, and does the 5 V supply still have enough current?

Exam tip: use NESA's symbols The figure above is a block-level overview of what connects to what. When a question asks you to draw a wiring diagram, use the symbols in the NESA Course Specifications (p.24): capacitor, diode, resistor, 2-way and on/off switches, speaker, motor, LED, lightbulb, integrated circuit, voltage source, DC voltage source and amplifier, and clearly label any other component. All twelve are drawn in Course Tools: Wiring Diagrams.

Before power-on: check the diagram against the build with the power off; test continuity and polarity with a multimeter; connect grounds first; and add one part at a time, testing each.

Online Tool Suggestion: Try Tinkercad Circuits or Wokwi to develop wiring diagrams and simulate Arduino/ESP32 hardware connections virtually before building.

Exam tip: "Develop" a wiring diagram A develop answer produces the diagram, not only a description of it. Show every component with a labelled pin, mark the power and ground for each, show the separate motor supply and the common ground, and add a legend for the wire colours or line styles. Marks are lost for unlabelled pins, motors powered from a microcontroller pin, and missing grounds.

Designing for People with Disability

🎯 (SE-11-05)

📌 Determine specialist requirements that influence the design and functions of mechatronic systems designed for people with disability

Accessibility Considerations

When designing mechatronic systems for users with disabilities, engineers must consider:

Mobility Disabilities

  • Prosthetics and Exoskeletons: Systems controlled by electromyography (muscle signals), requiring real-time sensor fusion and responsive actuation.
  • Wheelchair Automation: Systems with joystick sensitivity adjustment, voice command control, or autonomous navigation for obstacle avoidance.

Visual Impairments

  • Haptic Feedback: Vibration patterns in controllers to provide tactile information instead of visual displays.
  • Spatial Audio: Sound-based navigation cues to guide users around obstacles.
  • Obstacle Detection: Ultrasonic or infrared sensors to alert users to hazards.

Speech and Hearing Impairments

  • Visual Feedback Systems: LED indicators, screen displays for status information.
  • Sign Language Recognition: Computer vision systems to interpret sign language commands.
  • Text-Based Interfaces: Keyboard or touch input for users with speech difficulties.

Severe Mobility Limitations

  • Sip-and-Puff Control: Systems activated by gentle air pressure from the mouth for users with very limited mobility.
  • Eye Gaze Tracking: Cameras or head-mounted sensors detecting where the user looks to control a cursor or robotic arm.
  • Brain-computer interfaces: EEG or implanted sensors that interpret brain activity to control external devices. These are mostly at the research and trial stage.

Specialist requirements often add complexity and cost, but they fundamentally expand accessibility and enable people with disabilities to perform tasks they could not otherwise accomplish independently.

Determining the specialist requirements

To determine requirements, ask the people who will use the system, and their carers and therapists, what they need, and involve them in testing. Guessing what a user needs leads to devices that are unused. Then turn each need into a requirement you can test.

NeedDesign responseTestable requirement
Cannot press small buttonsLarge switches, or a single switch with scanningEvery control can be operated with a 5 cm target or one switch
Cannot see a displayAudio prompts and haptic feedbackEvery status change is announced by sound or vibration
Cannot hear an alarmFlashing light and vibrationAlerts are visible from across the room
Tires quicklyLow actuation force, and automation of repeated tasksAny control needs less than a set, small force
May move unpredictably near the deviceSpeed and force limits, soft stops, an emergency stop within reachMotion stops within a set time when the stop is pressed

Other requirements include reliability (a failed wheelchair controller is a safety risk, so it needs a fail-safe stop), adjustability (people differ and change over time), comfort, cost, and privacy for any data collected. Australia's Disability Discrimination Act 1992 makes it unlawful to discriminate against people with disability in areas such as education, employment and access to goods and services, which is one reason accessible design matters beyond good practice.

Worked example: single-switch scanning

A user who can press only one large switch controls a room menu. The system highlights each option in turn, and a press selects the one currently lit. The scan speed is a configuration setting, because users differ.

pseudocode — single-switch scanning
BEGIN scanMenu
    index ← 0
    selected ← FALSE
    WHILE selected = FALSE
        HIGHLIGHT option[index]
        WAIT scanSeconds
        IF switch pressed during the wait THEN
            selected ← TRUE
        ELSE
            index ← index + 1
            IF index = 4 THEN
                index ← 0
            ENDIF
        ENDIF
    ENDWHILE
    PERFORM option[index]
END scanMenu
Exam tip: "Determine" specialist requirements Tie each requirement to a specific user need, then to a design feature and how it would be tested. A weak answer says "it should be easy to use". A strong answer says: "A user with limited hand movement needs a single-switch input, so the controller scans options at an adjustable speed, and the design is tested with the user." Name the disability group only when the scenario does, and avoid assumptions about what a person can do.
Syllabus Part 2 Designing control algorithms

Developing Control Algorithms

🎯 (SE-11-02, SE-11-08)

📌 Develop, modify and apply algorithms to control a mechatronic system

A control algorithm reads sensors, decides what to do, and drives actuators, over and over. You develop one from the task, modify it when testing shows a problem, and apply it to the real hardware.

Developing an algorithm

  1. Define the inputs and outputs. What does the algorithm read (two reflectance sensors) and what can it command (left and right wheel speeds)?
  2. Write the rules in plain language, including the case where nothing matches.
  3. Express them in pseudocode and a flowchart using NESA notation.
  4. Trace the algorithm with test data, including boundary values, before writing any code.
  5. Code it, run it on the hardware, and record what happens.
pseudocode — line-following robot
BEGIN followLine
    WHILE running
        left ← READ leftSensor
        right ← READ rightSensor
        IF left = dark AND right = light THEN
            turnLeft()
        ELSE
            IF right = dark AND left = light THEN
                turnRight()
            ELSE
                driveForward()
            ENDIF
        ENDIF
    ENDWHILE
END followLine

Flowchart

Line-following robot

The same algorithm as the pseudocode: read two sensors, choose one of three actions, repeat while the robot is running.

What to notice
  • The loop test running? sits at the top, and every action returns to it.
  • Each action is a subprogram (turnLeft, turnRight, driveForward) that is defined elsewhere.

Try thisTrace the flowchart with both sensors reading dark. Which action runs, and is that what a junction should do?

Trace table

A sensor is compared with a threshold found by calibration. Here, readings run from 0 (dark) to 1023 (light), and a reading below 400 counts as dark.

Stepleftrightleft = dark?right = dark?Action
1850820NoNodriveForward
2300800YesNoturnLeft
3820250NoYesturnRight
4310290YesYesdriveForward (the ELSE branch)
5830810NoNodriveForward
Python — the decision as a function
DARK_BELOW = 400          # readings run 0 (dark) to 1023 (light); found by calibration

def decide(left, right):
    left_dark = left < DARK_BELOW
    right_dark = right < DARK_BELOW
    if left_dark and not right_dark:
        return "turnLeft"
    elif right_dark and not left_dark:
        return "turnRight"
    else:
        return "driveForward"

for left, right in [(850, 820), (300, 800), (820, 250), (310, 290), (830, 810)]:
    print(left, right, decide(left, right))
Expected output
850 820 driveForward
300 800 turnLeft
820 250 turnRight
310 290 driveForward
830 810 driveForward

Modifying the algorithm

Modifying the algorithm Testing shows the robot zig-zags on straight lines. Changing turnLeft() and turnRight() to slow one wheel rather than stop it gives smoother steering. Record each change and why you made it.

The zig-zag happens because the robot only has three actions, so it is always over-correcting. A better rule steers in proportion to how far the line is off centre: the further the difference between the two readings, the harder it turns.

Python — version 2: steering in proportion
BASE = 60                 # base wheel speed, percent
KP = 0.05                 # change in speed for each unit of sensor difference

def clamp(v, lo, hi):
    return max(lo, min(hi, v))

def wheel_speeds(left, right):
    correction = (right - left) * KP      # positive: the line is under the left sensor
    return clamp(BASE - correction, 0, 100), clamp(BASE + correction, 0, 100)

for left, right in [(850, 820), (300, 800), (820, 250)]:
    l, r = wheel_speeds(left, right)
    print(left, right, round(l, 1), round(r, 1))
Expected output (left wheel, right wheel)
850 820 61.5 58.5
300 800 35.0 85.0
820 250 88.5 31.5
VersionProblem found in testingChange madeResult
1Zig-zags on straight linesNone yet: three fixed actionsReaches the end, slowly and jerkily
2Overshoots on sharp bends at 60% speedProportional steering, KP = 0.05Smooth on straights; still overshoots the sharpest bend
3Overshoot on sharp bendsLower BASE speed to 45%Completes the whole track

The rows are sample entries that show how to keep a change log; the code for version 2 is above. Change one thing at a time so you know which change helped.

Applying the algorithm

To apply the algorithm, connect it to the real sensors and motors: replace the test numbers with readings from the ADC, pass the returned wheel speeds to the motor driver, and calibrate the threshold on the actual track under the actual lighting. A track that reads 300 in the classroom may read 500 under bright sunlight.

A second algorithm: avoiding obstacles

pseudocode — obstacle avoidance with an ultrasonic sensor
BEGIN avoidObstacles
    WHILE running
        distance ← READ ultrasonicSensor
        IF distance < 15 THEN
            reverse()
            turnRight()
        ELSE
            IF distance < 40 THEN
                slowDown()
            ELSE
                driveForward()
            ENDIF
        ENDIF
    ENDWHILE
END avoidObstacles
Stepdistance (cm)distance < 15?distance < 40?Action
180NoNodriveForward
235NoYesslowDown
312Yes—reverse, turnRight
440NoNodriveForward (boundary: not below 40)
515NoYesslowDown (boundary: not below 15)

Steps 4 and 5 test the boundary values, where most decision errors hide. Order matters too: the closest range must be tested first, or a 12 cm reading would match the "slow down" rule and the robot would never reverse.

Exam tip: "Develop", "modify" and "apply" Expect to write or complete an algorithm in NESA pseudocode or a flowchart. Keep the keywords in capitals with matching END words, use ← for assignment, nest a second IF inside the ELSE (one ENDIF for each IF), and label every branch of a decision. To modify an algorithm, say what the test showed, which line you changed and why the change should fix it. To apply it, trace it with the values the question gives. Show the trace table: it is the easiest way to earn and check marks.

Open and Closed Control Systems

🎯 (SE-11-02)

📌 Explore the algorithmic patterns, code and applications for open and closed control systems

The choice of control system depends on the task requirements for accuracy and self-correction. Engineers often use decision trees to map out these control paths.

Open loop and closed loop

System Type Analysis / Description Pros/Cons
Open Loop Operates on a fixed sequence or timer without feedback.A sprinkler system on a timer. Pros: Simple, low cost.
Cons: Cannot adjust to changes (e.g., if it rains).
Closed Loop Uses sensor feedback to adjust output in real-time.A thermostat maintaining room temperature. Pros: Accurate, self-correcting.
Cons: Complex, potential for instability.

More examples show the pattern. Open loop: a toaster's timer, a washing machine's fixed cycle, a stepper motor told to take 200 steps (it assumes every step happened). Closed loop: a car's cruise control (measures speed), a servo (an internal sensor reports its angle), and a drone that reads its tilt hundreds of times a second. Many systems combine both: a stepper-driven 3D printer axis moves open loop, but a limit switch closes a loop at start-up to find the home position.

Block diagram

Open loop and closed loop control

A closed loop adds a sensor so the controller can compare what is happening with what it wants.

What to notice
  • Only the closed loop measures its output.
  • Compare works out the error: setpoint minus the measured value.

Try thisA greenhouse vent opens at 26 °C. Label each block of the closed loop for it.

The parts of a closed loop

  • Setpoint: the value you want (26 °C).
  • Sensor and feedback: the measured value that is fed back to the controller.
  • Error: setpoint minus measured value. Some systems, such as cooling, define it the other way round so a positive error opens the vent.
  • Controller: the algorithm that turns the error into a command.
  • Actuator and process: the device that acts (vent, heater) and the thing being controlled (air temperature).
  • Disturbance: anything outside the loop that pushes the process away from the setpoint, such as a cloud or an open door.

Worked example: a thermostat with a hysteresis band

The simplest closed loop is on/off control: heater on when too cold, off when warm enough. If the controller switched at exactly one temperature, the heater would flick on and off many times a minute as the reading wobbles around it, wearing out a relay. A hysteresis band (also called a dead band) fixes this with two thresholds. In the band between them, the heater keeps whatever state it already has.

pseudocode — thermostat with a hysteresis band
BEGIN thermostat
    lowLimit ← 19
    highLimit ← 21
    heaterOn ← FALSE
    WHILE running
        temp ← READ temperatureSensor
        IF temp < lowLimit THEN
            heaterOn ← TRUE
        ELSE
            IF temp > highLimit THEN
                heaterOn ← FALSE
            ENDIF
        ENDIF
        SET heater TO heaterOn
        WAIT 1 second
    ENDWHILE
END thermostat

Flowchart

Thermostat with a hysteresis band

Two decisions set the heater state; when neither is true, the heater state does not change.

What to notice
  • When the temperature is between 19 and 21 both decisions answer No, so heaterOn keeps its old value.
  • The variable heaterOn is the controller's memory; without it there is no band.

Try thisChange the limits to 19.5 and 20.5. What happens to the number of times the heater switches, and to the temperature swing?

Steptemp (°C)temp < 19?temp > 21?heaterOn after
118.2Yes—TRUE
219.4NoNoTRUE (kept)
320.3NoNoTRUE (kept)
421.2NoYesFALSE
520.6NoNoFALSE (kept)
619.8NoNoFALSE (kept)
718.9Yes—TRUE

The band trades accuracy for stability: the temperature swings between about 19 and 21 °C but the heater switched only twice in seven readings. A narrower band gives a steadier temperature and more switching; a wider one saves the relay and allows a bigger swing. A sensor with noise larger than the band would still cause chatter, so the band is chosen with the noise in mind.

Choosing a control pattern

PatternHow it decidesSuitsWeakness
Open loop (timer or fixed sequence)Runs a schedule; never checksPredictable tasks where the outcome is not criticalCannot correct errors or disturbances
On/off with hysteresisTwo thresholds and a remembered stateHeaters, pumps, fansAlways swings around the setpoint
ProportionalOutput grows with the errorVents, speed, positionCan settle slightly away from the setpoint
PIDAdds the accumulated and the changing errorDrones, precise motorsNeeds tuning; can become unstable

The simulator in the closed-loop section later on this page lets you compare these patterns on a heated room.

HSC writing sample "An open loop algorithm suits the greenhouse misting system, which runs for 30 seconds every hour, because exact humidity is not critical and it needs no sensor. The roof vent needs closed loop control: the algorithm reads the temperature, compares it with the 26 °C setpoint and adjusts the vent, so it corrects itself when the weather changes."
Exam tip: "Explore" open and closed control An explore question is open, so cover the range rather than one example. Define each type, give an application of each, say what the closed loop adds (a sensor, a comparison and a correction), and weigh cost against accuracy. Name the parts of the loop for the scenario in the question: setpoint, sensor, error, controller, actuator. A common mistake is calling a system closed loop because it has a sensor, when the sensor's reading never changes what the actuator does.

Features of Autonomous Control

🎯 (SE-11-02)

📌 Outline the features of an algorithm and program code used for autonomous control

Autonomous control means the system carries out its task without a person steering it. It is more than an automatic timer: an autonomous system senses its surroundings, chooses between actions and adapts when conditions change. Its program is usually organised as sense, decide, act, repeated continuously.

  • Sensor Fusion: Combining data from multiple sensors (e.g., distance sensors, IMU accelerometers, temperature sensors) to build a comprehensive understanding of the environment. Simple algorithms average sensor readings; advanced algorithms use Kalman filters for optimal estimation.
  • Decision Logic: Complex conditionals and state machines that determine what action to take based on current sensor readings and system state. Example: if an obstacle is detected within 20 cm, turn left when the left sensor is clear, otherwise turn right.
  • PID Control Loop: Proportional-Integral-Derivative control adjusts actuator output based on error (difference between desired and actual state). Common in maintaining speed, temperature, or position.
  • State Management: Tracking the current state of the system (idle, moving, gripper_open, gripper_closed) and transitioning between states safely based on sensor feedback and logic.
  • Real-Time Response: Meeting strict timing requirements, responding to sensors and issuing commands within specified time windows. Delays in real-time systems can cause crashes or safety failures.
  • Fallback Safety Logic: Default safe states (e.g., "all motors stop" if communication is lost) to prevent uncontrolled operation. A watchdog timer restarts the controller if the program stops responding.

State management in code

A robot vacuum shows how states and events combine. Each state lists the events it responds to and the state each event leads to; any event that does not apply is ignored.

StateEventNext state
IDLEstartCLEANING
CLEANINGobstacleAVOIDING
CLEANINGbattery_lowRETURNING
AVOIDINGclearCLEANING
RETURNINGdockedCHARGING
CHARGINGbattery_fullIDLE
any state except IDLE and CHARGINGfaultERROR (motors stop)
Python — a state machine as a table
TRANSITIONS = {
    "IDLE":      {"start": "CLEANING"},
    "CLEANING":  {"obstacle": "AVOIDING", "battery_low": "RETURNING", "fault": "ERROR"},
    "AVOIDING":  {"clear": "CLEANING", "fault": "ERROR"},
    "RETURNING": {"docked": "CHARGING", "fault": "ERROR"},
    "CHARGING":  {"battery_full": "IDLE"},
    "ERROR":     {"reset": "IDLE"},
}

def next_state(state, event):
    return TRANSITIONS[state].get(event, state)    # an event that does not apply changes nothing

state = "IDLE"
for event in ["start", "obstacle", "clear", "battery_low", "docked", "battery_full", "obstacle"]:
    new = next_state(state, event)
    print(state, "+", event, "->", new)
    state = new
Expected output
IDLE + start -> CLEANING
CLEANING + obstacle -> AVOIDING
AVOIDING + clear -> CLEANING
CLEANING + battery_low -> RETURNING
RETURNING + docked -> CHARGING
CHARGING + battery_full -> IDLE
IDLE + obstacle -> IDLE

Keeping the transitions in a table makes the behaviour easy to check and extend: adding a state means adding a row, not rewriting nested if statements. The last line shows the safe default: an obstacle event while idle is ignored.

Autonomous algorithms typically use flowcharts, state diagrams, and pseudocode to be designed clearly before implementation. Testing autonomous systems requires extensive simulation and careful incremental real-world validation.

State diagram

A traffic light as a finite state machine

The system is always in exactly one state, and events move it to the next.

What to notice
  • Timers trigger the normal cycle; an emergency signal can interrupt any state.
  • Autonomous control code often follows a state machine like this.

Try thisAdd a Pedestrian crossing state. Which state should it follow, and what ends it?

Exam tip: "Outline" autonomous control Give the features as short, specific points and tie each to an example: a state machine keeps track of what the robot is doing, sensor fusion combines several sensors, and fallback logic stops the motors if a sensor fails. Distinguish autonomous (senses and decides for itself) from automatic (follows a fixed schedule). Two or three features, each with an example, is stronger than a list of six names.

Try it

State machine: a pedestrian crossing

Send events to a state machine and see which rule fires. Enable JavaScript to use this tool. In short: a controller is in one state, an event may move it to another state if a condition allows it, and it remembers things such as a pending request.

Syllabus Part 3 Programming and building

Producing a Mechatronic System

🎯 (SE-11-01, SE-11-02, SE-11-05, SE-11-06, SE-11-07)

📌 Design, develop and produce a mechatronic system for a real-world problem

  • software control
  • mechanical engineering
  • electronics and mathematics
Running example — the smart greenhouse A microcontroller reads a temperature sensor and a soil-moisture sensor, opens a roof vent with a servo when it gets too hot, and runs a pump when the soil is dry. The examples in this part build it one piece at a time. The Example Major Project shows a full version of the same idea.

Start with the real-world problem

The school's seedlings die over long weekends because nobody opens the vent on hot days or waters the beds. A good design starts from the problem, then turns it into requirements that can be tested.

RequirementTypeHow it will be tested
Keep air temperature at or below 26 °CFunctionalHeat the sensor to 29 °C and check that the vent opens about 30°
Water when soil moisture falls below 30% and stop above 45%FunctionalMove the sensor between dry and wet samples and watch the pump
Never run the pump for more than 30 seconds at a timeSafetyDisconnect the moisture sensor and confirm the pump stops
Run from a 5 V supply and a separate 12 V pump supplyConstraintMeasure the current on each supply
Cost less than a set budgetConstraintCheck the bill of materials

Integrated System Design

Creating a complete mechatronic system requires simultaneous consideration of three domains:

Software Control

Algorithms and code that orchestrate sensor input, decision logic, and actuator output. Must be reliable, efficient, and responsive. In the greenhouse: read the sensors, decide with a proportional vent rule and a hysteresis pump rule, drive the servo and pump, and stop safely on a fault.

Mechanical Engineering

The physical structure, joints, materials, and mechanisms. Must support the required range of motion, handle the forces generated, and integrate smoothly with electronics. In the greenhouse: a lever from the servo to the vent flap. A servo horn 2 cm long turning through 90° moves the linkage along an arc of length s = r × θ = 2 cm × (π ÷ 2) ≈ 3.1 cm, which is how far the vent can open. The servo must also be strong enough to lift the flap; a longer lever gives more travel but needs more torque.

Electronics and Mathematics

Power circuits, signal conditioning, control mathematics (PID tuning), and sensor calibration. Requires understanding electrical properties of components and mathematical models of system behaviour. In the greenhouse: the ADC conversion from a count to °C, a straight-line calibration for the moisture sensor, the proportional gain, and the power budget.

Success requires communication and iteration between all three disciplines. For example, software delays might come from inadequate motor response, which is a mechanical/control issue; noise in sensor readings might require better filtering (electrical) or algorithm adjustments (software).

Working through the build

  1. Divide the system into subsystems (sensing, vent, watering, power, user interface) and give each a clear input and output.
  2. Choose the parts against the requirements and the power budget.
  3. Draw the wiring diagram and pin map before touching a wire.
  4. Simulate, then build one subsystem at a time, testing each on its own.
  5. Integrate, test the whole system, then evaluate it against the requirements table.

Documenting the build

Documentation lets someone else (or you, next term) understand, repair and improve the system. It also provides the evidence that the system meets its requirements.

DocumentWhat it records
Requirements and design notesThe problem, the requirements and the reasons for each design choice
Wiring diagram and pin mapEvery connection, kept up to date with each change
Bill of materialsEvery part, quantity, supplier and cost
Commented, versioned codeWhy each section exists; a version number or date for each release
Test logThe test, the date, the expected and actual result, and the outcome
Change logEach modification, the problem that prompted it, and whether it worked
Photographs and a safety noteThe finished build and the hazards (electrical, moving parts, batteries) with how each is controlled
Exam tip: "Design, develop and produce" Answers to a design-and-develop question follow the build order: requirements, design (subsystems, wiring, algorithm), development (code and tests) and evaluation against the requirements. Say which of the three domains (software control, mechanical, electronics and mathematics) each decision belongs to, and give a number or a test for each requirement so it can be checked. A vague requirement such as "keep the plants healthy" cannot be tested.

Driving Mechatronic Devices

🎯 (SE-11-05, SE-11-06)

📌 Implement algorithms and design programming code to drive mechatronic devices

Code must correctly interface with hardware. Each kind of device needs a matching kind of signal from the microcontroller:

InterfaceWhat it doesUsed for
Digital outputSets a pin HIGH or LOWLED, relay or pump driver
PWM outputSwitches a pin on and off quickly; the fraction of time on is the duty cycleMotor speed, LED brightness, servo angle
ADC inputTurns a voltage into a numberTemperature, light and soil sensors
I2C, SPI, UARTSerial buses that exchange bytes with other chipsIMUs, displays, other microcontrollers
InterruptRuns a function the moment a pin changesEmergency stop, encoder pulses
  • PWM (Pulse Width Modulation): Varying the duty cycle (on-time vs off-time) of a square wave to control motor speed or LED brightness. At a 25% duty cycle on a 3.3 V pin, the motor sees an average of about 0.25 × 3.3 ≈ 0.8 V.
  • Analog-to-Digital Conversion: Reading analog sensor values (e.g., 0–5 V) as digital numbers (0–1023) for processing.
  • Serial Communication: Sending commands to and receiving data from sensors, actuators, and other microcontrollers. I2C uses two wires shared by many devices, each with its own address.
  • Interrupt Handling: Responding immediately to urgent events (e.g., collision detection) without waiting for the main program loop.

Driving a servo

A hobby servo expects a pulse every 20 ms (50 Hz). The pulse width sets the angle: many servos treat 0.5 ms as 0° and 2.5 ms as 180° (check the datasheet, as some use 1 to 2 ms). So 90° needs 500 + 90 × 2000 ÷ 180 = 1500 µs, a duty cycle of 1.5 ÷ 20 = 7.5%.

MicroPython — driving a servo with PWM
from machine import Pin, PWM

vent = PWM(Pin(15), freq=50)      # servos expect a 50 Hz signal

def set_angle(degrees):
    # 0.5 ms pulse = 0°, 2.5 ms pulse = 180°
    pulse_us = int(500 + degrees * 2000 / 180)
    vent.duty_ns(pulse_us * 1000)

set_angle(90)   # open the vent halfway

Driving a DC motor

A DC motor needs more current than a pin can give, so it is driven through an H-bridge motor driver. Two direction pins choose forward or reverse, and a PWM pin sets the speed. Keep the decision (what should the pins be?) in a pure function that can be tested on a laptop, and the hardware writes in a small separate function.

Python — turning a speed into driver pin settings
def motor_command(speed):
    """Turn a speed from -100 to 100 into (direction pin 1, direction pin 2, duty percent)."""
    speed = max(-100, min(100, speed))
    if speed > 0:
        return (1, 0, speed)
    if speed < 0:
        return (0, 1, -speed)
    return (0, 0, 0)

for s in (100, 40, 0, -60, 250):
    print(s, motor_command(s))
Expected output
100 (1, 0, 100)
40 (1, 0, 40)
0 (0, 0, 0)
-60 (0, 1, 60)
250 (1, 0, 100)

The last line shows why clamping matters: a command of 250 is limited to 100 before it reaches the driver.

Driving a stepper motor

A stepper turns a fixed angle for each pulse from its driver. A common motor has a 1.8° step angle, so one revolution is 360 ÷ 1.8 = 200 steps and a 90° turn is 50 steps. Microstepping splits each step: with 16 microsteps a 90° turn is 800 pulses. The program counts pulses instead of reading a sensor, which is open loop control, so an overloaded motor can silently lose steps.

Try it

PWM explorer

Change the duty cycle of a PWM signal or the pulse width for a servo. Enable JavaScript to use this tool. In short: average voltage = duty cycle × supply voltage, and a servo angle is set by the width of a pulse repeated every 20 ms.

Polling and interrupts

Polling checks an input each time round the main loop. It is simple, but an event that arrives during a long sleep() is noticed late. An interrupt makes the CPU pause the main program and run a short handler function as soon as the event occurs. Keep handlers very short: set a flag or switch off an output, and leave printing and slow work to the main loop. The emergency stop in the user-interface section uses this approach.


Simulations and Prototypes

🎯 (SE-11-06, SE-11-08)

📌 Develop simulations and prototypes of a potential mechatronic system to test programming code

Simulation vs Prototyping

Simulations (Wokwi, TinkerCAD, Gazebo) allow testing code in a risk-free virtual environment. Useful for debugging algorithms, testing edge cases, and exploring design variations before building hardware. However, simulations don't capture real-world issues like friction, vibration, electromagnetic interference, or component tolerances.

Physical prototypes identify these real-world factors. Starting with a minimal viable prototype (MVP) and iteratively improving it is more cost-effective than building a perfect system immediately. Prototyping also allows testing with actual users and refining the design based on their feedback.

SimulationPhysical prototype
Cost and riskFree; nothing can breakCosts parts; mistakes can damage them
Speed of changeEdit and rerun in secondsRewiring and rebuilding take time
RealismIdeal sensors, no friction or noiseShows real noise, friction and power problems
Best forChecking logic and wiring earlyTuning and final testing

Developing a simulation

A circuit simulator such as Wokwi can run MicroPython on a virtual Raspberry Pi Pico with virtual sensors and servos. You can also write your own simulation in code. The idea is to replace the physical world with a model: a function that says how the sensor reading changes when the actuator acts. The control code runs against the model exactly as it would against the real hardware.

Python — testing the vent algorithm against a simulated greenhouse
def vent_angle_for(temp, setpoint=26, kp=10):
    return max(0, min(180, (temp - setpoint) * kp))

class SimGreenhouse:
    """A very simple model: the sun warms the air, an open vent cools it."""
    def __init__(self, temp=24.0, outside=20.0, sun=0.30):
        self.temp = temp
        self.outside = outside
        self.sun = sun                      # degrees gained per minute

    def step(self, vent_angle):
        cooling = 0.0005 * vent_angle * (self.temp - self.outside)
        self.temp += self.sun - cooling
        return self.temp

def run(kp, minutes=60):
    room = SimGreenhouse()
    angle = 0
    for _ in range(minutes):
        temp = room.step(angle)
        angle = vent_angle_for(temp, kp=kp)
    return round(room.temp, 1), round(angle)

for kp in (10, 30, 60):
    print("KP =", kp, "-> temperature and vent angle after 60 min:", run(kp))
Expected output
KP = 10 -> temperature and vent angle after 60 min: (31.2, 52)
KP = 30 -> temperature and vent angle after 60 min: (28.4, 72)
KP = 60 -> temperature and vent angle after 60 min: (27.4, 82)

The simulation found a design fault without touching hardware: with KP = 10 the greenhouse settles at 31 °C, five degrees above the setpoint, because the vent only opens 52° at that temperature. A higher gain fixes most of it. The model's numbers are invented for the exercise, so the simulation shows the behaviour of the algorithm and not the true temperatures. Before trusting a model, compare it with measurements from the real greenhouse and adjust it until they agree.

Fake hardware for testing

The same idea works for a single function: replace read_temperature() with a fake that returns a scripted list of values, including bad ones (a stuck sensor, a very high spike). Then check that the control code responds safely. Faults that are dangerous or awkward to create on a real system, such as an unplugged sensor, are easy to simulate.

Stages of prototyping

StageWhat it isWhat it tests
Mock-upCardboard, paper or foam modelSize, layout, reach and how people would use it
Breadboard prototypeParts plugged together without soldering, on a temporary frameThat the wiring works and the code drives the parts
Working prototypeSoldered or screwed connections, real mechanism, real powerRepeatability, heat, vibration, battery life
Final buildPrinted circuit board or tidy wiring, enclosure, cable managementLong-run reliability and safety
Tip Build the greenhouse circuit in Wokwi first. When the vent opens at the right temperature in the simulation, move the same code onto the real board.

Programming a Closed Loop Control System

🎯 (SE-11-02, SE-11-07)

📌 Design, develop and implement programming code for a closed loop control system

Closed loop systems use sensor feedback to adjust actuator output continuously. Implementation requires:

  • Clearly defining the setpoint (desired state) and feedback (actual state).
  • Calculating the error (setpoint − feedback, or feedback − setpoint if the system cools).
  • Applying a control algorithm (on/off with hysteresis, proportional, PID, fuzzy logic, state machine) to generate corrective output.
  • Iterating: reading sensors, calculating, and updating actuators in a tight loop. The right rate depends on how fast the system changes: a greenhouse might loop once a second, a self-balancing robot hundreds of times a second.

Proportional control

MicroPython — closed loop vent control
SETPOINT = 26          # °C we want to hold
KP = 10                # degrees of vent per °C of error

while True:
    temp = read_temperature()          # feedback: the actual state
    error = temp - SETPOINT            # this loop cools, so hotter than the setpoint = positive error
    angle = max(0, min(180, error * KP))  # bigger error → wider vent, limited to the servo's range
    set_angle(angle)                   # act on the error
    sleep(1)                           # repeat once a second

This is proportional control: the correction grows with the error. Unlike an open loop timer, it keeps adjusting when a cloud passes or the door is left open. A trace of five readings shows the behaviour:

Readingtemp (°C)error = temp − 26error × KPVent angle after limiting
127.01.01010°
228.52.52525°
327.51.51515°
426.40.444°
525.8−0.2−20° (cannot be negative)

On/off control with hysteresis

The pump in the greenhouse uses the on/off pattern from the previous part. Its two thresholds (30% and 45% moisture) form the band, and the pump keeps its current state between them:

Python — hysteresis as a function
PUMP_ON_BELOW = 30
PUMP_OFF_ABOVE = 45

def pump_next_state(pump_on, moisture):
    if moisture < PUMP_ON_BELOW:
        return True
    if moisture > PUMP_OFF_ABOVE:
        return False
    return pump_on                    # inside the band: keep the current state

pump = False
for moisture in [50, 35, 28, 33, 40, 46, 41]:
    pump = pump_next_state(pump, moisture)
    print(moisture, "ON" if pump else "OFF")
Expected output
50 OFF
35 OFF
28 ON
33 ON
40 ON
46 OFF
41 OFF

Adding an integral term: PID

The simulation earlier showed proportional control settling above the setpoint: when the error is small, so is the correction, so a small error can persist. This is steady-state error. An integral term adds up the error over time, so a persistent error keeps growing the correction until the error is gone. A derivative term reacts to how fast the error is changing, which damps overshoot. Together with the proportional term they make a PID controller. Add the code below to the simulation from the previous part (it reuses SimGreenhouse):

Python — proportional and integral control (PI)
class PID:
    def __init__(self, kp, ki=0.0, low=0, high=180):
        self.kp, self.ki = kp, ki
        self.low, self.high = low, high
        self.integral = 0.0

    def update(self, error, dt=1):
        """Return the vent angle for this error (measured minus setpoint)."""
        command = self.kp * error + self.ki * self.integral
        if self.low < command < self.high:      # anti-windup: stop adding up while the vent is at its limit
            self.integral += error * dt
        return max(self.low, min(self.high, command))

def run_pid(controller, minutes=240):
    room = SimGreenhouse()
    angle = 0
    for _ in range(minutes):
        temp = room.step(angle)
        angle = controller.update(temp - 26)
    return round(room.temp, 1), round(angle)

print("P only :", run_pid(PID(kp=30)))
print("P and I:", run_pid(PID(kp=30, ki=1.0)))
Expected output (temperature and vent angle after 240 minutes)
P only : (28.4, 72)
P and I: (26.0, 100)

The integral term removed the 2.4 °C offset. Anti-windup matters: if the vent is fully open and the room is still too warm, an unchecked integral would keep growing and the controller would overshoot for a long time after conditions change.

Increase…Response speedOvershootSteady-state errorWatch for
Kp (proportional)FasterIncreasesReduces but does not removeOscillation if too high
Ki (integral)Slightly fasterCan increaseRemovesWindup; slow oscillation
Kd (derivative)Little changeReducesNo direct effectAmplifies sensor noise

Explore it: closed loop simulator

The simulator below runs a heated room under three controllers. It shows the ideas above with a heater instead of a vent: heat leaks out through the walls, and the controller decides how hard to run the heater.

This simulator needs JavaScript. Without it, the ideas are: an open loop heater runs at a fixed output and the room drifts when the weather changes; an on/off controller with a band keeps the room within a few degrees of the setpoint but switches on and off; a proportional controller settles a little away from the setpoint, and a larger Kp reduces that offset.

Watch for instability A higher gain corrects faster, but if it is too high, or the sensor is slow to respond, the system overcorrects and oscillates. Make one change at a time and record the effect.

Integrating Sensors, Actuators and End Effectors

🎯 (SE-11-07)

📌 Apply programming code to integrate sensors, actuators and end effectors/manipulators

Each part may work on its own, but the system only works when sensors, actuators and end effectors run together under one program. Integrate in small steps: add one component, test it, then add the next.

Integrating Components into Viable Subsystems

Each subsystem (gripper, movement, vision) must be coded, tested, and debugged in isolation before integration. Components share power supplies, communication buses, and may have timing dependencies—careful integration prevents conflicts.

MicroPython — sensor, pump and vent working together
while True:
    temp = read_temperature()
    moisture = read_moisture()           # 0 = dry, 100 = soaked

    set_angle(vent_angle_for(temp))      # vent subsystem

    if moisture < 30 and not pump.value():
        pump.on()                        # watering subsystem
    elif moisture > 45:
        pump.off()                       # gap between 30 and 45 stops rapid on/off

    sleep(1)
Watch for conflicts The pump and servo both draw current at the same moment, and a long sleep() in one subsystem delays every other one. Keep each step short and share one main loop.

Complete worked example: the greenhouse program

Here is the finished controller. It is split into two files. control.py holds the decisions and imports no hardware, so it can be tested on a laptop. main.py connects those decisions to the pins in the wiring table. Copy both files onto the board; MicroPython runs main.py at power-on. Comments such as (6) refer to the explanation table below.

Python — control.py (decision logic, no hardware)
# control.py - the decision logic only. It imports no hardware modules,
# so it can be tested on a laptop with ordinary Python.
SETPOINT = 26                      # °C to hold
KP = 10                            # degrees of vent per °C of error
DRY_RAW, WET_RAW = 45000, 20000    # calibration: readings in dry air and in water
PUMP_ON_BELOW = 30                 # % moisture
PUMP_OFF_ABOVE = 45

def raw_to_celsius(raw, vref=3.3):
    volts = raw / 65535 * vref
    return (volts - 0.5) * 100     # TMP36: 10 mV per °C, 0.5 V at 0 °C

def raw_to_moisture(raw):
    percent = (DRY_RAW - raw) * 100 / (DRY_RAW - WET_RAW)
    return max(0, min(100, percent))

def sensor_ok(temp):
    return -10 < temp < 60         # anything outside this range means a fault

def vent_angle_for(temp, setpoint=SETPOINT, kp=KP):
    return max(0, min(180, (temp - setpoint) * kp))

def pump_next_state(pump_on, moisture):
    if moisture < PUMP_ON_BELOW:
        return True
    if moisture > PUMP_OFF_ABOVE:
        return False
    return pump_on                 # inside the band: keep the current state

def smooth(readings, new, size=5):
    readings.append(new)
    if len(readings) > size:
        readings.pop(0)            # keep only the last 5 readings
    return sum(readings) / len(readings)
MicroPython — main.py (sensors, actuators and the main loop)
# main.py - greenhouse controller for a Raspberry Pi Pico-class board (MicroPython)
from machine import Pin, PWM, ADC
import utime
import control

# (1) Hardware, matching the wiring table
temp_adc = ADC(26)                             # TMP36 on GP26
soil_adc = ADC(27)                             # soil moisture sensor on GP27
vent = PWM(Pin(15), freq=50)                   # servo signal on GP15
pump = Pin(16, Pin.OUT, value=0)               # pump driver on GP16, starts OFF
stop_button = Pin(14, Pin.IN, Pin.PULL_UP)     # emergency stop: reads 0 when pressed

# (2) State
PUMP_MAX_MS = 30000                            # never run the pump longer than 30 s
stopped = False
pump_locked = False
pump_started = 0
readings = []

def set_angle(degrees):                        # (3) output helper
    vent.duty_ns(int(500 + degrees * 2000 / 180) * 1000)

def emergency_stop(pin):                       # (4) interrupt handler: short and safe
    global stopped
    pump.off()
    stopped = True

stop_button.irq(trigger=Pin.IRQ_FALLING, handler=emergency_stop)

while not stopped:                             # (5) main loop
    raw_temp = control.raw_to_celsius(temp_adc.read_u16())         # (6) sense
    moisture = control.raw_to_moisture(soil_adc.read_u16())

    if not control.sensor_ok(raw_temp):        # (7) fail safe
        pump.off()
        print("SENSOR FAULT: pump off")
    else:
        temp = control.smooth(readings, raw_temp)                  # (8) decide and act: vent
        angle = control.vent_angle_for(temp)
        set_angle(angle)

        wanted = control.pump_next_state(pump.value() == 1, moisture)   # (9) decide and act: pump
        if wanted and not pump_locked:
            if not pump.value():
                pump_started = utime.ticks_ms()
            pump.on()
            if utime.ticks_diff(utime.ticks_ms(), pump_started) > PUMP_MAX_MS:
                pump.off()
                pump_locked = True             # needs a restart: something is wrong
                print("PUMP TIMEOUT")
        else:
            pump.off()

        print("temp %.1f C  vent %d deg  soil %d%%  pump %s" %      # (10) log
              (temp, round(angle), round(moisture), "ON" if pump.value() else "OFF"))
    utime.sleep(1)

vent.duty_ns(0)                                # (11) stop driving the servo
print("STOPPED")

Line-by-line explanation

PartWhat it does and why
control.py constantsThe setpoint, the gain, the two calibration readings and the pump thresholds are named values at the top, so a change is made in one place. DRY_RAW and WET_RAW come from calibration (see the testing section).
raw_to_celsiusConverts the ADC value (0 to 65535) to volts, then to °C using the TMP36's 10 mV per °C and 0.5 V offset.
raw_to_moistureA straight-line calibration: the dry reading maps to 0% and the wet reading to 100%. max(0, min(100, …)) keeps the result in range.
sensor_okA sanity check. A disconnected or shorted sensor gives an impossible temperature, and the program must not act on it.
vent_angle_forProportional control: 10° of vent for each °C above 26, limited to the servo's 0 to 180° range.
pump_next_stateHysteresis: on below 30%, off above 45%, otherwise the previous state.
smoothA moving average of the last five readings.
(1) HardwareCreates an object for each device, using the pin numbers from the wiring table. The pump pin starts at 0 so the pump is off at power-on. The button uses the internal pull-up, so it reads 0 when pressed.
(2) StateVariables that persist between loops: whether the emergency stop was pressed, whether the pump has been locked out, when the pump started, and the recent readings.
(3) set_angleTurns an angle into a pulse width (0.5 to 2.5 ms) and sends it to the servo.
(4) emergency_stopAn interrupt handler. It runs the moment the button is pressed, switches the pump off and sets a flag. It does no slow work, and the main loop checks the flag.
(5) while not stoppedThe main loop repeats until the flag is set.
(6) SenseReads both sensors and converts them to °C and %.
(7) Fail safeIf the temperature is impossible, switch the pump off and report the fault instead of acting on bad data.
(8) VentSmooths the temperature, calculates the vent angle and moves the servo.
(9) PumpAsks the hysteresis function what the pump should do. When it turns on, the start time is recorded; if it has run for more than 30 seconds the program switches it off and locks it out, in case the moisture sensor has failed and the pump would otherwise flood the bed.
(10) LogPrints one line per second: this is diagnostic data for testing and for optimisation.
(11) After the loopStops sending pulses to the servo and reports that the system has stopped.
Example output from a test run with simulated sensor readings, then the button pressed
temp 27.0 C  vent 10 deg  soil 50%  pump OFF
temp 27.7 C  vent 17 deg  soil 35%  pump OFF
temp 27.7 C  vent 17 deg  soil 28%  pump ON
temp 27.3 C  vent 13 deg  soil 33%  pump ON
temp 27.0 C  vent 10 deg  soil 46%  pump OFF
STOPPED

Read the log: the temperature readings are smoothed, so the vent follows the average and not each spike; the pump switches on at 28% soil moisture, stays on at 33% (inside the band) and switches off at 46%. The vent angles are small because the temperature is only just above the setpoint.


Control Algorithms That Enhance Performance

🎯 (SE-11-08)

📌 Implement specific control algorithms that enhance the performance of a mechatronic system

Control Algorithm Optimisation

Performance means how quickly, accurately, smoothly and reliably the system does its job. Specific algorithms enhance it:

TechniqueProblem it solvesCost or risk
PID tuningSlow response, overshoot, steady-state errorNeeds testing; wrong gains oscillate
Acceleration rampingJerky starts, wheel slip, current surgesSlower to reach full speed
Sensor filteringNoisy or spiky readingsAdds lag: the filtered value trails the truth
Non-blocking timingA long sleep() delays every other taskSlightly more complex code
  • PID Tuning: Adjusting proportional, integral, and derivative gains to achieve fast, stable response without overshoot. A practical method: start with Ki and Kd at zero; raise Kp until the system responds quickly; if it oscillates, back Kp off; then add a little Ki to remove the steady-state error, and Kd only if overshoot remains. Change one gain at a time and keep a record.
  • Acceleration Ramping: Gradually increasing motor speed to reduce jerky movements and mechanical stress.
  • Sensor Filtering: Averaging or smoothing noisy sensor readings to improve decision quality.

Sensor filtering

MicroPython — smoothing a noisy sensor
readings = []

def smoothed_temperature():
    readings.append(read_temperature())
    if len(readings) > 5:
        readings.pop(0)                 # keep the last 5 readings
    return sum(readings) / len(readings)  # moving average

Without smoothing, a single noisy reading can make the vent twitch open and shut. Averaging the last five readings makes the system calmer, and the servo lasts longer. Different filters suit different noise, so compare them on the same data:

Python — three filters on a reading with a spike
def moving_average(values):
    return sum(values) / len(values)

def median(values):
    ordered = sorted(values)
    return ordered[len(ordered) // 2]

def exponential(previous, new, alpha=0.3):
    return alpha * new + (1 - alpha) * previous

readings = [24.0, 24.2, 30.9, 24.1, 24.3]        # 30.9 is a one-off spike

print("Average:", round(moving_average(readings), 1))
print("Median :", median(readings))

smoothed = readings[0]
for value in readings[1:]:
    smoothed = exponential(smoothed, value)
print("Exponential:", round(smoothed, 1))
Expected output
Average: 25.5
Median : 24.2
Exponential: 25.1

The true temperature is about 24.2 °C. The average and the exponential filter are both pulled up by the spike; the median ignores it. The average is the simplest choice for random noise; the median is better for occasional spikes; the exponential filter needs no list of past readings, so it uses little memory. Every filter adds lag, so a heavily filtered sensor reacts late to real change.

Acceleration ramping

Jumping a motor from 0 to full speed draws a large current surge, can make wheels slip and jolts a payload. A ramp limits how much the command can change each loop:

Python — limiting the change in speed
def ramp(current, target, max_step=10):
    if target > current:
        return min(target, current + max_step)
    return max(target, current - max_step)

speed = 0
for _ in range(6):
    speed = ramp(speed, 45)
    print(speed, end=" ")
print()
Expected output
10 20 30 40 45 45

Timing without blocking

Calling sleep(1) stops the whole program for a second, so a button press or a second sensor cannot be checked. Instead, record when the last control step ran and act only when enough time has passed:

MicroPython — a control step once a second without blocking
import utime

last_step = utime.ticks_ms()

while True:
    now = utime.ticks_ms()
    if utime.ticks_diff(now, last_step) >= 1000:   # one second has passed
        last_step = now
        control_step()                             # read, decide, act
    check_buttons()                                # runs on every pass, many times a second

ticks_diff is used because the tick counter wraps around; subtracting the values directly would fail at the wrap.


A User Interface to Control the System

🎯 (SE-11-07)

📌 Design, develop and implement a user interface (UI) to control a mechatronic system

Even autonomous systems often need interfaces for:

  • Manual Override: Emergency stop buttons, mode selection switches.
  • Status Display: LED indicators, LCD screens showing system state, error codes, sensor readings.
  • Configuration: Buttons to set parameters (speed, sensitivity, operating mode).
  • Accessibility: Voice control, touchless interfaces for users with disabilities.

UI code must be responsive and not interfere with real-time control loops. Keeping the interface code separate from the control logic (an idea related to the Model-View-Controller pattern) improves maintainability: you can change the display without touching the control algorithm.

Designing the interface

  1. Who uses it, and for what? The greenhouse users are students and a teacher: check the state, switch to manual, and stop everything in an emergency.
  2. Choose inputs and outputs that suit those tasks.
  3. Give feedback for every action (a press should change the display or a light) and make errors hard to make.
  4. Test with real users and change what confuses them.
ElementTypePurposeDesign note
Emergency stop buttonInputStop the pump at onceLarge, red, easy to reach; wired to an interrupt
Mode buttonInputSwitch between AUTO and MANUALDebounced; the display shows the new mode straight away
Open and close buttonsInputMove the vent by hand in MANUALSteps of 10° within the servo limits
16 × 2 character displayOutputTemperature, vent angle, soil moisture and modeFixed layout so values do not jump about
Status LED and buzzerOutputShow a fault without reading the displayDifferent patterns for different faults; not colour alone

Interface code

Buttons "bounce": one press can make several rapid contacts. The code ignores any press that comes too soon after the previous one. The functions below hold the interface decisions, so they can be tested without a display or buttons.

Python — mode switching, debounce, manual control and display layout
MODES = ["AUTO", "MANUAL"]

def next_mode(mode):
    return MODES[(MODES.index(mode) + 1) % len(MODES)]

def accept_press(now_ms, last_ms, gap_ms=200):
    """Ignore contact bounce: a new press must come at least gap_ms after the last one."""
    return now_ms - last_ms >= gap_ms

def manual_angle(angle, button):
    step = 10 if button == "open" else -10
    return max(0, min(180, angle + step))

def status_lines(temp, angle, moisture, mode):
    line1 = "T:%4.1fC V:%3d" % (temp, round(angle))
    line2 = "Soil:%3d%% %s" % (round(moisture), mode)
    return line1.ljust(16)[:16], line2.ljust(16)[:16]

mode, last = "AUTO", -1000
for t in (0, 30, 500):                       # the press at 30 ms is contact bounce
    if accept_press(t, last):
        mode, last = next_mode(mode), t
    print(t, mode)

angle = 170
for b in ("open", "open", "close"):
    angle = manual_angle(angle, b)
    print(b, angle)

for line in status_lines(27.3, 13, 33, "AUTO"):
    print("|" + line + "|")
Expected output
0 MANUAL
30 MANUAL
500 AUTO
open 180
open 180
close 170
|T:27.3C V: 13   |
|Soil: 33% AUTO  |

The press at 30 ms is ignored, so the mode changes only twice. The second open stays at 180 because the angle is clamped to the servo's range.

The emergency stop

MicroPython — an emergency stop button
stop_button = Pin(14, Pin.IN, Pin.PULL_UP)
stopped = False

def emergency_stop(pin):
    global stopped
    pump.off()                           # switching a pin is fast and safe inside a handler
    stopped = True                       # the main loop sees this flag and shows "STOPPED"

# an interrupt reacts at once, even in the middle of the main loop
stop_button.irq(trigger=Pin.IRQ_FALLING, handler=emergency_stop)

The handler only switches the pump off and sets a flag. Updating a display inside a handler is slow and can fail, so the main loop does it. For a machine that can hurt someone, a software stop is not enough: a real emergency stop is also wired to cut the power to the actuators, so it works even if the program has crashed.

Exam tip: "Design, develop and implement" a user interface Link every interface element to a user need and to a safety or usability reason. Include feedback (what the user sees or hears after each action), a manual override and an emergency stop, and say how the UI code stays separate from the control loop. Mention accessibility where the scenario suggests it: for example, not relying on colour alone for a fault.

Unit Testing Control Algorithms

🎯 (SE-11-06, SE-11-07)

📌 Create and use unit tests to determine the effectiveness and repeatability of each component’s control algorithm

Mechatronic systems require rigorous testing of each component. A unit test checks one small piece (a function or a single device) against a known input and an expected result. Testing has two aims: effectiveness (does the algorithm do the right thing?) and repeatability (does it do the same thing every time?).

  • Sensor Tests: Verify sensors return expected values for known inputs (temperature sensor in ice vs boiling water).
  • Actuator Tests: Confirm motors respond correctly to commands (PWM 50% = half speed).
  • Algorithm Tests: Verify logic produces correct outputs (e.g., a collision avoidance routine turns away from obstacles).
  • Integration Tests: Test subsystems working together (e.g., vision detects object + gripper activates + movement ceases).
  • Stress Tests: Run systems continuously for extended periods to identify degradation or reliability issues.

Unit tests for the control algorithm

Because the decisions live in control.py with no hardware imports, the tests run on a laptop. Choose test data on purpose: a normal value, the boundaries (the edges of each rule), and values outside the expected range.

FunctionInputExpected outputWhy this case
vent_angle_for20 °C0°Below the setpoint: the vent stays shut
vent_angle_for29 °C30°Normal case: (29 − 26) × 10
vent_angle_for60 °C180°Above the servo's range: the output is limited
raw_to_moistureThe dry and wet readings0% and 100%Calibration end points
raw_to_moistureDrier than dry, wetter than wet0% and 100%Out-of-range readings are limited
pump_next_state29%, 40% (on and off), 46%on, stays on, stays off, offBelow, inside and above the hysteresis band
sensor_ok−40 °CFalseA disconnected sensor must be rejected
Python — unit testing the control functions
# test_control.py - runs on a laptop with ordinary Python: python3 test_control.py
import control

def test_closed_when_cool():
    assert control.vent_angle_for(20) == 0

def test_opens_in_proportion():
    assert control.vent_angle_for(29) == 30

def test_never_exceeds_servo_range():
    assert control.vent_angle_for(60) == 180

def test_moisture_end_points():
    assert control.raw_to_moisture(control.DRY_RAW) == 0
    assert control.raw_to_moisture(control.WET_RAW) == 100

def test_moisture_is_limited_to_0_and_100():
    assert control.raw_to_moisture(60000) == 0       # drier than the dry reading
    assert control.raw_to_moisture(10000) == 100     # wetter than the wet reading

def test_pump_hysteresis():
    assert control.pump_next_state(False, 29) is True     # below the band: on
    assert control.pump_next_state(True, 40) is True      # inside the band: stays on
    assert control.pump_next_state(False, 40) is False    # inside the band: stays off
    assert control.pump_next_state(True, 46) is False     # above the band: off

def test_bad_sensor_rejected():
    assert control.sensor_ok(24.0)
    assert not control.sensor_ok(-40)                # a disconnected sensor

def test_celsius_conversion():
    assert abs(control.raw_to_celsius(14894) - 25) < 0.1   # 0.75 V from a TMP36 is 25 °C

failed = 0
tests = [f for name, f in sorted(globals().items()) if name.startswith("test_")]
for test in tests:
    try:
        test()
        print("PASS", test.__name__)
    except AssertionError:
        print("FAIL", test.__name__)
        failed += 1
print(len(tests) - failed, "passed,", failed, "failed")
Expected output
PASS test_bad_sensor_rejected
PASS test_celsius_conversion
PASS test_closed_when_cool
PASS test_moisture_end_points
PASS test_moisture_is_limited_to_0_and_100
PASS test_never_exceeds_servo_range
PASS test_opens_in_proportion
PASS test_pump_hysteresis
8 passed, 0 failed

Tests protect against mistakes made later. If someone removed the max(0, …) from vent_angle_for, the vent would be commanded to a negative angle when the room is cool, and test_closed_when_cool would fail straight away. A test that fails for the right reason is a success: it found the bug before the greenhouse did. (MicroPython has no built-in test framework, which is another reason to keep logic in a file that also runs on a laptop.)

Testing and calibrating sensors

Real sensors are not perfect: two identical parts can read differently, and readings drift with temperature and age. Calibration compares the sensor with a known reference and corrects the difference. A two-point calibration uses a low and a high reference and draws a straight line between them.

  1. Measure the sensor in a low reference (ice water, close to 0 °C) and a high reference (boiling water, close to 100 °C at sea level; use a reference thermometer if you have one). Say the sensor reads 1.8 °C and 97.5 °C.
  2. Work out the gain: (100 − 0) ÷ (97.5 − 1.8) ≈ 1.045.
  3. Correct every later reading: corrected = 0 + (reading − 1.8) × 1.045.
  4. Check the result against a third reference, such as a room-temperature thermometer.
Python — two-point calibration
def make_calibration(raw_low, ref_low, raw_high, ref_high):
    gain = (ref_high - ref_low) / (raw_high - raw_low)
    return lambda raw: ref_low + (raw - raw_low) * gain

correct = make_calibration(1.8, 0.0, 97.5, 100.0)
print(round(correct(1.8), 1), round(correct(97.5), 1), round(correct(25.0), 1))
Expected output
0.0 100.0 24.2

The soil moisture sensor is calibrated the same way: record its raw reading in dry air (DRY_RAW) and in a glass of water, up to its marked line (WET_RAW), and the program maps the range between them to 0–100%. Record every calibration with the date, the references used and the values found, and repeat it when a sensor is replaced.

Repeatability

Repeatability testing ensures the system behaves consistently across multiple trials—critical for autonomous systems where unpredictable behaviour is unacceptable. Logging sensor data and actuator commands during tests helps engineers debug failures and improve reliability. Set a pass criterion before testing, then compare.

Python — judging repeatability from five trials
trials = [30, 29, 31, 30, 30]          # vent angle (degrees) after heating the sensor to 29 C, five runs
target, tolerance = 30, 3
mean = sum(trials) / len(trials)
spread = max(trials) - min(trials)
print("mean", mean, "spread", spread, "PASS" if abs(mean - target) <= tolerance and spread <= 2 * tolerance else "FAIL")
Expected output
mean 30.0 spread 2 PASS

Because the algorithm is a separate function, it can be tested on a laptop without any hardware. Repeatability is then checked on the real system by running the same test several times, for example heating the sensor to 29 °C five times and confirming the vent reaches about 30° each time. The trial values above are example data; on a real system you record your own. A small spread means the system is repeatable; a large spread points to a loose linkage, a noisy sensor or unstable code.

A test plan and log

TestTypePass criterionResult (example entry)
Temperature sensor in ice water and boiling waterSensorWithin ±2 °C of the references after calibrationPass: 0.0 and 100.0 °C
Servo commanded to 0°, 90° and 180°ActuatorVent flap reaches each position without buzzing or stallingPass
Control functions (the unit tests above)AlgorithmAll tests pass8 of 8 pass
Heat the sensor and dry the soil togetherIntegrationVent opens and pump runs without the controller resettingPass
Run for 24 hoursStressNo resets, no drift greater than 1 °CTo be done

A test log records the date, the test, the expected and actual result, and what was changed after a failure, so that anyone can see that the system was checked and how it improved.

Exam tip: "Create and use" unit tests A full answer gives the test input, the expected output and the reason for choosing it, and covers a normal case, a boundary and an out-of-range case. State what is being tested (the algorithm, a sensor or an actuator) and how you would judge repeatability: repeat the test, record the results and compare the spread with a criterion set in advance. Do not write "test it and see if it works" without stating what "works" means.