diff --git a/CMakeLists.txt b/CMakeLists.txt
index e12c94b9f02141d7cb8f9277afb0909182917f10..fdd3380862f195decb1c8dd2ef116d189b5b3b93 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -8,7 +8,9 @@ project(visualising)
 ## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
 ## is used, also find other catkin packages
 find_package(catkin REQUIRED COMPONENTS
-  rospy
+   rospy
+   std_msgs
+   message_generation
 )
 
 ## System dependencies are found with CMake's conventions
@@ -18,7 +20,7 @@ find_package(catkin REQUIRED COMPONENTS
 ## Uncomment this if the package has a setup.py. This macro ensures
 ## modules and global scripts declared therein get installed
 ## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
-# catkin_python_setup()
+catkin_python_setup()
 
 ################################################
 ## Declare ROS messages, services and actions ##
@@ -45,11 +47,11 @@ find_package(catkin REQUIRED COMPONENTS
 ##   * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)
 
 ## Generate messages in the 'msg' folder
-# add_message_files(
-#   FILES
+add_message_files(
+   FILES
 #   Message1.msg
 #   Message2.msg
-# )
+)
 
 ## Generate services in the 'srv' folder
 # add_service_files(
@@ -66,10 +68,10 @@ find_package(catkin REQUIRED COMPONENTS
 # )
 
 ## Generate added messages and services with any dependencies listed here
-# generate_messages(
-#   DEPENDENCIES
-#   std_msgs  # Or other packages containing msgs
-# )
+generate_messages(
+   DEPENDENCIES
+   std_msgs  # Or other packages containing msgs
+)
 
 ################################################
 ## Declare ROS dynamic reconfigure parameters ##
@@ -104,6 +106,7 @@ catkin_package(
 #  INCLUDE_DIRS include
 #  LIBRARIES visualising
 #  CATKIN_DEPENDS rospy
+   CATKIN_DEPENDS message_runtime
 #  DEPENDS system_lib
 )
 
@@ -157,10 +160,10 @@ include_directories(
 
 ## Mark executable scripts (Python etc.) for installation
 ## in contrast to setup.py, you can choose the destination
-# catkin_install_python(PROGRAMS
+catkin_install_python(PROGRAMS
 #   scripts/my_python_script
-#   DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
-# )
+   DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
+)
 
 ## Mark executables for installation
 ## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html
diff --git a/package.xml b/package.xml
index 62596d0620ff31982d22eb27fdb0ea3cbf3f8062..14d518d3f10b6e34c678efcdc5141a6b17dc212d 100644
--- a/package.xml
+++ b/package.xml
@@ -37,13 +37,13 @@
   <!--   <build_depend>roscpp</build_depend> -->
   <!--   <exec_depend>roscpp</exec_depend> -->
   <!-- Use build_depend for packages you need at compile time: -->
-  <!--   <build_depend>message_generation</build_depend> -->
+  <build_depend>message_generation</build_depend> 
   <!-- Use build_export_depend for packages you need in order to build against this package: -->
   <!--   <build_export_depend>message_generation</build_export_depend> -->
   <!-- Use buildtool_depend for build tool packages: -->
   <!--   <buildtool_depend>catkin</buildtool_depend> -->
   <!-- Use exec_depend for packages you need at runtime: -->
-  <!--   <exec_depend>message_runtime</exec_depend> -->
+  <exec_depend>message_runtime</exec_depend> 
   <!-- Use test_depend for packages you need only for testing: -->
   <!--   <test_depend>gtest</test_depend> -->
   <!-- Use doc_depend for packages you need only for building documentation: -->
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000000000000000000000000000000000000..ae086d42738c966245e0bbfca0bf685297156a76
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+from distutils.core import setup
+from catkin_pkg.python_setup import generate_distutils_setup
+
+setup_args = generate_distutils_setup(
+    packages=['visualising'],
+    package_dir={'': 'src'}
+)
+
+setup(**setup_args)
diff --git a/src/visualising/animation.py b/src/visualising/animation.py
new file mode 100755
index 0000000000000000000000000000000000000000..162c2982d6abc6a0a426619433a9d082dc485321
--- /dev/null
+++ b/src/visualising/animation.py
@@ -0,0 +1,149 @@
+#!/usr/bin/env python
+import re
+from .exception import AnimationException
+
+
+class Animation:
+    def __init__(self, frames, frame_time, num_iter):
+        # Set frames, number of frames and number of ensembles.
+        try:
+            animation_len = len(frames)
+        except TypeError:
+            raise AnimationException("P: -- ERROR -- The animation is empty!")
+
+        if animation_len % 2 != 0:
+            raise AnimationException("P: -- ERROR -- Animation must contain an even number of frames!")
+        else:
+            for frame in frames:
+                try:
+                    frame_len = len(frame)
+                except TypeError:
+                    raise AnimationException("P: -- ERROR -- At least one frame is empty!")
+
+                if frame_len != 48:
+                    raise AnimationException("P: -- ERROR -- At least one frame doesn't have exactly 48 values!")
+                else:
+                    is_numeric = True
+                    is_positive = True
+                    is_small = True
+
+                    for value in frame:
+                        is_numeric = is_numeric and isinstance(value, int)
+                        is_positive = is_positive and not (value < 0)
+                        is_small = is_small and value < 256
+
+                    if not is_numeric:
+                        raise AnimationException("P: -- ERROR -- Not all values in the animation are integers!")
+                    elif not is_positive:
+                        raise AnimationException("P: -- ERROR -- Not all values in the animation are positive!")
+                    elif not is_small:
+                        raise AnimationException("P: -- ERROR -- One or more values in the animation are to large!")
+                    else:
+                        self.frames = frames
+                        self.num_frames = animation_len
+                        self.num_ensembles = int(animation_len / 2)
+
+        # Set frame time.
+        if not isinstance(frame_time, int):
+            raise AnimationException("P: -- ERROR -- Frame time must be an integer!")
+        elif frame_time < 0:
+            raise AnimationException("P: -- ERROR -- Frame time must be positive!")
+        elif frame_time > 4294967295:
+            raise AnimationException("P: -- ERROR -- Animation delay is to high!")
+        else:
+            self.frame_time = frame_time
+
+        # Set number of iterations.
+        if not isinstance(num_iter, int):
+            raise AnimationException("P: -- ERROR -- Number of iterations must be an integer!")
+        elif num_iter > 255:
+            raise AnimationException("P: -- ERROR -- Animation has to many iterations!")
+        elif num_iter <= 0:
+            raise AnimationException("P: -- ERROR -- Animation must have at least one iteration!")
+        else:
+            self.num_iter = num_iter
+
+    @staticmethod
+    def read_frames_from_file(filename):
+        try:
+            file = open(filename, 'r')
+        except AttributeError:
+            raise AnimationException("P: -- ERROR -- The file is incorrectly formatted!")
+        except FileNotFoundError:
+            raise AnimationException("P: -- ERROR -- The specified file does not exist!")
+        except TypeError:
+            raise AnimationException("P: -- ERROR -- No animation file was specified!")
+
+        animation = []
+        for line in file:
+            frame = []
+            for value in line.split(','):
+                frame.append(int(re.search(r'\d+', value).group()))
+            animation.append(frame)
+
+        return animation
+
+    @staticmethod
+    def create_empty_ensemble():
+        frame1 = [0] * 16
+        frame2 = [0] * 16
+        return [frame1, frame2]
+
+    @staticmethod
+    def set_pixel_color(ensemble, pixel, r, g, b):
+        try:
+            ensemble_len = len(ensemble)
+        except TypeError:
+            raise AnimationException("P: -- ERROR -- Ensemble is empty!")
+
+        if ensemble_len != 2:
+            raise AnimationException("P: -- ERROR -- Ensemble doesn't contain exactly two frames!")
+        else:
+            for frame in ensemble:
+                try:
+                    frame_len = len(frame)
+                except TypeError:
+                    raise AnimationException("P: -- ERROR -- At least one frame is empty!")
+
+                if frame_len != 48:
+                    raise AnimationException("P: -- ERROR -- At least one frame doesn't have exactly 48 values!")
+                else:
+                    is_numeric = True
+                    is_positive = True
+                    is_small = True
+
+                    for value in frame:
+                        is_numeric = is_numeric and isinstance(value, int)
+                        is_positive = is_positive and not (value < 0)
+                        is_small = is_small and value < 256
+
+                    if not is_numeric:
+                        raise AnimationException("P: -- ERROR -- Not all values in the ensemble are integers!")
+                    elif not is_positive:
+                        raise AnimationException("P: -- ERROR -- Not all values in the ensemble are positive!")
+                    elif not is_small:
+                        raise AnimationException("P: -- ERROR -- One or more values in the ensemble are to large!")
+                    else:
+                        if not (0 < pixel < 33):
+                            raise AnimationException("P: -- ERROR -- Pixel has to be in the range of 1 to 32!")
+                        elif not (0 < r < 256):
+                            raise AnimationException("P: -- ERROR -- Red channel value has to be in the range of 1 to "
+                                                     "255!")
+                        elif not (0 < g < 256):
+                            raise AnimationException("P: -- ERROR -- Green channel value has to be in the range of 1 "
+                                                     "to 255!")
+                        elif not (0 < b < 256):
+                            raise AnimationException("P: -- ERROR -- Blue channel value has to be in the range of 1 "
+                                                     "to 255!")
+                        else:
+                            if pixel < 16:
+                                index = 0
+                            else:
+                                index = 1
+                                pixel = pixel - 16
+
+                            ensemble[index][pixel + 0] = r
+                            ensemble[index][pixel + 1] = g
+                            ensemble[index][pixel + 2] = b
+
+                            return ensemble
diff --git a/src/visualising/arduino.py b/src/visualising/arduino.py
new file mode 100755
index 0000000000000000000000000000000000000000..2ed3d5d7bec57d72948fa0fe01fdfb1d071b7cc1
--- /dev/null
+++ b/src/visualising/arduino.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+import time
+from . message import FrameMsg, InstrMsg
+from . animation import Animation
+from . exception import ArduinoException
+
+
+class Arduino:
+    def __init__(self, connection):
+        self.connection = connection
+
+    # Translates a 8-bit message from Arduino and prints the translation.
+    @staticmethod
+    def print_response(msg):
+        arduino_msg = {
+            240: "A: -- ERROR -- Animation has to many frame ensembles!",
+            241: "A: -- ERROR -- No animation is loaded!",
+            242: "A: -- ERROR -- Instruction was not recognized!",
+            243: "A: -- ERROR -- No data message expected!",
+            244: "A: -- ERROR -- Message has wrong format!",
+            245: "A: -- ERROR -- Animation is playing!",
+            246: "A: -- ERROR -- Animation has no iteration!",
+            247: "A: -- ERROR -- Unequal number of frames for the left and right NeoPixel ring!",
+            248: "A: -- ERROR -- Animation has no frame ensemble!",
+            0: "A: No Response!",
+            15: "A: -- SUCCESS -- Waiting for frames to be send!",
+            31: "A: -- SUCCESS -- Animation playback has been started!",
+            47: "A: -- SUCCESS -- Frame successfully received!",
+            63: "A: -- SUCCESS -- Last frame successfully received!",
+            79: "A: -- SUCCESS -- Animation successfully played!",
+            95: "A: -- SUCCESS -- Animation playback has been paused!",
+            111: "A: -- SUCCESS -- Animation playback has been resumed",
+        }
+
+        try:
+            print(arduino_msg[int.from_bytes(msg, byteorder='big')])
+        except KeyError:
+            raise ArduinoException("P: -- ERROR -- Unknown response from Arduino!")
+
+    # Creates one instruction messages and multiple frame messages to load an animation
+    # onto the Arduino.
+    def load_animation(self, animation):
+        if animation.num_ensembles > 16:
+            raise ArduinoException("P: -- ERROR -- Animation hast to many frames!")
+        else:
+            self.connection.send_msg(InstrMsg('A', animation).create_arduino_msg())
+            msg = self.connection.receive_msg()
+
+            Arduino.print_response(msg)
+            if bytes(msg) == bytes(b'\x0f'):
+                for frame in animation.frames:
+                    self.connection.send_msg(FrameMsg(frame).create_arduino_msg())
+                    msg = self.connection.receive_msg()
+
+                    d = 10
+                    while bytes(msg) != bytes(b'\x2f') and bytes(msg) != bytes(b'\x3f') and d > 0:
+                        d = d - 1
+                        Arduino.print_response(msg)
+                        self.connection.send_msg(FrameMsg(frame).create_arduino_msg())
+                        msg = self.connection.receive_msg()
+
+                    if d > 0:
+                        Arduino.print_response(msg)
+                    else:
+                        raise ArduinoException("P: -- ERROR -- Unable to send frame message!")
+
+    # Creates instruction message and sends it to the Arduino,
+    # to play a loaded animation.
+    def start_playback(self):
+        self.connection.send_msg(InstrMsg('B').create_arduino_msg())
+        Arduino.print_response(self.connection.receive_msg())
+
+    # Creates instruction message and sends it to the Arduino,
+    # to pause a playing animation.
+    def pause_playback(self):
+        self.connection.send_msg(InstrMsg('C').create_arduino_msg())
+        Arduino.print_response(self.connection.receive_msg())
+
+    # Creates instruction message and sends it to the Arduino,
+    # to resume playback of a loaded animation.
+    def resume_playback(self):
+        self.connection.send_msg(InstrMsg('D').create_arduino_msg())
+        Arduino.print_response(self.connection.receive_msg())
+
+    # Streams an animation to the Arduino. For this purpose, two frames of
+    # the animation are repeatedly sent to the Arduino. The Arduino plays
+    # the received frames and then confirms the playback, whereupon new
+    # frames are sent.
+    def stream_animation(self, animation):
+        for _ in range(animation.num_iter):
+            for i in range(animation.num_ensembles):
+                ensemble = [animation.frames[2 * i + 0], animation.frames[2 * i + 1]]
+                self.load_animation(Animation(ensemble, animation.frame_time, 1))
+                self.start_playback()
+
+                curr_time = time.time()  # Current time in milliseconds.
+                # Waits 10 times the animation delay for a response.
+                while bytes(self.connection.receive_msg()) != bytes(b'\x4F'):
+                    if time.time() > curr_time + animation.frame_time / 100:
+                        raise ArduinoException("P: -- ERROR -- Arduino is not responding!")
diff --git a/src/visualising/connection.py b/src/visualising/connection.py
new file mode 100755
index 0000000000000000000000000000000000000000..7d861ed3e370c48accffa941983ab4f3d0c4175a
--- /dev/null
+++ b/src/visualising/connection.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+import time
+import serial
+
+from . exception import ConnectionException
+from serial.serialutil import SerialException
+
+
+class Connection:
+    def __init__(self, port, baud):
+        self.connection = serial.Serial()
+        self.connection.port = port
+        self.connection.baudrate = baud
+        self.connection.timeout = 1
+        try:
+            self.connection.open()  # Establishes a serial connection.
+        except SerialException:
+            raise ConnectionException("P: -- ERROR -- No serial connection established!")
+        except FileNotFoundError:
+            raise ConnectionException("P: -- ERROR -- The specified port does not exist!")
+
+        time.sleep(2)  # Prevents errors regarding pyserial.
+
+    # Send a message.
+    def send_msg(self, msg):
+        for byte in msg:
+            self.connection.write(byte)
+        self.connection.reset_output_buffer()  # Clear serial output buffer.
+
+    # Receive a message.
+    def receive_msg(self):
+        msg = self.connection.read(1)
+        self.connection.reset_input_buffer()  # Clear serial input buffer.
+        return msg
diff --git a/src/visualising/exception.py b/src/visualising/exception.py
new file mode 100755
index 0000000000000000000000000000000000000000..82abeb9b58619c848f5a1d6bc3e3ff01a6af33b3
--- /dev/null
+++ b/src/visualising/exception.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+class Error(Exception):
+    pass
+
+
+class AnimationException(Error):
+    def __init__(self, message):
+        self.message = message
+
+
+class ConnectionException(Error):
+    def __init__(self, message):
+        self.message = message
+
+
+class ArduinoException(Error):
+    def __init__(self, message):
+        self.message = message
diff --git a/src/visualising/message.py b/src/visualising/message.py
new file mode 100755
index 0000000000000000000000000000000000000000..353426756bef4e748d8578685f52f1da7a29d86c
--- /dev/null
+++ b/src/visualising/message.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+
+class FrameMsg:
+    def __init__(self, frame):
+        self.frame = frame
+
+    # Creates a 50-bit data Message. The first and last bit are intended for authentication
+    # as a data message. The remaining 48 bits store exactly one animation frame.
+    def create_arduino_msg(self):
+        msg = [bytes('{', 'ascii')]
+
+        for color in self.frame:
+            msg.append(color.to_bytes(1, byteorder='big'))
+
+        msg.append(bytes('}', 'ascii'))
+
+        return msg
+
+
+class InstrMsg:
+    def __init__(self, instr, animation=None):
+        self.instr = instr
+        self.animation = animation
+
+    # Creates a 50-bit instruction message. Since only 7 bits at most are required for the
+    # actual message, most of the message consists only of zeros. The first and last bit
+    # are intended for authentication as an instruction message.
+    def create_arduino_msg(self):
+        msg = [bytes('[', 'ascii'), bytes(self.instr, 'ascii')]
+
+        if self.animation is not None:
+            msg.append(self.animation.num_frames.to_bytes(1, byteorder='big'))
+
+            frame_time_bytes = self.animation.frame_time.to_bytes(4, byteorder='big')
+            msg.append((frame_time_bytes[0]).to_bytes(1, byteorder='big'))
+            msg.append((frame_time_bytes[1]).to_bytes(1, byteorder='big'))
+            msg.append((frame_time_bytes[2]).to_bytes(1, byteorder='big'))
+            msg.append((frame_time_bytes[3]).to_bytes(1, byteorder='big'))
+
+            msg.append(self.animation.num_iter.to_bytes(1, byteorder='big'))
+
+            for _ in range(41):
+                msg.append((0).to_bytes(1, byteorder='big'))
+        else:
+            for _ in range(47):
+                msg.append((0).to_bytes(1, byteorder='big'))
+
+        msg.append(bytes(']', 'ascii'))
+
+        return msg