10.4 User interfaces for control¶
Why it matters¶
When you build a mechatronic system, the user interface is how people interact with and understand your device. A well-designed interface can make the difference between a system that’s easy to use and one that’s confusing or even dangerous.
Good control interfaces provide clear information about what the system is doing, allow users to give commands easily, and most importantly, communicate problems clearly when something goes wrong. Whether it’s a simple LED status light or a comprehensive dashboard, the interface is your system’s voice.
Concepts¶
Command Line Interfaces (CLI) for control¶
Command Line Interfaces are text-based interfaces where users type commands to control the system. While they might seem old-fashioned, CLIs are powerful for mechatronic systems because they:
-
Work over simple communication links (like serial connections)
-
Can be automated with scripts
-
Provide precise control with detailed parameters
-
Are easy to debug and log
Let’s build a basic CLI for a robotic arm:
class RoboticArmCLI:
def __init__(self):
self.arm_position = {"x": 0, "y": 0, "z": 0}
self.gripper_closed = False
self.arm_speed = 50 # Percentage
self.emergency_stop = False
self.last_error = None
# Available commands
self.commands = {
"move": self.move_arm,
"grip": self.control_gripper,
"speed": self.set_speed,
"status": self.show_status,
"stop": self.emergency_stop_command,
"reset": self.reset_system,
"help": self.show_help
}
def parse_command(self, command_line):
"""Parse a command line into command and arguments"""
parts = command_line.strip().split()
if not parts:
return None, []
command = parts[0].lower()
args = parts[1:]
return command, args
def move_arm(self, args):
"""Move arm to specified position: move x y z"""
if self.emergency_stop:
return "❌ Error: Emergency stop active. Use 'reset' first."
if len(args) != 3:
return "❌ Error: Move command requires 3 coordinates (x y z)"
try:
new_x, new_y, new_z = map(float, args)
# Safety checks
if abs(new_x) > 100 or abs(new_y) > 100 or abs(new_z) > 100:
return "❌ Error: Position out of safe range (-100 to 100)"
# Simulate movement
self.arm_position = {"x": new_x, "y": new_y, "z": new_z}
return f"✅ Moving to position: x={new_x}, y={new_y}, z={new_z}"
except ValueError:
return "❌ Error: Invalid coordinates. Use numbers only."
def control_gripper(self, args):
"""Control gripper: grip open/close"""
if self.emergency_stop:
return "❌ Error: Emergency stop active. Use 'reset' first."
if len(args) != 1:
return "❌ Error: Grip command requires 'open' or 'close'"
action = args[0].lower()
if action == "open":
self.gripper_closed = False
return "✅ Gripper opened"
elif action == "close":
self.gripper_closed = True
return "✅ Gripper closed"
else:
return "❌ Error: Grip command must be 'open' or 'close'"
def set_speed(self, args):
"""Set arm movement speed: speed 0-100"""
if len(args) != 1:
return "❌ Error: Speed command requires one value (0-100)"
try:
new_speed = int(args[0])
if 0 <= new_speed <= 100:
self.arm_speed = new_speed
return f"✅ Speed set to {new_speed}%"
else:
return "❌ Error: Speed must be between 0 and 100"
except ValueError:
return "❌ Error: Speed must be a number"
def show_status(self, args):
"""Show current system status"""
status_lines = [
"🤖 Robotic Arm Status:",
f" Position: x={self.arm_position['x']}, y={self.arm_position['y']}, z={self.arm_position['z']}",
f" Gripper: {'Closed' if self.gripper_closed else 'Open'}",
f" Speed: {self.arm_speed}%",
f" Emergency Stop: {'🚨 ACTIVE' if self.emergency_stop else '✅ Clear'}"
]
if self.last_error:
status_lines.append(f" Last Error: {self.last_error}")
return "\n".join(status_lines)
def emergency_stop_command(self, args):
"""Activate emergency stop"""
self.emergency_stop = True
return "🚨 EMERGENCY STOP ACTIVATED - All movement halted"
def reset_system(self, args):
"""Reset emergency stop and errors"""
self.emergency_stop = False
self.last_error = None
return "✅ System reset - Emergency stop cleared"
def show_help(self, args):
"""Show available commands"""
help_text = """
🤖 Robotic Arm Control Commands:
move x y z - Move arm to position (x, y, z coordinates)
grip open/close - Open or close gripper
speed 0-100 - Set movement speed percentage
status - Show current system status
stop - Emergency stop (halts all movement)
reset - Clear emergency stop and errors
help - Show this help message
exit - Exit control interface
Examples:
move 10 20 30 - Move to position x=10, y=20, z=30
grip close - Close the gripper
speed 75 - Set speed to 75%
"""
return help_text.strip()
def process_command(self, command_line):
"""Process a single command and return the result"""
command, args = self.parse_command(command_line)
if command is None:
return "❌ Error: Empty command. Type 'help' for available commands."
if command == "exit":
return "exit"
if command in self.commands:
try:
result = self.commands[command](args)
return result
except Exception as e:
self.last_error = str(e)
return f"❌ Error: {e}"
else:
return f"❌ Error: Unknown command '{command}'. Type 'help' for available commands."
def run_interactive_session(self):
"""Run an interactive CLI session"""
print("🤖 Robotic Arm Control Interface")
print("Type 'help' for commands or 'exit' to quit")
print("-" * 40)
while True:
try:
user_input = input("arm> ").strip()
if not user_input:
continue
result = self.process_command(user_input)
if result == "exit":
print("👋 Goodbye!")
break
print(result)
except KeyboardInterrupt:
print("\n👋 Goodbye!")
break
except EOFError:
print("\n👋 Goodbye!")
break
# Demonstrate the CLI
arm_cli = RoboticArmCLI()
# Test various commands
test_commands = [
"help",
"status",
"move 25 30 15",
"grip close",
"speed 80",
"status",
"move 200 50 25", # This should fail - out of range
"stop",
"move 10 10 10", # This should fail - emergency stop active
"reset",
"status"
]
print("🧪 Testing CLI Commands:")
print("=" * 50)
for cmd in test_commands:
print(f"\nCommand: {cmd}")
result = arm_cli.process_command(cmd)
print(result)
Dashboard interfaces¶
Dashboards provide a visual overview of your system’s status. They show multiple pieces of information at once and update in real-time. For mechatronic systems, dashboards typically display:
-
Current sensor readings
-
System status indicators
-
Control buttons or sliders
-
Alerts and warnings
-
Historical data or trends
Let’s create a text-based dashboard for a greenhouse monitoring system:
import time
import random
from datetime import datetime
class GreenhouseDashboard:
def __init__(self):
# Sensor data
self.temperature = 22.5
self.humidity = 65.0
self.soil_moisture = 45.0
self.light_level = 750
# Control systems
self.heater_on = False
self.fan_on = False
self.irrigation_on = False
self.grow_lights_on = False
# System status
self.alerts = []
self.last_update = datetime.now()
# Settings
self.target_temp = 24.0
self.target_humidity = 70.0
self.target_soil_moisture = 60.0
def update_sensors(self):
"""Simulate sensor readings"""
# Add some random variation to simulate real sensors
self.temperature += random.uniform(-0.5, 0.5)
self.humidity += random.uniform(-2.0, 2.0)
self.soil_moisture += random.uniform(-1.0, 1.0)
self.light_level += random.uniform(-50, 50)
# Keep values in reasonable ranges
self.temperature = max(10, min(40, self.temperature))
self.humidity = max(20, min(95, self.humidity))
self.soil_moisture = max(0, min(100, self.soil_moisture))
self.light_level = max(0, min(1000, self.light_level))
self.last_update = datetime.now()
def check_conditions(self):
"""Check for alert conditions"""
self.alerts.clear()
# Temperature alerts
if self.temperature < self.target_temp - 3:
self.alerts.append("🔵 Temperature too low")
elif self.temperature > self.target_temp + 3:
self.alerts.append("🔴 Temperature too high")
# Humidity alerts
if self.humidity < self.target_humidity - 10:
self.alerts.append("🟡 Humidity too low")
elif self.humidity > self.target_humidity + 10:
self.alerts.append("🟡 Humidity too high")
# Soil moisture alerts
if self.soil_moisture < 30:
self.alerts.append("🟤 Soil too dry - irrigation needed")
elif self.soil_moisture > 80:
self.alerts.append("🟤 Soil too wet - check drainage")
# Light level alerts
if self.light_level < 300:
self.alerts.append("🌙 Low light - consider grow lights")
def update_controls(self):
"""Update control systems based on conditions"""
# Temperature control
if self.temperature < self.target_temp - 1:
self.heater_on = True
self.fan_on = False
elif self.temperature > self.target_temp + 1:
self.heater_on = False
self.fan_on = True
else:
self.heater_on = False
self.fan_on = False
# Irrigation control
if self.soil_moisture < 50:
self.irrigation_on = True
else:
self.irrigation_on = False
# Grow lights control
if self.light_level < 400:
self.grow_lights_on = True
else:
self.grow_lights_on = False
def create_progress_bar(self, value, max_value, width=20, target=None):
"""Create a text-based progress bar"""
percentage = min(100, (value / max_value) * 100)
filled = int((percentage / 100) * width)
empty = width - filled
bar = "█" * filled + "░" * empty
# Add target marker if provided
if target is not None:
target_pos = int((target / max_value) * width)
if 0 <= target_pos < width:
bar_list = list(bar)
bar_list[target_pos] = "│"
bar = "".join(bar_list)
return f"[{bar}] {value:.1f}"
def render_dashboard(self):
"""Render the complete dashboard"""
# Clear screen (simplified)
print("\n" * 50) # Scroll up to simulate clear
print("🏡 GREENHOUSE CONTROL DASHBOARD")
print("=" * 60)
print(f"Last Update: {self.last_update.strftime('%H:%M:%S')}")
print()
# Sensor readings section
print("📊 SENSOR READINGS")
print("-" * 30)
temp_bar = self.create_progress_bar(self.temperature, 40, target=self.target_temp)
print(f"🌡️ Temperature: {temp_bar}°C (Target: {self.target_temp}°C)")
humidity_bar = self.create_progress_bar(self.humidity, 100, target=self.target_humidity)
print(f"💧 Humidity: {humidity_bar}% (Target: {self.target_humidity}%)")
moisture_bar = self.create_progress_bar(self.soil_moisture, 100, target=self.target_soil_moisture)
print(f"🌱 Soil Moisture: {moisture_bar}% (Target: {self.target_soil_moisture}%)")
light_bar = self.create_progress_bar(self.light_level, 1000)
print(f"☀️ Light Level: {light_bar} lux")
print()
# Control systems section
print("🎛️ CONTROL SYSTEMS")
print("-" * 30)
heater_status = "🔥 ON " if self.heater_on else "⚫ OFF"
fan_status = "💨 ON " if self.fan_on else "⚫ OFF"
irrigation_status = "💦 ON " if self.irrigation_on else "⚫ OFF"
lights_status = "💡 ON " if self.grow_lights_on else "⚫ OFF"
print(f"Heater: {heater_status}")
print(f"Fan: {fan_status}")
print(f"Irrigation: {irrigation_status}")
print(f"Grow Lights: {lights_status}")
print()
# Alerts section
print("🚨 ALERTS")
print("-" * 30)
if self.alerts:
for alert in self.alerts:
print(f" {alert}")
else:
print(" ✅ All systems normal")
print()
# Status summary
print("📈 SYSTEM STATUS")
print("-" * 30)
active_systems = sum([self.heater_on, self.fan_on, self.irrigation_on, self.grow_lights_on])
print(f"Active Control Systems: {active_systems}/4")
print(f"Alert Level: {'🔴 HIGH' if len(self.alerts) >= 3 else '🟡 MEDIUM' if len(self.alerts) >= 1 else '🟢 LOW'}")
def run_dashboard(self, duration=30):
"""Run the dashboard for a specified duration"""
print("Starting Greenhouse Dashboard...")
print("Press Ctrl+C to stop")
try:
start_time = time.time()
while time.time() - start_time < duration:
self.update_sensors()
self.check_conditions()
self.update_controls()
self.render_dashboard()
time.sleep(2) # Update every 2 seconds
except KeyboardInterrupt:
print("\n\nDashboard stopped.")
# Create and demonstrate the dashboard
greenhouse = GreenhouseDashboard()
# Show a few dashboard updates
print("🧪 Greenhouse Dashboard Demo:")
print("=" * 50)
for i in range(3):
print(f"\n--- Update {i+1} ---")
greenhouse.update_sensors()
greenhouse.check_conditions()
greenhouse.update_controls()
greenhouse.render_dashboard()
time.sleep(1)
Clear status and error messaging¶
Good error messages are crucial for mechatronic systems. They should be:
-
Clear: Easy to understand without technical jargon
-
Specific: Tell exactly what went wrong
-
Actionable: Suggest what the user should do
-
Visible: Hard to miss or ignore
Let’s build a comprehensive messaging system:
class SystemMessaging:
def __init__(self):
self.message_history = []
self.current_status = "OK"
# Message severity levels
self.SEVERITY_LEVELS = {
"INFO": {"symbol": "ℹ️", "color": "blue"},
"WARNING": {"symbol": "⚠️", "color": "yellow"},
"ERROR": {"symbol": "❌", "color": "red"},
"CRITICAL": {"symbol": "🚨", "color": "red"},
"SUCCESS": {"symbol": "✅", "color": "green"}
}
def create_message(self, severity, title, description, action=None, code=None):
"""Create a structured message"""
timestamp = datetime.now().strftime("%H:%M:%S")
message = {
"timestamp": timestamp,
"severity": severity,
"title": title,
"description": description,
"action": action,
"code": code,
"id": len(self.message_history) + 1
}
self.message_history.append(message)
return message
def format_message(self, message):
"""Format a message for display"""
severity_info = self.SEVERITY_LEVELS.get(message["severity"], {"symbol": "•", "color": "white"})
# Main message line
output = f"{severity_info['symbol']} [{message['timestamp']}] {message['title']}"
# Description
if message["description"]:
output += f"\n {message['description']}"
# Suggested action
if message["action"]:
output += f"\n 💡 Action: {message['action']}"
# Error code
if message["code"]:
output += f"\n 🔍 Code: {message['code']}"
return output
def info(self, title, description=None, action=None):
"""Log an info message"""
message = self.create_message("INFO", title, description, action)
return self.format_message(message)
def warning(self, title, description=None, action=None, code=None):
"""Log a warning message"""
message = self.create_message("WARNING", title, description, action, code)
return self.format_message(message)
def error(self, title, description=None, action=None, code=None):
"""Log an error message"""
message = self.create_message("ERROR", title, description, action, code)
return self.format_message(message)
def critical(self, title, description=None, action=None, code=None):
"""Log a critical error message"""
message = self.create_message("CRITICAL", title, description, action, code)
self.current_status = "CRITICAL"
return self.format_message(message)
def success(self, title, description=None):
"""Log a success message"""
message = self.create_message("SUCCESS", title, description)
return self.format_message(message)
def show_recent_messages(self, count=5):
"""Show the most recent messages"""
recent = self.message_history[-count:] if len(self.message_history) > count else self.message_history
output = f"📝 Recent Messages (showing {len(recent)}):\n"
output += "-" * 40 + "\n"
for message in recent:
output += self.format_message(message) + "\n\n"
return output
# Example usage for different mechatronic scenarios
messaging = SystemMessaging()
print("🧪 Testing System Messaging:")
print("=" * 50)
# Scenario 1: Normal operation
print(messaging.info("System startup", "Robotic arm control system initialized"))
print()
print(messaging.success("Calibration complete", "All axes calibrated successfully"))
print()
# Scenario 2: Warning situations
print(messaging.warning(
"High temperature detected",
"Motor temperature is 78°C, approaching safety limit of 80°C",
"Reduce operation speed or allow cooling time",
"TEMP_HIGH_001"
))
print()
print(messaging.warning(
"Low battery level",
"Battery charge is 15%, system may shut down soon",
"Connect charger or return to charging station",
"POWER_LOW_002"
))
print()
# Scenario 3: Error situations
print(messaging.error(
"Sensor communication failure",
"Unable to read from pressure sensor on port 3",
"Check sensor wiring and connections",
"SENSOR_COMM_003"
))
print()
print(messaging.error(
"Movement blocked",
"Obstacle detected in path, cannot complete movement to target position",
"Clear obstacle or choose alternate path",
"MOTION_BLOCKED_004"
))
print()
# Scenario 4: Critical situations
print(messaging.critical(
"Emergency stop activated",
"Safety interlock triggered due to unexpected object in work area",
"Remove object and reset safety system before continuing",
"SAFETY_STOP_005"
))
print()
# Show message history
print(messaging.show_recent_messages())
Guided example¶
Let’s put it all together by building a complete control interface for an automated sorting system:
class SortingSystemInterface:
def __init__(self):
self.system = {
"conveyor_speed": 0, # 0-100%
"sorting_arm_position": "home",
"items_processed": 0,
"items_sorted": {"red": 0, "blue": 0, "green": 0, "reject": 0},
"system_running": False,
"emergency_stop": False
}
self.messaging = SystemMessaging()
self.current_item = None
self.processing_queue = ["red", "blue", "green", "red", "reject", "blue"]
def render_main_display(self):
"""Render the main control display"""
print("\n" * 30) # Clear screen
print("🏭 AUTOMATED SORTING SYSTEM CONTROL")
print("=" * 60)
# System status header
status_icon = "🟢" if self.system["system_running"] else "🔴"
emergency_icon = "🚨" if self.system["emergency_stop"] else "✅"
print(f"System Status: {status_icon} {'RUNNING' if self.system['system_running'] else 'STOPPED'}")
print(f"Emergency Stop: {emergency_icon} {'ACTIVE' if self.system['emergency_stop'] else 'CLEAR'}")
print()
# Main controls
print("🎛️ MAIN CONTROLS")
print("-" * 20)
print("1. Start System")
print("2. Stop System")
print("3. Emergency Stop")
print("4. Reset Emergency")
print("5. Adjust Speed")
print()
# Current operation
print("⚙️ CURRENT OPERATION")
print("-" * 20)
print(f"Conveyor Speed: {self.system['conveyor_speed']}%")
print(f"Sorting Arm: {self.system['sorting_arm_position']}")
print(f"Current Item: {self.current_item if self.current_item else 'None'}")
print(f"Items in Queue: {len(self.processing_queue)}")
print()
# Statistics
print("📊 SORTING STATISTICS")
print("-" * 20)
print(f"Total Processed: {self.system['items_processed']}")
for color, count in self.system['items_sorted'].items():
print(f"{color.capitalize():>10}: {count:>3}")
print()
# Quick actions
print("⚡ QUICK ACTIONS")
print("-" * 15)
print("s - Start/Stop e - Emergency r - Reset q - Quit")
def process_menu_choice(self, choice):
"""Process main menu selections"""
if choice == "1":
return self.start_system()
elif choice == "2":
return self.stop_system()
elif choice == "3":
return self.emergency_stop()
elif choice == "4":
return self.reset_emergency()
elif choice == "5":
return self.adjust_speed()
else:
return self.messaging.warning("Invalid selection", f"'{choice}' is not a valid menu option")
def process_quick_action(self, action):
"""Process quick action keys"""
if action == "s":
if self.system["system_running"]:
return self.stop_system()
else:
return self.start_system()
elif action == "e":
return self.emergency_stop()
elif action == "r":
return self.reset_emergency()
elif action == "q":
return "quit"
else:
return self.messaging.warning("Unknown action", f"'{action}' is not a recognized quick action")
def start_system(self):
"""Start the sorting system"""
if self.system["emergency_stop"]:
return self.messaging.error(
"Cannot start system",
"Emergency stop is active",
"Reset emergency stop first",
"START_BLOCKED_001"
)
if self.system["system_running"]:
return self.messaging.warning("System already running")
self.system["system_running"] = True
self.system["conveyor_speed"] = 50 # Default speed
return self.messaging.success("System started", "Sorting operations beginning")
def stop_system(self):
"""Stop the sorting system normally"""
if not self.system["system_running"]:
return self.messaging.info("System already stopped")
self.system["system_running"] = False
self.system["conveyor_speed"] = 0
self.system["sorting_arm_position"] = "home"
return self.messaging.info("System stopped", "Normal shutdown completed")
def emergency_stop(self):
"""Activate emergency stop"""
self.system["emergency_stop"] = True
self.system["system_running"] = False
self.system["conveyor_speed"] = 0
self.system["sorting_arm_position"] = "emergency_position"
return self.messaging.critical(
"Emergency stop activated",
"All motion halted immediately for safety",
"Investigate cause and reset when safe",
"EMERGENCY_001"
)
def reset_emergency(self):
"""Reset emergency stop"""
if not self.system["emergency_stop"]:
return self.messaging.info("Emergency stop not active")
self.system["emergency_stop"] = False
self.system["sorting_arm_position"] = "home"
return self.messaging.success("Emergency reset", "System ready for normal operation")
def adjust_speed(self):
"""Adjust conveyor speed"""
if not self.system["system_running"]:
return self.messaging.warning(
"Cannot adjust speed",
"System must be running to adjust speed"
)
print("\n🎚️ Speed Adjustment")
print(f"Current speed: {self.system['conveyor_speed']}%")
print("Enter new speed (0-100):")
try:
new_speed = int(input("Speed: "))
if 0 <= new_speed <= 100:
old_speed = self.system["conveyor_speed"]
self.system["conveyor_speed"] = new_speed
return self.messaging.success(
"Speed adjusted",
f"Conveyor speed changed from {old_speed}% to {new_speed}%"
)
else:
return self.messaging.error(
"Invalid speed",
f"Speed must be between 0 and 100, got {new_speed}"
)
except ValueError:
return self.messaging.error("Invalid input", "Please enter a number")
except (EOFError, KeyboardInterrupt):
return self.messaging.info("Speed adjustment cancelled")
def simulate_sorting_operation(self):
"""Simulate processing one item"""
if not self.system["system_running"] or self.system["emergency_stop"]:
return None
if not self.processing_queue:
return self.messaging.info("Queue empty", "No more items to process")
# Get next item
item_color = self.processing_queue.pop(0)
self.current_item = item_color
# Simulate processing time based on speed
processing_time = max(0.5, 2.0 - (self.system["conveyor_speed"] / 100))
# Process the item
self.system["items_processed"] += 1
self.system["items_sorted"][item_color] += 1
# Move sorting arm
self.system["sorting_arm_position"] = f"sorting_{item_color}"
result = self.messaging.success(
f"Item sorted",
f"{item_color.capitalize()} item sorted successfully (#{self.system['items_processed']})"
)
# Return arm to home
self.system["sorting_arm_position"] = "home"
self.current_item = None
return result
def run_interface(self):
"""Run the main interface loop"""
print("🏭 Sorting System Interface Starting...")
last_message = None
last_auto_process = time.time()
while True:
self.render_main_display()
# Show last message if any
if last_message:
print("\n" + "=" * 60)
print("📢 SYSTEM MESSAGE:")
print(last_message)
print("=" * 60)
# Auto-process items if system is running
if (self.system["system_running"] and
not self.system["emergency_stop"] and
time.time() - last_auto_process > 3): # Process every 3 seconds
auto_result = self.simulate_sorting_operation()
if auto_result:
last_message = auto_result
last_auto_process = time.time()
print("\nCommand (1-5, s/e/r/q, or Enter for refresh): ", end="")
try:
user_input = input().strip().lower()
if not user_input:
continue # Just refresh
if user_input == "q":
print("👋 Shutting down interface...")
break
# Check if it's a quick action (single character)
if len(user_input) == 1 and user_input in "serq":
result = self.process_quick_action(user_input)
if result == "quit":
break
last_message = result
else:
# Try processing as menu choice
last_message = self.process_menu_choice(user_input)
except (KeyboardInterrupt, EOFError):
print("\n👋 Interface terminated.")
break
except Exception as e:
last_message = self.messaging.error("System error", str(e))
# Demonstrate the interface
sorting_interface = SortingSystemInterface()
# Run a quick demo
print("🧪 Sorting System Interface Demo:")
print("=" * 50)
# Simulate some operations
demo_commands = ["1", "5", "75", "s", "e", "r", "2"]
for cmd in demo_commands:
print(f"\nSimulating command: {cmd}")
if cmd == "75":
# Skip speed input simulation for demo
continue
elif cmd in "12345":
result = sorting_interface.process_menu_choice(cmd)
else:
result = sorting_interface.process_quick_action(cmd)
if result and result != "quit":
print(result)
time.sleep(1)
print("\n🎯 Interface demo complete!")
Try it¶
Exercise 1: Design a CLI for different mechatronic systems
Create CLI interfaces for various mechatronic applications. Design commands for drone control, 3D printing, and security systems that include safety-critical commands and example usage sequences.
Sample Solution
def design_cli_commands(system_type):
"""Design CLI commands for different mechatronic systems"""
cli_designs = {
"drone": {
"commands": [
"takeoff [height] - Takeoff to specified height (default 10m)",
"land [location] - Land at current position or specified coordinates",
"goto x y z - Fly to coordinates x,y,z",
"hover [duration] - Hover in place for specified seconds",
"camera on/off - Control camera recording",
"battery - Show battery status and flight time remaining",
"emergency - Emergency landing",
"status - Show position, altitude, speed, and system status"
],
"safety_commands": ["emergency", "land", "battery"],
"example_usage": [
"takeoff 15",
"goto 100 200 15",
"camera on",
"hover 30",
"land"
]
},
"3d_printer": {
"commands": [
"load filename.gcode - Load G-code file for printing",
"start - Begin printing loaded file",
"pause - Pause current print job",
"resume - Resume paused print job",
"stop - Stop current print and return to home",
"temp hotend/bed temp - Set temperatures",
"move x y z - Move print head to position",
"home [axis] - Home all axes or specific axis",
"status - Show temperatures, progress, and position"
],
"safety_commands": ["stop", "home", "temp"],
"example_usage": [
"load test_cube.gcode",
"temp hotend 200",
"temp bed 60",
"home",
"start"
]
},
"security_system": {
"commands": [
"arm home/away - Arm system for home or away mode",
"disarm [code] - Disarm system with security code",
"status zone - Show status of all zones or specific zone",
"bypass zone_number - Temporarily bypass a zone",
"test zone_number - Test a specific sensor/zone",
"history [hours] - Show event history",
"alert silence - Silence current alarms",
"config - Enter configuration mode"
],
"safety_commands": ["disarm", "alert", "status"],
"example_usage": [
"status",
"arm away",
"status zone 3",
"disarm 1234"
]
}
}
if system_type in cli_designs:
design = cli_designs[system_type]
print(f"\n🎛️ CLI Design for {system_type.replace('_', ' ').title()}")
print("=" * 50)
print("Available Commands:")
for cmd in design["commands"]:
print(f" {cmd}")
print(f"\nSafety-Critical Commands: {', '.join(design['safety_commands'])}")
print("\nExample Usage Sequence:")
for i, cmd in enumerate(design["example_usage"], 1):
print(f" {i}. {cmd}")
else:
print(f"CLI design not available for {system_type}")
# Test different CLI designs
for system in ["drone", "3d_printer", "security_system"]:
design_cli_commands(system)
Exercise 2: Create status indicators for different conditions
Build visual status indicators for various system states. Create a status dashboard that shows battery level, connectivity, sensor status, motor status, and safety conditions with appropriate icons and color coding.
Sample Solution
class StatusIndicators:
def __init__(self):
self.indicators = {
"battery": {"level": 85, "charging": False},
"connectivity": {"wifi": True, "bluetooth": False, "cellular": True},
"sensors": {"temperature": "ok", "pressure": "warning", "motion": "error"},
"motors": {"left": "running", "right": "stopped", "gripper": "running"},
"safety": {"emergency_stop": False, "doors_closed": True, "area_clear": True}
}
def battery_indicator(self):
"""Create battery status indicator"""
level = self.indicators["battery"]["level"]
charging = self.indicators["battery"]["charging"]
# Choose battery icon based on level
if level >= 75:
icon = "🔋"
elif level >= 50:
icon = "🔋"
elif level >= 25:
icon = "🪫"
else:
icon = "🪫"
# Add charging indicator
if charging:
icon += "⚡"
# Color coding
if level < 20:
status = "🔴 CRITICAL"
elif level < 40:
status = "🟡 LOW"
else:
status = "🟢 OK"
return f"{icon} {level}% {status} {'(Charging)' if charging else ''}"
def connectivity_indicator(self):
"""Create connectivity status indicator"""
wifi = "📶" if self.indicators["connectivity"]["wifi"] else "📵"
bluetooth = "🔗" if self.indicators["connectivity"]["bluetooth"] else "❌"
cellular = "📱" if self.indicators["connectivity"]["cellular"] else "📵"
return f"WiFi: {wifi} | Bluetooth: {bluetooth} | Cellular: {cellular}"
def sensor_status_indicator(self):
"""Create sensor status overview"""
sensors = self.indicators["sensors"]
status_icons = {"ok": "✅", "warning": "⚠️", "error": "❌", "offline": "⚫"}
status_display = []
for sensor, status in sensors.items():
icon = status_icons.get(status, "❓")
status_display.append(f"{sensor.title()}: {icon}")
return " | ".join(status_display)
def motor_status_indicator(self):
"""Create motor status display"""
motors = self.indicators["motors"]
status_icons = {"running": "🟢", "stopped": "🔴", "error": "❌", "maintenance": "🟡"}
motor_display = []
for motor, status in motors.items():
icon = status_icons.get(status, "❓")
motor_display.append(f"{motor.title()}: {icon}")
return " | ".join(motor_display)
def safety_status_indicator(self):
"""Create safety system overview"""
safety = self.indicators["safety"]
emergency = "🚨 ACTIVE" if safety["emergency_stop"] else "✅ Clear"
doors = "🟢 Closed" if safety["doors_closed"] else "🔴 Open"
area = "✅ Clear" if safety["area_clear"] else "⚠️ Occupied"
# Overall safety status
if safety["emergency_stop"] or not safety["doors_closed"]:
overall = "🔴 UNSAFE"
elif not safety["area_clear"]:
overall = "🟡 CAUTION"
else:
overall = "🟢 SAFE"
return f"{overall} | E-Stop: {emergency} | Doors: {doors} | Area: {area}"
def create_status_dashboard(self):
"""Create complete status dashboard"""
dashboard = []
dashboard.append("🖥️ SYSTEM STATUS DASHBOARD")
dashboard.append("=" * 50)
dashboard.append(f"🔋 Power: {self.battery_indicator()}")
dashboard.append(f"📡 Network: {self.connectivity_indicator()}")
dashboard.append(f"📊 Sensors: {self.sensor_status_indicator()}")
dashboard.append(f"⚙️ Motors: {self.motor_status_indicator()}")
dashboard.append(f"🛡️ Safety: {self.safety_status_indicator()}")
dashboard.append("=" * 50)
return "\n".join(dashboard)
def simulate_status_changes(self):
"""Simulate various status changes"""
scenarios = [
{
"name": "Normal Operation",
"changes": {}
},
{
"name": "Low Battery Warning",
"changes": {"battery": {"level": 25, "charging": False}}
},
{
"name": "Connectivity Issues",
"changes": {"connectivity": {"wifi": False, "bluetooth": False, "cellular": True}}
},
{
"name": "Sensor Malfunction",
"changes": {"sensors": {"temperature": "error", "pressure": "offline", "motion": "ok"}}
},
{
"name": "Emergency Situation",
"changes": {
"safety": {"emergency_stop": True, "doors_closed": False, "area_clear": False},
"motors": {"left": "stopped", "right": "stopped", "gripper": "stopped"}
}
}
]
for scenario in scenarios:
print(f"\n🎬 Scenario: {scenario['name']}")
print("-" * 40)
# Apply changes
for category, updates in scenario["changes"].items():
if category in self.indicators:
self.indicators[category].update(updates)
# Show dashboard
print(self.create_status_dashboard())
# Reset for next scenario (simplified)
self.__init__()
# Demonstrate status indicators
status_system = StatusIndicators()
status_system.simulate_status_changes()
Recap¶
In this section, you learned about creating effective user interfaces for mechatronic control systems:
Key concepts:
-
Command Line Interfaces (CLI): Text-based interfaces that provide precise control and are easy to automate and debug.
-
Dashboard interfaces: Visual displays that show multiple pieces of information at once, with real-time updates and clear status indicators.
-
Clear messaging: Structured approach to system messages that are clear, specific, actionable, and appropriately visible.
-
Status indicators: Visual and text-based ways to communicate system state, using icons, colors, and progress bars effectively.
Why this matters for mechatronics:
-
Good interfaces prevent user errors that could damage equipment or cause safety issues
-
Clear status information helps operators understand what the system is doing
-
Effective error messages help diagnose and fix problems quickly
-
Well-designed controls make systems more efficient and easier to use
Design principles:
-
Make critical information highly visible
-
Use consistent symbols and colors across your interface
-
Provide clear feedback for every user action
-
Include safety features like emergency stops and confirmation dialogs
-
Design for your specific users and use cases
-
Test interfaces with real users in realistic conditions
Interface types to consider:
-
Simple LED indicators for basic status
-
Text-based CLIs for detailed control and automation
-
Dashboard displays for monitoring multiple systems
-
Mobile apps for remote control and monitoring
-
Voice interfaces for hands-free operation
In the next section, we’ll explore how to test these interfaces and the systems they control.