diff --git a/org.fortiss.af3.platform.raspberry.ui/.gitignore b/org.fortiss.af3.platform.raspberry.ui/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..237ede120c79c583d7292bd0e94832d2b863ce18
--- /dev/null
+++ b/org.fortiss.af3.platform.raspberry.ui/.gitignore
@@ -0,0 +1,2 @@
+build/
+target/
diff --git a/org.fortiss.af3.platform.raspberry.ui/icons/console.png b/org.fortiss.af3.platform.raspberry.ui/icons/console.png
new file mode 100644
index 0000000000000000000000000000000000000000..5cf0d0e5de7790df73b28f4c7ee8a27382541f0f
Binary files /dev/null and b/org.fortiss.af3.platform.raspberry.ui/icons/console.png differ
diff --git a/org.fortiss.af3.platform.raspberry.ui/src/org/fortiss/af3/platform/raspberry/ui/handler/CameraInputHandler.java b/org.fortiss.af3.platform.raspberry.ui/src/org/fortiss/af3/platform/raspberry/ui/handler/CameraInputHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..d10c0ac65763baadf88f442a5542980248abba4e
--- /dev/null
+++ b/org.fortiss.af3.platform.raspberry.ui/src/org/fortiss/af3/platform/raspberry/ui/handler/CameraInputHandler.java
@@ -0,0 +1,42 @@
+/*--------------------------------------------------------------------------+
+$Id: CameraInputHandler.java 23956 2018-04-11 14:15:25Z barner $
+|                                                                          |
+| Copyright 2011 ForTISS GmbH                     |
+|                                                                          |
+| Licensed under the Apache License, Version 2.0 (the "License");          |
+| you may not use this file except in compliance with the License.         |
+| You may obtain a copy of the License at                                  |
+|                                                                          |
+|    http://www.apache.org/licenses/LICENSE-2.0                            |
+|                                                                          |
+| Unless required by applicable law or agreed to in writing, software      |
+| distributed under the License is distributed on an "AS IS" BASIS,        |
+| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+| See the License for the specific language governing permissions and      |
+| limitations under the License.                                           |
++--------------------------------------------------------------------------*/
+package org.fortiss.af3.platform.raspberry.ui.handler;
+
+import static org.fortiss.af3.platform.raspberry.ui.AF3PlatformRaspberryUIActivator.getImageDescriptor;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.fortiss.af3.platform.raspberry.model.RaspberryPiReceiverBase;
+import org.fortiss.af3.platform.raspberry.model.camera.CameraDistanceLeft;
+import org.fortiss.tooling.kernel.ui.extension.base.NamedCommentedModelElementHandlerBase;
+
+/**
+ * Handler for {@link CameraDistanceLeft}s and other camera model elements.
+ * 
+ * @author hoelzl
+ * @author $Author: barner $
+ * @version $Rev: 23956 $
+ * @ConQAT.Rating GREEN Hash: D80CF94CBBADB4627AAEDA1F34ABF0A5
+ */
+public class CameraInputHandler extends
+		NamedCommentedModelElementHandlerBase<RaspberryPiReceiverBase> {
+	/** {@inheritDoc} */
+	@Override
+	public ImageDescriptor getIconImageDescriptor() {
+		return getImageDescriptor("icons/unknown.png");
+	}
+}
diff --git a/org.fortiss.af3.platform.raspberry.ui/src/org/fortiss/af3/platform/raspberry/ui/handler/MotorControlInputHandler.java b/org.fortiss.af3.platform.raspberry.ui/src/org/fortiss/af3/platform/raspberry/ui/handler/MotorControlInputHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..5b594be50d91ba2c4c1d3c7ba6c4cf311c389239
--- /dev/null
+++ b/org.fortiss.af3.platform.raspberry.ui/src/org/fortiss/af3/platform/raspberry/ui/handler/MotorControlInputHandler.java
@@ -0,0 +1,41 @@
+/*--------------------------------------------------------------------------+
+$Id: MotorControlInputHandler.java 23956 2018-04-11 14:15:25Z barner $
+|                                                                          |
+| Copyright 2011 ForTISS GmbH                     |
+|                                                                          |
+| Licensed under the Apache License, Version 2.0 (the "License");          |
+| you may not use this file except in compliance with the License.         |
+| You may obtain a copy of the License at                                  |
+|                                                                          |
+|    http://www.apache.org/licenses/LICENSE-2.0                            |
+|                                                                          |
+| Unless required by applicable law or agreed to in writing, software      |
+| distributed under the License is distributed on an "AS IS" BASIS,        |
+| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+| See the License for the specific language governing permissions and      |
+| limitations under the License.                                           |
++--------------------------------------------------------------------------*/
+package org.fortiss.af3.platform.raspberry.ui.handler;
+
+import static org.fortiss.af3.platform.raspberry.ui.AF3PlatformRaspberryUIActivator.getImageDescriptor;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.fortiss.af3.platform.raspberry.model.motorcontrol.MotorControlInput;
+import org.fortiss.tooling.kernel.ui.extension.base.NamedCommentedModelElementHandlerBase;
+
+/**
+ * Handler for {@link MotorControlInput}s.
+ * 
+ * @author hoelzl
+ * @author $Author: barner $
+ * @version $Rev: 23956 $
+ * @ConQAT.Rating GREEN Hash: D80CF94CBBADB4627AAEDA1F34ABF0A5
+ */
+public class MotorControlInputHandler extends
+		NamedCommentedModelElementHandlerBase<MotorControlInput> {
+	/** {@inheritDoc} */
+	@Override
+	public ImageDescriptor getIconImageDescriptor() {
+		return getImageDescriptor("icons/unknown.png");
+	}
+}
diff --git a/org.fortiss.af3.platform.raspberry.ui/src/org/fortiss/af3/platform/raspberry/ui/handler/MotorControlOutputHandler.java b/org.fortiss.af3.platform.raspberry.ui/src/org/fortiss/af3/platform/raspberry/ui/handler/MotorControlOutputHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..d5e36ceb79008e523ee096d8e38d3741daccc181
--- /dev/null
+++ b/org.fortiss.af3.platform.raspberry.ui/src/org/fortiss/af3/platform/raspberry/ui/handler/MotorControlOutputHandler.java
@@ -0,0 +1,41 @@
+/*--------------------------------------------------------------------------+
+$Id: MotorControlOutputHandler.java 23956 2018-04-11 14:15:25Z barner $
+|                                                                          |
+| Copyright 2011 ForTISS GmbH                     |
+|                                                                          |
+| Licensed under the Apache License, Version 2.0 (the "License");          |
+| you may not use this file except in compliance with the License.         |
+| You may obtain a copy of the License at                                  |
+|                                                                          |
+|    http://www.apache.org/licenses/LICENSE-2.0                            |
+|                                                                          |
+| Unless required by applicable law or agreed to in writing, software      |
+| distributed under the License is distributed on an "AS IS" BASIS,        |
+| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+| See the License for the specific language governing permissions and      |
+| limitations under the License.                                           |
++--------------------------------------------------------------------------*/
+package org.fortiss.af3.platform.raspberry.ui.handler;
+
+import static org.fortiss.af3.platform.raspberry.ui.AF3PlatformRaspberryUIActivator.getImageDescriptor;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.fortiss.af3.platform.raspberry.model.motorcontrol.MotorControlOutput;
+import org.fortiss.tooling.kernel.ui.extension.base.NamedCommentedModelElementHandlerBase;
+
+/**
+ * Handler for {@link MotorControlOutput}s.
+ * 
+ * @author hoelzl
+ * @author $Author: barner $
+ * @version $Rev: 23956 $
+ * @ConQAT.Rating GREEN Hash: D80CF94CBBADB4627AAEDA1F34ABF0A5
+ */
+public class MotorControlOutputHandler extends
+		NamedCommentedModelElementHandlerBase<MotorControlOutput> {
+	/** {@inheritDoc} */
+	@Override
+	public ImageDescriptor getIconImageDescriptor() {
+		return getImageDescriptor("icons/unknown.png");
+	}
+}
diff --git a/org.fortiss.af3.platform.raspberry/.gitignore b/org.fortiss.af3.platform.raspberry/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..237ede120c79c583d7292bd0e94832d2b863ce18
--- /dev/null
+++ b/org.fortiss.af3.platform.raspberry/.gitignore
@@ -0,0 +1,2 @@
+build/
+target/
diff --git a/org.fortiss.af3.platform.raspberry/src/org/fortiss/af3/platform/raspberry/generator/executable/MultiUnitMainGenerator.java b/org.fortiss.af3.platform.raspberry/src/org/fortiss/af3/platform/raspberry/generator/executable/MultiUnitMainGenerator.java
new file mode 100644
index 0000000000000000000000000000000000000000..c8df8b528c187f324db7d145178b2993d5225a94
--- /dev/null
+++ b/org.fortiss.af3.platform.raspberry/src/org/fortiss/af3/platform/raspberry/generator/executable/MultiUnitMainGenerator.java
@@ -0,0 +1,1060 @@
+/*--------------------------------------------------------------------------+
+$Id: MultiUnitMainGenerator.java 23075 2018-02-01 14:25:45Z ext.konstantin.tschernik $
+|                                                                          |
+| Copyright 2017 fortiss GmbH                     |
+|                                                                          |
+| Licensed under the Apache License, Version 2.0 (the "License");          |
+| you may not use this file except in compliance with the License.         |
+| You may obtain a copy of the License at                                  |
+|                                                                          |
+|    http://www.apache.org/licenses/LICENSE-2.0                            |
+|                                                                          |
+| Unless required by applicable law or agreed to in writing, software      |
+| distributed under the License is distributed on an "AS IS" BASIS,        |
+| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+| See the License for the specific language governing permissions and      |
+| limitations under the License.                                           |
++--------------------------------------------------------------------------*/
+package org.fortiss.af3.platform.raspberry.generator.executable;
+
+import static org.fortiss.af3.component.utils.ComponentArchitectureUtils.findSourcePort;
+import static org.fortiss.af3.component.utils.ComponentArchitectureUtils.isAtomicComponent;
+import static org.fortiss.af3.deployment.utils.DeploymentUtils.findDeployedExecutionUnits;
+import static org.fortiss.af3.platform.raspberry.generator.templates.RasPiCTemplates.getMultiUnitMainCFile;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.conqat.lib.commons.collections.Pair;
+import org.fortiss.af3.component.model.Channel;
+import org.fortiss.af3.component.model.Component;
+import org.fortiss.af3.component.model.InputPort;
+import org.fortiss.af3.component.model.OutputPort;
+import org.fortiss.af3.component.model.Port;
+import org.fortiss.af3.deployment.model.Deployment;
+import org.fortiss.af3.expression.model.definitions.Enumeration;
+import org.fortiss.af3.expression.model.definitions.TypeDefinition;
+import org.fortiss.af3.expression.model.types.TBool;
+import org.fortiss.af3.expression.model.types.TDefinedType;
+import org.fortiss.af3.expression.model.types.TDouble;
+import org.fortiss.af3.expression.model.types.TInt;
+import org.fortiss.af3.generator.common.model.source.AbstractUnit;
+import org.fortiss.af3.platform.model.ExecutionUnit;
+import org.fortiss.af3.platform.model.PlatformConnectorUnit;
+import org.fortiss.af3.platform.model.Receiver;
+import org.fortiss.af3.platform.model.Transmitter;
+import org.fortiss.af3.platform.raspberry.model.ActuatorPWM;
+import org.fortiss.af3.platform.raspberry.model.ConsoleOutput;
+import org.fortiss.af3.platform.raspberry.model.RaspberryPi;
+import org.fortiss.af3.platform.raspberry.model.brick.AccelerationXSensor;
+import org.fortiss.af3.platform.raspberry.model.brick.AccelerationYSensor;
+import org.fortiss.af3.platform.raspberry.model.brick.AccelerationZSensor;
+import org.fortiss.af3.platform.raspberry.model.brick.ActuatorDigits;
+import org.fortiss.af3.platform.raspberry.model.brick.AngularVelocityXSensor;
+import org.fortiss.af3.platform.raspberry.model.brick.AngularVelocityYSensor;
+import org.fortiss.af3.platform.raspberry.model.brick.AngularVelocityZSensor;
+import org.fortiss.af3.platform.raspberry.model.brick.LaserRangeSensor;
+import org.fortiss.af3.platform.raspberry.model.brick.UltraSonicSensor;
+import org.fortiss.af3.platform.raspberry.model.camera.CameraDetectionStateLeft;
+import org.fortiss.af3.platform.raspberry.model.camera.CameraDetectionStateRight;
+import org.fortiss.af3.platform.raspberry.model.camera.CameraDistanceLeft;
+import org.fortiss.af3.platform.raspberry.model.camera.CameraDistanceRight;
+import org.fortiss.af3.platform.raspberry.model.camera.CameraYawAngle;
+import org.fortiss.af3.platform.raspberry.model.gamepad.Button1;
+import org.fortiss.af3.platform.raspberry.model.gamepad.Button2;
+import org.fortiss.af3.platform.raspberry.model.gamepad.Button3;
+import org.fortiss.af3.platform.raspberry.model.gamepad.Button4;
+import org.fortiss.af3.platform.raspberry.model.gamepad.ButtonL2;
+import org.fortiss.af3.platform.raspberry.model.gamepad.ButtonR2;
+import org.fortiss.af3.platform.raspberry.model.gamepad.GamepadReceiverBase;
+import org.fortiss.af3.platform.raspberry.model.motorcontrol.MotorControlInput;
+import org.fortiss.af3.platform.raspberry.model.motorcontrol.MotorControlOutput;
+import org.fortiss.af3.platform.raspberry.model.rumblepad.ButtonA;
+import org.fortiss.af3.platform.raspberry.model.rumblepad.ButtonB;
+import org.fortiss.af3.platform.raspberry.model.rumblepad.ButtonHome;
+import org.fortiss.af3.platform.raspberry.model.rumblepad.ButtonSelect;
+import org.fortiss.af3.platform.raspberry.model.rumblepad.ButtonStart;
+import org.fortiss.af3.platform.raspberry.model.rumblepad.ButtonX;
+import org.fortiss.af3.platform.raspberry.model.rumblepad.ButtonY;
+import org.fortiss.af3.platform.raspberry.model.rumblepad.DPadDown;
+import org.fortiss.af3.platform.raspberry.model.rumblepad.DPadLeft;
+import org.fortiss.af3.platform.raspberry.model.rumblepad.DPadRight;
+import org.fortiss.af3.platform.raspberry.model.rumblepad.DPadUp;
+import org.fortiss.af3.platform.raspberry.model.rumblepad.L2_Position;
+import org.fortiss.af3.platform.raspberry.model.rumblepad.R2_Position;
+import org.fortiss.af3.platform.raspberry.model.rumblepad.RumbleMagnitudeStrong;
+import org.fortiss.af3.platform.raspberry.model.rumblepad.RumbleMagnitudeWeak;
+import org.fortiss.af3.platform.raspberry.model.rumblepad.RumblepadReceiverBase;
+import org.fortiss.af3.project.model.typesystem.IType;
+import org.fortiss.tooling.kernel.extension.data.ITransformationContext;
+import org.fortiss.tooling.kernel.model.INamedElement;
+
+/**
+ * Separate class for generating the main.c file for use with CAN-based multi-unit deployments.
+ * 
+ * @author hoelzl
+ * @author $Author: ext.konstantin.tschernik $
+ * @version $Rev: 23075 $
+ * @ConQAT.Rating RED Hash:
+ */
+class MultiUnitMainGenerator {
+	private static final int WAITING_SLEEP_IN_MICROS = 50;
+	private Deployment deployment;
+	private CanTransmissionCatalog catalog;
+	private RaspberryPi executionUnit;
+	private List<Component> atomicComponents;
+	private List<Pair<ExecutionUnit, Component>> deployedComponents;
+	private List<Pair<PlatformConnectorUnit, Port>> deployedPorts;
+	private HashMap<InputPort, Receiver> atomicInputToReceiver = new HashMap<>();
+	private HashMap<OutputPort, Transmitter> atomicOutputToTransmitter = new HashMap<>();
+	private ITransformationContext context;
+
+	private boolean useCamera;
+	private boolean useGamepad;
+	private boolean useRumblepad;
+	private boolean useDigits;
+	private boolean usePWM;
+	private boolean useUS;
+	private boolean useLaser;
+	private boolean useACC;
+	private boolean useConsole;
+	private boolean useRumble;
+	private OutputPort rumbleWeakPort = null;
+	private OutputPort rumbleStrongPort = null;
+	private boolean useVesc;
+	private int nrOfUsSensors = 0;
+
+	/** Constructor. */
+	public MultiUnitMainGenerator(Deployment deployment, CanTransmissionCatalog catalog,
+			RaspberryPi executionUnit, List<Component> atomicComponents,
+			List<Pair<ExecutionUnit, Component>> deployedComponents,
+			List<Pair<PlatformConnectorUnit, Port>> deployedPorts, ITransformationContext context) {
+		this.deployment = deployment;
+		this.catalog = catalog;
+		this.executionUnit = executionUnit;
+		this.deployedComponents = deployedComponents;
+		this.atomicComponents = atomicComponents;
+		this.deployedPorts = deployedPorts;
+		this.context = context;
+		for(Pair<PlatformConnectorUnit, Port> p : deployedPorts) {
+			PlatformConnectorUnit connector = p.getFirst();
+			analyzeDeployedPort(connector);
+			if(connector instanceof Receiver && p.getSecond() instanceof InputPort) {
+				InputPort deployedInput = (InputPort)p.getSecond();
+				for(Port atomicPort : findTargetPorts(deployedInput.getComponent(), deployedInput)) {
+					if(atomicPort instanceof InputPort) {
+						atomicInputToReceiver.put((InputPort)atomicPort, (Receiver)connector);
+					}
+				}
+			} else if(connector instanceof Transmitter && p.getSecond() instanceof OutputPort) {
+				OutputPort deployedOutput = (OutputPort)p.getSecond();
+				Port atomicPort = findSourcePort(deployedOutput.getComponent(), deployedOutput);
+				if(atomicPort instanceof OutputPort) {
+					atomicOutputToTransmitter.put((OutputPort)atomicPort, (Transmitter)connector);
+				}
+			}
+		}
+	}
+
+	/** Analyzes which {@link Receiver}s and {@link Transmitter}s are used. */
+	private void analyzeDeployedPort(PlatformConnectorUnit p) {
+		if(!useGamepad && p instanceof GamepadReceiverBase) {
+			useGamepad = true;
+		}
+		if(!useRumblepad &&
+				(p instanceof RumblepadReceiverBase || p instanceof RumbleMagnitudeWeak || p instanceof RumbleMagnitudeStrong)) {
+			useRumblepad = true;
+		}
+		if(!usePWM && p instanceof ActuatorPWM) {
+			usePWM = true;
+		}
+		if(!useUS && p instanceof UltraSonicSensor) {
+			useUS = true;
+		}
+		if(!useLaser && p instanceof LaserRangeSensor) {
+			useLaser = true;
+		}
+		if(!useDigits && p instanceof ActuatorDigits) {
+			useDigits = true;
+		}
+		if(!useACC &&
+				(p instanceof AccelerationXSensor || p instanceof AccelerationYSensor ||
+						p instanceof AccelerationZSensor || p instanceof AngularVelocityXSensor ||
+						p instanceof AngularVelocityYSensor || p instanceof AngularVelocityZSensor)) {
+			useACC = true;
+		}
+		if(!useConsole && p instanceof ConsoleOutput) {
+			useConsole = true;
+		}
+		if(!useVesc && (p instanceof MotorControlOutput || p instanceof MotorControlInput)) {
+			useVesc = true;
+		}
+		if(!useCamera &&
+				(p instanceof CameraYawAngle || p instanceof CameraDistanceLeft ||
+						p instanceof CameraDistanceRight || p instanceof CameraDetectionStateLeft) ||
+				p instanceof CameraDetectionStateRight) {
+			useCamera = true;
+		}
+	}
+
+	/** Creates the main.c file for deployments with a single execution units. */
+	public AbstractUnit createMultiUnitMain() {
+		String remoteUnitSetupCode = createRemoteUnitSetupCode();
+		String systemIncludes = createSystemIncludes();
+		String sensorVariables = createSensorVariables(deployedComponents, deployedPorts);
+		String systemInitCode = createInitCode(deployedComponents);
+		String remotePorts = createPortBuffers();
+		String componentSetup = createComponentStructureCode();
+		String componentImpl = createComponentImpl();
+
+		return getMultiUnitMainCFile(executionUnit.getName(), executionUnit.isCoordinatorUnit(),
+				executionUnit.getCanCoordinationID(), sensorVariables, systemInitCode,
+				remoteUnitSetupCode, systemIncludes, remotePorts, componentSetup, componentImpl,
+				executionUnit.getCycleTime(), WAITING_SLEEP_IN_MICROS);
+	}
+
+	/** Creates the C code for in-memory model of the AF3 component architecture. */
+	private String createComponentStructureCode() {
+		StringBuilder sb = new StringBuilder();
+		sb.append("// Instantiate components\n");
+		for(Component c : atomicComponents) {
+			createComponentInstantiationCode(sb, c);
+		}
+		instantiateOutputPorts(sb);
+		instantiateAndLinkInputPorts(sb);
+		return sb.toString();
+	}
+
+	/** Creates code for output ports. */
+	private void instantiateOutputPorts(StringBuilder sb) {
+		for(Component c : atomicComponents) {
+			String compId = identifierName(c);
+			sb.append("// Output ports of ").append(c.getName()).append("\n");
+			for(OutputPort outport : c.getOutputPorts()) {
+				if(atomicOutputToTransmitter.containsKey(outport)) {
+					// deployed to hardware
+					// FIXME: an atomic output might be needed remotely and on HW
+					continue;
+				}
+				String portVar = af3PortName(outport);
+				sb.append("\t").append(portVar).append(" = af3_port_create(");
+				sb.append(outport.getId()).append(", \"").append(outport.getName());
+				sb.append("\", ").append(getTypeName(outport)).append(", ");
+				boolean needsTransmission = catalog.isOutputNeededRemotely(outport);
+				sb.append(needsTransmission ? "true" : "false");
+				sb.append(");\n");
+				sb.append("\taf3_component_add_output(inst_").append(compId);
+				sb.append(", ").append(portVar).append(");\n");
+				sb.append("\tcopy_to_output_port_").append(getTypePostfix(outport)).append("(&")
+						.append(novalPortName(outport));
+				sb.append(", &").append(identifierName(outport));
+				sb.append(", ").append(portVar).append(");\n");
+			}
+		}
+	}
+
+	/** Returns the postfix for port copy functions of the given port. */
+	private Object getTypePostfix(Port port) {
+		IType type = port.getPortSpecification().getType();
+		if(type instanceof TInt) {
+			return "int";
+		}
+		if(type instanceof TDouble) {
+			return "double";
+		}
+		if(type instanceof TBool) {
+			return "boolean";
+		}
+		if(type instanceof TDefinedType) {
+			TypeDefinition tdef = ((TDefinedType)type).getDef();
+			if(tdef instanceof Enumeration) {
+				return "int";
+			}
+		}
+		return "unsupported_type";
+	}
+
+	/** Returns the type name of the given port. */
+	private Object getTypeName(Port port) {
+		IType type = port.getPortSpecification().getType();
+		if(type instanceof TInt) {
+			return "TYPE_INTEGER";
+		}
+		if(type instanceof TDouble) {
+			return "TYPE_DOUBLE";
+		}
+		if(type instanceof TBool) {
+			return "TYPE_BOOLEAN";
+		}
+		if(type instanceof TDefinedType) {
+			TypeDefinition tdef = ((TDefinedType)type).getDef();
+			if(tdef instanceof Enumeration) {
+				return "TYPE_INTEGER";
+			}
+		}
+		return "UNSUPPORTED_TYPE";
+	}
+
+	/** Creates remote input port proxies, input port buffers and links channels. */
+	private void instantiateAndLinkInputPorts(StringBuilder sb) {
+		// set for avoiding duplicate remote output port instantiation
+		HashSet<OutputPort> remoteOutputs = new HashSet<>();
+		for(Component c : atomicComponents) {
+			sb.append("// Local and remote input ports and channels of ").append(c.getName())
+					.append("\n");
+			for(InputPort inport : c.getInputPorts()) {
+				if(atomicInputToReceiver.containsKey(inport)) {
+					// HW allocation => no af3_port needed
+					continue;
+				}
+				String inPortName = af3PortName(inport);
+				sb.append("\t").append(inPortName).append(" = af3_port_create(");
+				sb.append(inport.getId()).append(", \"").append(inport.getName()).append("\", ");
+				sb.append(getTypeName(inport)).append(", false);\n");
+				sb.append("\taf3_component_add_input(inst_").append(identifierName(c));
+				sb.append(", ").append(inPortName).append(");\n");
+
+				OutputPort sourceOut = catalog.getSourcePort(inport);
+				// handle remote or local channel creation
+				if(catalog.hasRemoteSource(inport)) {
+					if(!remoteOutputs.contains(sourceOut)) {
+						createRemoteProxy(sb, sourceOut);
+						remoteOutputs.add(sourceOut);
+					}
+				}
+				if(sourceOut != null) {
+					createChannel(sb, sourceOut, inport);
+				}
+			}
+		}
+	}
+
+	/** Creates a channel between the given ports. */
+	private void createChannel(StringBuilder sb, OutputPort outport, InputPort inport) {
+		sb.append("\taf3_channel_create(").append(inport.getId());
+		sb.append(", \"ChannelTo").append(inport.getName()).append("\", ");
+		sb.append(af3PortName(outport)).append(", ");
+		sb.append(af3PortName(inport)).append(");\n");
+	}
+
+	/** Creates the remote input instantiation code. */
+	private void createRemoteProxy(StringBuilder sb, OutputPort remoteOut) {
+		// remote outputs instantiated only once
+		RaspberryPi owner = catalog.getRaspberryPi(remoteOut);
+		sb.append("\taf3_port_t* ").append(af3PortName(remoteOut));
+		sb.append(" = af3_component_remote_proxy_input_create(");
+		sb.append(catalog.getCanId(owner)).append(", ").append(remoteOut.getId());
+		sb.append(", \"").append(remoteOut.getName()).append("\", ");
+		sb.append(getTypeName(remoteOut)).append(");\n");
+	}
+
+	/** Creates code for instantiating components. */
+	private void createComponentInstantiationCode(StringBuilder sb, Component c) {
+		String compId = identifierName(c);
+		RaspberryPi owner = catalog.getRaspberryPi(c);
+		sb.append("// Instantiation of ").append(c.getName()).append("\n");
+		sb.append("\taf3_component_t* inst_").append(compId).append(" = af3_component_create(");
+		sb.append(catalog.getCanId(owner)).append(", \"").append(c.getName()).append("\", ");
+		sb.append(c.isStronglyCausal() ? "false" : "true");
+		sb.append(", impl_").append(compId).append(");\n");
+		sb.append("\taf3_component_system_add(inst_").append(compId).append(");\n");
+		sb.append("\tinit_").append(compId).append("();\n");
+	}
+
+	/** Creates static functions for each component. */
+	private String createComponentImpl() {
+		StringBuilder sb = new StringBuilder();
+		for(Component atomic : atomicComponents) {
+			createComponentImpl(atomic, sb);
+		}
+		return sb.toString();
+	}
+
+	/** Creates the component implementation code for the given component. */
+	private void createComponentImpl(Component atomic, StringBuilder sb) {
+		String compId = identifierName(atomic);
+		sb.append("// Implementation of ").append(atomic.getName()).append("\n");
+		sb.append("static void impl_").append(compId).append("(af3_component_t* comp) {\n");
+		if(useUS || useLaser || useACC) {
+			// Get Current time for the sensors
+			sb.append("uint64_t curr_time = time_util_get_current_micros();\n");
+		}
+		for(InputPort inport : atomic.getInputPorts()) {
+			if(atomicInputToReceiver.containsKey(inport)) {
+				// local hardware input ports
+				Receiver rec = atomicInputToReceiver.get(inport);
+				sb.append(createReadCode(rec, inport));
+			} else {
+				// local remote input ports
+				// FIXME: port type
+				sb.append("\tcopy_from_input_port_").append(getTypePostfix(inport));
+				sb.append("(").append(af3PortName(inport));
+				sb.append(", &").append(novalPortName(inport));
+				sb.append(", &").append(identifierName(inport)).append(");\n");
+			}
+		}
+		sb.append("\tperform_step_").append(compId).append("();\n");
+		useRumble = false;
+		for(OutputPort outport : atomic.getOutputPorts()) {
+			if(atomicOutputToTransmitter.containsKey(outport)) {
+				// local hardware output ports
+				Transmitter trn = atomicOutputToTransmitter.get(outport);
+				sb.append(createWriteCode(trn, outport));
+			} else {
+				// local or remote output port
+				// FIXME: port type
+				sb.append("\tcopy_to_output_port_int(&").append(novalPortName(outport));
+				sb.append(", &").append(identifierName(outport));
+				sb.append(", ").append(af3PortName(outport)).append(");\n");
+			}
+		}
+		if(useRumble) {
+			sb.append(createRumbleCode());
+		}
+		sb.append("}\n\n");
+	}
+
+	/** Setup code for remote units (used only in coordinator mode). */
+	private String createRemoteUnitSetupCode() {
+		if(!executionUnit.isCoordinatorUnit()) {
+			return "";
+		}
+		StringBuilder sb = new StringBuilder();
+		for(ExecutionUnit ecu : findDeployedExecutionUnits(deployment)) {
+			if(ecu != executionUnit && ecu instanceof RaspberryPi) {
+				RaspberryPi raspi = (RaspberryPi)ecu;
+				String idName = identifierName(raspi);
+				sb.append("protocol_can_remote_unit_config_t* ");
+				sb.append(idName).append(" = protocol_remote_unit_config_create(\"");
+				sb.append(idName).append("\", ");
+				sb.append(raspi.getCanCoordinationID()).append(");\n");
+				sb.append("list_util_append(remote_units, ");
+				sb.append(idName).append(");\n");
+
+			}
+		}
+		return sb.toString();
+	}
+
+	/** Creates declarations of remote unit buffers. */
+	private String createPortBuffers() {
+		StringBuilder sb = new StringBuilder();
+		for(Component atm : atomicComponents) {
+			for(InputPort inport : atm.getInputPorts()) {
+				if(!atomicInputToReceiver.containsKey(inport)) {
+					sb.append("static af3_port_t* " + af3PortName(inport) + " = NULL;\n");
+				}
+			}
+			for(OutputPort outport : atm.getOutputPorts()) {
+				if(!atomicOutputToTransmitter.containsKey(outport)) {
+					sb.append("static af3_port_t* " + af3PortName(outport) + " = NULL;\n");
+				}
+			}
+		}
+		return sb.toString();
+	}
+
+	/** Returns port name with 'af3_port_' prefix. */
+	private String af3PortName(Port port) {
+		return "af3_port_" + identifierName(port);
+	}
+
+	/** Returns port name with 'noval_' prefix. */
+	private String novalPortName(Port port) {
+		return "noval_" + identifierName(port);
+	}
+
+	/** Create #include for component architecture code. */
+	private String createSystemIncludes() {
+		StringBuilder sb = new StringBuilder();
+		for(Component atomic : atomicComponents) {
+			sb.append("#include \"" + identifierName(atomic) + ".h\"\n");
+		}
+		if(useCamera) {
+			sb.append("#include <camera_client.h>\n");
+		}
+		if(useGamepad) {
+			sb.append("#include <gamepad.h>\n");
+		}
+		if(useRumblepad) {
+			sb.append("#include <rumblepad.h>\n");
+		}
+		if(usePWM) {
+			sb.append("#include <temp_actuator.h>\n");
+		}
+		if(useUS || useLaser || useDigits || useACC) {
+			sb.append("#include <ip_connection.h>\n");
+		}
+		if(useUS) {
+			sb.append("#include <bricklet_distance_us.h>\n");
+		}
+		if(useLaser) {
+			sb.append("#include <bricklet_laser_range_finder.h>\n");
+		}
+		if(useACC) {
+			sb.append("#include <brick_imu_v2.h>\n");
+		}
+		if(useDigits) {
+			sb.append("#include <bricklet_segment_display_4x7.h>\n");
+		}
+		if(useVesc) {
+			sb.append("#include <commands.h>\n");
+			sb.append("#include <hal.h>\n");
+		}
+		sb.append("#include <stdio.h>\n");
+		return sb.toString();
+	}
+
+	/** Create the Sensor Variables and functions. */
+	private String createSensorVariables(List<Pair<ExecutionUnit, Component>> deployedComponents,
+			List<Pair<PlatformConnectorUnit, Port>> deployedPorts) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("float Q = 0.022;\n");
+		sb.append("float R = 0.917;\n");
+		sb.append("float estimates[9] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};\n");
+		sb.append("float last_p[9] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};\n");
+		sb.append("float kalman_filter(float value, int sensor) {\n");
+		sb.append("\tif(sensor < 0 || sensor > 8) return -1;\n");
+		sb.append("\tfloat temp_est = estimates[sensor];\n");
+		sb.append("\tfloat p_temp = last_p[sensor] + Q;\n");
+		sb.append("\tfloat K = p_temp * (1.0*(p_temp + R));\n");
+		sb.append("\tfloat est = temp_est + K * (value - temp_est);\n");
+		sb.append("\tfloat P = (1-K)*p_temp;\n");
+		sb.append("\tlast_p[sensor] = P;\n");
+		sb.append("\testimates[sensor] = est;\n");
+		sb.append("\treturn est;\n");
+		sb.append("}\n\n");
+		if(usePWM) {
+			sb.append("int pwm_engine_fd = -1;\n");
+			sb.append("int pwm_steering_fd = -1;\n");
+		}
+		if(useDigits) {
+			sb.append("SegmentDisplay4x7 segment_display;\n");
+			sb.append("static void set_led_display (SegmentDisplay4x7 *segment_display, uint16_t value, bool showHex) {\n");
+			sb.append("static const uint8_t digits[] = {0x3f,0x06,0x5b,0x4f,\n");
+			sb.append("                                 0x66,0x6d,0x7d,0x07,\n");
+			sb.append("                                 0x7f,0x6f,0x77,0x7c,\n");
+			sb.append("                                 0x39,0x5e,0x79,0x71};\n");
+			sb.append("if(showHex) {\n");
+			sb.append("uint8_t segments[4] = {digits[(value >> 12) & 0x0F], digits[(value >> 8) & 0x0F], digits[(value >> 4) & 0x0F], digits[value & 0x0F]};\n");
+			sb.append("segment_display_4x7_set_segments(segment_display, segments, 5, false);\n");
+			sb.append("} else {\n");
+			sb.append("uint8_t segments[4] = {digits[(value%10000)/1000], digits[(value%1000)/100], digits[(value%100)/10], digits[value%10]};\n");
+			sb.append("segment_display_4x7_set_segments(segment_display, segments, 5, false);\n");
+			sb.append("}\n");
+			sb.append("}\n\n");
+		}
+		if(useUS) {
+			sb.append("uint16_t ultra_sonic_A;\n");
+			sb.append("uint64_t us_A_last_cb_time = 0;\n");
+			sb.append("char* uid_us_A = \"zpW\";\n");
+			sb.append("void us_A_callback(uint16_t distance, void *data) {\n");
+			sb.append("ultra_sonic_A = kalman_filter(distance, 0);\n");
+			sb.append("us_A_last_cb_time = time_util_get_current_micros();\n");
+			sb.append("}\n");
+			sb.append("uint16_t ultra_sonic_B;\n");
+			sb.append("uint64_t us_B_last_cb_time = 0;\n");
+			sb.append("char* uid_us_B = \"zqN\";\n");
+			sb.append("void us_B_callback(uint16_t distance, void *data) {\n");
+			sb.append("us_B_last_cb_time = time_util_get_current_micros();\n");
+			sb.append("ultra_sonic_B = kalman_filter(distance, 1);\n");
+			sb.append("}\n\n");
+		}
+		if(useLaser) {
+			sb.append("int16_t laser_distance;\n");
+			sb.append("uint64_t laser_last_cb_time = 0;\n");
+			sb.append("void laser_callback(uint16_t distance, void *data) {\n");
+			sb.append("laser_distance = kalman_filter(distance, 2);\n");
+			sb.append("laser_last_cb_time = time_util_get_current_micros();\n");
+			sb.append("}\n\n");
+		}
+		if(useACC) {
+			sb.append("float acceleration_X, acceleration_Y, acceleration_Z;\n");
+			sb.append("uint64_t acc_last_cb_time = 0;\n");
+			sb.append("void acc_callback(int16_t x, int16_t y, int16_t z, void* data) {\n");
+			sb.append("if (abs(x - acceleration_X) > 2 * 10 * 100 ||\n");
+			sb.append("abs(y - acceleration_Y) > 2 * 10 * 100 ||\n");
+			sb.append("abs(z - acceleration_Z) > 2 * 10 * 100)\n");
+			sb.append("    return;\n");
+			sb.append("acceleration_X = kalman_filter(x, 3) / 100.0;\n");
+			sb.append("acceleration_Y = kalman_filter(y, 4) / 100.0;\n");
+			sb.append("acceleration_Z = kalman_filter(z, 5) / 100.0;\n");
+			sb.append("acc_last_cb_time = time_util_get_current_micros();\n");
+			sb.append("}\n\n");
+			sb.append("float angularVelocity_X, angularVelocity_Y, angularVelocity_Z;\n");
+			sb.append("uint64_t angV_last_cb_time = 0;\n");
+			sb.append("void angV_callback(int16_t x, int16_t y, int16_t z, void* data) {\n");
+			sb.append("angularVelocity_X = kalman_filter(x, 6) / 16;\n");
+			sb.append("angularVelocity_Y = kalman_filter(y, 7) / 16;\n");
+			sb.append("angularVelocity_Z = kalman_filter(z, 8) / 16;\n");
+			sb.append("angV_last_cb_time = time_util_get_current_micros();\n");
+			sb.append("}\n\n");
+		}
+		return sb.toString();
+	}
+
+	/** Returns the port and ID identifier. */
+	private String identifierName(INamedElement element) {
+		String name = element.getName() != null ? element.getName().replace(' ', '_') : "";
+		return name.replace(' ', '_') + "_ID_" + element.getId();
+	}
+
+	/** Creates the read code for the given receiver and port. */
+	private String createWriteCode(Transmitter transmitter, OutputPort outport) {
+		if(transmitter instanceof ActuatorPWM) {
+			ActuatorPWM act = (ActuatorPWM)transmitter;
+			String result = "if(!noval_" + identifierName(outport) + ") {\n";
+			if(act.getChannelID() == 0) {
+				result +=
+						"temp_actuator_device_set_target(pwm_engine_fd, 0, (uint16_t)" +
+								identifierName(outport) + ");\n";
+			} else {
+				result +=
+						"temp_actuator_device_set_target(pwm_steering_fd, 0, (uint16_t)" +
+								identifierName(outport) + ");\n";
+			}
+			result += "}\n";
+			return result;
+		}
+		if(transmitter instanceof ActuatorDigits) {
+			ActuatorDigits digits = (ActuatorDigits)transmitter;
+			String result = "if(!noval_" + identifierName(outport) + ") {\n";
+			result +=
+					"set_led_display(&segment_display, (uint16_t)" + identifierName(outport) +
+							", " + digits.isShowHexValue() + ");\n";
+			result += "}\n";
+			return result;
+		}
+		if(transmitter instanceof ConsoleOutput) {
+			String result = "\tif(!noval_" + identifierName(outport) + ") {\n";
+			result +=
+					"\t\tprintf(\"" + outport.getName() + " = %i\\n\", " + identifierName(outport) +
+							");\n";
+			result += "\t} else {\n";
+			result += "\t\tprintf(\"" + outport.getName() + " = NoVal\\n\");\n";
+			result += "\t}\n";
+			return result;
+		}
+		if(transmitter instanceof RumbleMagnitudeWeak) {
+			useRumble = true;
+			rumbleWeakPort = outport;
+		}
+		if(transmitter instanceof RumbleMagnitudeStrong) {
+			useRumble = true;
+			rumbleStrongPort = outport;
+		}
+		if(transmitter instanceof MotorControlOutput) {
+			String result = "if(!noval_" + identifierName(outport) + ") {\n";
+			result += "setVelocity(" + identifierName(outport) + ");\n";
+			result += "}\n";
+			return result;
+		}
+		return "";
+	}
+
+	/** Creates Code for Rumble */
+	private String createRumbleCode() {
+		StringBuilder sb = new StringBuilder();
+		sb.append("rumblepad_set_rumble(");
+		if(rumbleStrongPort != null) {
+			sb.append("!noval_" + identifierName(rumbleStrongPort) + " ? " +
+					identifierName(rumbleStrongPort) + " : 0, ");
+		} else {
+			sb.append("0, ");
+		}
+		if(rumbleWeakPort != null) {
+			sb.append("!noval_" + identifierName(rumbleWeakPort) + " ? " +
+					identifierName(rumbleWeakPort) + " : 0, ");
+		} else {
+			sb.append("0, ");
+		}
+		sb.append("cycle_time * 0.9, 0);\n");
+		return sb.toString();
+	}
+
+	/** Creates the read code for the given receiver and port. */
+	private String createReadCode(Receiver receiver, InputPort inport) {
+		if(receiver instanceof CameraDistanceLeft) {
+			StringBuilder sb = new StringBuilder();
+			sb.append("noval_" + identifierName(inport) + " = false;\n");
+			sb.append(identifierName(inport)).append(" = camera_client_get_distance_left();\n");
+			return sb.toString();
+		}
+		if(receiver instanceof CameraDistanceRight) {
+			StringBuilder sb = new StringBuilder();
+			sb.append("noval_" + identifierName(inport) + " = false;\n");
+			sb.append(identifierName(inport)).append(" = camera_client_get_distance_right();\n");
+			return sb.toString();
+		}
+		if(receiver instanceof CameraDetectionStateLeft) {
+			StringBuilder sb = new StringBuilder();
+			sb.append("noval_" + identifierName(inport) + " = false;\n");
+			sb.append(identifierName(inport)).append(
+					" = camera_client_get_detection_state_left();\n");
+			return sb.toString();
+		}
+		if(receiver instanceof CameraDetectionStateRight) {
+			StringBuilder sb = new StringBuilder();
+			sb.append("noval_" + identifierName(inport) + " = false;\n");
+			sb.append(identifierName(inport)).append(
+					" = camera_client_get_detection_state_right();\n");
+			return sb.toString();
+		}
+		if(receiver instanceof CameraYawAngle) {
+			StringBuilder sb = new StringBuilder();
+			sb.append("noval_" + identifierName(inport) + " = false;\n");
+			sb.append(identifierName(inport)).append(" = camera_client_get_yaw_angle();\n");
+			return sb.toString();
+		}
+		if(receiver instanceof UltraSonicSensor) {
+			StringBuilder sb = new StringBuilder();
+			UltraSonicSensor sensor = (UltraSonicSensor)receiver;
+			if(nrOfUsSensors == 0) {
+				sb.append("noval_" + identifierName(inport) + " = true;\n");
+				sb.append("if (!strcmp(\"" + sensor.getUniqueBrickletID() + "\", uid_us_A)) {\n");
+				sb.append("float diff_A = (curr_time - us_A_last_cb_time) / (1.0 * SECONDS_IN_MICROS);\n");
+				sb.append("if (diff_A < 1.0) {\n");
+				sb.append("noval_" + identifierName(inport) + " = false;\n");
+				sb.append(identifierName(inport) + " = ultra_sonic_A;\n}\n");
+				sb.append("}\n");
+			} else if(nrOfUsSensors == 1) {
+				sb.append("noval_" + identifierName(inport) + " = true;\n");
+				sb.append("if (!strcmp(\"" + sensor.getUniqueBrickletID() + "\", uid_us_B)){\n");
+				sb.append("float diff_B = (curr_time - us_B_last_cb_time) / (1.0 * SECONDS_IN_MICROS);\n");
+				sb.append("if (diff_B < 1.0) {\n");
+				sb.append("noval_" + identifierName(inport) + " = false;\n");
+				sb.append(identifierName(inport) + " = ultra_sonic_B;\n}\n");
+				sb.append("}\n");
+			}
+			nrOfUsSensors++;
+			return sb.toString();
+		}
+		if(receiver instanceof LaserRangeSensor) {
+			StringBuilder sb = new StringBuilder();
+			sb.append("float diff_laser = (curr_time - laser_last_cb_time) / (1.0 * SECONDS_IN_MICROS);\n");
+			sb.append("if (diff_laser < 1.0) {\n");
+			sb.append("noval_" + identifierName(inport) + " = false;\n");
+			sb.append(identifierName(inport) + " = laser_distance;\n");
+			sb.append("} else {\n");
+			sb.append("noval_" + identifierName(inport) + " = true;\n");
+			sb.append("}\n");
+			return sb.toString();
+		}
+		if(receiver instanceof AccelerationXSensor) {
+			StringBuilder sb = new StringBuilder();
+			sb.append("float diff_accX = (curr_time - acc_last_cb_time) / (1.0 * SECONDS_IN_MICROS);\n");
+			sb.append("if (diff_accX < 1.0) {\n");
+			sb.append("noval_" + identifierName(inport) + " = false;\n");
+			sb.append(identifierName(inport) + " = acceleration_X;\n");
+			sb.append("} else {\n");
+			sb.append("noval_" + identifierName(inport) + " = true;\n");
+			sb.append("}\n");
+			return sb.toString();
+		}
+		if(receiver instanceof AccelerationYSensor) {
+			StringBuilder sb = new StringBuilder();
+			sb.append("float diff_accY = (curr_time - acc_last_cb_time) / (1.0 * SECONDS_IN_MICROS);\n");
+			sb.append("if (diff_accY < 1.0) {\n");
+			sb.append("noval_" + identifierName(inport) + " = false;\n");
+			sb.append(identifierName(inport) + " = acceleration_Y;\n");
+			sb.append("} else {\n");
+			sb.append("noval_" + identifierName(inport) + " = true;\n");
+			sb.append("}\n");
+			return sb.toString();
+		}
+		if(receiver instanceof AccelerationZSensor) {
+			StringBuilder sb = new StringBuilder();
+			sb.append("float diff_accZ = (curr_time - acc_last_cb_time) / (1.0 * SECONDS_IN_MICROS);\n");
+			sb.append("if (diff_accZ < 1.0) {\n");
+			sb.append("noval_" + identifierName(inport) + " = false;\n");
+			sb.append(identifierName(inport) + " = acceleration_Z;\n");
+			sb.append("} else {\n");
+			sb.append("noval_" + identifierName(inport) + " = true;\n");
+			sb.append("}\n");
+			return sb.toString();
+		}
+		if(receiver instanceof AngularVelocityXSensor) {
+			StringBuilder sb = new StringBuilder();
+			sb.append("float diff_angV_X = (curr_time - angV_last_cb_time) / (1.0 * SECONDS_IN_MICROS);\n");
+			sb.append("if (diff_angV_X < 1.0) {\n");
+			sb.append("noval_" + identifierName(inport) + " = false;\n");
+			sb.append(identifierName(inport) + " = angularVelocity_X;\n");
+			sb.append("} else {\n");
+			sb.append("noval_" + identifierName(inport) + " = true;\n");
+			sb.append("}\n");
+			return sb.toString();
+		}
+		if(receiver instanceof AngularVelocityYSensor) {
+			StringBuilder sb = new StringBuilder();
+			sb.append("float diff_angV_Y = (curr_time - angV_last_cb_time) / (1.0 * SECONDS_IN_MICROS);\n");
+			sb.append("if (diff_angV_Y < 1.0) {\n");
+			sb.append("noval_" + identifierName(inport) + " = false;\n");
+			sb.append(identifierName(inport) + " = angularVelocity_Y;\n");
+			sb.append("} else {\n");
+			sb.append("noval_" + identifierName(inport) + " = true;\n");
+			sb.append("}\n");
+			return sb.toString();
+		}
+		if(receiver instanceof AngularVelocityZSensor) {
+			StringBuilder sb = new StringBuilder();
+			sb.append("float diff_angV_Z = (curr_time - angV_last_cb_time) / (1.0 * SECONDS_IN_MICROS);\n");
+			sb.append("if (diff_angV_Z < 1.0) {\n");
+			sb.append("noval_" + identifierName(inport) + " = false;\n");
+			sb.append(identifierName(inport) + " = angularVelocity_Z;\n");
+			sb.append("} else {\n");
+			sb.append("noval_" + identifierName(inport) + " = true;\n");
+			sb.append("}\n");
+			return sb.toString();
+		}
+		if(receiver instanceof MotorControlInput) {
+			StringBuilder sb = new StringBuilder();
+			sb.append("noval_" + identifierName(inport) + " = false;\n");
+			sb.append(identifierName(inport) + " = getVelocity();\n");
+			return sb.toString();
+		}
+		if(receiver instanceof Button1) {
+			return gamepadReadCode("gamepad_get_button_state", "GAMEPAD_BUTTON_1", inport);
+		}
+		if(receiver instanceof Button2) {
+			return gamepadReadCode("gamepad_get_button_state", "GAMEPAD_BUTTON_2", inport);
+		}
+		if(receiver instanceof Button3) {
+			return gamepadReadCode("gamepad_get_button_state", "GAMEPAD_BUTTON_3", inport);
+		}
+		if(receiver instanceof Button4) {
+			return gamepadReadCode("gamepad_get_button_state", "GAMEPAD_BUTTON_4", inport);
+		}
+		if(receiver instanceof org.fortiss.af3.platform.raspberry.model.gamepad.ButtonL1) {
+			return gamepadReadCode("gamepad_get_button_state", "GAMEPAD_BUTTON_L1", inport);
+		}
+		if(receiver instanceof ButtonL2) {
+			return gamepadReadCode("gamepad_get_button_state", "GAMEPAD_BUTTON_L2", inport);
+		}
+		if(receiver instanceof org.fortiss.af3.platform.raspberry.model.gamepad.ButtonR1) {
+			return gamepadReadCode("gamepad_get_button_state", "GAMEPAD_BUTTON_R1", inport);
+		}
+		if(receiver instanceof ButtonR2) {
+			return gamepadReadCode("gamepad_get_button_state", "GAMEPAD_BUTTON_R2", inport);
+		}
+		if(receiver instanceof org.fortiss.af3.platform.raspberry.model.gamepad.Right_StickX_Position) {
+			return gamepadReadCode("gamepad_get_axis_position",
+					"GAMEPAD_AXIS_RIGHT_STICK_HORIZONTAL", inport);
+		}
+		if(receiver instanceof org.fortiss.af3.platform.raspberry.model.gamepad.Right_StickY_Position) {
+			return gamepadReadCode("gamepad_get_axis_position",
+					"GAMEPAD_AXIS_RIGHT_STICK_VERTICAL", inport);
+		}
+		if(receiver instanceof org.fortiss.af3.platform.raspberry.model.gamepad.Left_StickX_Position) {
+			return gamepadReadCode("gamepad_get_axis_position",
+					"GAMEPAD_AXIS_LEFT_STICK_HORIZONTAL", inport);
+		}
+		if(receiver instanceof org.fortiss.af3.platform.raspberry.model.gamepad.Left_StickY_Position) {
+			return gamepadReadCode("gamepad_get_axis_position", "GAMEPAD_AXIS_LEFT_STICK_VERTICAL",
+					inport);
+		}
+		if(receiver instanceof ButtonA) {
+			return gamepadReadCode("rumblepad_get_button_state", "RUMBLEPAD_BUTTON_A", inport);
+		}
+		if(receiver instanceof ButtonB) {
+			return gamepadReadCode("rumblepad_get_button_state", "RUMBLEPAD_BUTTON_B", inport);
+		}
+		if(receiver instanceof ButtonX) {
+			return gamepadReadCode("rumblepad_get_button_state", "RUMBLEPAD_BUTTON_X", inport);
+		}
+		if(receiver instanceof ButtonY) {
+			return gamepadReadCode("rumblepad_get_button_state", "RUMBLEPAD_BUTTON_Y", inport);
+		}
+		if(receiver instanceof ButtonStart) {
+			return gamepadReadCode("rumblepad_get_button_state", "RUMBLEPAD_BUTTON_START", inport);
+		}
+		if(receiver instanceof ButtonSelect) {
+			return gamepadReadCode("rumblepad_get_button_state", "RUMBLEPAD_BUTTON_SELECT", inport);
+		}
+		if(receiver instanceof ButtonHome) {
+			return gamepadReadCode("rumblepad_get_button_state", "RUMBLEPAD_BUTTON_MODE", inport);
+		}
+		if(receiver instanceof DPadDown) {
+			return gamepadReadCode("rumblepad_get_button_state", "RUMBLEPAD_DPAD_DOWN", inport);
+		}
+		if(receiver instanceof DPadUp) {
+			return gamepadReadCode("rumblepad_get_button_state", "RUMBLEPAD_DPAD_UP", inport);
+		}
+		if(receiver instanceof DPadLeft) {
+			return gamepadReadCode("rumblepad_get_button_state", "RUMBLEPAD_DPAD_LEFT", inport);
+		}
+		if(receiver instanceof DPadRight) {
+			return gamepadReadCode("rumblepad_get_button_state", "RUMBLEPAD_DPAD_RIGHT", inport);
+		}
+		if(receiver instanceof org.fortiss.af3.platform.raspberry.model.rumblepad.ButtonL1) {
+			return gamepadReadCode("rumblepad_get_button_state", "RUMBLEPAD_BUTTON_L1", inport);
+		}
+		if(receiver instanceof org.fortiss.af3.platform.raspberry.model.rumblepad.ButtonR1) {
+			return gamepadReadCode("rumblepad_get_button_state", "RUMBLEPAD_BUTTON_R1", inport);
+		}
+		if(receiver instanceof org.fortiss.af3.platform.raspberry.model.rumblepad.ButtonL3) {
+			return gamepadReadCode("rumblepad_get_button_state", "RUMBLEPAD_BUTTON_L3", inport);
+		}
+		if(receiver instanceof org.fortiss.af3.platform.raspberry.model.rumblepad.ButtonR3) {
+			return gamepadReadCode("rumblepad_get_button_state", "RUMBLEPAD_BUTTON_R3", inport);
+		}
+		if(receiver instanceof L2_Position) {
+			return gamepadReadCode("rumblepad_get_axis_position", "RUMBLEPAD_AXIS_L2", inport);
+		}
+		if(receiver instanceof R2_Position) {
+			return gamepadReadCode("rumblepad_get_axis_position", "RUMBLEPAD_AXIS_R2", inport);
+		}
+		if(receiver instanceof org.fortiss.af3.platform.raspberry.model.rumblepad.Left_StickX_Position) {
+			return gamepadReadCode("rumblepad_get_axis_position",
+					"RUMBLEPAD_AXIS_LEFT_STICK_HORIZONTAL", inport);
+		}
+		if(receiver instanceof org.fortiss.af3.platform.raspberry.model.rumblepad.Left_StickY_Position) {
+			return gamepadReadCode("rumblepad_get_axis_position",
+					"RUMBLEPAD_AXIS_LEFT_STICK_VERTICAL", inport);
+		}
+		if(receiver instanceof org.fortiss.af3.platform.raspberry.model.rumblepad.Right_StickX_Position) {
+			return gamepadReadCode("rumblepad_get_axis_position",
+					"RUMBLEPAD_AXIS_RIGHT_STICK_HORIZONTAL", inport);
+		}
+		if(receiver instanceof org.fortiss.af3.platform.raspberry.model.rumblepad.Right_StickY_Position) {
+			return gamepadReadCode("rumblepad_get_axis_position",
+					"RUMBLEPAD_AXIS_RIGHT_STICK_VERTICAL", inport);
+		}
+		return "";
+	}
+
+	/** Creates read code for gamepad state. */
+	private String gamepadReadCode(String gamepadFun, String btnId, InputPort inport) {
+		String noval = "\tnoval_" + identifierName(inport) + " = false;\n";
+		String setBtn = "\t" + identifierName(inport) + " = " + gamepadFun + "(" + btnId + ");\n";
+		return noval + setBtn;
+	}
+
+	/** Create the initialize code. */
+	private String createInitCode(List<Pair<ExecutionUnit, Component>> deployedComponents) {
+		int createdUsSensors = 0;
+		StringBuilder sb = new StringBuilder();
+		if(useCamera) {
+			String addr = executionUnit.getCameraServerAddress();
+			String port = executionUnit.getCameraServerPort();
+			sb.append("camera_client_initialize(\"" + addr + "\", \"" + port + "\");\n");
+		}
+		if(useUS || useLaser || useDigits || useACC) {
+			sb.append("IPConnection brick_connection;\n");
+			sb.append("ipcon_create(&brick_connection);\n");
+			sb.append("if(ipcon_connect(&brick_connection, BRICK_HOST, BRICK_PORT) < 0) {\n");
+			sb.append("perror(\"Failed to connect to brick sub-system.\");\n");
+			sb.append("return 1;\n");
+			sb.append("}\n\n");
+		}
+		if(useUS) {
+			for(Pair<PlatformConnectorUnit, Port> p : deployedPorts) {
+				Receiver rec = atomicInputToReceiver.get(p.getSecond());
+				if(createdUsSensors >= 2)
+					break;
+				if(rec instanceof UltraSonicSensor) {
+					UltraSonicSensor sensor = (UltraSonicSensor)p.getFirst();
+					if(createdUsSensors == 0) {
+						sb.append("DistanceUS DistanceUS_A;\n");
+						sb.append("uid_us_A = \"" + sensor.getUniqueBrickletID() + "\";\n");
+						sb.append("distance_us_create(&DistanceUS_A, \"" +
+								sensor.getUniqueBrickletID() + "\", &brick_connection);\n");
+						sb.append("distance_us_register_callback(&DistanceUS_A, DISTANCE_US_CALLBACK_DISTANCE, (void*)us_A_callback, NULL);\n");
+						sb.append("distance_us_set_distance_callback_period(&DistanceUS_A, 10);\n");
+					} else {
+						sb.append("DistanceUS DistanceUS_B;\n");
+						sb.append("uid_us_B = \"" + sensor.getUniqueBrickletID() + "\";\n");
+						sb.append("distance_us_create(&DistanceUS_B, \"" +
+								sensor.getUniqueBrickletID() + "\", &brick_connection);\n");
+						sb.append("distance_us_register_callback(&DistanceUS_B, DISTANCE_US_CALLBACK_DISTANCE, (void*)us_B_callback, NULL);\n");
+						sb.append("distance_us_set_distance_callback_period(&DistanceUS_B, 10);\n");
+					}
+					createdUsSensors++;
+				}
+			}
+		}
+		if(useLaser) {
+			sb.append("LaserRangeFinder laser;\n");
+			sb.append("laser_range_finder_create(&laser, \"CQ9\", &brick_connection);\n");
+			sb.append("laser_range_finder_enable_laser(&laser);\n");
+			sb.append("laser_range_finder_register_callback(&laser, LASER_RANGE_FINDER_CALLBACK_DISTANCE, (void*)laser_callback, NULL);\n");
+			sb.append("laser_range_finder_set_distance_callback_period(&laser, 10);\n");
+		}
+		if(useDigits) {
+			sb.append("segment_display_4x7_create(&segment_display, \"wNK\", &brick_connection);\n");
+		}
+		if(useACC) {
+			sb.append("IMUV2 imu;\n");
+			sb.append("imu_v2_create(&imu, \"6xCD6s\", &brick_connection);\n");
+			sb.append("imu_v2_register_callback(&imu, IMU_V2_CALLBACK_LINEAR_ACCELERATION, (void*)acc_callback, NULL);\n");
+			sb.append("imu_v2_set_linear_acceleration_period(&imu, 5);\n");
+			sb.append("imu_v2_register_callback(&imu, IMU_V2_CALLBACK_ANGULAR_VELOCITY, (void*)angV_callback, NULL);\n");
+			sb.append("imu_v2_set_angular_velocity_period(&imu, 5);\n");
+		}
+		if(usePWM) {
+			sb.append("pwm_steering_fd = temp_actuator_initialize(\"/dev/ttyACM0\");\n");
+			sb.append("pwm_engine_fd = temp_actuator_initialize(\"/dev/ttyACM2\");\n\n");
+		}
+		if(useGamepad) {
+			sb.append("gamepad_configuration_t* gamepad_config = malloc(sizeof(gamepad_configuration_t));\n");
+			sb.append("gamepad_config->device_id = \"/dev/input/js0\";\n");
+			sb.append("gamepad_config->waiting_sleep_in_micros = " + WAITING_SLEEP_IN_MICROS +
+					";\n");
+			sb.append("gamepad_config->axis_callback = NULL;\n");
+			sb.append("gamepad_config->button_callback = NULL;\n");
+			sb.append("gamepad_initialize(gamepad_config);\n\n");
+		}
+		if(useRumblepad) {
+			sb.append("while(access(\"/dev/input/js0\", F_OK) == -1) {\n");
+			sb.append("sleep(1);\n");
+			sb.append("printf(\"Could not access gamepad device. Trying again in 1s.\\n\");\n");
+			sb.append("}\n");
+			sb.append("rumblepad_configuration_t* rumblepad_config = malloc(sizeof(rumblepad_configuration_t));\n");
+			sb.append("rumblepad_config->device_id = \"/dev/input/js0\";\n");
+			sb.append("rumblepad_config->waiting_sleep_in_micros = " + WAITING_SLEEP_IN_MICROS +
+					";\n");
+			sb.append("rumblepad_config->axis_callback = NULL;\n");
+			sb.append("rumblepad_config->button_callback = NULL;\n");
+			sb.append("rumblepad_initialize(rumblepad_config);\n\n");
+		}
+		if(useVesc) {
+			sb.append("initUSB(\"/dev/vesc\", B115200);\n");
+		}
+		return sb.toString();
+	}
+
+	/** Creates the function call code. */
+	private String makeCall(String function, Component c) {
+		StringBuilder sb = new StringBuilder();
+		sb.append(function).append('_').append(c.getName());
+		sb.append("_ID_").append(c.getId()).append("();\n");
+		return sb.toString();
+	}
+
+	/** Find sub-structure of component port targets. */
+	public static List<Port> findTargetPorts(Component boundary, Port port) {
+		List<Port> targets = new ArrayList<Port>();
+		if(isAtomicComponent(port.getComponent())) {
+			targets.add(port);
+			return targets;
+		}
+		LinkedList<Port> explore = new LinkedList<Port>();
+		for(Channel ch : port.getOutgoingChannels()) {
+			explore.add(ch.getTarget());
+		}
+		while(!explore.isEmpty()) {
+			Port testee = explore.removeFirst();
+			if(testee.getComponent() == boundary || isAtomicComponent(testee.getComponent()) ||
+					testee.getOutgoing().isEmpty()) {
+				targets.add(testee);
+			} else {
+				for(Channel ch : testee.getOutgoingChannels()) {
+					explore.add(ch.getTarget());
+				}
+			}
+		}
+		return targets;
+	}
+}
diff --git a/org.fortiss.af3.platform.raspberry/src/org/fortiss/af3/platform/raspberry/generator/templates/MultiUnitMainFile.stg b/org.fortiss.af3.platform.raspberry/src/org/fortiss/af3/platform/raspberry/generator/templates/MultiUnitMainFile.stg
new file mode 100644
index 0000000000000000000000000000000000000000..d37961543e8d792adf624ad75570b45906943f75
--- /dev/null
+++ b/org.fortiss.af3.platform.raspberry/src/org/fortiss/af3/platform/raspberry/generator/templates/MultiUnitMainFile.stg
@@ -0,0 +1,117 @@
+group MainFile;
+
+MainFile(UNIT_NAME,
+	COORDINATOR_OR_WORKER,
+	CAN_ID_LOCAL_UNIT,
+	SYSTEM_INIT_CODE,
+	SENSOR_VARIABLES,
+	REMOTE_UNIT_SETUP_CODE,
+	SYSTEM_INCLUDES,
+	COMPONENT_PORTS,
+	COMPONENT_SETUP,
+	COMPONENT_IMPL,
+	CYCLE_TIME_IN_MILLIS,
+	THREAD_SLEEPTIME_IN_MICROS) ::= <<
+// due to current data dictionary declaration of GENTYPE_boolean
+// system include must be first
+/******************** BEGIN_@GENERATED ***********************/
+$SYSTEM_INCLUDES$
+/********************* END_@GENERATED ************************/
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <unistd.h>
+
+#include <af3.h>
+#include <af3_component.h>
+#include <af3_component_remote_proxy.h>
+#include <debugprint.h>
+#include <listutil.h>
+#include <protocol_control_center.h>
+#include <protocol_coordinator.h>
+#include <protocol_factory.h>
+#include <timeutil.h>
+
+#include "data.h"
+
+int global_debug_print_level = DEBUG_PRINT_LEVEL_NONE;
+const int cycle_time = $CYCLE_TIME_IN_MILLIS$;
+#define BRICK_HOST "localhost"
+#define BRICK_PORT 4223
+
+/******************** BEGIN_@GENERATED ***********************/
+$COMPONENT_PORTS$
+
+$SENSOR_VARIABLES$
+/********************* END_@GENERATED ************************/
+
+/******************** BEGIN_@GENERATED ***********************/
+$COMPONENT_IMPL$
+/********************* END_@GENERATED ************************/
+
+static void initialize_af3_subsystem() {
+	af3_module_initialize("$UNIT_NAME$", 0, NULL, NULL);
+	af3_component_module_initialize($THREAD_SLEEPTIME_IN_MICROS$, default_send_remote_impl);
+	af3_component_remote_proxy_module_initialize();
+	can_thread_add_reception_listener(default_recv_remote_impl);
+
+/******************** BEGIN_@GENERATED ***********************/
+$COMPONENT_SETUP$
+/********************* END_@GENERATED ************************/
+}
+
+static void worker() {
+	af3_component_run_system();
+	af3_component_remote_proxy_clear_inputs();
+}
+
+int main(int argc, char** argv) {
+	if(argc >= 2) {
+		int dbg = atoi(argv[1]);
+		if(dbg < 0) {
+			dbg = 0;
+		} else if (dbg > 3) {
+			dbg = 3;
+		}
+		global_debug_print_level = dbg;
+	}
+	if (!can_thread_create("can0")) {
+		return -1;
+	}
+	can_thread_set_thread_sleep_in_micros($THREAD_SLEEPTIME_IN_MICROS$);
+
+/******************** BEGIN_@GENERATED ***********************/
+	$SYSTEM_INIT_CODE$
+/********************* END_@GENERATED ************************/
+
+	initialize_af3_subsystem();
+
+	protocol_can_config_t* can_config = protocol_can_config_create(0x01, 0x00, $CAN_ID_LOCAL_UNIT$);
+
+	protocol_worker_clock_config_t* clock_config =
+			protocol_worker_clock_config_create(cycle_time, 0, $THREAD_SLEEPTIME_IN_MICROS$);
+
+	protocol_worker_computation_config_t* computation_config =
+			protocol_worker_computation_config_create("$UNIT_NAME$", worker, 0);
+
+#if $COORDINATOR_OR_WORKER$ == 1
+	// create list of remote units
+	list_t* remote_units = list_util_create_list();
+/******************** BEGIN_@GENERATED ***********************/
+	$REMOTE_UNIT_SETUP_CODE$
+/********************* END_@GENERATED ************************/
+	// run the unit coordinator protocol mode
+	int result = protocol_coordinator_main(can_config, clock_config, computation_config, remote_units);
+#else
+	// run the unit in worker protocol mode
+	int result = protocol_worker_main(can_config, clock_config, computation_config);
+#endif
+	can_thread_terminate();
+
+	free(can_config);
+	free(clock_config);
+	free(computation_config);
+
+	return result;
+}
+>>