diff --git a/Description.pdf b/Description.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..c94d222095610b0947b32ef7e46102710878f6cd
Binary files /dev/null and b/Description.pdf differ
diff --git a/MBotBoardBalancer-1.0-SNAPSHOT.jar b/MBotBoardBalancer-1.0-SNAPSHOT.jar
new file mode 100644
index 0000000000000000000000000000000000000000..852e004a556788e31a1a6183f230cff2e134ee8e
Binary files /dev/null and b/MBotBoardBalancer-1.0-SNAPSHOT.jar differ
diff --git a/MbotFuzzyBalance_Tile.png b/MbotFuzzyBalance_Tile.png
new file mode 100644
index 0000000000000000000000000000000000000000..fa5e7e8d4a33a6cdba869bc6f86d893c2df840a0
Binary files /dev/null and b/MbotFuzzyBalance_Tile.png differ
diff --git a/MbotFuzzyBalancer_1.png b/MbotFuzzyBalancer_1.png
new file mode 100644
index 0000000000000000000000000000000000000000..84569d513496b1e7bbd02219314484ae121e9fef
Binary files /dev/null and b/MbotFuzzyBalancer_1.png differ
diff --git a/MbotFuzzyLogicBalancing.MP4 b/MbotFuzzyLogicBalancing.MP4
new file mode 100644
index 0000000000000000000000000000000000000000..524b3fce1e6eaadc8df7d29b03ef2aaef104af6b
Binary files /dev/null and b/MbotFuzzyLogicBalancing.MP4 differ
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..488a865328443f664faa3835f62049f8c0c47537
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,16 @@
+FOLDER CONTAINS:
+
+- Source Code with the pom.xml for Maven
+
+Note: The fuzzy logic library must be manually added to the Maven
+repository before building the project via Maven. See the library's folder.
+
+- Fuzzy Logic .fcl file for balancing the car 
+
+- Client .jar file to control the Mbot car
+	
+HOW TO USE THE CLIENT:
+
+- Run the .jar
+- Provide the full path to the .fcl , i.e."C:Path\To\The\myFuzzySets.fcl"
+- Provide the bluetooth port for the communication, i.e "COM3" 
\ No newline at end of file
diff --git a/balance.fcl b/balance.fcl
new file mode 100644
index 0000000000000000000000000000000000000000..e9025989b43194d5aa616ff57d4b0136eaa873e4
--- /dev/null
+++ b/balance.fcl
@@ -0,0 +1,42 @@
+FUNCTION_BLOCK balance	// Block definition
+
+VAR_INPUT				// Define input variables
+	angle : REAL;
+END_VAR
+
+VAR_OUTPUT				// Define output variable
+	direction : REAL;
+END_VAR
+
+FUZZIFY angle			// Fuzzify input variable
+	TERM rightStrong := (-50,1) (-5, 1) (-4, 0);
+	TERM rightLight  := (-5,0) (-3, 1) (-2, 0);
+	TERM balanced 	 := (-2, 0) (0, 1)  (2, 0);
+	TERM leftLight   := (2,0) (3, 1) (5, 0);
+	TERM leftStrong  := (4, 0) (5, 1)  (50, 1);
+END_FUZZIFY
+
+DEFUZZIFY direction			// Defzzzify output variable
+	TERM driveBackFast    := (-110, 1)(-70, 1) (-60, 0);
+	TERM driveBackSlow    := (-60, 0)(-50, 1) (-40, 1) (-30, 0);
+	TERM standStill       := (-30, 0)(0, 1) (30, 0);
+	TERM driveForwardSlow := (30, 0)(40, 1) (50, 1) (60, 0);
+	TERM driveForwardFast := (60, 0)(70, 1) (110, 1);
+	METHOD : COG;		// Use 'Center Of Gravity' defuzzification method
+	DEFAULT := 0;		// Default value is 0 (if no rule activates defuzzifier)
+END_DEFUZZIFY
+
+RULEBLOCK No1
+	AND : MIN;			// Use 'min' for 'and' (also implicit use 'max' for 'or' to fulfill DeMorgan's Law)
+	ACT : MIN;			// Use 'min' activation method
+	ACCU : MAX;			// Use 'max' accumulation method
+
+	RULE 1 : IF angle IS leftStrong  THEN direction IS driveForwardFast;
+	RULE 2 : IF angle IS leftLight   THEN direction IS driveForwardSlow;
+	RULE 3 : IF angle IS balanced    THEN direction IS standStill;
+	RULE 4 : IF angle IS rightLight  THEN direction IS driveBackSlow;
+	RULE 5 : IF angle IS rightStrong THEN direction IS driveBackFast;
+
+END_RULEBLOCK
+
+END_FUNCTION_BLOCK
diff --git a/jFuzzyLogicLibrary/jFuzzyLogic-1.0.jar b/jFuzzyLogicLibrary/jFuzzyLogic-1.0.jar
new file mode 100644
index 0000000000000000000000000000000000000000..14c404d4c9f973f4b3c55fa1f5168ec5f3c7a228
Binary files /dev/null and b/jFuzzyLogicLibrary/jFuzzyLogic-1.0.jar differ
diff --git a/jFuzzyLogicLibrary/jFuzzyLogicInstallGuide.txt b/jFuzzyLogicLibrary/jFuzzyLogicInstallGuide.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3ce83bece086a046cc088e0c303b65d10f64c3e1
--- /dev/null
+++ b/jFuzzyLogicLibrary/jFuzzyLogicInstallGuide.txt
@@ -0,0 +1,21 @@
+Since the libary doesn't exist in the public maven repository, it has to be manually added to the project. 
+
+- Run the line below to save the jar in the local repo.
+- Note that I have given it a version number, since the library's authors don't provide anything like it.
+
+mvn install:install-file -Dfile=jFuzzyLogic-1.0.jar -DgroupId=net.sourceforge -DartifactId=jFuzzyLogic -Dversion=1.0 -Dpackaging=jar
+
+And then I can load it in the pom with :
+
+<dependencies>
+  <dependency>
+    <groupId>net.sourceforge</groupId>
+    <artifactId>jFuzzyLogic</artifactId>
+    <version>1.0</version>
+  </dependency>
+</dependencies>
+
+--------------------------------------------------------------------------------
+
+Note: I used the following guide: 
+https://softwarecave.org/2014/06/14/adding-external-jars-into-maven-project/.
diff --git a/logging.properties b/logging.properties
new file mode 100644
index 0000000000000000000000000000000000000000..c009e977afac214293c2bc3373c9612e9998d22e
--- /dev/null
+++ b/logging.properties
@@ -0,0 +1,6 @@
+handlers = java.util.logging.ConsoleHandler
+.level= INFO
+java.util.logging.ConsoleHandler.level = INFO
+java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
+# Pattern works since Java 7
+java.util.logging.SimpleFormatter.format = [%1$tc] %4$s: %2$s - %5$s %6$s%n
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2cc22464ccf182fedbb87721f71f5df2792c49cf
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.omilab.omirob</groupId>
+    <artifactId>MBotBoardBalancer</artifactId>
+    <version>1.0-SNAPSHOT</version>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.11</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.sparetimelabs</groupId>
+            <artifactId>purejavacomm</artifactId>
+            <version>1.0.1</version>
+        </dependency>
+        <dependency>
+            <groupId>net.java.dev.jna</groupId>
+            <artifactId>jna</artifactId>
+            <version>4.2.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>1.7.21</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-jdk14</artifactId>
+            <version>1.7.21</version>
+        </dependency>
+
+        <!-- Fuzzy Logic -->
+        <dependency>
+            <groupId>net.sourceforge</groupId>
+            <artifactId>jFuzzyLogic</artifactId>
+            <version>1.0</version>
+        </dependency>
+
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>java</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <mainClass>org.omilab.omirob.mbot.balancer.App</mainClass>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <version>3.0.0</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <transformers>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                    <mainClass>org.omilab.omirob.mbot.balancer.App</mainClass>
+                                </transformer>
+                            </transformers>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+        </plugins>
+    </build>
+    <repositories>
+        <repository>
+            <id>sparetimelabs</id>
+            <name>sparetimelabs</name>
+            <url>http://www.sparetimelabs.com/maven2</url>
+        </repository>
+    </repositories>
+</project>
\ No newline at end of file
diff --git a/presentation.pdf b/presentation.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..27ad448af06bebc40aea4baf2280d8843131cec7
Binary files /dev/null and b/presentation.pdf differ
diff --git a/src/main/java/org/omilab/omirob/mbot/balancer/App.java b/src/main/java/org/omilab/omirob/mbot/balancer/App.java
new file mode 100644
index 0000000000000000000000000000000000000000..e420ede385d275424ffd29df0d074ce8e10656e9
--- /dev/null
+++ b/src/main/java/org/omilab/omirob/mbot/balancer/App.java
@@ -0,0 +1,29 @@
+package org.omilab.omirob.mbot.balancer;
+
+import javafx.application.Application;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+
+import java.io.IOException;
+
+/**
+ * Created by Mangat on 10.02.2017.
+ */
+public class App extends Application {
+
+    public static void main(String[] args) {
+        launch(args);
+    }
+
+    @Override
+    public void start(Stage primaryStage) throws IOException {
+        Parent root = FXMLLoader.load(getClass().getResource("/mbotController.fxml"));
+        primaryStage.setTitle("Mbot - Board Balancer");
+        Scene scene = new Scene(root);
+        //scene.getStylesheets().add(getClass().getResource("/fxml/app.css").toExternalForm());
+        primaryStage.setScene(scene);
+        primaryStage.show();
+    }
+}
diff --git a/src/main/java/org/omilab/omirob/mbot/balancer/MbotController.java b/src/main/java/org/omilab/omirob/mbot/balancer/MbotController.java
new file mode 100644
index 0000000000000000000000000000000000000000..b0b9823eb168e177f52e333662457f10da787983
--- /dev/null
+++ b/src/main/java/org/omilab/omirob/mbot/balancer/MbotController.java
@@ -0,0 +1,157 @@
+package org.omilab.omirob.mbot.balancer;
+
+import javafx.application.Platform;
+import javafx.concurrent.Task;
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.scene.control.Button;
+import javafx.scene.control.TextArea;
+import javafx.scene.control.TextField;
+import net.sourceforge.jFuzzyLogic.FIS;
+import org.omilab.omirob.mbot.client.IMbotEvent;
+import org.omilab.omirob.mbot.client.MbotClient;
+import purejavacomm.CommPortIdentifier;
+
+import java.io.FileInputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.ResourceBundle;
+
+
+/**
+ * Created by Mangat on 10.02.2017.
+ */
+public class MbotController implements Initializable, IMbotEvent {
+
+    @FXML
+    Button btnStart;
+
+    @FXML
+    Button btnStop;
+
+    @FXML
+    TextField txtFCLFile;
+
+    @FXML
+    TextField txtComPort;
+
+
+    @FXML
+    TextArea txtStatus;
+
+    private MbotClient mc;
+
+    private boolean stop;
+
+    @Override
+    public void initialize(URL location, ResourceBundle resources) {
+
+        printCommPorts();
+
+    }
+
+
+    @FXML
+    public void startBot(ActionEvent event) {
+
+        try {
+
+            stop = false;
+            String comPortName  =  txtComPort.getText();
+            String fuzzyFCLFile =  txtFCLFile.getText();
+
+            if(comPortName == null || fuzzyFCLFile == null || comPortName.isEmpty() || fuzzyFCLFile.isEmpty())
+            {
+                txtStatus.appendText("Please provide all necessary inputs! \n");
+                return;
+            }
+
+
+            //MBot Initialization
+            mc = new MbotClient(CommPortIdentifier.getPortIdentifier(comPortName));
+            mc.addListener(this);
+            mc.reset();
+
+            // Fuzzy Logic
+            FileInputStream fileInputStream = new FileInputStream(fuzzyFCLFile);
+            FIS fis = FIS.load(fileInputStream,true);
+
+            btnStart.setDisable(true);
+
+            Thread th = new Thread(new Task<Void>() {
+                @Override
+                protected Void call() throws Exception {
+
+                    while(true)
+                    {
+                        if(stop){
+
+                            int speed = 0;
+                            mc.motorLeft(speed);
+                            mc.motorRight(speed);
+                            mc.close();
+                            Platform.runLater(new Runnable() {
+                                @Override
+                                public void run() {
+                                   btnStart.setDisable(false);
+                                }
+                            });
+                            return null;
+                        }
+
+                        float y=mc.readGyro(2);
+                        // Set inputs
+                        System.out.println("y: " + y);
+                        fis.setVariable("angle",y);
+                        // Evaluate
+                        fis.evaluate();
+                        int speed = (int)fis.getVariable("direction").getValue();
+                        mc.motorLeft(speed);
+                        mc.motorRight(speed);
+                        System.out.println("Direction:" + speed);
+                    }
+
+                }
+            });
+            th.setDaemon(true);
+            th.start();
+
+        } catch(Exception e)
+        {
+            Platform.runLater(new Runnable() {
+                @Override
+                public void run() {
+                    txtStatus.appendText(e.getMessage());
+                    btnStart.setDisable(false);
+                }
+            });
+            e.printStackTrace();
+        }
+        return;
+
+    }
+
+    @FXML
+    public void stopBot(ActionEvent event)
+    {
+        stop = true;
+    }
+
+    private void printCommPorts(){
+        Enumeration<CommPortIdentifier> identifiers = CommPortIdentifier.getPortIdentifiers();
+        txtStatus.appendText("Detected com ports:\n");
+        while(identifiers.hasMoreElements()){
+            CommPortIdentifier id = (CommPortIdentifier) identifiers.nextElement();
+            txtStatus.appendText(String.format("Name: \"%s\", Type: %s, Owner: %s\n",id.getName(), id.getPortType(), id.isCurrentlyOwned()?id.getCurrentOwner():"-"));
+        }
+
+    }
+
+    @Override
+    public void onButton(boolean pressed) {
+        System.out.println("button" +pressed);
+    }
+}
+
+
diff --git a/src/main/java/org/omilab/omirob/mbot/client/DeviceType.java b/src/main/java/org/omilab/omirob/mbot/client/DeviceType.java
new file mode 100644
index 0000000000000000000000000000000000000000..afa615467d122ade96ccdb8e04124ca53d8b1be3
--- /dev/null
+++ b/src/main/java/org/omilab/omirob/mbot/client/DeviceType.java
@@ -0,0 +1,64 @@
+package org.omilab.omirob.mbot.client;
+
+/**
+ * Device IDs from mBot firmware files(mbot_firmware.ino, orion_firmware.ino)
+ *
+ * @author    Martin Kunz <martin.michael.kunz@gmail.com>
+ */
+public enum DeviceType {
+    NONE((byte)0),
+    ULTRASONIC_SENSOR((byte)1),
+    TEMPERATURE_SENSOR((byte)2),
+    LIGHT_SENSOR((byte)3),
+    POTENTIONMETER((byte)4),
+    JOYSTICK((byte)5),
+    GYRO((byte)6),
+    SOUND_SENSOR((byte)7),
+    RGBLED((byte)8),
+    SEVSEG((byte)9),
+    MOTOR((byte)10),
+    SERVO((byte)11),
+    ENCODER((byte)12),
+    IR((byte)13),
+    IRREMOTE((byte)14),
+    PIRMOTION((byte)15),
+    INFRARED((byte)16),
+    LINEFOLLOWER((byte)17),
+    IRREMOTECODE((byte)18),
+    SHUTTER((byte)20),
+    LIMITSWITCH((byte)21),
+    BUTTON((byte)22),
+    HUMITURE((byte)23),
+    FLAMESENSOR((byte)24),
+    GASSENSOR((byte)25),
+    COMPASS((byte)26),
+    DIGITAL((byte)30),
+    ANALOG((byte)31),
+    PWM((byte)32),
+    SERVO_PIN((byte)33),
+    TONE((byte)34),
+    BUTTON_INNER((byte)35),
+    ULTRASONIC_ARDUINO((byte)36),
+    PULSEIN((byte)37),
+    STEPPER((byte)40),
+    LEDMATRIX((byte)41),
+    TIMER((byte)50),
+    TOUCH_SENSOR((byte)51);
+
+    private final byte id;
+
+    private DeviceType(final byte id) {
+        this.id = id;
+    }
+
+    public byte getId() {
+        return id;
+    }
+
+    public static DeviceType getById(int id) {
+        for(DeviceType e : values()) {
+            if(e.id==id) return e;
+        }
+        return null;
+    }
+}
diff --git a/src/main/java/org/omilab/omirob/mbot/client/IMbotEvent.java b/src/main/java/org/omilab/omirob/mbot/client/IMbotEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..302227cbfc0d177e19a15c10047e19a383ec0812
--- /dev/null
+++ b/src/main/java/org/omilab/omirob/mbot/client/IMbotEvent.java
@@ -0,0 +1,10 @@
+package org.omilab.omirob.mbot.client;
+
+/**
+ * Event Interface for mBot onboard button
+ *
+ * @author    Martin Kunz <martin.michael.kunz@gmail.com>
+ */
+public interface IMbotEvent {
+    void onButton(boolean pressed);
+}
diff --git a/src/main/java/org/omilab/omirob/mbot/client/MbotClient.java b/src/main/java/org/omilab/omirob/mbot/client/MbotClient.java
new file mode 100644
index 0000000000000000000000000000000000000000..4fdbdc417e8a3afb87fc95a83f1a6e2e1d5d59be
--- /dev/null
+++ b/src/main/java/org/omilab/omirob/mbot/client/MbotClient.java
@@ -0,0 +1,423 @@
+package org.omilab.omirob.mbot.client;
+
+//import com.sun.istack.internal.NotNull;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import purejavacomm.*;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.*;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Client library for original mBot firmware
+ * Make sure matching mBlock firmware is running on your device (mbot_firmwae.ino, orion_firmware.ino, ...)
+ *
+ * @author    Martin Kunz <martin.michael.kunz@gmail.com>
+ */
+public class MbotClient implements Runnable, AutoCloseable {
+    private static final Logger log = LoggerFactory.getLogger(MbotClient.class);
+
+    private static final byte TYPE_READ = 0x01;
+    private static final byte TYPE_WRITE = 0x02;
+    private static final byte TYPE_RESET = 0x04;
+    private static final byte TYPE_START = 0x05;
+    private static final byte MB_MCU_DATATYPE_ZERO = 0x00;
+    private static final byte MB_MCU_DATATYPE_BYTE = 0x01;
+    private static final byte MB_MCU_DATATYPE_FLOAT = 0x02;
+    private static final byte MB_MCU_DATATYPE_SHORT = 0x03;
+    private static final byte MB_MCU_DATATYPE_STRING = 0x04; //length(byte)+string
+    private static final byte MB_MCU_DATATYPE_DOUBLE = 0x05;
+
+    public final static int PORT_LIGHTSENSOR_INTERNAL = 6;
+    public final static int RGB_INTERNAL_IDX = 2;
+    public final static int LEDMATRIX_ACTION_STRING = 1;
+    public final static int LEDMATRIX_ACTION_BITMAP = 2;
+    public final static int LEDMATRIX_ACTION_CLOCK = 3;
+    public final static byte REMOTE_LEFT = 0x07;
+    public final static byte REMOTE_UP = 0x40;
+    public final static byte REMOTE_RIGHT = 0x09;
+    public final static byte REMOTE_DOWN = 0x19;
+    public final static byte REMOTE_A = 0x45;
+    public final static byte REMOTE_B = 0x46;
+    public final static byte REMOTE_C = 0x47;
+    public final static byte REMOTE_D = 0x44;
+    public final static byte REMOTE_E = 0x43;
+    public final static byte REMOTE_F = 0x0D;
+    public final static byte REMOTE_OK = 0x15;
+    public final static byte REMOTE_0 = 0x16;
+    public final static byte REMOTE_1 = 0x0C;
+    public final static byte REMOTE_2 = 0x18;
+    public final static byte REMOTE_3 = 0x5E;
+    public final static byte REMOTE_4 = 0x08;
+    public final static byte REMOTE_5 = 0x1C;
+    public final static byte REMOTE_6 = 0x5A;
+    public final static byte REMOTE_7 = 0x42;
+    public final static byte REMOTE_8 = 0x52;
+    public final static byte REMOTE_9 = 0x4A;
+
+
+    private final InputStream portIS;
+    private final OutputStream portOS;
+    private SerialPort serialPort;
+    Set<LinkedBlockingQueue<Result>> subscribers = Collections.synchronizedSet(new HashSet(new LinkedBlockingQueue<>()));
+    Set<IMbotEvent> externalSubscribers = Collections.synchronizedSet(new HashSet<>());
+    private volatile boolean running = true;
+    private Thread thread;
+
+    // #define GET 1
+    // #define RUN 2
+    // #define RESET 4
+    // #define START 5
+
+    public MbotClient(CommPortIdentifier portid) throws PortInUseException, IOException, UnsupportedCommOperationException {
+        serialPort = (SerialPort) portid.open("asdf", 3500);
+        serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
+        serialPort.setSerialPortParams(115200, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
+                SerialPort.PARITY_NONE);
+        serialPort.disableReceiveFraming();
+        serialPort.disableReceiveThreshold();
+        serialPort.disableReceiveTimeout();
+        portIS = serialPort.getInputStream();
+        portOS = serialPort.getOutputStream();
+        thread = new Thread(this);
+        thread.setName("Serial port read thread");
+        thread.start();
+    }
+
+    @Override
+    public void close() {
+        running = false;
+        thread.interrupt();
+        serialPort.close();
+    }
+
+    public void addListener(IMbotEvent eventif) {
+        externalSubscribers.add(eventif);
+    }
+
+
+    public void reset() throws IOException {
+        waitForResult(TYPE_RESET, DeviceType.NONE, new byte[]{}, Void.class);
+    }
+
+    public float readUltraSonic(int port) throws IOException {
+        Result r = waitForResult(TYPE_READ, DeviceType.ULTRASONIC_SENSOR, new byte[]{(byte) port}, Float.class);
+        return (float) r.result;
+    }
+
+    public float readLineFollower(int port) throws IOException {
+        Result r = waitForResult(TYPE_READ, DeviceType.LINEFOLLOWER, new byte[]{(byte) port}, Float.class);
+        return (float) r.result;
+    }
+
+    /**
+     * @param port light sensor port Internal: Nr. 6
+     * @return intensity (0..1024)
+     * @throws IOException
+     */
+    public float readLightSensor(int port) throws IOException {
+        byte[] payload = new byte[1];
+        payload[0] = (byte) (port & 0xFF);
+        Result r = waitForResult(TYPE_READ, DeviceType.LIGHT_SENSOR, payload, Float.class);
+        return (float) r.result;
+    }
+
+    public float readLightSensorOnboard() throws IOException {
+        return readLightSensor(6);
+    }
+
+    public float readSoundSensor(int port) throws IOException {
+        byte[] payload = new byte[1];
+        payload[0] = (byte) (port & 0xFF);
+        Result r = waitForResult(TYPE_READ, DeviceType.SOUND_SENSOR, payload, Float.class);
+        return (float) r.result;
+    }
+
+    public float readPotentiometer(int port) throws IOException {
+        byte[] payload = new byte[1];
+        payload[0] = (byte) (port & 0xFF);
+        Result r = waitForResult(TYPE_READ, DeviceType.POTENTIONMETER, payload, Float.class);
+        return (float) r.result;
+    }
+
+    public void motorLeft(int speed) throws IOException {
+        speed = -speed;
+        byte[] payload = new byte[3];
+        payload[0] = 0x09; //left motor port
+        payload[1] = (byte) (speed & 0xFF);
+        payload[2] = (byte) (speed >> 8 & 0xFF);
+        Result r = waitForResult(TYPE_WRITE, DeviceType.MOTOR, payload, Void.class);
+    }
+
+
+    public void motorRight(int speed) throws IOException {
+        byte[] payload = new byte[3];
+        payload[0] = 0x0a; //right motor
+        payload[1] = (byte) (speed & 0xFF);
+        payload[2] = (byte) (speed >> 8 & 0xFF);
+        Result r = waitForResult(TYPE_WRITE, DeviceType.MOTOR, payload, Void.class);
+    }
+
+    /**
+     * @param idx (1: left led, 2: right led, 0: both)
+     * @param r   red (0..255)
+     * @param g   green (0..255)
+     * @param b   blue(0..255)
+     * @throws IOException
+     */
+    public void rgbLEDOnboard(int idx, int r, int g, int b) throws IOException {
+        rgbLED(0x07, idx, r, g, b);
+    }
+
+    public void rgbLED(int port, int idx, int r, int g, int b) throws IOException {
+        byte[] payload = new byte[6];
+        payload[0] = (byte) (port & 0xFF); //port
+        payload[1] = (byte) (MbotClient.RGB_INTERNAL_IDX & 0xFF); //slot
+        payload[2] = (byte) (idx & 0xFF); //idx
+        payload[3] = (byte) (r & 0xFF);
+        payload[4] = (byte) (g & 0xFF);
+        payload[5] = (byte) (b & 0xFF);
+        Result rr = waitForResult(TYPE_WRITE, DeviceType.RGBLED, payload, Void.class);
+    }
+
+    public void tone(int hz, int tone_time) throws IOException {
+        byte[] payload = new byte[3];
+        payload[0] = (byte) (hz & 0xFF);
+        payload[1] = (byte) (0);
+        payload[2] = (byte) (tone_time & 0xFF);
+        Result r = waitForResult(TYPE_WRITE, DeviceType.TONE, payload, Void.class);
+    }
+
+    public void ledMatrixString(int port, int x, int y, String s) throws IOException {
+        byte[] bstring = s.getBytes();
+        byte[] payload = new byte[5 + bstring.length];
+        payload[0] = (byte) (port & 0xFF);
+        payload[1] = (byte) (0x01);
+        payload[2] = (byte) (x);
+        payload[3] = (byte) (y);
+        payload[4] = (byte) (bstring.length & 0xFF);
+        for (int i = 0; i < bstring.length; i++)
+            payload[5 + i] = bstring[i];
+        Result r = waitForResult(TYPE_WRITE, DeviceType.LEDMATRIX, payload, Void.class);
+    }
+
+    public void ledMatrixBitmap(int port, int x, int y, byte[] bitmap) throws IOException {
+        byte[] payload = new byte[5 + bitmap.length];
+        payload[0] = (byte) (port & 0xFF);
+        payload[1] = (byte) (0x02);
+        payload[2] = (byte) (x);
+        payload[3] = (byte) (y);
+        for (int i = 0; i < bitmap.length; i++)
+            payload[5 + i] = bitmap[i];
+
+        Result r = waitForResult(TYPE_WRITE, DeviceType.LEDMATRIX, payload, Void.class);
+    }
+
+    public byte readIRCmd() {
+        byte[] payload = new byte[1];
+        payload[0] = 0x00;
+        Result r = waitForResult(TYPE_READ, DeviceType.IRREMOTECODE, payload, Byte.class);
+        return (byte) r.result;
+    }
+
+    public void irSend(String s) {
+        byte[] bstring = s.getBytes();
+        byte[] payload = new byte[1 + bstring.length];
+        payload[0] = (byte) (bstring.length + 3 & 0xFF);
+        for (int i = 0; i < bstring.length; i++)
+            payload[1 + i] = bstring[i];
+        Result r = waitForResult(TYPE_WRITE, DeviceType.IR, payload, Void.class);
+    }
+
+
+    public float readPirMotion(int port) {
+        byte[] payload = new byte[1];
+        payload[0] = (byte) (port & 0xFF);
+        Result r = waitForResult(TYPE_READ, DeviceType.PIRMOTION, payload, Float.class);
+        return (float) r.result;
+    }
+
+    public float readJoystick(int port, int slot) {
+        byte[] payload = new byte[2];
+        payload[0] = (byte) (port & 0xFF);
+        payload[1] = (byte) (slot & 0xFF);
+        Result r = waitForResult(TYPE_READ, DeviceType.JOYSTICK, payload, Float.class);
+        return (float) r.result;
+    }
+
+
+    public byte readHumiture(int port, int idx) {
+        byte[] payload = new byte[2];
+        payload[0] = (byte) (port & 0xFF);
+        payload[1] = (byte) (idx & 0xFF);
+        Result r = waitForResult(TYPE_READ, DeviceType.HUMITURE, payload, Byte.class);
+        return (byte) r.result;
+    }
+
+    public float readGyro(int axis) {
+        byte[] payload = new byte[2];
+        payload[0] = (byte) (0 & 0xFF);
+        payload[1] = (byte) (axis & 0xFF);
+
+        Result r = waitForResult(TYPE_READ, DeviceType.GYRO, payload, Float.class);
+        return (float) r.result;
+    }
+
+    public void stepper(Port port, int maxspeed, int distance) throws IOException {
+        byte[] payload = new byte[7];
+        payload[0] = (byte) port.getId();
+        payload[1] = (byte) (maxspeed & 0xFF);
+        payload[2] = (byte) (maxspeed >> 8 & 0xFF);
+        payload[3] = (byte) (distance & 0xFF);
+        payload[4] = (byte) (distance >> 8 & 0xFF);
+        payload[5] = (byte) (distance >> 16 & 0xFF);
+        payload[6] = (byte) (distance >> 24 & 0xFF);
+        Result r = waitForResult(TYPE_WRITE, DeviceType.STEPPER, payload, Void.class);
+    }
+
+    public void servo(Port port, Port slot, int degree)  throws IOException {
+        byte[] payload = new byte[3];
+        payload[0] = port.getId();
+        payload[1] = slot.getId();
+        payload[2] = (byte) (degree);
+        Result r = waitForResult(TYPE_WRITE, DeviceType.SERVO, payload, Void.class);
+    }
+
+    private void send(byte type, DeviceType deviceType, byte[] payload) throws IOException {
+        byte[] data = new byte[6 + payload.length];
+        data[0] = (byte) (0xFF);
+        data[1] = (byte) (0x55);
+        data[2] = (byte) (payload.length + 3);
+        data[3] = (byte) (0); //index
+        data[4] = (byte) (type); //action
+        data[5] = (byte) deviceType.getId(); //device
+        System.arraycopy(payload, 0, data, 6, payload.length);
+        portOS.write(data);
+        portOS.flush();
+    }
+
+    final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
+    public static String bytesToHex(byte[] bytes) {
+        char[] hexChars = new char[bytes.length * 3];
+        for (int j = 0; j < bytes.length; j++) {
+            int v = bytes[j] & 0xFF;
+            hexChars[j * 3] = hexArray[v >>> 4];
+            hexChars[j * 3 + 1] = hexArray[v & 0x0F];
+            hexChars[j * 3 + 2] = ',';
+        }
+        return new String(hexChars);
+    }
+
+    private Result waitForResult(byte typeRW, DeviceType deviceType, byte[] payload, Class type) {
+        LinkedBlockingQueue<Result> q = new LinkedBlockingQueue<>();
+        subscribers.add(q);
+        try {
+            while (true) {
+                send(typeRW, deviceType, payload);
+                while (true) {
+                    Result r = q.poll(200, TimeUnit.MILLISECONDS);
+                    if (r == null)
+                        break; //send again
+
+                    if (r.result == null) {
+                        if (type == Void.class)
+                            return r;
+                    } else {
+                        if (type.isAssignableFrom(r.result.getClass())) {
+                            return r;
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        } finally {
+            subscribers.remove(q);
+        }
+    }
+
+    //@NotNull
+    private Result readResponse() throws IOException{
+        byte[] sync = new byte[2];
+        while (true) {
+            sync[0] = sync[1];
+            sync[1] = (byte) portIS.read();
+            if (Arrays.equals(sync, new byte[]{(byte) 0xFF, (byte) 0x55}))
+                break;
+        }
+
+        byte[] first = new byte[2];
+        first[0] = (byte) portIS.read();
+        first[1] = (byte) portIS.read();
+        if (Arrays.equals(first, new byte[]{(byte) 0x0d, (byte) 0x0a})) { //OK reply (Motors, ..?)
+            Result r = new Result<Void>();
+            return r;
+        } else if (Arrays.equals(first, new byte[]{(byte) 0x80, (byte) 0x01})) { //device button press
+            Result r = new Result<Boolean>();
+            r.deviceType = DeviceType.BUTTON_INNER;
+            r.result = portIS.read() == 1 ? true : false;
+            readSuffix();
+            return r;
+        } else {
+            byte index = first[0];
+            byte type = first[1];
+            switch (type) {
+                case MB_MCU_DATATYPE_FLOAT: {//float
+                    byte[] data = new byte[4];
+                    portIS.read(data);
+                    Result r = new Result<Float>();
+                    r.result = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getFloat();
+                    r.deviceType = DeviceType.getById(type);
+                    readSuffix();
+                    return r;
+                }
+                case MB_MCU_DATATYPE_BYTE: {//byte
+                    byte res = (byte) portIS.read();
+                    Result r = new Result<Byte>();
+                    r.result = res;
+                    r.deviceType = DeviceType.getById(type);
+                    readSuffix();
+                    return r;
+                }
+                default:
+                    log.warn("Unknows Type: " + type);
+                    Result r = new Result<Void>();
+                    return r;
+            }
+        }
+    }
+
+    private void readSuffix() throws IOException {
+        byte[] suffix = new byte[2]; //od 0a
+        portIS.read(suffix);
+    }
+
+    @Override
+    public void run() {
+        try {
+            while (running) {
+                Result r = readResponse();
+                for (LinkedBlockingQueue<Result> q : subscribers) {
+                    q.put(r);
+                }
+
+                for (IMbotEvent s : externalSubscribers) {
+                    if (r.deviceType == DeviceType.BUTTON_INNER) {
+                        s.onButton((Boolean) r.result);
+                    }
+                }
+            }
+        } catch (InterruptedException|IOException e) {
+            running = false;
+            log.info("Serialport Thread terminating");
+        }
+    }
+}
diff --git a/src/main/java/org/omilab/omirob/mbot/client/Port.java b/src/main/java/org/omilab/omirob/mbot/client/Port.java
new file mode 100644
index 0000000000000000000000000000000000000000..3df61059c2bd9702b21ad1f93dc7680159b03a77
--- /dev/null
+++ b/src/main/java/org/omilab/omirob/mbot/client/Port.java
@@ -0,0 +1,52 @@
+package org.omilab.omirob.mbot.client;
+
+/**
+ * Port and Slot IDs as defined in MePort.h
+ *
+ * @author    Martin Kunz <martin.michael.kunz@gmail.com>
+ */
+public enum Port {
+    NC((byte)0),
+    PORT_1((byte)0x01),
+    PORT_2((byte)0x02),
+    PORT_3((byte)0x03),
+    PORT_4((byte)0x04),
+    PORT_5((byte)0x05),
+    PORT_6((byte)0x06),
+    PORT_7((byte)0x07),
+    PORT_8((byte)0x08),
+    PORT_9((byte)0x09),
+    PORT_10((byte)0x0a),
+    PORT_11((byte)0x0b),
+    PORT_12((byte)0x0c),
+    PORT_13((byte)0x0d),
+    PORT_14((byte)0x0e),
+    PORT_15((byte)0x0f),
+    PORT_16((byte)0x10),
+    M1((byte)0x09),
+    M2((byte)0x09),
+    PORT_RGB((byte)0x05),
+    PORT_LightSensor((byte)0x05),
+    SLOT1((byte)1),
+    SLOT2((byte)1),
+    SLOT3((byte)1),
+    SLOT4((byte)1)
+    ;
+
+    private final byte id;
+
+    Port(final byte id) {
+        this.id = id;
+    }
+
+    public byte getId() {
+        return id;
+    }
+
+    public static Port getById(int id) {
+        for(Port e : values()) {
+            if(e.id==id) return e;
+        }
+        return null;
+    }
+}
diff --git a/src/main/java/org/omilab/omirob/mbot/client/Result.java b/src/main/java/org/omilab/omirob/mbot/client/Result.java
new file mode 100644
index 0000000000000000000000000000000000000000..3b625cfa9d6c3524308b27a6625ccbf345adf3a7
--- /dev/null
+++ b/src/main/java/org/omilab/omirob/mbot/client/Result.java
@@ -0,0 +1,11 @@
+package org.omilab.omirob.mbot.client;
+
+/**
+ * @author    Martin Kunz <martin.michael.kunz@gmail.com>
+ */
+public class Result<T> {
+    public int index;
+    public T result;
+    public DeviceType deviceType;
+
+}
diff --git a/src/main/resources/mbotController.fxml b/src/main/resources/mbotController.fxml
new file mode 100644
index 0000000000000000000000000000000000000000..50de686f2fa975a93c1c4d655e93bd184b9e919d
--- /dev/null
+++ b/src/main/resources/mbotController.fxml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.control.TextArea?>
+<?import javafx.scene.control.TextField?>
+<?import javafx.scene.layout.AnchorPane?>
+<?import javafx.scene.layout.ColumnConstraints?>
+<?import javafx.scene.layout.GridPane?>
+<?import javafx.scene.layout.RowConstraints?>
+<?import javafx.scene.text.Font?>
+
+<AnchorPane maxHeight="584.0" maxWidth="800.0" minHeight="400.0" minWidth="800.0" prefHeight="551.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.omilab.omirob.mbot.balancer.MbotController">
+   <children>
+      <GridPane hgap="20.0" layoutX="23.0" layoutY="40.0" prefHeight="480.0" prefWidth="760.0" vgap="20.0" AnchorPane.leftAnchor="20.0" AnchorPane.rightAnchor="20.0" AnchorPane.topAnchor="40.0">
+        <columnConstraints>
+          <ColumnConstraints hgrow="SOMETIMES" maxWidth="188.0" minWidth="-Infinity" prefWidth="188.0" />
+          <ColumnConstraints hgrow="SOMETIMES" maxWidth="577.5" minWidth="-Infinity" prefWidth="552.0" />
+        </columnConstraints>
+        <rowConstraints>
+            <RowConstraints maxHeight="55.428558349609375" minHeight="32.0" prefHeight="38.28570556640625" vgrow="SOMETIMES" />
+          <RowConstraints maxHeight="88.0" minHeight="30.5" prefHeight="31.0" vgrow="SOMETIMES" />
+            <RowConstraints maxHeight="201.5" minHeight="150.0" prefHeight="201.0" vgrow="NEVER" />
+            <RowConstraints minHeight="150.0" prefHeight="30.0" vgrow="NEVER" />
+        </rowConstraints>
+         <children>
+            <Label alignment="TOP_LEFT" text="COM Port*" GridPane.rowIndex="1" GridPane.valignment="TOP">
+               <font>
+                  <Font size="20.0" />
+               </font></Label>
+            <Label alignment="TOP_LEFT" text="Fuzzy Set File .fcl*">
+               <font>
+                  <Font size="20.0" />
+               </font>
+            </Label>
+            <TextField fx:id="txtFCLFile" prefHeight="33.0" prefWidth="505.0" GridPane.columnIndex="1">
+               <font>
+                  <Font size="14.0" />
+               </font></TextField>
+            <TextArea fx:id="txtStatus" prefHeight="92.0" prefWidth="530.0" GridPane.columnIndex="1" GridPane.rowIndex="2" />
+            <GridPane GridPane.columnIndex="1" GridPane.rowIndex="3">
+              <columnConstraints>
+                <ColumnConstraints hgrow="SOMETIMES" maxWidth="1.7976931348623157E308" minWidth="10.0" prefWidth="100.0" />
+                <ColumnConstraints hgrow="SOMETIMES" maxWidth="1.7976931348623157E308" minWidth="10.0" prefWidth="100.0" />
+              </columnConstraints>
+              <rowConstraints>
+                <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
+              </rowConstraints>
+               <children>
+                  <Button fx:id="btnStart" mnemonicParsing="false" onAction="#startBot" prefHeight="112.0" prefWidth="258.0" text="Start">
+                     <font>
+                        <Font name="System Bold" size="39.0" />
+                     </font>
+                  </Button>
+                  <Button fx:id="btnStop" mnemonicParsing="false" onAction="#stopBot" prefHeight="112.0" prefWidth="265.0" text="Stop" GridPane.columnIndex="1">
+                     <font>
+                        <Font name="System Bold" size="39.0" />
+                     </font>
+                  </Button>
+               </children>
+            </GridPane>
+            <TextField fx:id="txtComPort" prefHeight="33.0" prefWidth="505.0" GridPane.columnIndex="1" GridPane.rowIndex="1">
+               <font>
+                  <Font size="14.0" />
+               </font></TextField>
+            <Label alignment="TOP_LEFT" text="Status" GridPane.rowIndex="2">
+               <font>
+                  <Font size="20.0" />
+               </font>
+            </Label>
+         </children>
+      </GridPane>
+      <GridPane>
+        <rowConstraints>
+          <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
+          <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
+          <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
+        </rowConstraints>
+      </GridPane>
+   </children>
+</AnchorPane>