diff --git a/CLC-qthing/SiliconTorch/Hardware/MAX31856.cpp b/CLC-qthing/SiliconTorch/Hardware/MAX31856.cpp
index 7bc7fa0aaf20393e3d4c33ac333d610fc1662318..5a601921e950322deb0fc500757d7725f69b3835 100644
--- a/CLC-qthing/SiliconTorch/Hardware/MAX31856.cpp
+++ b/CLC-qthing/SiliconTorch/Hardware/MAX31856.cpp
@@ -326,33 +326,15 @@ namespace MAX31856 {
 
     spi = new SoftSPI(miso, mosi, sclk, cs);
 
-    setPwrLnRjFrq(F50Hz);
-
-    //return;
-
-    for (u8 addr = 0; addr < (0x0F+1); addr++) {
-
-      u8 data = spi->readU8(addr);
-
-      ESP_LOGI(TAG, "Addr[ 0x%02X ] ~~> Data[ 0x%02X ]", addr, data);
-    }
-
-    setPwrLnRjFrq(F60Hz);
-    ESP_LOGI(TAG, "----");
-
-
-    for (u8 addr = 0; addr < (0x0F+1); addr++) {
-
-      u8 data = spi->readU8(addr);
+  }
 
-      ESP_LOGI(TAG, "Addr[ 0x%02X ] ~~> Data[ 0x%02X ]", addr, data);
-    }
 
+  f32 MAX31856::getTemperature() {
+    return tcreg2float(spi->readU24(LTCB::addr));
   }
 
 
 
-
   void MAX31856::setThermocoupleType(ThermocoupleType tc) {
     return mutex.run ([&]() {
 
@@ -505,51 +487,109 @@ namespace MAX31856 {
     });
   }
 
+  void MAX31856::setOCFaultMode(OCFaultMode fm) {
+    return mutex.run ([&]() {
 
+      CR0 reg;
+      reg.value   = spi->readU8(CR0::addr);
+      reg.OCFAULT = fm & 0x03;
 
+      spi->writeU8(CR0::addr, reg.value);
+    });
+  }
 
+  OCFaultMode MAX31856::getOCFaultMode() {
+    return mutex.runReturn<OCFaultMode> ([&]() {
 
+      CR0 reg;
+      reg.value = spi->readU8(CR0::addr);
 
+      return (OCFaultMode)reg.OCFAULT;
+    });
+  }
 
 
 
-
-  void MAX31856::clearFaultBit() {
+  void MAX31856::setConversionMode(bool cmode) {
     return mutex.run ([&]() {
 
       CR0 reg;
-      reg.value    = spi->readU8(CR0::addr);
-      reg.FAULTCLR = 1;
+      reg.value = spi->readU8(CR0::addr);
+      reg.CMODE = cmode;
 
       spi->writeU8(CR0::addr, reg.value);
     });
   }
 
+  bool MAX31856::getConversionMode() {
+    return mutex.runReturn<bool> ([&]() {
 
+      CR0 reg;
+      reg.value = spi->readU8(CR0::addr);
 
+      return (bool)reg.CMODE;
+    });
+  }
 
+  void MAX31856::triggerConversion() {
+    return mutex.run ([&]() {
 
+      CR0 reg;
+      reg.value   = spi->readU8(CR0::addr);
+      reg.ONESHOT = 1;
 
+      spi->writeU8(CR0::addr, reg.value);
+    });
+  }
 
+  void MAX31856::setCJComp(bool en) {
+    return mutex.run ([&]() {
 
+      CR0 reg;
+      reg.value = spi->readU8(CR0::addr);
+      reg.CJ    = en ^ true;
 
+      spi->writeU8(CR0::addr, reg.value);
+    });
+  }
 
-  void MAX31856::mainLoop() {
-    while (true) {
+  bool MAX31856::getCJComp() {
+    return mutex.runReturn<bool> ([&]() {
 
+      CR0 reg;
+      reg.value = spi->readU8(CR0::addr);
 
+      return (bool)(reg.CJ ^ true);
+    });
+  }
 
 
 
-      ESP_LOGI(TAG, "LoL");
+  u8 MAX31856::readU8(u8 addr) {
+    return mutex.runReturn<u8> ([&]() {
+      return spi->readU8(addr);
+    });
+  }
 
-      vTaskDelay(100 / portTICK_PERIOD_MS);
-    }
+  void MAX31856::writeU8(u8 addr, u8 value) {
+    mutex.run ([&]() {
+      spi->writeU8(addr, value);
+    });
   }
 
 
-  // bool MAX31856::getIRQState() {
-  //   return gpio_get_level((gpio_num_t)ioIRQ);
-  // }
+
+
+  void MAX31856::clearFaultBit() {
+    return mutex.run ([&]() {
+
+      CR0 reg;
+      reg.value    = spi->readU8(CR0::addr);
+      reg.FAULTCLR = 1;
+
+      spi->writeU8(CR0::addr, reg.value);
+    });
+  }
+
 
 }
diff --git a/CLC-qthing/SiliconTorch/Hardware/MAX31856.hpp b/CLC-qthing/SiliconTorch/Hardware/MAX31856.hpp
index 2dcfea9afa053965a9151ce8212fb6de674d7619..151fc8beab838cf6d6aad186a86d6dd4a003cf04 100644
--- a/CLC-qthing/SiliconTorch/Hardware/MAX31856.hpp
+++ b/CLC-qthing/SiliconTorch/Hardware/MAX31856.hpp
@@ -40,6 +40,12 @@ namespace MAX31856 {
     InterruptMode  = 1
   };
 
+  enum OCFaultMode : u8 {
+    OC_DISABLED = 0x00,
+    OC_ENABLED0 = 0x01,
+    OC_ENABLED1 = 0x02,
+    OC_ENABLED2 = 0x03
+  };
 
   // #################################
   // ###                           ###
@@ -273,8 +279,8 @@ namespace MAX31856 {
       // but from my POV they can be public as the user CANNOT misuse them in any way
       // TODO: elaborate on this…
 
-      u8 readAddr ();
-      u8 writeAddr();
+      u8   readU8(u8 addr);
+      void writeU8(u8 addr, u8 value);
 
 
       void                        setPwrLnRjFrq(PowerlineFrequencyRejection pfr);  // TODO: Better Name…?!
@@ -286,6 +292,22 @@ namespace MAX31856 {
       void                        setFaultMode(FaultMode fm);
       FaultMode                   getFaultMode();
 
+      void                        setOCFaultMode(OCFaultMode fm);
+      OCFaultMode                 getOCFaultMode();
+
+
+
+      void setConversionMode(bool cmode);
+      bool getConversionMode();
+
+      void triggerConversion();
+
+      void setCJComp(bool en);
+      bool getCJComp();
+
+
+      f32 getTemperature();
+
 
       void clearFaultBit();
 
@@ -302,8 +324,6 @@ namespace MAX31856 {
       bool isTCOpenCircuitfault();
 
 
-      void mainLoop();
-
       const u8 cs;
       const u8 mosi;
       const u8 miso;
@@ -311,19 +331,10 @@ namespace MAX31856 {
 
     protected:
 
-
       SoftSPI* spi;
 
-
-
-      // bool getIRQState();
-
-      //void mainLoop();
-
       SpiderLib::ManagedMutex mutex;
 
-      SpiderLib::Util::LambdaTask* loopTask = NULL;
-
   };
 
 }
diff --git a/CLC-qthing/SiliconTorch/Service/SpiderFurnace.cpp b/CLC-qthing/SiliconTorch/Service/SpiderFurnace.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5cd06d6e0db28879820f983161cc5f8b5ba8a549
--- /dev/null
+++ b/CLC-qthing/SiliconTorch/Service/SpiderFurnace.cpp
@@ -0,0 +1,128 @@
+#include "SpiderFurnace.hpp"
+
+// C++ system level
+#include <cstdio>     // sprintf
+// #include <functional>
+
+// ESP32 specific
+#include "esp_log.h"
+#include "driver/gpio.h"
+
+// project specific
+#include <Types.hpp>
+#include <Time.hpp>
+#include "SiliconTorch/NVSExplorer.hpp"
+
+// qthing stuff
+#include <qthing>
+#include "SiliconTorch/CyanBus.hpp"
+
+// misc
+#include <nlohmann/json.hpp>
+
+
+using nlohmann::json;
+using SpiderLib::Time;
+
+static const char* TAG = "SpiderFurnace";
+
+
+namespace SiliconTorch {
+
+  namespace Service {
+
+    namespace SpiderFurnace {
+
+      void SpiderFurnace::init() {
+        setIcon("🔥");
+        setName("SpiderFurnace");
+        setNameSpace("SpiderFurnace");
+      }
+
+      void SpiderFurnace::start() {
+
+        // TODO: Make configurable"
+        const u8 MISO = 17;
+        const u8 MOSI = 16;
+        const u8 SCLK = 27;
+        const u8 CS   = 26;
+
+        tc0 = new MAX31856::MAX31856(MISO, MOSI, SCLK, CS);
+
+        // TODO: Configure tc0!!
+        tc0->setConversionMode(false);               // disable automatic conversions
+
+        tc0->setCJComp(true);                        // enable cold-junction compensation
+        tc0->setPwrLnRjFrq(MAX31856::F50Hz);
+        tc0->setOCFaultMode(MAX31856::OC_ENABLED0);  // TODO: Check resistors/datasheet!!
+
+        tc0->setConversionMode(true);                // re-enable automatic conversions
+
+
+        f32 t = tc0->getTemperature();
+        for (u8 n = 0; n < 10; n++)
+          tc0dat->insert(t);
+
+
+        pidTask = new SpiderLib::Util::LambdaTask([&](){
+          TickType_t lastWakeTime = xTaskGetTickCount();
+          tc0age = Time::ms();
+          while (true) {
+
+            faultReg.value = tc0->readU8(MAX31856::SR::addr);
+
+            if (!hasFault()) {
+              f32 t = tc0->getTemperature();
+
+              f32 deltaT = abs(t - temperature());
+              //if (deltaT < 0) deltaT *= -1.0f;
+
+              f32 tc0age_ = (Time::ms() - tc0age) / 1000.0f;  // change to seconds
+              if (deltaT > tc0age_ * MaxTempChangePerSecond) {  // temp changed too fast!
+                faultReg.OPEN = true;  // mimic fault!
+              } else {
+                tc0dat->insert(t);
+                tc0age = Time::ms();
+              }
+            }
+
+            ESP_LOGI(TAG, "T: %.2f °C    Fault: %s", temperature(), hasFault() ? "true" : "false");
+
+            if (!hasFault()) {
+
+              // Do actual PID stuff…
+
+            } else {
+              // PWM = 0
+            }
+
+            vTaskDelayUntil(&lastWakeTime, 1000 / TICK_FREQ / portTICK_PERIOD_MS);
+          }
+        });
+
+
+      }
+
+
+      json SpiderFurnace::getConfigJSON() const {
+
+        json out;
+
+        out["bla"] = "fasel";
+
+        return out;
+      }
+
+
+      f32 SpiderFurnace::temperature() const {
+        return tc0dat->average();
+      }
+
+      bool SpiderFurnace::hasFault() const {
+        return faultReg.value > 0;
+      }
+    }
+  }
+}
+
+
diff --git a/CLC-qthing/SiliconTorch/Service/SpiderFurnace.hpp b/CLC-qthing/SiliconTorch/Service/SpiderFurnace.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..230f383c12a2db7ee2f28f30ee70fce6bd1bcdec
--- /dev/null
+++ b/CLC-qthing/SiliconTorch/Service/SpiderFurnace.hpp
@@ -0,0 +1,88 @@
+#pragma once
+
+// C++ system level
+#include <vector>
+// #include <cstring>     // memset, strncmp
+// #include <cstdlib>     // TODO: is this for memcpy?
+// #include <functional>
+
+// ESP32 specific
+#include "esp_log.h"
+
+// project specific
+#include <SpiderLib.hpp>
+#include "Service.hpp"
+#include "../Hardware/MAX31856.hpp"
+
+// qthing stuff
+#include "SiliconTorch/FxCyanRGB8.hpp"
+// #include <qthing>
+
+// misc
+#include <nlohmann/json.hpp>
+
+
+namespace SiliconTorch {
+
+  namespace Service {
+
+    namespace SpiderFurnace {
+
+
+      constexpr u8  TICK_FREQ              = 10;   // Hz
+      constexpr f32 MaxTempChangePerSecond = 100;  // K
+
+
+      enum FurnaceState {
+        RUNNING,
+        PAUSED,
+        STOPPED
+      };
+
+
+
+      class SpiderFurnace : public ServiceManager::Service {
+
+        public:
+
+          void init();
+
+          void start();
+
+          virtual nlohmann::json getConfigJSON() const;
+
+
+
+          bool hasFault() const;
+
+          f32 temperature() const;
+
+
+
+
+
+          // TODO: should we block copy/assignment by default…?
+          SpiderFurnace() {};
+          SpiderFurnace(const SpiderFurnace&) = delete;
+          SpiderFurnace& operator=(SpiderFurnace const&) = delete;
+
+        private:
+
+          FurnaceState state = RUNNING;
+
+          MAX31856::SR faultReg;
+
+          u64 tc0age = 0;  // ms
+          MAX31856::MAX31856* tc0;
+          SlidingWindow<f32>* tc0dat = new SlidingWindow<f32>(8);  // TODO: Make configurable!
+
+
+          SpiderLib::Util::LambdaTask* pidTask = NULL;
+
+
+      };
+
+
+    }
+  }
+}
diff --git a/CLC-qthing/SiliconTorch/Service/__services__.cpp b/CLC-qthing/SiliconTorch/Service/__services__.cpp
index 1c4dd7aab4f1ceafc7f83f71e64b4c100a6a109c..b830724b1f865a3c372e31048a56e8a341f4be76 100644
--- a/CLC-qthing/SiliconTorch/Service/__services__.cpp
+++ b/CLC-qthing/SiliconTorch/Service/__services__.cpp
@@ -5,6 +5,7 @@
 #include "CyanBus.hpp"
 #include "FxPublish.hpp"
 #include "CyanStripe.hpp"
+#include "SpiderFurnace.hpp"
 
 
 
@@ -19,6 +20,7 @@ namespace SiliconTorch {
       mgr->registerService(new SiliconTorch::Service::CyanBus());
       mgr->registerService(new SiliconTorch::Service::FxPublish());
       mgr->registerService(new SiliconTorch::Service::CyanStripe::CyanStripe());
+      mgr->registerService(new SiliconTorch::Service::SpiderFurnace::SpiderFurnace());
 
     }
 
diff --git a/CLC-qthing/SpiderLib/SlidingWindow.hpp b/CLC-qthing/SpiderLib/SlidingWindow.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..73398883e545b5d1c379c3dca0ef61f8073c040e
--- /dev/null
+++ b/CLC-qthing/SpiderLib/SlidingWindow.hpp
@@ -0,0 +1,43 @@
+#pragma once
+
+#include "NumberTypes.hpp"
+
+
+template <typename numerical_t>
+class SlidingWindow {
+  public:
+    SlidingWindow(u32 size);
+
+    numerical_t average();
+    void insert(numerical_t value);
+  private:
+    u32 size;
+    u32 current = 0;
+    numerical_t *values;
+};
+
+
+template <typename numerical_t>
+SlidingWindow<numerical_t>::SlidingWindow(u32 size) {
+  this->size = size;
+  
+  values = new numerical_t[size];
+
+  for (u32 i = 0; i < size; i++) insert(0);
+}
+
+template <typename numerical_t>
+void SlidingWindow<numerical_t>::insert(numerical_t value) {
+  values[current++] = value;
+
+  if (current >= size) current = 0;
+}
+
+template <typename numerical_t>
+numerical_t SlidingWindow<numerical_t>::average() {
+  numerical_t avg = 0;
+
+  for (u32 i = 0; i < size; i++) avg += values[i];
+
+  return avg / (numerical_t)size;
+}
diff --git a/CLC-qthing/SpiderLib/SpiderLib.hpp b/CLC-qthing/SpiderLib/SpiderLib.hpp
index 0cf4380f010c674d586e4e864ab74d52b6e02307..bdecc3dc4e43acc75de2e4282d0d5f9a7683c36f 100644
--- a/CLC-qthing/SpiderLib/SpiderLib.hpp
+++ b/CLC-qthing/SpiderLib/SpiderLib.hpp
@@ -17,3 +17,4 @@
 #include <SpiderLib/NumberTypes.hpp>
 #include <SpiderLib/ObjectTypes.hpp>
 #include <SpiderLib/ManagedMutex.hpp>
+#include <SpiderLib/SlidingWindow.hpp>
diff --git a/CLC-qthing/device_main.cpp b/CLC-qthing/device_main.cpp
index 43f5df63b5fa7f7a10a1c87557b97db4775298ff..a37b75d84dea611066f9534334161a65af16b666 100644
--- a/CLC-qthing/device_main.cpp
+++ b/CLC-qthing/device_main.cpp
@@ -31,7 +31,7 @@ SiliconTorch::ServiceManager::ServiceManager* mgr = NULL;
 
 
 // #include "SiliconTorch/Hardware/AS3935.hpp"
-#include "SiliconTorch/Hardware/MAX31856.hpp"
+// #include "SiliconTorch/Hardware/MAX31856.hpp"
 
 
 
@@ -45,25 +45,6 @@ void device_main() {
   qthing::power_managment_max_power();
 
 
-  constexpr u8 MISO = 17;
-  constexpr u8 MOSI = 16;
-  constexpr u8 SCLK = 27;
-  constexpr u8 CS   = 26;
-
-
-  u32 tmp = 0;
-
-  tmp = 0x000000;  ESP_LOGI("txreg2float", "tcreg[ 0x%08X ]  temp[ %.2f °C ]", tmp, MAX31856::tcreg2float(tmp));  //   0.0
-  tmp = 0x000100;  ESP_LOGI("txreg2float", "tcreg[ 0x%08X ]  temp[ %.2f °C ]", tmp, MAX31856::tcreg2float(tmp));  //   0.0625
-  tmp = 0xFFFF00;  ESP_LOGI("txreg2float", "tcreg[ 0x%08X ]  temp[ %.2f °C ]", tmp, MAX31856::tcreg2float(tmp));  // - 0.0625
-  tmp = 0xD60000;  ESP_LOGI("txreg2float", "tcreg[ 0x%08X ]  temp[ %.2f °C ]", tmp, MAX31856::tcreg2float(tmp));  //  13.375
-  tmp = 0xFF2900;  ESP_LOGI("txreg2float", "tcreg[ 0x%08X ]  temp[ %.2f °C ]", tmp, MAX31856::tcreg2float(tmp));  // -13.375
-  tmp = 0xFFF000;  ESP_LOGI("txreg2float", "tcreg[ 0x%08X ]  temp[ %.2f °C ]", tmp, MAX31856::tcreg2float(tmp));  // - 1.0
-
-  MAX31856::MAX31856* temp = new MAX31856::MAX31856(MISO, MOSI, SCLK, CS);
-
-  //temp->mainLoop();
-
 
 
 
@@ -122,6 +103,9 @@ void device_main() {
   xSemaphoreTake(mutex000, portMAX_DELAY);  // avoid races with MQTT subscriptions (TODO: do we lock with WLAN atm???)
 
   qthing::enable_wifi();
+
+  mgr->start();
+
   return;