Industrial Software Architectures
This reference provides detailed coverage of professional embedded software architectures used in automotive, aerospace, and industrial automation. Each architecture is analyzed with its design principles, use cases, and trade-offs.
Architecture Selection Guide
| Domain |
Standard |
Typical MCU |
Safety Level |
Complexity |
| Automotive |
AUTOSAR Classic |
Infineon AURIX, Renesas RH850 |
ASIL-D |
High |
| Automotive (new) |
AUTOSAR Adaptive |
ARM Cortex-A, x86 |
ASIL-B/D |
Very High |
| Aerospace |
ARINC 653 |
PowerPC, ARM |
DAL-A |
Very High |
| Industrial |
IEC 61131-3 |
Various PLCs |
SIL 3 |
Medium |
| Medical |
IEC 62304 |
ARM Cortex-M/A |
Class C |
High |
| Railway |
EN 50128 |
Various |
SIL 4 |
Very High |
Overview
AUTOSAR (AUTomotive Open System ARchitecture) is the dominant standard for automotive ECU software. It defines a layered architecture that separates application software from hardware.
┌─────────────────────────────────────────────────────────────────────┐
│ AUTOSAR Classic Architecture │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Application Layer │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ SWC 1 │ │ SWC 2 │ │ SWC 3 │ │ SWC n │ │ │
│ │ │(Sensor) │ │(Control)│ │(Actuator│ │ ... │ │ │
│ │ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │ │
│ │ │ │ │ │ │ │
│ │ └───────────┴─────┬─────┴───────────┘ │ │
│ └─────────────────────────┼────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────▼────────────────────────────────────┐ │
│ │ RTE (Runtime Environment) │ │
│ │ Generated code - connects SWCs to BSW │ │
│ └─────────────────────────┬────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────▼────────────────────────────────────┐ │
│ │ Basic Software (BSW) │ │
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
│ │ │ Services Layer │ │ │
│ │ │ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ │ │
│ │ │ │ Os │ │ Com │ │ Dcm │ │ Dem │ │ NvM │ │ Fee │ │ │ │
│ │ │ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │ │ │
│ │ └─────────────────────────────────────────────────────────┘ │ │
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
│ │ │ ECU Abstraction Layer │ │ │
│ │ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │
│ │ │ │ IoHwAb│ │CanIf │ │EcuM │ │ComM │ │ │ │
│ │ │ └──────┘ └──────┘ └──────┘ └──────┘ │ │ │
│ │ └─────────────────────────────────────────────────────────┘ │ │
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
│ │ │ MCAL (Microcontroller Abstraction Layer) │ │ │
│ │ │ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ │ │
│ │ │ │ Dio │ │ Adc │ │ Pwm │ │ Can │ │ Spi │ │ Gpt │ │ │ │
│ │ │ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │ │ │
│ │ └─────────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────▼────────────────────────────────────┐ │
│ │ Microcontroller │ │
│ └──────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
Key Components
| Component |
Purpose |
Example |
| SWC (Software Component) |
Application logic |
Engine control algorithm |
| RTE (Runtime Environment) |
Communication layer |
Generated from config |
| Os |
Task scheduling |
OSEK/VDX compliant |
| Com |
Signal routing |
CAN/LIN/FlexRay abstraction |
| Dcm |
Diagnostics |
UDS protocol (ISO 14229) |
| Dem |
Diagnostic Event Manager |
Fault memory |
| NvM |
Non-volatile memory |
Parameter storage |
| MCAL |
Hardware drivers |
Chip-vendor specific |
Configuration-Driven Development
Traditional development:
Write code → Compile → Test → Repeat
AUTOSAR development:
┌─────────────────────────────────────────────────────────┐
│ 1. System Design (OEM) │
│ └── Define SWCs, interfaces, signals │
│ │
│ 2. ECU Extract (Tier 1) │
│ └── ARXML files defining this ECU's config │
│ │
│ 3. Configuration (Tools) │
│ └── Vector DaVinci, EB tresos, ETAS ISOLAR │
│ │
│ 4. Code Generation │
│ └── RTE, BSW configuration → C code │
│ │
│ 5. Implementation │
│ └── Write SWC code (the actual application) │
│ │
│ 6. Integration │
│ └── Compile everything, flash to ECU │
└─────────────────────────────────────────────────────────┘
~80% of code is generated
~20% is hand-written application logic
Pros and Cons
| Pros |
Cons |
| Standardized interfaces - SWCs from different suppliers integrate |
Steep learning curve - months to become productive |
| Hardware independence - same SWC runs on different MCUs |
Tool cost - €50k-500k+ for toolchain licenses |
| Reusability - SWCs can be reused across projects/vehicles |
Configuration complexity - thousands of parameters |
| Safety support - designed for ISO 26262 ASIL-D |
Resource overhead - needs powerful MCU (RAM, flash) |
| Ecosystem - large community, training, certified BSW |
Rigidity - hard to do quick prototypes |
| Diagnostics built-in - UDS, fault memory, logging |
Version dependencies - AUTOSAR 4.x vs 3.x incompatible |
| OEM requirement - often mandatory for automotive work |
Overkill for simple ECUs - overhead not justified |
When to Use
✅ Use AUTOSAR Classic when:
- Building automotive ECUs (engine, transmission, body, chassis)
- OEM requires it (most do for new projects)
- Multiple suppliers collaborate on same ECU
- Safety certification needed (ASIL-B to ASIL-D)
- Long product lifecycle (10-15 years)
- Complex communication (CAN, LIN, FlexRay, Ethernet)
❌ Don't use AUTOSAR Classic when:
- Prototyping or proof-of-concept
- Simple, single-function device
- Cost-sensitive high-volume consumer products
- No automotive requirements
- Small team without AUTOSAR experience
Lessons for Simpler Projects (No License Needed)
Even without expensive AUTOSAR tools, you can apply these concepts:
| AUTOSAR Concept |
Apply to Your Robot |
How |
| Layered architecture |
Separate HW from logic |
Create drivers/, app/, services/ folders |
| Software Components |
Modular code |
Each feature in its own file/class |
| RTE concept |
Defined interfaces |
Use function signatures, not global variables |
| Configuration |
Separate config from code |
config.py with all constants |
| MCAL abstraction |
Hardware abstraction layer |
hal.py wrapping machine.Pin, etc. |
# Your robot can use AUTOSAR-inspired structure:
# hal.py (like MCAL) - hardware abstraction
class GPIO:
@staticmethod
def read(pin): return Pin(pin).value()
# services.py (like BSW) - common services
class Logger:
@staticmethod
def log(msg): print(f"[{time.ticks_ms()}] {msg}")
# app.py (like SWC) - your application
class LineFollower:
def __init__(self, sensors, motors):
self.sensors = sensors # Injected dependency
self.motors = motors
def update(self):
pos = self.sensors.get_position()
self.motors.set_correction(pos * KP)
# main.py - composition root
sensors = LineSensors()
motors = Motors()
app = LineFollower(sensors, motors)
while True:
app.update()
Key insight: The AUTOSAR pattern of "configuration over code" and "interfaces between components" makes code more testable and maintainable—even in a 500-line MicroPython project.
Overview
AUTOSAR Adaptive is designed for high-performance ECUs running service-oriented applications (ADAS, autonomous driving, infotainment).
┌─────────────────────────────────────────────────────────────────────┐
│ AUTOSAR Adaptive Architecture │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Adaptive Applications (AA) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ ADAS App │ │ Parking │ │ V2X App │ │ OTA App │ │ │
│ │ │ (C++14) │ │ (C++14) │ │ (C++14) │ │ (C++14) │ │ │
│ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │
│ └───────┼────────────┼────────────┼────────────┼───────────────┘ │
│ │ │ │ │ │
│ ┌───────▼────────────▼────────────▼────────────▼───────────────┐ │
│ │ ARA (AUTOSAR Runtime for Adaptive) │ │
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
│ │ │ Functional Clusters │ │ │
│ │ │ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ │ │
│ │ │ │ CM │ │ EM │ │ SM │ │ UCM │ │ IAM │ │ Log │ │ │ │
│ │ │ │Comm │ │Exec │ │State│ │Updt │ │Auth │ │ │ │ │ │
│ │ │ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │ │ │
│ │ └─────────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────▼────────────────────────────────────┐ │
│ │ Operating System (POSIX-compliant) │ │
│ │ Linux, QNX, PikeOS, etc. │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────▼────────────────────────────────────┐ │
│ │ Hardware (High-Performance) │ │
│ │ ARM Cortex-A, x86, GPU accelerators │ │
│ └──────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
Classic vs Adaptive Comparison
| Aspect |
AUTOSAR Classic |
AUTOSAR Adaptive |
| Target |
Traditional ECUs |
High-performance compute |
| Language |
C (MISRA) |
C++14/17 |
| OS |
OSEK/AUTOSAR OS |
POSIX (Linux, QNX) |
| Communication |
Signal-based (CAN frames) |
Service-oriented (SOME/IP) |
| Update |
Flash via diagnostic tool |
OTA updates |
| Resources |
KB RAM, MB flash |
GB RAM, GB storage |
| Scheduling |
Static (configured) |
Dynamic (processes) |
| Safety |
ASIL-D native |
ASIL-B typical, D possible |
| Boot time |
Milliseconds |
Seconds |
| Typical ECU |
Body, powertrain |
ADAS, gateway, infotainment |
Pros and Cons
| Pros |
Cons |
| Modern language - C++14 with standard library |
New standard - less mature than Classic |
| Service-oriented - flexible, dynamic discovery |
Requires powerful hardware - expensive ECUs |
| OTA updates - field updateable applications |
Linux expertise needed - different skill set |
| Parallel execution - multi-core, multi-process |
Boot time - seconds vs milliseconds |
| AI/ML friendly - GPU support, high memory |
Safety certification harder - dynamic behavior |
| SOME/IP - Ethernet-native, high bandwidth |
Tooling immature - compared to Classic |
Lessons for Simpler Projects
| Adaptive Concept |
Apply to Your Robot |
How |
| Service-oriented |
Publish/subscribe pattern |
Use callbacks, event queues |
| Dynamic discovery |
Runtime configuration |
Load config from file at startup |
| Process isolation |
Separate concerns |
Multiple .py files, clear boundaries |
| Modern C++ patterns |
Clean Python |
Classes, type hints, dataclasses |
# Service-oriented pattern (like SOME/IP, but simpler)
class EventBus:
def __init__(self):
self.subscribers = {}
def subscribe(self, event_type, callback):
if event_type not in self.subscribers:
self.subscribers[event_type] = []
self.subscribers[event_type].append(callback)
def publish(self, event_type, data):
for callback in self.subscribers.get(event_type, []):
callback(data)
# Usage - components don't know about each other
bus = EventBus()
# Sensor publishes
def sensor_loop():
pos = read_line_position()
bus.publish("line_position", pos)
# Controller subscribes
def on_position(pos):
correction = pid.update(pos)
motors.set(correction)
bus.subscribe("line_position", on_position)
Key insight: Service-oriented architecture decouples components. Your sensor doesn't need to know about your motor controller—it just publishes data. This makes testing and modification much easier.
ARINC 653 (Aerospace)
Overview
ARINC 653 defines a standard for time and space partitioning in safety-critical avionics. It's used in Integrated Modular Avionics (IMA) where multiple functions share hardware.
┌─────────────────────────────────────────────────────────────────────┐
│ ARINC 653 Architecture │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Time Partitioning (Major Frame = 100ms example) │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ 0 10 20 30 40 50 60 70 80 90 100ms │ │
│ │ ├────┼────┼────┼────┼────┼────┼────┼────┼────┼────┤ │ │
│ │ │████│ │████│ │████│ │████│ │████│ │ Flight │ │
│ │ │ │████│ │████│ │████│ │████│ │████│ Engine │ │
│ │ │ │ │ ██│ │ │ ██│ │ │ ██│ │ Display │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ Space Partitioning (Memory Isolation) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Partition 1 │ │ Partition 2 │ │ Partition 3 │ │
│ │ Flight Ctrl │ │ Engine Mon │ │ Display │ │
│ │ │ │ │ │ │ │
│ │ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │
│ │ │ App Code │ │ │ │ App Code │ │ │ │ App Code │ │ │
│ │ └──────────┘ │ │ └──────────┘ │ │ └──────────┘ │ │
│ │ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │
│ │ │ App Data │ │ │ │ App Data │ │ │ │ App Data │ │ │
│ │ └──────────┘ │ │ └──────────┘ │ │ └──────────┘ │ │
│ │ DAL-A │ │ DAL-B │ │ DAL-C │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │ │
│ └────────────────┴────────────────┘ │
│ │ │
│ ┌───────────────────────▼──────────────────────────────────────┐ │
│ │ ARINC 653 RTOS (Separation Kernel) │ │
│ │ VxWorks 653, PikeOS, LynxOS-178, INTEGRITY-178 │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────▼──────────────────────────────────────┐ │
│ │ Board Support Package (BSP) │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────▼──────────────────────────────────────┐ │
│ │ Hardware (IMA Module) │ │
│ └──────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
Key Concepts
| Concept |
Description |
| Partition |
Isolated execution environment with own memory, time budget |
| Major Frame |
Complete scheduling cycle (all partitions run once) |
| Minor Frame |
Smallest schedulable unit |
| Health Monitor |
Detects partition failures, takes recovery action |
| APEX API |
Standard application interface (process, time, communication) |
| DAL |
Design Assurance Level (A=catastrophic, E=no effect) |
APEX API Examples
/* ARINC 653 APEX API - standardized across all RTOS vendors */
/* Process management */
CREATE_PROCESS(&process_attr, &process_id, &return_code);
START(process_id, &return_code);
STOP_SELF();
/* Time management */
PERIODIC_WAIT(&return_code); /* Wait until next period */
TIMED_WAIT(timeout, &return_code);
GET_TIME(¤t_time, &return_code);
/* Inter-partition communication */
CREATE_SAMPLING_PORT("ALTITUDE", 4, SOURCE, 100ms, &port_id, &return_code);
WRITE_SAMPLING_MESSAGE(port_id, &altitude, 4, &return_code);
READ_SAMPLING_MESSAGE(port_id, &altitude, &validity, &return_code);
/* Intra-partition communication */
CREATE_BUFFER("CMD_QUEUE", 10, 64, FIFO, &buffer_id, &return_code);
SEND_BUFFER(buffer_id, &message, 64, INFINITE, &return_code);
Pros and Cons
| Pros |
Cons |
| Proven isolation - mathematically verified separation |
Expensive - RTOS licenses €100k+, tools €500k+ |
| Mixed criticality - DAL-A and DAL-D on same hardware |
Rigid scheduling - time slots fixed at design |
| DO-178C ready - designed for certification |
Static configuration - no runtime flexibility |
| Portable - APEX API same across RTOS vendors |
Limited dynamic behavior - no malloc, threads |
| Deterministic - proven WCET, no jitter |
Complex integration - partitions from different suppliers |
| Long lifecycle - 30+ year aircraft life |
Specialized skills - small expert pool |
When to Use
✅ Use ARINC 653 when:
- Building avionics (flight control, engine control)
- DO-178C certification required (DAL-A, B, C)
- Multiple functions must share hardware (IMA)
- Mixed criticality on single processor
- Deterministic timing absolutely required
- Aircraft program (commercial, military, space)
❌ Don't use ARINC 653 when:
- Non-safety-critical aerospace (ground equipment)
- Cost is primary concern
- Rapid development needed
- Dynamic behavior required
- Small team without aerospace experience
Lessons for Simpler Projects
| ARINC 653 Concept |
Apply to Your Robot |
How |
| Time partitioning |
Time-sliced tasks |
Give each task a time budget |
| Space partitioning |
Memory isolation |
Don't use global variables |
| Health monitoring |
Watchdog/self-check |
Detect and recover from errors |
| Deterministic timing |
Fixed loop rate |
Use time.sleep_ms(10) consistently |
| Defined interfaces |
Explicit data passing |
Pass data through function args |
# Time-partitioned approach (simplified ARINC 653 concept)
class Scheduler:
def __init__(self):
self.partitions = []
def add_partition(self, name, function, period_ms, budget_ms):
self.partitions.append({
'name': name,
'func': function,
'period': period_ms,
'budget': budget_ms,
'last_run': 0
})
def run(self):
now = time.ticks_ms()
for p in self.partitions:
if time.ticks_diff(now, p['last_run']) >= p['period']:
start = time.ticks_ms()
p['func']() # Run the partition
elapsed = time.ticks_diff(time.ticks_ms(), start)
# Budget overrun detection (like health monitor)
if elapsed > p['budget']:
print(f"WARNING: {p['name']} overran budget!")
p['last_run'] = now
# Usage
scheduler = Scheduler()
scheduler.add_partition("sensors", read_sensors, period_ms=10, budget_ms=5)
scheduler.add_partition("control", run_control, period_ms=10, budget_ms=3)
scheduler.add_partition("display", update_oled, period_ms=100, budget_ms=20)
while True:
scheduler.run()
Key insight: Time budgeting and overrun detection help you find bugs early. If your sensor reading suddenly takes 50ms instead of 5ms, you know something is wrong before the robot crashes.
IEC 61131-3 (Industrial Automation)
Overview
IEC 61131-3 standardizes PLC programming languages and program organization. It's the foundation of factory automation software.
┌─────────────────────────────────────────────────────────────────────┐
│ IEC 61131-3 Architecture │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Program Organization Units (POUs) │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ PROGRAM │ │
│ │ ├── Main control program │ │
│ │ ├── Called by task scheduler │ │
│ │ └── Contains: VAR, logic, FB calls │ │
│ ├──────────────────────────────────────────────────────────────┤ │
│ │ FUNCTION_BLOCK (FB) │ │
│ │ ├── Reusable with internal state (memory) │ │
│ │ ├── Like a class with instance data │ │
│ │ └── Example: PID controller, motor starter │ │
│ ├──────────────────────────────────────────────────────────────┤ │
│ │ FUNCTION │ │
│ │ ├── Stateless, no memory between calls │ │
│ │ ├── Like a pure function │ │
│ │ └── Example: SQRT, MAX, type conversion │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ Programming Languages │
│ ┌─────────────┬─────────────┬─────────────┬─────────────────────┐ │
│ │ IL │ ST │ LD │ FBD │ SFC │ │
│ │ Instruction │ Structured │ Ladder │ Function │Sequential│ │
│ │ List │ Text │ Diagram │ Block │ Function│ │
│ │ (assembly) │ (Pascal) │ (relay) │ (visual) │ Chart │ │
│ └─────────────┴─────────────┴─────────────┴───────────┴─────────┘ │
│ │
│ Task Configuration │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Task_Motion: Cyclic 1ms, Priority HIGH │ │
│ │ Task_Logic: Cyclic 10ms, Priority MEDIUM │ │
│ │ Task_HMI: Cyclic 100ms, Priority LOW │ │
│ │ Task_Alarm: Event-driven, Priority HIGHEST │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Programming Languages Comparison
| Language |
Type |
Best For |
Example |
| LD (Ladder) |
Graphical |
Discrete logic, electricians |
Relay replacement |
| FBD (Function Block) |
Graphical |
Continuous control, process |
PID loops |
| ST (Structured Text) |
Textual |
Complex algorithms, programmers |
Math, strings |
| IL (Instruction List) |
Textual |
Low-level, legacy |
Assembly-like |
| SFC (Sequential Function Chart) |
Graphical |
Sequences, batches |
Machine cycles |
Structured Text Example
(* IEC 61131-3 Structured Text - Motor Control *)
PROGRAM MotorControl
VAR
StartButton: BOOL; (* Input: Start pushbutton *)
StopButton: BOOL; (* Input: Stop pushbutton *)
Overload: BOOL; (* Input: Thermal overload *)
MotorContactor: BOOL; (* Output: Main contactor *)
RunningLamp: BOOL; (* Output: Running indicator *)
FaultLamp: BOOL; (* Output: Fault indicator *)
MotorFB: FB_MotorStarter; (* Function block instance *)
END_VAR
(* Call the motor starter function block *)
MotorFB(
Start := StartButton AND NOT Overload,
Stop := StopButton OR Overload,
Running => MotorContactor,
Fault => FaultLamp
);
RunningLamp := MotorContactor;
END_PROGRAM
(* Reusable Function Block *)
FUNCTION_BLOCK FB_MotorStarter
VAR_INPUT
Start: BOOL;
Stop: BOOL;
END_VAR
VAR_OUTPUT
Running: BOOL;
Fault: BOOL;
END_VAR
VAR
Latched: BOOL; (* Internal state - remembered between calls *)
END_VAR
(* Start-Stop logic with latch *)
IF Start AND NOT Latched THEN
Latched := TRUE;
ELSIF Stop THEN
Latched := FALSE;
Fault := TRUE;
END_IF;
Running := Latched;
END_FUNCTION_BLOCK
Sequential Function Chart (SFC)
┌─────────────────────────────────────────────────────────────────┐
│ Bottle Filling Machine SFC │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────┐ │
│ │ S0 │ INIT │
│ │ │ Entry: Reset all outputs │
│ └──┬──┘ │
│ │ StartButton │
│ ┌──▼──┐ │
│ │ S1 │ WAIT_BOTTLE │
│ │ │ Entry: ConveyorOn := TRUE │
│ └──┬──┘ │
│ │ BottleSensor │
│ ┌──▼──┐ │
│ │ S2 │ FILL │
│ │ │ Entry: FillValve := TRUE │
│ │ │ StartTimer(T1, 5s) │
│ └──┬──┘ │
│ │ T1.Done OR LevelHigh │
│ ┌──▼──┐ │
│ │ S3 │ CAP │
│ │ │ Entry: FillValve := FALSE │
│ │ │ CapperDown := TRUE │
│ └──┬──┘ │
│ │ CapSensor │
│ ┌──▼──┐ │
│ │ S4 │ RELEASE │
│ │ │ Entry: CapperDown := FALSE │
│ │ │ EjectCylinder := TRUE │
│ └──┬──┘ │
│ │ NOT BottleSensor │
│ └────────────────────────► Back to S1 │
│ │
│ Parallel Branch Example (simultaneous actions): │
│ ┌───────┬───────┐ │
│ │ S5a │ S5b │ Both run simultaneously │
│ │ Heat │ Stir │ │
│ └───┬───┴───┬───┘ │
│ └───┬───┘ Synchronize (both must complete) │
│ ▼ │
└─────────────────────────────────────────────────────────────────┘
Pros and Cons
| Pros |
Cons |
| Industry standard - all PLC vendors support it |
Vendor lock-in - extensions, libraries proprietary |
| Multiple languages - choose best for task |
Limited abstraction - no OOP (until 61131-3 3rd ed.) |
| Deterministic - cyclic execution, predictable timing |
Memory constraints - PLCs have limited RAM |
| Visual programming - LD/FBD readable by electricians |
Testing harder - no unit test frameworks (improving) |
| Proven reliability - decades of industrial use |
Version control difficult - binary project files |
| I/O integration - direct mapping to physical I/O |
Simulation limited - compared to Simulink |
| Safety variants - PLCopen Safety, TUV certified |
No standard networking - until recent OPC UA push |
When to Use
✅ Use IEC 61131-3 when:
- Factory/process automation
- Machine control (discrete manufacturing)
- Building automation (HVAC, lighting)
- Safety systems (with PLCopen Safety)
- Team includes electricians/technicians
- Deterministic I/O response required
- Long-term support needed (20+ years)
❌ Don't use IEC 61131-3 when:
- High-speed signal processing
- Complex algorithms (use embedded + PLC hybrid)
- Consumer products
- Cost-sensitive mass production
- Rapid prototyping
Lessons for Simpler Projects
| IEC 61131-3 Concept |
Apply to Your Robot |
How |
| Function Blocks |
Reusable classes |
Create class PID, class MotorController |
| Cyclic tasks |
Fixed-rate execution |
Main loop with consistent timing |
| SFC sequences |
State machines |
Your state machine from Lab! |
| VAR_INPUT/OUTPUT |
Explicit interfaces |
Function parameters, not globals |
| Structured Text |
Clean code |
Readable, well-organized Python |
# IEC 61131-3 Function Block concept in Python
class FB_PID:
"""Function Block: PID Controller (like IEC 61131-3 FB)"""
# VAR_INPUT
def __init__(self, kp, ki, kd, dt):
self.kp = kp
self.ki = ki
self.kd = kd
self.dt = dt
# VAR (internal state - retained between calls)
self._integral = 0
self._prev_error = 0
def __call__(self, setpoint, actual):
"""FB call - like FB_PID(SP:=100, PV:=actual, OUT=>correction)"""
# VAR_INPUT
error = setpoint - actual
# Algorithm
p = self.kp * error
self._integral += error * self.dt
i = self.ki * self._integral
d = self.kd * (error - self._prev_error) / self.dt
self._prev_error = error
# VAR_OUTPUT
return p + i + d
# Usage - instantiate like a PLC function block
pid_line = FB_PID(kp=1.0, ki=0.1, kd=0.05, dt=0.01)
pid_speed = FB_PID(kp=2.0, ki=0.0, kd=0.0, dt=0.01) # Different instance
# Call like IEC 61131-3
correction = pid_line(setpoint=0, actual=line_position)
Key insight: The Function Block pattern (stateful, reusable components) is one of the best ideas from PLCs. Your PID controller, motor driver, and sensor readers should all be self-contained "function blocks" that you can instantiate and call.
RTOS Comparison
Overview
Between bare-metal (your super loop) and full OS (Linux), there's a middle ground: Real-Time Operating Systems. Here's how common RTOS options compare:
RTOS Feature Comparison
| Feature |
FreeRTOS |
Zephyr |
RTEMS |
VxWorks |
QNX |
| License |
MIT (free) |
Apache 2.0 (free) |
BSD (free) |
Commercial ($$$) |
Commercial ($$$) |
| Footprint |
6-12 KB |
8-512 KB |
20+ KB |
20+ KB |
50+ KB |
| Pico support |
Yes (RP2040) |
Yes |
No |
No |
No |
| Safety cert |
SAFERTOS ($$) |
Optional |
RTEMS-SMP |
DO-178C, ISO 26262 |
ISO 26262, IEC 61508 |
| Complexity |
Low |
Medium |
Medium |
High |
High |
| Documentation |
Good |
Excellent |
Good |
Excellent |
Good |
| Community |
Huge |
Growing |
Niche |
Commercial |
Commercial |
┌────────────────────────────────────────────────────────────────────┐
│ Execution Environment Spectrum │
├────────────────────────────────────────────────────────────────────┤
│ │
│ Bare-Metal RTOS Full OS (Linux) │
│ (your robot) (FreeRTOS) (Raspberry Pi) │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Your │ │ Task 1 │ │ Process │ │
│ │ Code │ │ Task 2 │ │ Process │ │
│ │ │ │ Task 3 │ │ Process │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │
│ │ ┌────┴────┐ ┌────┴────┐ │
│ │ │ RTOS │ │ Linux │ │
│ │ │ Kernel │ │ Kernel │ │
│ │ └────┬────┘ └────┬────┘ │
│ │ │ │ │
│ ┌────┴──────────────────┴────────────────────┴────┐ │
│ │ Hardware │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ Latency: ~100ns ~1-10µs ~100µs-10ms │
│ RAM: 0 overhead 6-50KB 256MB+ │
│ Complexity: You manage all Scheduler helps Full services │
│ Debug: printf/LED RTOS-aware tools GDB, traces │
│ Best for: Simple, fast Multiple tasks Complex apps │
│ │
└────────────────────────────────────────────────────────────────────┘
| Symptom |
Bare-Metal Problem |
RTOS Solution |
| "My sensor reading blocks motor control" |
Sequential execution |
Separate tasks with priorities |
"I have 10 time.sleep() calls competing" |
No preemption |
RTOS scheduler handles timing |
| "Fast task and slow task conflict" |
One loop speed |
Priority-based preemption |
| "I need exact 1ms timing" |
Loop jitter |
Hardware timer + task |
| "Code is spaghetti of flags" |
Manual scheduling |
Task abstraction |
FreeRTOS Example (C, but concepts apply)
// FreeRTOS task structure (conceptual)
// High-priority task: motor control (runs every 1ms)
void vMotorTask(void *pvParameters) {
for (;;) {
read_encoders();
update_pid();
set_motor_pwm();
vTaskDelay(pdMS_TO_TICKS(1)); // Sleep 1ms
}
}
// Medium-priority task: sensors (runs every 10ms)
void vSensorTask(void *pvParameters) {
for (;;) {
read_line_sensors();
read_imu();
vTaskDelay(pdMS_TO_TICKS(10));
}
}
// Low-priority task: display (runs every 100ms)
void vDisplayTask(void *pvParameters) {
for (;;) {
update_oled();
vTaskDelay(pdMS_TO_TICKS(100));
}
}
// Main: create tasks and start scheduler
int main(void) {
xTaskCreate(vMotorTask, "Motor", 256, NULL, 3, NULL); // Priority 3
xTaskCreate(vSensorTask, "Sensor", 256, NULL, 2, NULL); // Priority 2
xTaskCreate(vDisplayTask, "Display", 512, NULL, 1, NULL); // Priority 1
vTaskStartScheduler(); // Never returns
}
| RTOS Concept |
Apply Without RTOS |
How |
| Task priorities |
Check important things first |
Motor before display in your loop |
| Fixed-rate tasks |
Time-based execution |
Check elapsed time, not sleep() |
| Task isolation |
Modular functions |
Each "task" is a function with own state |
| Queues |
Event buffers |
Python list as message queue |
| Semaphores |
Flags for coordination |
Boolean flags with careful access |
# RTOS-inspired bare-metal scheduler (no license needed!)
class CooperativeScheduler:
"""Mimics RTOS task scheduling without an RTOS"""
def __init__(self):
self.tasks = []
def add_task(self, name, func, period_ms, priority):
self.tasks.append({
'name': name,
'func': func,
'period': period_ms,
'priority': priority,
'next_run': 0
})
# Sort by priority (higher priority = lower number = runs first)
self.tasks.sort(key=lambda t: t['priority'])
def run_once(self):
now = time.ticks_ms()
for task in self.tasks:
if time.ticks_diff(now, task['next_run']) >= 0:
task['func']()
task['next_run'] = now + task['period']
return # Run one task per call (cooperative)
# Usage
scheduler = CooperativeScheduler()
scheduler.add_task("motor", motor_control, period_ms=5, priority=1)
scheduler.add_task("sensors", read_sensors, period_ms=10, priority=2)
scheduler.add_task("display", update_display, period_ms=100, priority=3)
while True:
scheduler.run_once()
Key insight: You can get 80% of RTOS benefits with 20% of the complexity by using a simple cooperative scheduler. The discipline of thinking "what are my tasks and priorities?" improves code even without a real RTOS.
Safety Standards Comparison
Overview
| Standard |
Domain |
Levels |
Key Concept |
| IEC 61508 |
Generic functional safety |
SIL 1-4 |
Base standard, all domains reference it |
| ISO 26262 |
Automotive |
ASIL A-D |
Adapted from IEC 61508 for vehicles |
| DO-178C |
Aerospace |
DAL A-E |
Software in airborne systems |
| IEC 62304 |
Medical devices |
Class A-C |
Software lifecycle for medical |
| EN 50128 |
Railway |
SIL 0-4 |
Train control, signaling |
Safety Integrity Levels
┌─────────────────────────────────────────────────────────────────────┐
│ Safety Levels Comparison │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Consequence │ IEC 61508 │ ISO 26262 │ DO-178C │
│ ────────────┼─────────────────┼───────────────┼───────────────────│
│ Catastrophic│ SIL 4 │ ASIL D │ DAL A │
│ (multiple │ PFH < 10^-8 │ │ 100% MC/DC │
│ deaths) │ │ │ │
│ ────────────┼─────────────────┼───────────────┼───────────────────│
│ Critical │ SIL 3 │ ASIL C │ DAL B │
│ (death/ │ PFH < 10^-7 │ │ 100% DC │
│ severe) │ │ │ │
│ ────────────┼─────────────────┼───────────────┼───────────────────│
│ Marginal │ SIL 2 │ ASIL B │ DAL C │
│ (injury) │ PFH < 10^-6 │ │ 100% Statement │
│ │ │ │ │
│ ────────────┼─────────────────┼───────────────┼───────────────────│
│ Minor │ SIL 1 │ ASIL A │ DAL D │
│ (minor │ PFH < 10^-5 │ │ │
│ injury) │ │ │ │
│ ────────────┼─────────────────┼───────────────┼───────────────────│
│ No effect │ None │ QM │ DAL E │
│ │ │ │ │
└─────────────────────────────────────────────────────────────────────┘
PFH = Probability of dangerous Failure per Hour
MC/DC = Modified Condition/Decision Coverage
DC = Decision Coverage
Development Effort by Safety Level
┌──────────────────────────────────────────────────────────────────┐
│ Relative Development Effort │
├──────────────────────────────────────────────────────────────────┤
│ │
│ QM/None │████ │ ~1x baseline │
│ ASIL-A │████████ │ ~2x │
│ ASIL-B │████████████ │ ~3x │
│ ASIL-C │████████████████████ │ ~5x │
│ ASIL-D │████████████████████████████ │ ~8-10x │
│ DAL-A │████████████████████████████████████ │ ~15-20x │
│ │
│ Factors increasing effort: │
│ - Documentation requirements (traceability matrices) │
│ - Review/inspection requirements (independent teams) │
│ - Testing requirements (coverage, independence) │
│ - Tool qualification (compilers, test tools) │
│ - Process audits and assessments │
│ - Formal methods (at highest levels) │
└──────────────────────────────────────────────────────────────────┘
Architecture Selection Decision Tree
┌─────────────────────┐
│ What are you building│
└──────────┬──────────┘
│
┌──────────────────────────┼──────────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Automotive │ │ Aerospace │ │ Industrial │
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│Safety-critical│ │ DO-178C req'd │ │ Safety-critical│
│ (ASIL)? │ │ (DAL)? │ │ (SIL)? │
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ │ │
┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐
│ Yes │ │ No │ │DAL A-C │DAL D-E│ │SIL 2-4 │SIL 0-1│
└──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│AUTOSAR │ │AUTOSAR │ │ARINC │ │Linux/ │ │Safety │ │Standard│
│Classic │ │Adaptive│ │653 │ │RTOS │ │PLC │ │PLC │
│+Safety │ │ │ │ │ │ │ │61511 │ │61131-3 │
└────────┘ └────────┘ └────────┘ └────────┘ └────────┘ └────────┘
Summary: When to Use What
| Architecture |
Use When |
Don't Use When |
| Bare-metal |
Learning, simple products, cost-critical |
Safety-critical, complex behavior |
| RTOS |
Multiple tasks, timing requirements |
Single simple function |
| AUTOSAR Classic |
Automotive ECU, multi-supplier |
Prototyping, non-automotive |
| AUTOSAR Adaptive |
ADAS, high-compute automotive |
Traditional body/powertrain ECU |
| ARINC 653 |
Avionics, mixed-criticality |
Non-aerospace, cost-sensitive |
| IEC 61131-3 |
Factory automation, PLCs |
Consumer products, high-speed DSP |
Further Reading