diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h
index b2e29a1c94..a3b44d5739 100644
--- a/Marlin/Configuration.h
+++ b/Marlin/Configuration.h
@@ -562,6 +562,7 @@
* ================================================================
* Analog Thermocouple Boards
* ================================================================
+ * -18 : ADS1118 with Thermocouple, e.g., Mightyboard rev G/H
* -4 : AD8495 with Thermocouple
* -1 : AD595 with Thermocouple
*
diff --git a/Marlin/src/inc/Conditionals-4-adv.h b/Marlin/src/inc/Conditionals-4-adv.h
index 664fd38e40..06e0355f63 100644
--- a/Marlin/src/inc/Conditionals-4-adv.h
+++ b/Marlin/src/inc/Conditionals-4-adv.h
@@ -483,6 +483,11 @@
#define TEMP_SENSOR_0_IS_AD8495 1
#elif TEMP_SENSOR_0 == -1
#define TEMP_SENSOR_0_IS_AD595 1
+#elif TEMP_SENSOR_0 == -18
+ #define HAS_ADS1118 1
+ #define TEMP_SENSOR_0_IS_ADS1118 1
+ #define TEMP_SENSOR_0_ADS_TMIN 0
+ #define TEMP_SENSOR_0_ADS_TMAX 1024
#elif TEMP_SENSOR_0 > 0
#define TEMP_SENSOR_0_IS_THERMISTOR 1
#if TEMP_SENSOR_0 == 1000
@@ -526,6 +531,11 @@
#define TEMP_SENSOR_1_IS_AD8495 1
#elif TEMP_SENSOR_1 == -1
#define TEMP_SENSOR_1_IS_AD595 1
+#elif TEMP_SENSOR_1 == -18
+ #define HAS_ADS1118 1
+ #define TEMP_SENSOR_1_IS_ADS1118 1
+ #define TEMP_SENSOR_1_ADS_TMIN 0
+ #define TEMP_SENSOR_1_ADS_TMAX 1024
#elif TEMP_SENSOR_1 > 0
#define TEMP_SENSOR_1_IS_THERMISTOR 1
#if TEMP_SENSOR_1 == 1000
diff --git a/Marlin/src/inc/Warnings.cpp b/Marlin/src/inc/Warnings.cpp
index 67a0aa26c7..f350184732 100644
--- a/Marlin/src/inc/Warnings.cpp
+++ b/Marlin/src/inc/Warnings.cpp
@@ -118,6 +118,10 @@
#warning "Warning! Don't use dummy thermistors (998/999) for final build!"
#endif
+#if ANY_THERMISTOR_IS(-18)
+ #warning "ADS1118 support (-18) is in development"
+#endif
+
#if NONE(HAS_RESUME_CONTINUE, HOST_PROMPT_SUPPORT, UNIT_TEST, NO_USER_FEEDBACK_WARNING)
#warning "Your Configuration provides no method to acquire user feedback! (Define NO_USER_FEEDBACK_WARNING to suppress this warning.)"
#endif
diff --git a/Marlin/src/libs/adc/adc_ads1118.cpp b/Marlin/src/libs/adc/adc_ads1118.cpp
new file mode 100644
index 0000000000..857f1739c7
--- /dev/null
+++ b/Marlin/src/libs/adc/adc_ads1118.cpp
@@ -0,0 +1,339 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+/**
+ * adc_ads1118.cpp - library for Texas Instruments ADS1118 - 16-Bit Analog-to-Digital Converter
+ * based in the sailfish code for ADS1118, ThermocoupleReader, TemperatureTable
+ * For implementation details, please take a look at the datasheet:
+ * https://www.ti.com/product/ADS1118
+ */
+
+#include "../../inc/MarlinConfig.h"
+
+#if HAS_ADS1118
+
+#include "adc_ads1118.h"
+
+#include "../../HAL/shared/Delay.h"
+#include "../../core/macros.h"
+
+#define ADS1118_CONV_MS 10
+#define ADS1118_CH_MASK 12
+
+// Constructor
+void ADS1118::init(uint8_t cs, uint8_t mosi, uint8_t miso, uint8_t sck) {
+ cs_pin = cs; mosi_pin = mosi; miso_pin = miso; sck_pin = sck;
+
+ //#define TEMP_0_CS_PIN 79 // E6
+ //#define TEMP_0_SCK_PIN 78 // E2
+ //#define TEMP_0_MISO_PIN 80 // E7
+ //#define TEMP_0_MOSI_PIN 84 // H2
+
+ pinMode(cs_pin, OUTPUT);
+ pinMode(mosi_pin, OUTPUT);
+ pinMode(sck_pin, OUTPUT);
+ pinMode(miso_pin, INPUT);
+
+ deselect();
+ sckLow();
+}
+
+// Sets ADS to start a single shot conversion. It will be read async when ready (after ADS1118_CONV_MS), non blocking
+void ADS1118::startConversion(const uint8_t pair) {
+ if (isBusy) return;
+
+ uint16_t config = 0x858B; // 0b 1000 0101 1000 1011 : SS start, single-ended off, gain ±2.048V, single-shot mode, 128SPS, ADC mode, Pullup enable, Write config
+ switch (pair) {
+ default:
+ case 0: config |= (0x0 << ADS1118_CH_MASK); currentchannel = 0; break; // AIN0-AIN1
+ case 1: config |= (0x3 << ADS1118_CH_MASK); currentchannel = 1; break; // AIN2-AIN3
+ }
+
+ digitalWrite(cs_pin, LOW);
+ transfer16(config);
+ digitalWrite(cs_pin, HIGH);
+
+ isBusy = true;
+ startTime = millis();
+}
+
+// Determine that a conversion is ready by its elapsed time, if true, reads data and stores in _lastValue
+bool ADS1118::ready() {
+ if (!isBusy) return true;
+ if (millis() - startTime >= ADS1118_CONV_MS) {
+ digitalWrite(cs_pin, LOW);
+ uint16_t raw = transfer16(0);
+ digitalWrite(cs_pin, HIGH);
+ lastValue = (int16_t)raw;
+ isBusy = false;
+ }
+ return !isBusy;
+}
+
+int16_t ADS1118::read() { return lastValue; }
+
+bool ADS1118::busy() { return isBusy; }
+
+void ADS1118::loop() { ready(); } // updates status and value
+
+// Sets ADS to start Continuous conversion mode
+void ADS1118::startContinuousConversion(const uint8_t channel_pair) {
+ SERIAL_ECHOLNPGM("ADS1118 Set to start conv");
+ if (isBusy) return;
+ // 0x048B b 0000 0100 1000 1011 : SS start, single-ended off, gain ±2.048V, Continuous conversion mode, 128SPS, ADC mode, Pullup enable, Write config
+ // 0x049B: read internal temp
+ uint16_t config = 0x048B;
+ switch (channel_pair) {
+ default:
+ case 0: config |= (0x0 << ADS1118_CH_MASK); currentchannel = 0; break; // AIN0-AIN1
+ case 1: config |= (0x3 << ADS1118_CH_MASK); currentchannel = 1; break; // AIN2-AIN3
+ }
+
+ digitalWrite(cs_pin, LOW);
+ transfer16(config);
+ digitalWrite(cs_pin, HIGH);
+
+ isBusy = true;
+ startTime = millis();
+ SERIAL_ECHOLNPGM("ADS1118 Leaving start conv");
+}
+
+// Check if ADS has a complete conversion
+bool ADS1118::checkDataReady() {
+ digitalWrite(cs_pin, LOW);
+ const uint8_t isReady = !digitalRead(miso_pin); // Read MISO, is low when ready
+ digitalWrite(cs_pin, HIGH);
+ return isReady;
+}
+
+// Read ADS data
+uint16_t ADS1118::readData() {
+ digitalWrite(cs_pin, LOW);
+ const uint16_t data = transfer16(0);
+ digitalWrite(cs_pin, HIGH);
+ return data;
+}
+
+// Read and write ADS data
+uint16_t ADS1118::readWriteData(const uint16_t config) {
+ digitalWrite(cs_pin, LOW);
+ const uint16_t data = transfer16(config);
+ digitalWrite(cs_pin, HIGH);
+ return data;
+}
+
+// Reads and returns a single channel inmediately with delay (blocking)
+int16_t ADS1118::readChannel(const uint8_t channel) {
+ const uint16_t config = configChannel(channel);
+ transfer16(config);
+
+ // ADS1118 takes ~8 ms to convert
+ delay(10);
+
+ const int16_t value = (int16_t)transfer16(config);
+ return value;
+}
+
+float ADS1118::readVoltage(const uint8_t channel, const float vref) {
+ const int16_t raw = readChannel(channel);
+ return (raw / 32768.0f) * vref; // scale 16 bits to voltage
+}
+
+float ADS1118::readInternalTemp() {
+ const uint16_t config = config_ADC_SS_TEMP; // Config internal temp read 0x8F80; // Config internal temp read & start SS conversion
+ select();
+ transfer16(config);
+ deselect();
+ delay(10);
+ select();
+ int16_t raw = (int16_t)transfer16(config);
+ deselect();
+ return convertInternalTemp(raw);
+}
+
+// Convert raw internal temperature data to °C
+float ADS1118::convertInternalTemp(const int16_t data) {
+ return float(data >> 2) * 0.03125f; // 14 bit left aligned, 0.03125 °C per LSB as datasheet
+}
+
+uint16_t ADS1118::configChannel(const uint8_t channel) {
+ uint16_t config = 0;
+
+ // Config single-ended inputs PGA ±2.048V, SS mode, 128SPS no pull up
+ switch (channel) {
+ case 0: config = 0xC583; break; // 0b 1100 0101 1000 0011 Ch1 & start SS conversion
+ case 1: config = 0xD583; break; // 0b 1101 0101 1000 0011 Ch2 & start SS conversion
+ case 2: config = 0xE583; break; // 0b 1110 0101 1000 0011 Ch3 & start SS conversion
+ case 3: config = 0xF583; break; // 0b 1111 0101 1000 0011 Ch4 & start SS conversion
+ default: config = 0x8583; break;
+ }
+
+ return config;
+}
+
+uint32_t ADS1118::transfer32(uint16_t data) {
+ uint16_t result_prev = transfer16(data); // send config, receive previous result
+ uint16_t config_echo = transfer16(0x0000); // send dummy, receive echo of configuration
+
+ // Combine both into a single 32-bit value
+ return ((uint32_t)result_prev << 16) | config_echo;
+}
+
+uint16_t ADS1118::readConfig() {
+ uint16_t config_echo;
+ select();
+ transfer16(0x0000); // send 16‑bit dummy, ignore reply; configuration is in the following 16‑bit transfer
+ config_echo = transfer16(0x0000); // send dummy, receive echo of configuration
+ SERIAL_ECHOPGM("ADS1118 Configuration: "); SERIAL_ECHOLN(config_echo);
+ deselect();
+ return config_echo;
+}
+
+uint16_t ADS1118::transfer16(uint16_t data) {
+ uint8_t high = transfer8((uint8_t)(data >> 8));
+ uint8_t low = transfer8((uint8_t)(data & 0xFF));
+ return (uint16_t)(high << 8) | low;
+}
+
+uint8_t ADS1118::transfer8(uint8_t data) {
+ uint8_t recv = 0;
+ for (uint8_t i = 0; i < 8; i++) {
+ // Send MSB first
+ if (data & 0x80) digitalWrite(mosi_pin, HIGH); else digitalWrite(mosi_pin, LOW);
+ data <<= 1;
+
+ sckHigh();
+ DELAY_NS(100); // small delay for stability
+
+ recv <<= 1;
+ if (digitalRead(miso_pin)) recv |= 0x01;
+
+ sckLow();
+ DELAY_NS(100);
+ }
+ return recv;
+}
+
+void ADS1118::select() { digitalWrite(cs_pin, LOW); }
+void ADS1118::deselect() { digitalWrite(cs_pin, HIGH); }
+
+void ADS1118::sckHigh() { digitalWrite(sck_pin, HIGH); }
+void ADS1118::sckLow() { digitalWrite(sck_pin, LOW); }
+
+// Static variable definitions
+uint8_t ADS1118::cs_pin = 0;
+uint8_t ADS1118::mosi_pin = 0;
+uint8_t ADS1118::miso_pin = 0;
+uint8_t ADS1118::sck_pin = 0;
+unsigned long ADS1118::startTime = 0;
+int16_t ADS1118::lastValue = 0;
+int16_t ADS1118::config = 0;
+uint16_t ADS1118::current_config = 0;
+uint16_t ADS1118::previous_config = 0;
+
+bool ADS1118::isBusy = false;
+uint8_t ADS1118::currentchannel = 0;
+
+// ADS1118, global instance
+ADS1118 ads1118;
+ThermocoupleK thck_0;
+ThermocoupleK thck_1;
+
+//#if TEMP_SENSOR_0_IS_ADS1118
+// #warning "ThcK 0 is enabled"
+// ThermocoupleK thck_0;
+//#endif
+
+//#if TEMP_SENSOR_1_IS_ADS1118
+// #warning "ThcK 1 is enabled"
+// ThermocoupleK thck_1;
+//#endif
+
+void ThermocoupleK::init() {}
+
+// --- Convert ADC reading to °C ---
+float ThermocoupleK:: tempReadtoCelsius(const int16_t rawADC) {
+ //Serial.println((int16_t)pgm_read_word(&ThermocoupleK_Lookup[TEMP_TABLE_SIZE - 1]));
+
+ if (rawADC > (int16_t) pgm_read_word(&table_thermocouple_k[TEMP_TABLE_SIZE - 1].adc))
+ return TEMP_MAX_TEMP;
+ if (rawADC < (int16_t) pgm_read_word(&table_thermocouple_k[0].adc))
+ return TEMP_MIN_TEMP;
+
+ // Linear search in the table (from lowest to highest ADC value)
+ for (uint16_t i = 0; i < TEMP_TABLE_SIZE - 1; i++) {
+
+ int16_t adc1 = pgm_read_word(&table_thermocouple_k[i].adc);
+ int16_t adc2 = pgm_read_word(&table_thermocouple_k[i + 1].adc);
+ // Serial.print(i); Serial.print(" "); Serial.print(adc1); Serial.print(" "); Serial.print(adc2);
+
+ if (rawADC >= adc1 && rawADC < adc2) { // in-between tableValue and nextValue
+ // Approximate temperature via linear interpolation
+ //float frac = float(rawADC - adc2) / float(adc1 - adc2);
+ int16_t t1 = pgm_read_word(&table_thermocouple_k[i].temp);
+ int16_t t2 = pgm_read_word(&table_thermocouple_k[i+1].temp);
+ // Serial.print(" "); Serial.print(t1); Serial.print(" "); Serial.print(t2);
+ float tempC = t1 + (float) (rawADC - adc1) * (float(t2 - t1) / float(adc2 - adc1)); // TEMP_TABLE_OFFSET + i + frac;
+ return tempC;
+ }
+ // Serial.println("");
+ }
+
+ return TEMP_MIN_TEMP;
+}
+
+float ThermocoupleK:: calcTempCelsius() {
+ _Tcold = ads1118.convertInternalTemp(_raw_cold);
+ _Thot = tempReadtoCelsius(_raw_hot);
+ //SERIAL_ECHOPGM("ADS1118 TCold "); SERIAL_ECHOLN(_Tcold);
+ //SERIAL_ECHOPGM("ADS1118 THot "); SERIAL_ECHOLN(_Thot);
+ return _Thot + _Tcold;
+}
+
+void ThermocoupleK::setRawCold(const int16_t raw_cold) {
+ _raw_cold = raw_cold;
+}
+
+void ThermocoupleK::setRawHot(const int16_t raw_hot) {
+ _raw_hot = raw_hot;
+}
+void ThermocoupleK::setTcold(const float tcold) {
+ _Tcold = tcold;
+}
+
+float ThermocoupleK::getTcold() {
+ return _Tcold;
+}
+
+void ThermocoupleK::setThot(const float thot) {
+ _Thot = thot;
+}
+
+float ThermocoupleK::getThot() {
+ return _Thot;
+}
+
+float ThermocoupleK:: getTempCelsius() {
+ return _Thot + _Tcold;
+}
+
+#endif // HAS_ADS1118
diff --git a/Marlin/src/libs/adc/adc_ads1118.h b/Marlin/src/libs/adc/adc_ads1118.h
new file mode 100644
index 0000000000..d8555db7a0
--- /dev/null
+++ b/Marlin/src/libs/adc/adc_ads1118.h
@@ -0,0 +1,213 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+#pragma once
+
+/**
+ * Based on Arduino Library for Texas Instruments ADS1118 - 16-Bit Analog-to-Digital Converter with internal Reference and Temperature Sensor
+ * https://www.ti.com/product/ADS1118
+ * https://github.com/ADS1xxx-Series-ADC-Libraries/ADS1118
+ */
+
+#include "../../inc/MarlinConfigPre.h"
+
+// ADS config register bits and values:
+// Bit 15: Single-shot conversion start
+#define ADS_SS_NOP 0x0000
+#define ADS_SS_START 0x8000
+// Bits 14-12 Mux
+#define INPUT_CHAN_0_1 0x0000 // *Default*
+#define INPUT_CHAN_0_3 0x1000
+#define INPUT_CHAN_1_3 0x2000
+#define INPUT_CHAN_2_3 0x3000
+#define INPUT_CHAN_0_G 0x4000
+#define INPUT_CHAN_1_G 0x5000
+#define INPUT_CHAN_2_G 0x6000
+#define INPUT_CHAN_3_G 0x7000
+
+/// Bits 11-9 ADC PGA gain select bits
+/// the gain setting sets the voltage range for the ADC. Full Scale Range
+/// voltage is the read value at 0x7FFF (the ADC returns a 16bit integer integer value)
+/// we use the highest possible gain setting - k-Type thermocouples have a voltage
+/// difference of ~12mV at 300C
+#define PGA_0_6_14 0x0000 // Gain = 1, Full Scale Voltage is 6.14V
+#define PGA_1_4_09 0x0200 // Gain = 1.5, Full Scale Voltage is 4.09V
+#define PGA_2_2_04 0x0400 // Gain = 3, Full Scale Voltage is 2.04V *Default*
+#define PGA_3_1_02 0x0600 // Gain = 6, Full Scale Voltage is 1.02V
+#define PGA_4_0_512 0x0800 // Gain = 12, Full Scale Voltage is 0.512V
+#define PGA_5_0_256 0x0A00 // Gain = 24, Full Scale Voltage is 0.256V
+
+/// Bit 8: operating mode: single sample or continous conversion
+#define CONTINUOUS_CONVERSION_MODE 0x0000 // continous conversion
+#define SINGLE_SHOT_MODE 0x0100 // single sample
+
+/// Bit 7-5: Data Rate, Sample Frequency select bits (Hz)
+#define SAMPLE_FREQ_860 0x00E0
+#define SAMPLE_FREQ_475 0x00C0
+#define SAMPLE_FREQ_250 0x00A0
+#define SAMPLE_FREQ_128 0x0080 //* default
+#define SAMPLE_FREQ_64 0x0060
+#define SAMPLE_FREQ_32 0x0040
+#define SAMPLE_FREQ_16 0x0020
+#define SAMPLE_FREQ_08 0x0000
+
+/// Bit 4: ADC mode (thermocouples) vs temperature sensor (on-board cold_junction temp sensor)
+#define ADC_MODE 0x0000
+#define TEMP_MODE 0x0010
+
+/// Bit 3: Pull up enable
+#define PULL_UP_DISABLE 0x0000
+#define PULL_UP_ENABLE 0x0008
+
+/// write new data to the config register ( if bits <2:1> are not <01> the config bytes are ignored)
+#define ADS_NOP 0x0000
+#define WRITE_CONFIG 0x0002
+
+/// Number of read cycles between cold junction temperature reads
+/// we don't need to read the cold junction temperature every cycle
+/// because we don't expect it to change much
+#define TEMP_CHECK_COUNT 120
+
+#define THERM_CHANNEL_ONE 0
+#define THERM_CHANNEL_TWO 1
+#define THERM_CHANNEL_HBP 2
+#define THERM_COLD_JUNCTION 3
+
+typedef struct {
+ int16_t adc;
+ int16_t temp;
+} ADC_Lookup;
+
+const static ADC_Lookup table_thermocouple_k[] PROGMEM = {
+ { -304, -64},
+ { -232, -48},
+ { -157, -32},
+ { -79, -16},
+ { 0, 0},
+ { 82, 16},
+ { 164, 32},
+ { 248, 48},
+ { 333, 64},
+ { 418, 80},
+ { 503, 96},
+ { 588, 112},
+ { 672, 128},
+ { 755, 144},
+ { 837, 160},
+ { 919, 176},
+ { 1001, 192},
+ { 1083, 208},
+ { 1165, 224},
+ { 1248, 240},
+ { 1331, 256},
+ { 1415, 272},
+ { 1499, 288},
+ { 1584, 304},
+ { 1754, 336},
+ { 1840, 352},
+ { 1926, 368},
+ { 2012, 384},
+ { 2099, 400}
+};
+
+#define TEMP_TABLE_SIZE (sizeof(table_thermocouple_k) / sizeof(table_thermocouple_k[0]))
+#define TEMP_MIN_TEMP 0
+#define TEMP_MAX_TEMP 300
+#define TEMP_TABLE_OFFSET 0 // grados Celsius por índice
+
+class ADS1118 {
+ public:
+ static void init(uint8_t cs, uint8_t mosi, uint8_t miso, uint8_t sck);
+ static void startConversion(const uint8_t pair); // start conversion
+ static bool ready(); // indicates whether it is already ready to be read
+ static int16_t read(); // read converted value
+ static bool busy(); // conversion status
+ static void loop(); // non‑blocking cycle
+
+ static bool checkDataReady();
+ static void startContinuousConversion(const uint8_t channel_pair);
+ static uint16_t readData();
+ static int16_t readChannel(const uint8_t channel);
+ static uint16_t readConfig ();
+ static uint16_t readWriteData(const uint16_t config);
+
+ float convertInternalTemp(const int16_t data);
+ float readInternalTemp();
+
+ uint16_t config_ADC_SS_CH0 = ADS_SS_START | INPUT_CHAN_0_1| PGA_5_0_256 | SINGLE_SHOT_MODE| SAMPLE_FREQ_128 | ADC_MODE | PULL_UP_ENABLE | WRITE_CONFIG;
+ uint16_t config_ADC_SS_CH1 = ADS_SS_START | INPUT_CHAN_2_3 | PGA_5_0_256 | SINGLE_SHOT_MODE| SAMPLE_FREQ_128 | ADC_MODE | PULL_UP_ENABLE | WRITE_CONFIG;
+ uint16_t config_ADC_SS_TEMP = ADS_SS_START | PGA_5_0_256 | SINGLE_SHOT_MODE | SAMPLE_FREQ_128 | TEMP_MODE | PULL_UP_ENABLE | WRITE_CONFIG;
+
+ static uint16_t current_config;
+ static uint16_t previous_config;
+
+ private:
+ static void spiTransfer(uint8_t data, uint8_t &resp);
+ static void writeWord(uint16_t word);
+ static void readWord(uint16_t &word);
+
+ static uint32_t transfer32 (uint16_t data);
+ static uint16_t transfer16 (uint16_t data);
+ static uint8_t transfer8 (uint8_t data);
+
+
+ static void select();
+ static void deselect();
+
+ static void sckHigh();
+ static void sckLow();
+
+ static uint8_t cs_pin, mosi_pin, miso_pin, sck_pin;
+ static unsigned long startTime;
+ static int16_t lastValue;
+ static int16_t config;
+
+ static bool isBusy;
+ static uint8_t currentchannel;
+
+ //uint16_t configForChannel(const uint8_t channel);
+ static uint16_t configChannel(const uint8_t channel);
+ float readVoltage(const uint8_t channel, const float vref);
+};
+
+class ThermocoupleK {
+ public:
+ void init();
+ float tempReadtoCelsius(int16_t rawADC);
+ float calcTempCelsius();
+ float getTempCelsius();
+ float _Tcold, _Thot;
+ int16_t _raw_cold, _raw_hot;
+
+ void setRawCold(int16_t raw_cold);
+ void setRawHot(int16_t raw_hot);
+
+ void setTcold(float tcold);
+ float getTcold();
+
+ void setThot(float thot);
+ float getThot();
+};
+
+extern ADS1118 ads1118;
+
+extern ThermocoupleK thck_0;
+extern ThermocoupleK thck_1;
diff --git a/Marlin/src/module/temperature.cpp b/Marlin/src/module/temperature.cpp
index 00bf83a345..945134fd95 100644
--- a/Marlin/src/module/temperature.cpp
+++ b/Marlin/src/module/temperature.cpp
@@ -207,6 +207,13 @@
#include "stepper.h"
#endif
+
+#define TEMP_SENSOR_IS_ADS(n, M) (ENABLED(TEMP_SENSOR_##n##_IS_ADS##M) || (ENABLED(TEMP_SENSOR_REDUNDANT_IS_ADS##M) && REDUNDANT_TEMP_MATCH(SOURCE, E##n)))
+
+#if HAS_ADS1118
+ #include "../libs/adc/adc_ads1118.h"
+#endif
+
#if ENABLED(FILAMENT_WIDTH_SENSOR)
#include "../feature/filwidth.h"
#endif
@@ -2576,10 +2583,37 @@ void Temperature::task() {
}
#endif
+
+#if ANY_THERMISTOR_IS(-18)
+
+ // Conversion for ADS1118 in differential mode (K-type)
+ // Each LSB bit ≈ 62.5 µV → ~1.5 °C (no calibration).
+ // Adjustable with GAIN and OFFSET from Configuration_adv.h
+
+ static constexpr celsius_float_t temp_ads1118(const uint8_t e) {
+ celsius_float_t temp = 0;
+ switch (e) {
+ case 0:
+ temp = thck_0.calcTempCelsius();
+ //SERIAL_ECHO("temp ads1118: "); SERIAL_ECHOLN(temp);
+ break;
+ case 1:
+ temp = thck_1.calcTempCelsius();
+ break;
+ default:
+ temp = -14.0f; // Fallback to error temperature
+ break;
+ }
+ return temp;
+ }
+
+#endif // ANY_THERMISTOR_IS(-18)
+
#if HAS_HOTEND
// Derived from RepRap FiveD extruder::getTemperature()
// For hot end temperature measurement.
celsius_float_t Temperature::analog_to_celsius_hotend(const raw_adc_t raw, const uint8_t e) {
+ //SERIAL_ECHOLN(e);
if (e >= HOTENDS) {
SERIAL_ERROR_START();
SERIAL_ECHO(e);
@@ -2605,6 +2639,8 @@ void Temperature::task() {
return temp_ad595(raw);
#elif TEMP_SENSOR_0_IS_AD8495
return temp_ad8495(raw);
+ #elif TEMP_SENSOR_0_IS_ADS1118
+ return temp_ads1118(e);
#else
break;
#endif
@@ -2624,6 +2660,8 @@ void Temperature::task() {
return temp_ad595(raw);
#elif TEMP_SENSOR_1_IS_AD8495
return temp_ad8495(raw);
+ #elif TEMP_SENSOR_1_IS_ADS1118
+ return temp_ads1118(e);
#else
break;
#endif
@@ -2875,6 +2913,31 @@ void Temperature::updateTemperaturesFromRawValues() {
temp_bed.setraw(read_max_tc_bed());
#endif
+ // Read ADC ADS1118
+ // Note: For ADS1118, we don't call setraw() because read_ads1118() returns int16_t
+ // (differential measurement can be negative) but raw_adc_t is uint16_t.
+ // Instead, the ThermocoupleK object (thck_0/thck_1) handles the conversion
+ // internally, and analog_to_celsius_hotend() will retrieve the computed
+ // temperature via thck_0.getThot(). This avoids type overflow and keeps
+ // the conversion logic centralized and ISR-light.
+ #if TEMP_SENSOR_IS_ADS(0, 1118)
+ #warning "ADS1118 is selected for hotend 0"
+ temp_hotend[0].setraw(READ_ADS(0));
+ //SERIAL_ECHOPGM("ADS1118 Tcold=");
+ //SERIAL_ECHO(thck_0.getTcold());
+ //SERIAL_ECHOPGM(" Thot=");
+ //SERIAL_ECHOLN(thck_0.getThot());
+ #endif
+
+ #if TEMP_SENSOR_IS_ADS(1, 1118)
+ #warning "ADS1118 is selected for hotend 1"
+ temp_hotend[1].setraw(READ_ADS(1));
+ //SERIAL_ECHOPGM("ADS1118 Tcold=");
+ //SERIAL_ECHO(thck_1.getTcold());
+ //SERIAL_ECHOPGM(" Thot=");
+ //SERIAL_ECHOLN(thck_1.getThot());
+ #endif
+
#if HAS_HOTEND
HOTEND_LOOP() temp_hotend[e].celsius = analog_to_celsius_hotend(temp_hotend[e].getraw(), e);
#endif
@@ -2891,7 +2954,8 @@ void Temperature::updateTemperaturesFromRawValues() {
TERN_(HAS_POWER_MONITOR, power_monitor.capture_values());
#if HAS_HOTEND
- #define _TEMPDIR(N) TEMP_SENSOR_IS_ANY_MAX_TC(N) ? 0 : TEMPDIR(N),
+
+ #define _TEMPDIR(N) (TEMP_SENSOR_IS_ANY_MAX_TC(N) || TEMP_SENSOR_IS_ADS(N,1118)) ? 0 : TEMPDIR(N),
static constexpr int8_t temp_dir[HOTENDS] = { REPEAT(HOTENDS, _TEMPDIR) };
HOTEND_LOOP() {
@@ -2975,6 +3039,30 @@ void Temperature::init() {
TERN_(PROBING_HEATERS_OFF, paused_for_probing = false);
+ //#define TEMP_0_CS_PIN 79 // E6
+ //#define TEMP_0_SCK_PIN 78 // E2
+ //#define TEMP_0_MISO_PIN 80 // E7
+ //#define TEMP_0_MOSI_PIN 84 // H2
+
+ #if HAS_ADS1118
+ ads1118.init(TEMP_0_CS_PIN, TEMP_0_MOSI_PIN, TEMP_0_MISO_PIN, TEMP_0_SCK_PIN); // Initialize the ADS1118, global instance
+ ads1118.readConfig();
+ #endif
+
+ // ADS TC related macros
+ #if TEMP_SENSOR_IS_ADS(0, 1118)
+ #warning "ADS1118 is selected for temp 0"
+ thck_0.init();
+ //SERIAL_ECHOLNPGM("ADS1118 start initial conversion for Tcold...");
+ thck_0.setTcold (ads1118.readInternalTemp());
+
+ ads1118.current_config = ads1118.config_ADC_SS_TEMP;
+ ads1118.previous_config = ads1118.current_config;
+ //SERIAL_ECHOPGM("ADS1118 Tcold: ");
+ //SERIAL_ECHOLN(thck_0.getTcold());
+ //ads1118.readConfig();
+ #endif
+
// Init (and disable) SPI thermocouples
#if TEMP_SENSOR_IS_ANY_MAX_TC(0) && PIN_EXISTS(TEMP_0_CS)
OUT_WRITE(TEMP_0_CS_PIN, HIGH);
@@ -3727,7 +3815,7 @@ void Temperature::disable_all_heaters() {
return max_tc_temp;
}
-#endif // HAS_MAX_TC
+#endif // TEMP_SENSOR_IS_MAX_TC(0) || TEMP_SENSOR_IS_MAX_TC(1) || TEMP_SENSOR_IS_MAX_TC(2)
#if TEMP_SENSOR_IS_MAX_TC(BED)
/**
@@ -3847,6 +3935,106 @@ void Temperature::disable_all_heaters() {
#endif // TEMP_SENSOR_IS_MAX_TC(BED)
+#if HAS_ADS1118
+
+ /**
+ * @brief Read ADS Thermocouple temperature.
+ *
+ * Reads the thermocouple board via HW or SW SPI, using a library (LIB_USR_x) or raw SPI reads.
+ * Doesn't strictly return a temperature; returns an "ADC Value" (i.e. raw register content).
+ * Currently only supports channel 0 (single extruder)
+ *
+ * @param hindex the hotend we're referencing (different channel in ADS1118)
+ * @return integer representing the board's buffer, to be converted later if needed
+ */
+ raw_adc_t Temperature::read_ads1118(const uint8_t hindex/*=0*/) {
+ #define ADS1118_HEAT_INTERVAL 250UL // 250 ms
+
+ //static raw_adc_t ads1118_coldJ_temp_current[2] = { 0, 0 };
+ //static raw_adc_t ads1118_hotJ_temp_current[2] = { 0, 0 };
+
+ static raw_adc_t ads1118_temp_previous[2] = { 0, 0 };
+ static uint8_t ads1118_errors[2] = { 0, 0 };
+ static millis_t next_ads1118_ms[2] = { 0, 0 };
+
+ static raw_adc_t ads_val = TEMP_SENSOR_0_ADS_TMAX;
+
+ static uint8_t sampleCount;
+
+ //static millis_t lastmillis;
+
+ const millis_t ms = millis();
+ //SERIAL_ECHOPGM("ADS1118 elapsed: "); SERIAL_ECHOLN(ms- lastmillis);
+ //lastmillis = ms;
+ if (PENDING(ms, next_ads1118_ms[hindex]) ) // || !ads1118.checkDataReady()
+ return ads1118_temp_previous[hindex]; // return cached value
+
+ next_ads1118_ms[hindex] = ms + ADS1118_HEAT_INTERVAL;
+
+ // To do: If there are more hotends enabled, cycle through different channels
+ int16_t raw;
+
+ if (sampleCount < 1) {
+ ads1118.previous_config = ads1118.current_config;
+ ads1118.current_config = ads1118.config_ADC_SS_TEMP;
+ sampleCount++;
+ }
+ else if (hindex == 0) {
+ ads1118.previous_config = ads1118.current_config;
+ ads1118.current_config = ads1118.config_ADC_SS_CH0;
+ sampleCount = 0;
+ }
+ else if (hindex == 1) {
+ ads1118.previous_config = ads1118.current_config;
+ ads1118.current_config = ads1118.config_ADC_SS_CH1;
+ sampleCount = 0;
+ }
+
+ raw = (int16_t) ads1118.readWriteData(ads1118.current_config);
+
+ if (ads1118.previous_config == ads1118.config_ADC_SS_TEMP) {
+ //thck_0.setTcold(ads1118.convertInternalTemp(raw));
+ thck_0.setRawCold(raw);
+ //SERIAL_ECHOPGM("Last read Raw cold: "); SERIAL_ECHOLN(raw);
+ //SERIAL_ECHOPGM("TCold "); SERIAL_ECHOLN(thck_0.getTcold());
+
+ }
+ else if (ads1118.previous_config == ads1118.config_ADC_SS_CH0) {
+ //ads1118_hotJ_temp_current[hindex] = raw;
+ //thck_0.setThot(thck_0.tempReadtoCelsius(raw));
+ thck_0.setRawHot(raw);
+ //SERIAL_ECHOPGM("Last read Raw hot: "); SERIAL_ECHOLN(raw);
+ //SERIAL_ECHOPGM("ADS1118 THot "); SERIAL_ECHOLN(thck_0.getThot());
+ }
+
+ //SERIAL_ECHOPGM("ADS1118 State:Read "); SERIAL_ECHOLN(curr_state); SERIAL_ECHOPGM(":"); SERIAL_ECHOLN(raw);
+
+ // Handle read error or disconnection : raw = 0x7FFF or 0x8000 (-32768)
+ if (raw == 0x7FFF || raw == -32768) {
+ ads1118_errors[hindex]++;
+ if (ads1118_errors[hindex] > 3) {
+ SERIAL_ERROR_START();
+ SERIAL_ECHOLNPGM("ADS1118 Fault: Conversion error!");
+ ads_val = (raw_adc_t)(TEMP_SENSOR_0_ADS_TMAX << 4); // force error
+ }
+ }
+ else if (raw < 32767){
+ ads1118_errors[hindex] = 0; // reset errors if ok
+ ads_val = (raw_adc_t) (((int16_t)raw) + 32768); // raw shift to unsigned int;
+ } else { // if we add 32767 to raw it will overflow
+ ads1118_errors[hindex] = 0; // reset errors if ok
+ SERIAL_ECHOLNPGM("ADS1118 Warn: Cannot shift adc read from signed to unsigned!");
+ ads_val = raw_adc_t(raw); // as is
+ }
+ //ads_val = raw_adc_t(3000); // raw;
+
+ ads1118_temp_previous[hindex] = ads_val; // cache value
+ //SERIAL_ECHOPGM("ADS1118 ads_val: "); SERIAL_ECHOLN(ads_val);
+ return ads_val; // return the raw value, it will not be used directly for conversion but for errors, (raw values are stored in thermocouple class)
+ }
+
+#endif // HAS_ADS1118
+
/**
* Update raw temperatures
*
diff --git a/Marlin/src/module/temperature.h b/Marlin/src/module/temperature.h
index f2ef4d0f6c..a122dbf2f0 100644
--- a/Marlin/src/module/temperature.h
+++ b/Marlin/src/module/temperature.h
@@ -1413,6 +1413,12 @@ class Temperature {
static raw_adc_t read_max_tc_bed();
#endif
+ // ADS Thermocouples
+ #if HAS_ADS1118
+ #define READ_ADS(N) read_ads1118(N)
+ static raw_adc_t read_ads1118(const uint8_t hindex=0);
+ #endif
+
#if HAS_AUTO_FAN
#if ENABLED(POWER_OFF_WAIT_FOR_COOLDOWN)
static bool autofans_on;
diff --git a/buildroot/tests/BTT_GTR_V1_0 b/buildroot/tests/BTT_GTR_V1_0
index 1ed65dcc90..0d6321cf6f 100755
--- a/buildroot/tests/BTT_GTR_V1_0
+++ b/buildroot/tests/BTT_GTR_V1_0
@@ -8,7 +8,8 @@ set -e
restore_configs
opt_set MOTHERBOARD BOARD_BTT_GTR_V1_0 SERIAL_PORT -1 \
- EXTRUDERS 8 TEMP_SENSOR_1 1 TEMP_SENSOR_2 1 TEMP_SENSOR_3 1 TEMP_SENSOR_4 1 TEMP_SENSOR_5 1 TEMP_SENSOR_6 1 TEMP_SENSOR_7 1
+ EXTRUDERS 8 TEMP_SENSOR_1 1 TEMP_SENSOR_2 1 TEMP_SENSOR_3 1 TEMP_SENSOR_4 1 TEMP_SENSOR_5 1 TEMP_SENSOR_6 1 TEMP_SENSOR_7 1 \
+ TEMP_SENSOR_0 -18 TEMP_0_MOSI_PIN PG15
# Not necessary to enable auto-fan for all extruders to hit problematic code paths
opt_set E0_AUTO_FAN_PIN PC10 E1_AUTO_FAN_PIN PC11 E2_AUTO_FAN_PIN PC12 NEOPIXEL_PIN PF13 \
X_DRIVER_TYPE TMC2208 Y_DRIVER_TYPE TMC2130 \
diff --git a/ini/features.ini b/ini/features.ini
index 03a9bdbba8..722ad769ee 100644
--- a/ini/features.ini
+++ b/ini/features.ini
@@ -32,6 +32,7 @@ HAS_MOTOR_CURRENT_I2C = SlowSoftI2CMaster
build_src_filter=+
LIB_MAX31855 = GadgetAngel MAX31855=https://github.com/GadgetAngel/Adafruit-MAX31855-V1.0.3-Mod-M/archive/dc9cc10ac2.zip
LIB_INTERNAL_MAX31865 = build_src_filter=+
+HAS_ADS1118 = build_src_filter=+
NEOPIXEL_LED = adafruit/Adafruit NeoPixel@~1.12.3
build_src_filter=+
I2C_AMMETER = peterus/INA226Lib@1.1.2
diff --git a/platformio.ini b/platformio.ini
index 9f562c0be9..61f2d92503 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -85,6 +85,7 @@ default_src_filter = + - -
-
-
; Library Code
+ -
-
- -
-