diff --git a/Manifest.toml b/Manifest.toml
new file mode 100644
index 0000000000000000000000000000000000000000..ecef8aa0108e0e782f66f1cb8a0d55f185e1544e
--- /dev/null
+++ b/Manifest.toml
@@ -0,0 +1,7 @@
+# This file is machine-generated - editing it directly is not advised
+
+julia_version = "1.7.3"
+manifest_format = "2.0"
+
+[[deps.Sockets]]
+uuid = "6462fe0b-24de-5631-8697-dd941f90decc"
diff --git a/Project.toml b/Project.toml
new file mode 100644
index 0000000000000000000000000000000000000000..5b47cb65021a0bf1930e1f941ef77888462fd2a4
--- /dev/null
+++ b/Project.toml
@@ -0,0 +1,2 @@
+[deps]
+Sockets = "6462fe0b-24de-5631-8697-dd941f90decc"
diff --git a/src/Omnibot.jl b/src/Omnibot.jl
new file mode 100644
index 0000000000000000000000000000000000000000..e27cb96d41db95aed6df5d2f75187a06ec18f4dc
--- /dev/null
+++ b/src/Omnibot.jl
@@ -0,0 +1,11 @@
+module Omnibot
+
+export connectomnibot
+export getx
+export gety
+export getz 
+export gettheta
+
+import connectomnibot.jl
+import setspeeds.jl
+import getcoordinates.jl
\ No newline at end of file
diff --git a/src/connectomnibot.jl b/src/connectomnibot.jl
new file mode 100644
index 0000000000000000000000000000000000000000..9c27ea8961f4a39dce0b8c257b086dfd956a05d9
--- /dev/null
+++ b/src/connectomnibot.jl
@@ -0,0 +1,19 @@
+using Sockets
+
+"""
+    connectomnibot(ip::String;port::Int=9998)
+
+Connect to the omnibot with `ip` and `port`. Return a `TCPSocket` with open connection.
+"""
+function connectomnibot(ip::String;port::Int=9998)
+    return connect(ip,port)
+end
+
+"""
+    connectomnibot(ip::IPv4;port::Int=9998)
+
+Also callable with an IPv4 type argument
+"""
+function connectomnibot(ip::IPv4;port::Int=9998)
+    return connect(ip,port)
+end
diff --git a/src/getcoordinates.jl b/src/getcoordinates.jl
new file mode 100644
index 0000000000000000000000000000000000000000..83d15c28ee7256d28dd33cd93055b858034631bd
--- /dev/null
+++ b/src/getcoordinates.jl
@@ -0,0 +1,92 @@
+using Sockets
+
+"""
+getx(bot::TCPSocket)
+
+Returns the x-coordinate of the omnibot as `Float64`.
+"""
+function getx(bot::TCPSocket;)
+msg = "rx"
+print(bot,msg)
+ret = readline(bot)
+if msg[1] == 'e'
+    error(ret)
+end
+try
+    return parse(ret,Float64)
+catch e
+    if isa(e, ArgumentError)
+        throw(ArgumentError("Received $ret from the bot, which is not a valid float."))
+    end
+end
+
+
+end
+"""
+gety(bot::TCPSocket)
+
+Returns the y-coordinate of the omnibot as `Float64`.
+"""
+function gety(bot::TCPSocket;)
+msg = "ry"
+print(bot,msg)
+ret = readline(bot)
+if msg[1] == 'e'
+    error(ret)
+end
+try
+    return parse(ret,Float64)
+catch e
+    if isa(e, ArgumentError)
+        throw(ArgumentError("Received $ret from the bot, which is not a valid float."))
+    end
+end
+
+
+end
+
+"""
+getz(bot::TCPSocket)
+
+Returns the z-coordinate of the omnibot as `Float64`.
+"""
+function getz(bot::TCPSocket;)
+msg = "rz"
+print(bot,msg)
+ret = readline(bot)
+if msg[1] == 'e'
+    error(ret)
+end
+try
+    return parse(ret,Float64)
+catch e
+    if isa(e, ArgumentError)
+        throw(ArgumentError("Received $ret from the bot, which is not a valid float."))
+    end
+end
+
+
+end
+
+"""
+gettheta(bot::TCPSocket)
+
+Returns the ``θ``-coordinate of the omnibot as `Float64`.
+"""
+function gettheta(bot::TCPSocket;)
+msg = "rtheta"
+print(bot,msg)
+ret = readline(bot)
+if msg[1] == 'e'
+    error(ret)
+end
+try
+    return parse(ret,Float64)
+catch e
+    if isa(e, ArgumentError)
+        throw(ArgumentError("Received $ret from the bot, which is not a valid float."))
+    end
+end
+
+
+end
\ No newline at end of file
diff --git a/src/setspeeds.jl b/src/setspeeds.jl
new file mode 100644
index 0000000000000000000000000000000000000000..e1c848637ff71c6596adf1100aee5141ae69b716
--- /dev/null
+++ b/src/setspeeds.jl
@@ -0,0 +1,41 @@
+using Sockets
+
+"""
+setspeed(bot::TCPSocket,i::Int,v::Int)
+
+sets the speed of servo `i` to `v`.
+
+# Arguments
+- `bot::TCPSocket`: the socket-connection to the robot.
+- `i::Int`: index of the servo, takes values 1-3.
+- `v::Int`: integer servo speed set-point.
+"""
+function setspeed(bot::TCPSocket,i::Int,v::Int)
+msg = 'w'*string(i)*string(v)
+print(bot,msg)
+ret = readline(bot)
+if msg[1] == 'e'
+    error(ret)
+end
+
+return ret
+end
+
+"""
+setspeeds(bot::TCPSocket,v::Vector{Int})
+
+Set the speed-setpoint of servos to the values in vector v.
+"""
+function setspeed(bot::TCPSocket,v::Int)
+fullret = ""
+for (i,s) in enumerate(v)
+    msg = 'w'*string(i)*string(s)
+    print(bot,msg)
+    ret = readline(bot)
+    if msg[1] == 'e'
+        error(ret)
+    end
+    fullret = fullret*ret*"\n"
+end
+return fullret
+end
\ No newline at end of file
diff --git a/test/testserver.py b/test/testserver.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ca5eceb0f3eef4b2043463cbfffb2ab4c3c7bda
--- /dev/null
+++ b/test/testserver.py
@@ -0,0 +1,74 @@
+"""
+A python script that can run some tests on the julia client which mimics the behavior of the real system.
+"""
+
+from time import sleep
+import logging
+
+# TCP connection
+import socket
+
+from random import random
+
+
+def run_server(HOST,PORT=9998):
+    
+
+    # Create socket s
+    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
+        s.bind((HOST, PORT)) # Bind the socket to specified port and ip
+        s.listen(1)          # Start listening for connections
+        
+        # Loop "while true" to keep receiving new connections until the application closes.
+        while True:
+            logging.info("Waiting for connection")
+            conn, addr = s.accept() # Wait for connection
+            logging.info("Connection received from"+str(addr))
+            # Connection received. Closes automatically once the session ends, and then waits for a new connection.
+            with conn:              
+                # Log info about the connection
+ 
+
+                while True:
+
+                    # Receive a message
+                    data = conn.recv(1024)
+
+                    # If the message is empty, close the connection.
+                    if not data:
+                        logging.debug("Empty input, stopping connection")
+                        break    
+
+                    try:
+                        inp = data.decode('utf-8')
+                        logging.debug(f"Received {inp}")
+                        command_type = inp[0]
+
+                        if command_type == 'w':
+                            # Handle writing speed to servos
+                            message = "Operation " + inp + " successful.\n"
+                            conn.sendall(message.encode('utf-8'))
+                        elif command_type == 'r':
+                            # Handle reading from the crazyflie
+                            message = str(round(random(),5))+"\n"
+                            conn.sendall(message.encode('utf-8'))
+                    except Exception as e:
+                        # If an exception is encountered, send it to the client.
+                        logging.exception(f"Exception encountered: {e}")
+                        message = "e:"+str(e)
+                        conn.sendall(message.encode('utf-8'))
+
+
+
+if __name__ == "__main__":
+
+    # Set IP (HOST) and port number (PORT)
+    # "" means listening to any available IP
+    HOST, PORT = "", 9998
+
+    # Define logging format
+    format = "%(asctime)s: %(message)s"
+    logging.basicConfig(format=format, level=logging.DEBUG,datefmt="%H:%M:%S")
+
+    run_server(HOST,PORT=PORT)
+    
\ No newline at end of file