diff --git a/preload/apps/blinkisync-receive/__init__.py b/preload/apps/blinkisync-receive/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..05810e28839fad4c7130ac40508ce04dbe074033
--- /dev/null
+++ b/preload/apps/blinkisync-receive/__init__.py
@@ -0,0 +1,292 @@
+import display
+import light_sensor
+import utime
+import buttons
+import os
+
+TRANSMISSION_RECEIVED_TXT = "blinki-received.txt"
+TRANSMISSION_CALIBRATE = [1, 0, 1, 0, 1, 0, 1, 0]
+TRANSMISSION_START_SIGNATURE = [0, 0, 0, 0, 0, 1, 0, 1]
+TRANSMISSION_END_SIGNATURE = [0, 0, 0, 0, 0, 0, 0, 0]
+TRANSMISSION_TIMING_SIGNATURE = [1, 0, 1, 1]
+TIMING = 75
+SCROLLSPEED = 20
+
+
+def init():
+    if TRANSMISSION_RECEIVED_TXT not in os.listdir("/"):
+        with open("/" + TRANSMISSION_RECEIVED_TXT, "w") as f:
+            f.write("blinki transmissions")
+
+
+def triangle(disp, x, y, left):
+    yf = 1 if left else -1
+    scale = 6
+    disp.line(x - scale * yf, int(y + scale / 2), x, y)
+    disp.line(x, y, x, y + scale)
+    disp.line(x, y + scale, x - scale * yf, y + int(scale / 2))
+
+
+def save_transmission():
+    with open(TRANSMISSION_RECEIVED_TXT, "a") as myfile:
+        myfile.write("\n" + decoded_string)
+
+
+def transmission_to_list(length=0):
+    global read_values_list
+    global read_values_current
+    read_values_current = light_sensor.read()
+    if length != 0:
+        while len(read_values_list) >= length:
+            read_values_list.pop(0)
+    read_values_list.append(read_values_current)
+
+
+def transmission_list_to_byte_list():
+    global read_values_list_binary
+    global read_values_list_copy
+    global read_values_list
+    if read_values_list[0] <= read_values_mean:
+        read_values_list_binary.append(0)
+        read_values_list_copy.append(0)
+    else:
+        read_values_list_binary.append(1)
+        read_values_list_copy.append(1)
+    del read_values_list[:1]
+    if len(read_values_list_binary) == 2:
+        if read_values_list_binary[:2] == [0, 0]:
+            read_values_list_binary_filtered.append(0)
+            del read_values_list_binary[:2]
+        if read_values_list_binary[:2] == [1, 1]:
+            read_values_list_binary_filtered.append(1)
+            del read_values_list_binary[:2]
+        if read_values_list_binary[:2] == [0, 1]:
+            read_values_list_binary_filtered.append(0)
+            del read_values_list_binary[:1]
+        if read_values_list_binary[:2] == [1, 0]:
+            read_values_list_binary_filtered.append(1)
+            del read_values_list_binary[:1]
+
+
+def get_transmission_mean():
+    global read_values_mean
+    transmission_to_list(8)
+    read_values_mean = sum(read_values_list) / len(read_values_list)
+
+
+def detect_transmission_start():
+    global read_values_list_binary_filtered
+    global transmission_started
+    #print(read_values_list_binary_filtered)
+    if read_values_list_binary_filtered[-8:] == TRANSMISSION_START_SIGNATURE:
+        transmission_started = 1
+
+
+def detect_transmission_end():
+    global transmission_ended
+    if read_values_list_binary_filtered[-8:] == TRANSMISSION_END_SIGNATURE:
+        transmission_ended = 1
+
+
+def strip_timecode():
+    global read_values_list_binary_filtered
+    if read_values_list_binary_filtered[:len(TRANSMISSION_TIMING_SIGNATURE)] == TRANSMISSION_TIMING_SIGNATURE:
+        del read_values_list_binary_filtered[:len(TRANSMISSION_TIMING_SIGNATURE)]
+    elif read_values_list_binary_filtered[1:len(TRANSMISSION_TIMING_SIGNATURE)+1] == TRANSMISSION_TIMING_SIGNATURE:
+        del read_values_list_binary_filtered[:len(TRANSMISSION_TIMING_SIGNATURE)+1]
+    elif read_values_list_binary_filtered[:len(TRANSMISSION_TIMING_SIGNATURE)-1] == TRANSMISSION_TIMING_SIGNATURE[1:len(TRANSMISSION_TIMING_SIGNATURE)]:
+        del read_values_list_binary_filtered[:len(TRANSMISSION_TIMING_SIGNATURE)-1]
+    else:
+        del read_values_list_binary_filtered[:len(TRANSMISSION_TIMING_SIGNATURE)-1]
+    if read_values_list_binary_filtered[0] == 1:
+        del read_values_list_binary_filtered[:1]
+
+
+def decode_byte():
+    global read_values_list_binary_filtered
+    global decoded_string
+    byte_string = ""
+    for item in read_values_list_binary_filtered[:8]:
+        byte_string += str(item)
+    del read_values_list_binary_filtered[:8]
+    decoded_string += chr(int(byte_string, 2))
+
+
+def decode_transmission():
+    global decoded_string
+    del read_values_list_binary_filtered[0]
+    while len(read_values_list_binary_filtered) >= 8+len(TRANSMISSION_TIMING_SIGNATURE):
+        decode_byte()
+        strip_timecode()
+
+
+def menu():
+    if app_state == 0:
+        disp.print("start", posy=0, fg=[255, 255, 255])
+        disp.print("receive", posy=20, fg=[255, 255, 255])
+        disp.print("calibration", posy=40, fg=[255, 255, 255])
+        disp.print("hold", posx=85, posy=60, fg=[0, 255, 255])
+        triangle(disp, 150, 66, False)
+    if app_state == 2:
+        disp.print("calibrating", posy=0, fg=[0, 255, 255])
+        disp.print(str(min(read_values_list)), posx=0, posy=20, fg=[255, 0, 0])
+        #disp.print(str(round(read_values_mean, 2)), posx=46, posy=40, fg=[0, 255, 255])
+        disp.print(str(max(read_values_list)), posx=50, posy=20, fg=[0, 255, 0])
+        disp.print("release", posx=36, posy=40, fg=[255, 255, 255])
+        disp.print("first", posx=36, posy=60, fg=[255, 255, 255])
+        triangle(disp, 150, 66, False)
+    if app_state == 1:
+        disp.print("waiting for", posy=0, fg=[255, 255, 255])
+        disp.print("transmission", posy=20, fg=[255, 255, 255])
+        printlist = ''.join(str(e) for e in read_values_list_binary_filtered[-22:])
+        disp.print(printlist, posx=0, posy=40, fg=[0, 255, 0])
+        disp.print("reset", posx=12, posy=60, fg=[255, 0, 0])
+        triangle(disp, 10, 66, True)
+    if app_state == 3:
+        disp.print("transmission", posy=0, fg=[255, 255, 255])
+        disp.print("started", posy=20, fg=[255, 255, 255])
+        printlist = ''.join(str(e) for e in read_values_list_binary_filtered[-22:])
+        disp.print(printlist, posx=0, posy=40, fg=[0, 255, 0])
+    if app_state == 5:
+        global offset_counter
+        linelength = len(decoded_string)
+        maxchars = 11
+        if linelength > maxchars:
+            offset = offset_counter//SCROLLSPEED
+            #print(offset)
+            #print(offset_counter)
+            disp.print(decoded_string[offset:maxchars+offset], posx=0, posy=20, fg=[0, 255, 0])
+            offset_counter += 1
+            if maxchars + offset > linelength:
+                offset_counter = 0
+        else:
+            disp.print(decoded_string, posx=0, posy=20, fg=[0, 255, 0])
+        disp.print("ended", posy=0, fg=[255, 255, 255])
+        disp.print("save", posx=85, posy=60, fg=[255, 255, 255])
+        disp.print("back", posx=15, posy=60, fg=[255, 255, 255])
+        triangle(disp, 10, 66, True)
+        triangle(disp, 150, 66, False)
+
+def reset():
+    global app_state
+    global offset_counter
+    global read_values_current
+    global read_values_list
+    global read_values_list_binary
+    global read_values_list_binary_filtered
+    global read_values_list_binary_copy
+    global read_values_list_copy
+    global cycle_start_time
+    global cycle_time
+    global transmission_decoded
+    global transmission_started
+    global transmission_ended
+    global mean_calculation_started
+    global counter
+    global decoded_string
+    global synchronized
+    app_state = 0
+    offset_counter = 0
+    read_values_current = 0
+    read_values_list = [0]
+    read_values_list_binary = [0]
+    read_values_list_binary_filtered = []
+    read_values_list_binary_copy = []
+    read_values_list_copy = []
+    cycle_start_time = 0
+    cycle_time = 0
+    transmission_decoded = ""
+    transmission_started = 0
+    transmission_ended = 0
+    mean_calculation_started = 0
+    counter = 0
+    decoded_string = ""
+    synchronized = 0
+
+app_state = 0
+offset_counter = 0
+read_values_current = 0
+read_values_list = [0]
+read_values_list_binary = [0]
+read_values_list_binary_filtered = []
+read_values_list_binary_copy = []
+read_values_list_copy = []
+cycle_start_time = 0
+cycle_time = 0
+transmission_decoded = ""
+read_values_mean = sum(read_values_list) / len(read_values_list)
+transmission_started = 0
+transmission_ended = 0
+mean_calculation_started = 0
+button_pressed = False
+disp = display.open()
+counter = 0
+decoded_string = ""
+synchronized = 0
+
+init()
+
+while True:
+    v = buttons.read(buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT | buttons.TOP_RIGHT)
+    if v == 0:
+        button_pressed = False
+    disp.clear()
+    if app_state == 0:
+        if v == 4:
+            mean_calculation_started = 1
+            app_state = 2
+            cycle_start_time = utime.time_ms()
+            decoded_string = ""
+    if app_state == 2:
+        get_transmission_mean()
+        if v == 0 and mean_calculation_started == 1: 
+            app_state = 1
+    if app_state == 1:
+        mean_calculation_started = 0
+        transmission_to_list()
+        transmission_list_to_byte_list()
+        detect_transmission_start()
+        if not button_pressed and v & buttons.BOTTOM_LEFT != 0:
+            button_pressed = True
+            reset()
+        #print(read_values_list)
+        #print(read_values_list_binary)
+        #print(read_values_list_binary_filtered)
+        if transmission_started == 1:
+            read_values_list = []
+            read_values_list_copy = []
+            read_values_list_binary_copy = []
+            read_values_list_binary = []
+            read_values_list_binary_filtered = []
+            app_state = 3
+    if app_state == 3:
+        transmission_to_list()
+        transmission_list_to_byte_list()
+        #filter_binary_list()
+        detect_transmission_end()
+        if transmission_ended == 1:
+            app_state = 4
+    if app_state == 4:
+        decode_transmission()
+        app_state = 5
+    if app_state == 5:
+        if not button_pressed and v & buttons.BOTTOM_RIGHT != 0:
+            button_pressed = True
+            print(decoded_string)
+            save_transmission()
+            reset()
+        if not button_pressed and v & buttons.BOTTOM_LEFT != 0:
+            button_pressed = True
+            reset()            
+    menu()
+    disp.update()
+    if app_state == 1 or app_state == 2 or app_state == 3:
+        while utime.time_ms() - cycle_start_time <= cycle_time:
+            wait = True
+            #print("wait")
+            #print(utime.time_ms())
+            #print(cycle_start_time)
+            #print(cycle_time)
+            #print("wait-end")
+        cycle_time += TIMING
+
diff --git a/preload/apps/blinkisync-receive/metadata.json b/preload/apps/blinkisync-receive/metadata.json
new file mode 100644
index 0000000000000000000000000000000000000000..17e305056e0f79d3b743e7fd2d5017be82164424
--- /dev/null
+++ b/preload/apps/blinkisync-receive/metadata.json
@@ -0,0 +1 @@
+{"name":"blinkisync-receive","description":"Transmit text over light. Firmware v1.8 required. https://card10.badge.events.ccc.de/blinkisync/","category":"utility","author":"torben","revision":1}
\ No newline at end of file
diff --git a/preload/apps/blinkisync-transmit/__init__.py b/preload/apps/blinkisync-transmit/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..621875f76f8e61d0e38bdbb7682a5c1a8584a6a4
--- /dev/null
+++ b/preload/apps/blinkisync-transmit/__init__.py
@@ -0,0 +1,156 @@
+import leds
+import utime
+import buttons
+import display
+import os
+
+TRANSMISSION_SEND_TXT = "blinki-transmission.txt"
+TRANSMISSION_CALIBRATE = [1, 0, 1, 0, 1, 0, 1, 0]
+TRANSMISSION_START_SIGNATURE = [0, 0, 0, 0, 0, 1, 0, 1]
+TRANSMISSION_END_SIGNATURE = [0, 0, 0, 0, 0, 0, 0, 0]
+#TRANSMISSION_END_SIGNATURE = [1, 1, 1, 1, 1, 1, 1, 1]
+TRANSMISSION_TIMING_SIGNATURE = [1, 0, 1, 1]
+TIMING = 150
+
+
+def init():
+    global input_string
+    if TRANSMISSION_SEND_TXT not in os.listdir("/"):
+        with open("/" + TRANSMISSION_SEND_TXT, "w") as f:
+            f.write("hello world!")
+    with open("/" + TRANSMISSION_SEND_TXT, "r") as f:
+        input_string = f.readlines()[0]
+
+
+def triangle(disp, x, y, left):
+    yf = 1 if left else -1
+    scale = 6
+    disp.line(x - scale * yf, int(y + scale / 2), x, y)
+    disp.line(x, y, x, y + scale)
+    disp.line(x, y + scale, x - scale * yf, y + int(scale / 2))
+
+
+def get_transmission():
+    str_to_byte_list = []
+    for character in input_string:
+        str_to_byte_list_dirty = " ".join(map(bin, bytearray(character, "utf-8")))
+        while len(str_to_byte_list_dirty) <= 8:
+            str_to_byte_list_dirty = "0" + str_to_byte_list_dirty
+        for item in str_to_byte_list_dirty:
+            if item == "1" or item == "0":
+                str_to_byte_list.append(int(item))
+    transmission = []
+    transmission.extend(TRANSMISSION_START_SIGNATURE)
+    transmission.extend(TRANSMISSION_TIMING_SIGNATURE)
+    while len(str_to_byte_list) // 8 > 0:
+        transmission.extend(str_to_byte_list[:8])
+        del str_to_byte_list[:8]
+        transmission.extend(TRANSMISSION_TIMING_SIGNATURE)
+    transmission.extend(str_to_byte_list)
+    transmission.extend(TRANSMISSION_END_SIGNATURE)
+    return transmission
+
+
+def send_stream():
+    global timing_test_list
+    global transmission
+    global app_state
+    if transmission[0] == 1:
+        print(transmission[1])
+        leds.set(13, [255, 255, 255])
+    elif transmission[0] == 0:
+        print(transmission[0])
+        leds.set(13, [0, 0, 0])
+    if len(transmission) == 1:
+        app_state = 3
+    del transmission[0]
+
+
+def send_calibration():
+    leds.set(13, [255, 255, 255])
+    utime.sleep_ms(int(TIMING/2))
+    leds.set(13, [0, 0, 0])
+
+
+def menu():
+    if app_state == 0:
+        disp.print("start", posy=0, fg=[255, 255, 255])
+        disp.print("transmit", posy=20, fg=[255, 255, 255])
+        disp.print("calibration", posy=40, fg=[255, 255, 255])
+        disp.print("hold", posx=85, posy=60, fg=[0, 255, 255])
+        triangle(disp, 150, 66, False)
+    if app_state == 1:
+        disp.print("calibrating", posy=0, fg=[0, 255, 255])
+        disp.print("release", posx=36, posy=40, fg=[255, 255, 255])
+        disp.print("second", posx=36, posy=60, fg=[255, 255, 255])
+    if app_state == 2:
+        disp.print(str("sending"), posy=20, fg=[255, 255, 255])
+        disp.print("reset", posx=70, posy=60, fg=[0, 255, 255])
+        triangle(disp, 150, 66, False)
+    if app_state == 3:
+        disp.print(str("done"), posy=20, fg=[255, 255, 255])
+
+
+app_state = 0
+input_string = ""
+button_pressed = False
+disp = display.open()
+counter = 0
+calibration_started = 0
+counter = 0
+start_time = 0
+start_time_set = 0
+cycle_time = 0
+cycle_start_time = 0
+
+init()
+transmission = get_transmission()
+
+while True:
+    if app_state == 2:
+        counter += 1
+    if app_state == 2 & start_time_set == 0:
+        start_time = utime.time()
+        start_time_set = 1
+    disp.clear()
+    v = buttons.read(buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT | buttons.TOP_RIGHT)
+    if v == 0:
+        button_pressed = False
+    if not button_pressed and v & buttons.BOTTOM_RIGHT != 0:
+        button_pressed = True
+        app_state += 1
+    if app_state == 0:
+        if v == 4:
+            calibration_started = 1
+            app_state = 1
+            cycle_start_time = utime.time_ms()
+    if app_state == 1:
+        send_calibration()
+        if v == 0 and calibration_started == 1:
+            calibration_started = 0
+            app_state = 2
+    if app_state == 2:
+        send_stream()
+    if app_state == 3:
+        transmission = get_transmission()
+        app_state = 0
+        cycle_time = 0
+    menu()
+    disp.update()
+    if app_state == 2:
+        end_time = utime.time()
+        print(counter)
+        print(end_time-start_time)
+    if app_state == 3:
+        print(counter)
+        print(end_time-start_time)
+    if app_state == 1 or app_state == 2:
+        while utime.time_ms() - cycle_start_time <= cycle_time:
+            wait = True
+            print("wait")
+            print(utime.time_ms())
+            print(cycle_start_time)
+            print(cycle_time)
+            print("wait-end")
+        cycle_time += TIMING
+
diff --git a/preload/apps/blinkisync-transmit/metadata.json b/preload/apps/blinkisync-transmit/metadata.json
new file mode 100644
index 0000000000000000000000000000000000000000..e918d672b94eea8ea8197761f83886505b6e2eca
--- /dev/null
+++ b/preload/apps/blinkisync-transmit/metadata.json
@@ -0,0 +1 @@
+{"name":"blinkisync-transmit","description":"Transmit text over light. Firmware v1.8 required. https://card10.badge.events.ccc.de/blinkisync/","category":"utility","author":"torben","revision":1}
\ No newline at end of file