diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt
index 44f6fa5..f9c6217 100644
--- a/main/CMakeLists.txt
+++ b/main/CMakeLists.txt
@@ -6,6 +6,7 @@ idf_component_register(
         "buzzer.cpp"
         "vfd.cpp"
         "display.cpp"
+        "cutter.cpp"
     INCLUDE_DIRS 
         "."
     )
diff --git a/main/config.hpp b/main/config.hpp
index e005d5a..2ea966a 100644
--- a/main/config.hpp
+++ b/main/config.hpp
@@ -31,6 +31,7 @@ extern "C" {
 #define GPIO_SW_PRESET2 GPIO_NUM_34
 #define GPIO_SW_PRESET3 GPIO_NUM_39
 
+#define GPIO_CUTTER_POS_SW GPIO_NUM_14
 #define GPIO_POTI GPIO_NUM_36
 #define ADC_CHANNEL_POTI ADC1_CHANNEL_0
 
diff --git a/main/cutter.cpp b/main/cutter.cpp
new file mode 100644
index 0000000..645ae4f
--- /dev/null
+++ b/main/cutter.cpp
@@ -0,0 +1,146 @@
+#include "cutter.hpp"
+
+const char* cutter_stateStr[5] = {"IDLE", "START", "CUTTING", "CANCELED", "TIMEOUT"}; //define strings for logging the state
+                                                                          
+
+//---------------------------
+//----- local functions -----
+//---------------------------
+//declare local functions
+void setState(cutter_state_t stateNew);
+bool checkTimeout();
+
+
+
+//---------------------------
+//----- local variables -----
+//---------------------------
+cutter_state_t state = cutter_state_t::IDLE;
+uint32_t timestamp_turnedOn;
+uint32_t msTimeout = 3000;
+static const char *TAG = "cutter"; //tag for logging
+
+
+
+
+//=========================
+//========= start =========
+//=========================
+void cutter_start(){
+    setState(cutter_state_t::START);
+    //starts motor on state change
+}
+
+
+
+//========================
+//========= stop =========
+//========================
+void cutter_stop(){
+    setState(cutter_state_t::CANCELED);
+    //starts motor on state change
+}
+
+
+
+//===========================
+//===== cutter_getState =====
+//===========================
+cutter_state_t cutter_getState(){
+    return state;
+}
+
+
+
+//---------------------------
+//-------- setState ---------
+//---------------------------
+//local function for changing state, taking corresponding actions and sending log output
+void setState(cutter_state_t stateNew){
+    //only proceed and send log output when state or direction actually changed
+    if ( state == stateNew) {
+        //already at target state -> do nothing
+        return;
+    }
+
+    //log old and new state
+    ESP_LOGI(TAG, "CHANGING state from: %i %s", (int)state, cutter_stateStr[(int)state]);
+    ESP_LOGI(TAG, "CHANGING state   to: %i %s", (int)stateNew, cutter_stateStr[(int)stateNew]);
+    //update stored state
+    state = stateNew;
+
+    switch(stateNew){
+        case cutter_state_t::IDLE:
+        case cutter_state_t::TIMEOUT:
+        case cutter_state_t::CANCELED:
+            //--- turn motor off ---
+            gpio_set_level(GPIO_RELAY, 0);
+            break;
+
+        case cutter_state_t::START:
+        case cutter_state_t::CUTTING:
+            //--- turn motor on ---
+            gpio_set_level(GPIO_RELAY, 1);
+            //update state, timestamp
+            timestamp_turnedOn = esp_log_timestamp();
+            break;
+    }
+}
+
+
+
+//--------------------------
+//------ checkTimeout ------
+//--------------------------
+//local function that checks for timeout
+bool checkTimeout(){
+    if (esp_log_timestamp() - timestamp_turnedOn > msTimeout){
+        setState(cutter_state_t::TIMEOUT);
+        return true;
+    } else {
+        return false;
+    }
+}
+
+
+
+
+//========================
+//======== handle ========
+//========================
+//function that handles the cutter logic -> has to be run repeatedly
+void cutter_handle(){
+
+    switch(state){
+        case cutter_state_t::IDLE:
+        case cutter_state_t::TIMEOUT:
+        case cutter_state_t::CANCELED:
+            //wait for state change via cutter_start();
+            break;
+
+        case cutter_state_t::START:
+            //--- moved away from idle position ---
+            if (gpio_get_level(GPIO_CUTTER_POS_SW) == 0){ //switch closed
+                                                          //FIXME: initialize gpio as input with PULLUP somewhere
+                setState(cutter_state_t::CUTTING);
+            }
+            //--- timeout ---
+            else {
+                checkTimeout();
+            }
+
+            break;
+
+        case cutter_state_t::CUTTING:
+            //--- idle position reached ---
+            if (gpio_get_level(GPIO_CUTTER_POS_SW) == 1){ //switch not closed
+                setState(cutter_state_t::IDLE);
+            }
+            //--- timeout ---
+            else {
+                checkTimeout();
+            }
+            break;
+    }
+}
+
diff --git a/main/cutter.hpp b/main/cutter.hpp
new file mode 100644
index 0000000..ec16b0b
--- /dev/null
+++ b/main/cutter.hpp
@@ -0,0 +1,31 @@
+extern "C"
+{
+#include <stdio.h>
+#include "esp_log.h"
+}
+
+#include "buzzer.hpp"
+#include "display.hpp"
+
+
+//--- variables ---
+//enum for state of cutter
+typedef enum cutter_state_t {IDLE, START, CUTTING, CANCELED, TIMEOUT} cutter_state_t;
+//string for logging the state name
+extern const char* cutter_stateStr[5];
+
+
+
+//--- functions ---
+//trigger cut cycle (no effect if already running)
+void cutter_start();
+
+//cancel cutting action
+void cutter_stop();
+
+//return current state
+cutter_state_t cutter_getState();
+//TODO: bool cutter_isOn() (simply return boolean instead of enum)
+
+//handle function - has to be run repeatedly
+void cutter_handle();
diff --git a/main/vfd.cpp b/main/vfd.cpp
index 6b0ed8e..79540f0 100644
--- a/main/vfd.cpp
+++ b/main/vfd.cpp
@@ -38,11 +38,9 @@ void vfd_setState(bool stateNew, vfd_direction_t directionNew){
                 gpio_set_level(GPIO_VFD_REV, 1);
                 break;
         }
-        gpio_set_level(GPIO_RELAY, 1);
     } else {
         gpio_set_level(GPIO_VFD_FWD, 0);
         gpio_set_level(GPIO_VFD_REV, 0);
-        gpio_set_level(GPIO_RELAY, 0);
     }
 }