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;