Skip to content

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

AUTOSAR Classic Platform

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.


AUTOSAR Adaptive Platform

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(&current_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

RTOS vs Bare-Metal vs Full OS

┌────────────────────────────────────────────────────────────────────┐
│                    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       │
│                                                                     │
└────────────────────────────────────────────────────────────────────┘

When to Move from Bare-Metal to RTOS

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
}

Lessons from RTOS for Your Bare-Metal Code

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