diff --git a/CLC-qthing/SiliconTorch/Service/SpiderFurnace.cpp b/CLC-qthing/SiliconTorch/Service/SpiderFurnace.cpp
index 5cd06d6e0db28879820f983161cc5f8b5ba8a549..387b3c8181cc46b59243723e93b1f66e830b370d 100644
--- a/CLC-qthing/SiliconTorch/Service/SpiderFurnace.cpp
+++ b/CLC-qthing/SiliconTorch/Service/SpiderFurnace.cpp
@@ -7,6 +7,7 @@
 // ESP32 specific
 #include "esp_log.h"
 #include "driver/gpio.h"
+#include "driver/ledc.h"
 
 // project specific
 #include <Types.hpp>
@@ -39,15 +40,10 @@ namespace SiliconTorch {
         setNameSpace("SpiderFurnace");
       }
 
-      void SpiderFurnace::start() {
 
-        // TODO: Make configurable"
-        const u8 MISO = 17;
-        const u8 MOSI = 16;
-        const u8 SCLK = 27;
-        const u8 CS   = 26;
+      void SpiderFurnace::start() {
 
-        tc0 = new MAX31856::MAX31856(MISO, MOSI, SCLK, CS);
+        tc0 = new MAX31856::MAX31856(IO_MISO, IO_MOSI, IO_SCLK, IO_CS);
 
         // TODO: Configure tc0!!
         tc0->setConversionMode(false);               // disable automatic conversions
@@ -59,14 +55,58 @@ namespace SiliconTorch {
         tc0->setConversionMode(true);                // re-enable automatic conversions
 
 
+        // Set-up Heater IO
+        gpio_config_t conf;
+
+        conf.intr_type    = GPIO_INTR_DISABLE;
+        conf.mode         = GPIO_MODE_OUTPUT;
+        conf.pin_bit_mask = 1ULL << IO_HEATER;
+        conf.pull_up_en   = GPIO_PULLUP_DISABLE;
+        conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
+
+        gpio_config(&conf);
+        gpio_set_level((gpio_num_t)IO_HEATER, 0);
+
+
+        // Set-up Heater PWM
+        ledc_timer_config_t         timer_cfg;
+        timer_cfg.duty_resolution = (ledc_timer_bit_t)PWM_BITS;
+        timer_cfg.freq_hz         = PWM_FREQ;
+        timer_cfg.speed_mode      = LEDC_HIGH_SPEED_MODE;
+        timer_cfg.timer_num       = LEDC_TIMER_1;                  // FxCyanF uses ch0
+        timer_cfg.clk_cfg         = LEDC_AUTO_CLK;
+
+        ledc_channel_config_t       ledc_channel;
+        ledc_channel.channel      = pwmCh;
+        ledc_channel.duty         = 0;
+        ledc_channel.gpio_num     = (gpio_num_t)IO_HEATER;
+        ledc_channel.speed_mode   = LEDC_HIGH_SPEED_MODE;
+        ledc_channel.hpoint       = 0;
+        ledc_channel.timer_sel    = LEDC_TIMER_1;
+        ledc_channel.intr_type    = LEDC_INTR_DISABLE; 
+
+        ledc_fade_func_install(0);
+        ledc_timer_config(&timer_cfg);
+        ledc_channel_config(&ledc_channel);
+
+
+        // pre-seed sliding window
         f32 t = tc0->getTemperature();
-        for (u8 n = 0; n < 10; n++)
+        for (u8 n = 0; n < SlidingWindowLength; n++)
           tc0dat->insert(t);
 
 
+        // Set-up PID controller
         pidTask = new SpiderLib::Util::LambdaTask([&](){
+
           TickType_t lastWakeTime = xTaskGetTickCount();
           tc0age = Time::ms();
+
+          f32 error      = 0.0f;
+          f32 sum_error  = 0.0f;
+          f32 diff_error = 0.0f;
+          f32 last_error = 0.0f;
+
           while (true) {
 
             faultReg.value = tc0->readU8(MAX31856::SR::addr);
@@ -86,23 +126,38 @@ namespace SiliconTorch {
               }
             }
 
-            ESP_LOGI(TAG, "T: %.2f °C    Fault: %s", temperature(), hasFault() ? "true" : "false");
+            f32 t = temperature();
 
-            if (!hasFault()) {
+            if (!hasFault() && t <= MAX_TEMP) {
+
+              error      = targetTemperature - t;
+              sum_error += error;  // TODO: "I festhalten"
+              diff_error = last_error - error;
+              last_error = error;
 
-              // Do actual PID stuff…
+              f32 out    = kP * error;
+              out       += kI * sum_error;
+              out       += kD * diff_error;
+
+              // if (out > MAX_PWM)
+              // TODO: "I festhalten" ??
+
+              setPWM(out);
 
             } else {
-              // PWM = 0
+              setPWM(0.0f);
             }
 
+            ESP_LOGI(TAG, "T: %.2f °C    Fault: %s    _i: %.2f   PWM: %.2f", t, hasFault() ? "true" : "false", sum_error, pwmOut);
+
             vTaskDelayUntil(&lastWakeTime, 1000 / TICK_FREQ / portTICK_PERIOD_MS);
           }
         });
-
-
       }
 
+      void SpiderFurnace::setTargetTemperature(f32 t) {
+        if (t >= 0.0f && t <= MAX_TEMP) targetTemperature = t;
+      }
 
       json SpiderFurnace::getConfigJSON() const {
 
@@ -113,6 +168,16 @@ namespace SiliconTorch {
         return out;
       }
 
+      void SpiderFurnace::setPWM(f32 value) {
+        if (value < 0.0f)    value = 0.0f;
+        if (value > MAX_PWM) value = MAX_PWM;
+
+        u32 maxVal = (1 << PWM_BITS) - 1;
+        u32 pwmVal = value * maxVal;
+
+        ledc_set_duty(LEDC_HIGH_SPEED_MODE, pwmCh, pwmVal);
+        ledc_update_duty(LEDC_HIGH_SPEED_MODE, pwmCh);
+      }
 
       f32 SpiderFurnace::temperature() const {
         return tc0dat->average();
diff --git a/CLC-qthing/SiliconTorch/Service/SpiderFurnace.hpp b/CLC-qthing/SiliconTorch/Service/SpiderFurnace.hpp
index 230f383c12a2db7ee2f28f30ee70fce6bd1bcdec..6dde523c1dd65ccfd5ae722788000ace4ecd5c4d 100644
--- a/CLC-qthing/SiliconTorch/Service/SpiderFurnace.hpp
+++ b/CLC-qthing/SiliconTorch/Service/SpiderFurnace.hpp
@@ -8,6 +8,7 @@
 
 // ESP32 specific
 #include "esp_log.h"
+#include "driver/ledc.h"
 
 // project specific
 #include <SpiderLib.hpp>
@@ -28,9 +29,20 @@ namespace SiliconTorch {
 
     namespace SpiderFurnace {
 
+      constexpr u8  PWM_BITS               =    12;
+      constexpr u32 PWM_FREQ               = 10000;     // Hz
+      constexpr u8  TICK_FREQ              =    10;     // Hz
+      constexpr u8  SlidingWindowLength    =     8;
+      constexpr f32 MaxTempChangePerSecond =  1000;     // K  // TODO: formally set to 100 but triggers too often?!
+      constexpr f32 MAX_TEMP               =  1320.0f;  // °C
+      constexpr f32 MAX_PWM                =     0.5f;  // p%
 
-      constexpr u8  TICK_FREQ              = 10;   // Hz
-      constexpr f32 MaxTempChangePerSecond = 100;  // K
+      // TODO: Make configurable"
+      constexpr u8 IO_CS                   =    26;
+      constexpr u8 IO_MISO                 =    17;
+      constexpr u8 IO_MOSI                 =    16;
+      constexpr u8 IO_SCLK                 =    27;
+      constexpr u8 IO_HEATER               =    25;
 
 
       enum FurnaceState {
@@ -57,7 +69,7 @@ namespace SiliconTorch {
 
           f32 temperature() const;
 
-
+          void setTargetTemperature(f32 t);
 
 
 
@@ -68,17 +80,25 @@ namespace SiliconTorch {
 
         private:
 
-          FurnaceState state = RUNNING;
+          f32 kP = 0.0f;
+          f32 kI = 0.0f;
+          f32 kD = 0.0f;
+
+          f32 targetTemperature = 0.0f;
+
+          // FurnaceState state = RUNNING;
+
+          ledc_channel_t pwmCh;
 
           MAX31856::SR faultReg;
 
           u64 tc0age = 0;  // ms
           MAX31856::MAX31856* tc0;
-          SlidingWindow<f32>* tc0dat = new SlidingWindow<f32>(8);  // TODO: Make configurable!
-
+          SlidingWindow<f32>* tc0dat = new SlidingWindow<f32>(SlidingWindowLength);
 
           SpiderLib::Util::LambdaTask* pidTask = NULL;
 
+          void setPWM(f32 value);
 
       };