Programing assistance, MAX31855 lags at temp over 1024

James5
Posts: 1
Joined: Wed Dec 17, 2025 6:48 am

Programing assistance, MAX31855 lags at temp over 1024

Postby James5 » Wed Dec 17, 2025 7:03 am

hi,
Not sure if this is the best spot to post, if not please let me know where is better.
I have a thermocouple (k type - is fine for my needseven if not the best)
the program i have works well upto 1024 deg C but then the program slows down and stops refreshing web.
it is for a gas kiln, to track the rate of change in temp, over 5 min and 30 min. (takes about 10 hours to fire kiln uptp 1230deg c.)

i assume it is because of the precision in floating point?
my programing skills are very basic,

code as follows,

Code: Select all

#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>
#include <Preferences.h>
#include <SPI.h>
#include <time.h>
#include <Adafruit_MAX31855.h>

// Hardware Configuration
#define MAXDO   19  // Data out
#define MAXCS   5   // Chip Select
#define MAXCLK  18  // Clock
#define LED_LOW 25 //blue
#define LED_MID 26  //green
#define LED_HIGH 27 //red

const int shortBlinkDuration = 250; // milliseconds
const int longBlinkDuration = 750;
const int pauseDuration = 500;
float avgTCCelsius =0;
float icnt=0;
float TCCelsius=0;

// Data Logging
#define LOG_INTERVAL 30000 // 30 seconds ( 30000)
#define TEMP_INTERVAL 5000 // 5 seconds ( 5000)
#define BUFFER_SIZE 1400  // 12 hours at 30s intervals

// Default Settings
const char *AP_SSID = "ThermoConfig";
float LOW_THRESH = 100.0;
float HIGH_THRESH = 170.0;

// NTP Configuration for Australia (AEST/AEDT)
const char *ntpServer = "au.pool.ntp.org"; // Australian NTP pool
const char *tz = "AEST-10AEDT-11,M10.1.0,M4.1.0/3"; // Auto DST adjustment
										  
struct TempReading
 {
   time_t timestamp; // Unix timestamp
  float temperature;
  float rate5min;
  float rate30min;
  float TCCelsius;
  float temp_avgTCCelsius;
};

//MAX6675 thermocouple(MAX6675_SCK, MAX6675_CS, MAX6675_MISO);
Adafruit_MAX31855 thermocouple(MAXCLK, MAXCS, MAXDO);

WebServer server(80);
Preferences prefs;
TempReading tempBuffer[BUFFER_SIZE];
int bufferIndex = 0;
int readingsCount = 0;
unsigned long lastReadingTime = 0;
unsigned long lastTEMPTime = 0;
float currentTemp = 0.0;
float rate5min = 0.0;
float rate30min = 0.0;
float currentRate5min = 0.0;
float currentRate30min = 0.0;
bool restartFlag = false;
unsigned long restartTime = 0;
bool timeSet = false;

void initSPI()
 {
  pinMode(MAXCS, OUTPUT);
  digitalWrite(MAXCS, HIGH);
 }

void updateRates() {
  currentRate5min = NAN;
  currentRate30min = NAN;
  if (readingsCount >= 10)
  {
    int idx = (bufferIndex - 10 + BUFFER_SIZE) % BUFFER_SIZE; 
    rate5min = (currentTemp - tempBuffer[idx].temperature) * 12.0f; //f float
	currentRate5min = rate5min;
  }
					   
  if (readingsCount >= 60)
  {
    int idx = (bufferIndex - 60 + BUFFER_SIZE) % BUFFER_SIZE; 
    rate30min = (currentTemp - tempBuffer[idx].temperature) * 2.0f;  //f float
	currentRate30min = rate30min;
  }
}

void updateLEDs() {
  float rate = !isnan(currentRate5min) ? currentRate5min : currentRate30min;
  digitalWrite(LED_LOW, LOW);
  digitalWrite(LED_MID, LOW);
  digitalWrite(LED_HIGH, LOW);

  if (isnan(rate)) return;
  
  if (rate < LOW_THRESH) digitalWrite(LED_LOW, HIGH);
  else if (rate > HIGH_THRESH) digitalWrite(LED_HIGH, HIGH);
  else digitalWrite(LED_MID, HIGH);
}

// Initialize NTP time synchronization with Australian settings
void initTime()
 {
  configTzTime(tz, ntpServer);
  Serial.println("Waiting for NTP time sync...");
  
  // Wait for time to be set
  time_t now;
  int attempts = 0;
  while ((now = time(nullptr)) < 1000000000 && attempts < 20)
  {
    Serial.print(".");
    delay(500);
    attempts++;
  }
  
  if (now < 1000000000)
  {
    Serial.println("\nFailed to get NTP time");
    timeSet = false;
  }
  else
  {
    Serial.println("\nTime synchronized!");
    timeSet = true;
  }
 }

// Get current timestamp
time_t getCurrentTime()
 {
  return time(nullptr);
}															   
											   
// Format timestamp as compact Australian time string (HH:MM:SS)
String formatTime(time_t timestamp)
 {
  if (timestamp == 0) return "N/A";
  
  struct tm *timeinfo;
  timeinfo = localtime(&timestamp);
  
  char buffer[10];  // HH:MM:SS format
  strftime(buffer, sizeof(buffer), "%H:%M:%S", timeinfo);
  return String(buffer);
}

// Format date for display (DD/MM/YYYY)
String formatDate(time_t timestamp)
 {
  if (timestamp == 0) return "N/A";
  
  struct tm *timeinfo;
  timeinfo = localtime(&timestamp);
  
  char buffer[11];  // DD/MM/YYYY format
  strftime(buffer, sizeof(buffer), "%d/%m/%Y", timeinfo);
  return String(buffer);
}
						
void handleRoot()
 {
  String html = R"rawhtml(
  <!DOCTYPE html>
  <html>
  <head>
    <meta charset="UTF-8">
    <title>Thermocouple Monitor</title>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <style>
body { font-family: Arial, sans-serif; }
      .container { max-width: 1200px; margin: 0 auto; }
      .grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; text-align:center;}
 
      .status-card { background: #29cc00; padding: 15px; border-radius: 5px; }
      button { padding: 12px; background: #29cc00; color: white; border: none; border-radius: 4px; cursor: pointer; }
      canvas { margin-top: 20px; }  // extra
      .alert { padding: 10px; margin: 10px 0; border-radius: 4px; }
      .success { background: #d4edda; color: #155724; }
      .error { background: #f8d7da; color: #721c24; }  
    </style>
  </head>
  <body>
    <div class="container">
      <h2 text-align:center;>Thermocouple Monitoring</h2>
      <div id="message" class="alert" style="display:none;"></div>
       <div class="grid">
        <div class="status-card"; >
          <p>Current Temp</p>
          <h1 style="font-size:3rem;" id="currentTemp">0.0 &deg;C</h1>
        </div>
        <div class="status-card">
          <p>5-min Rate</p>
          <h1 style="font-size:3rem;" id="rate5min">0.0 &deg;C/h</h1>
        </div>
        <div class="status-card">
          <p>30-min Rate</p>
          <h1 style="font-size:3rem;" id="rate30min">0.0 &deg;C/h</h1>
        </div>
      </div>
	
      <canvas id="tempChart"></canvas>
											  
      <div style="margin-top:20px;">
        <h3>Configuration</h3>
        <div>
          <label>Low Threshold: </label>
          <input type="number" id="lowThresh" step="0.1" value=")rawhtml" + String(LOW_THRESH) + R"rawhtml(">
          <label>High Threshold: </label>
          <input type="number" id="highThresh" step="0.1" value=")rawhtml" + String(HIGH_THRESH) + R"rawhtml(">
          <button onclick="updateThresholds()">Update</button>
        </div>
        
        <div style="margin-top:10px;">
          <h3>Download CSV</h3>
          <button onclick="downloadCSV()">Download</button>
        </div>
        
        <div style="margin-top:20px;">
          <h3>WiFi Settings</h3>
          <input type="text" id="ssid" placeholder="New SSID" value=")rawhtml" + String(WiFi.SSID()) + R"rawhtml(">
          <input type="password" id="password" placeholder="New Password">
          <button onclick="updateWiFi()">Update WiFi</button>
        </div>
										 
		<div style="margin-top:20px;">
		 <h3>Time Sync</h3>
          <button onclick="syncTime()">Sync Time</button>
        </div>							  
     </div>
    </div>

    <script>
      const ctx = document.getElementById('tempChart').getContext('2d');
      const chart = new Chart(ctx, {
        type: 'line',
        data: {
          labels: [],
          datasets: [{
            label: 'Temperature (C)',
            data: [],
            borderColor: 'rgb(75, 192, 192)',
            tension: 0.1
          }]
        },
        options: {  scales: { y: { beginAtZero: false } } }
      });

      function showMessage(text, isError) {
        const msgDiv = document.getElementById('message');
        msgDiv.textContent = text;
        msgDiv.className = isError ? 'alert error' : 'alert success';
        msgDiv.style.display = 'block';
        setTimeout(() => msgDiv.style.display = 'none', 5000);
      }

      function updateData() {
        fetch('/data')
          .then(r => r.json())
          .then(data => {
            document.getElementById('currentTemp').textContent = data.currentTemp.toFixed(1) + ' C';
            document.getElementById('rate5min').textContent = data.rate5min.toFixed(1) + ' C/h';
            document.getElementById('rate30min').textContent = data.rate30min.toFixed(1) + ' C/h';
            
            chart.data.labels = data.timestamps;
            chart.data.datasets[0].data = data.temperature;
            chart.update();
          });
      }
      
      function updateThresholds() {
        const low = document.getElementById('lowThresh').value;
        const high = document.getElementById('highThresh').value;
        fetch(`/config?low=${low}&high=${high}`)
          .then(r => r.text())
          .then(text => showMessage(text, false))
          .catch(e => showMessage('Error: ' + e, true));
      }
      
      function downloadCSV() {
        window.location.href = '/csv';
      }
      
      function updateWiFi() {
        const ssid = document.getElementById('ssid').value;
        const password = document.getElementById('password').value;
        
        if(!ssid) {
          showMessage('SSID cannot be empty!', true);
          return;
        }
        
        fetch(`/wifi?ssid=${encodeURIComponent(ssid)}&pass=${encodeURIComponent(password)}`)
          .then(r => r.text())
          .then(text => {
            showMessage(text, false);
            setTimeout(() => showMessage('Reconnecting in 5 seconds...', false), 1000);
            setTimeout(() => location.reload(), 6000);
          })
          .catch(e => showMessage('Error: ' + e, true));
      }
      
function syncTime() {
        fetch('/synctime')
          .then(r => r.text())
          .then(text => {
            showMessage(text, false);
            location.reload();
          })
          .catch(e => showMessage('Error: ' + e, true));
      }
																					   
      setInterval(updateData, 2000);
      updateData();
    </script>
  </body>
  </html>
  )rawhtml";

  server.send(200, "text/html; charset=utf-8", html);
}

void handleData()
 {
 String json = "{";
  json += "\"currentTemp\":" + String(currentTemp) + ",";
  json += "\"rate5min\":" + String(rate5min) + ",";
  json += "\"rate30min\":" + String(rate30min) + ",";
  
  json += "\"timestamps\":[";
  for(int i = 0; i < min(readingsCount, 61); i++)
  {
												 
    if(i > 0) json += ",";
	json += "\"" + String(i*0.5f) + "m\"";
  }
  json += "],";
  
  json += "\"temperature\":[";
  for(int i = 0; i < min(readingsCount, 61); i++) {
															
  
    if(i > 0) json += ",";
    int idx = (bufferIndex - min(readingsCount, 61) + i + BUFFER_SIZE) % BUFFER_SIZE;
		
    json += String(tempBuffer[idx].temperature);
  }
  json += "]}";
  
  server.send(200, "application/json; charset=utf-8", json);
}

void handleCSV()
 {
  // Use "C" instead of "amp deg C" to avoid encoding issues in CSV
  // String csv = "Time (s),Temperature (C)\n";
   String csv = formatDate(getCurrentTime()) +  "\n" + "Time,Temperature (C),5min Rate (C/h),30min Rate (C/h)\n";
  for(int i = 0; i < readingsCount; i++) {
			   
    int idx = (bufferIndex - readingsCount + i + BUFFER_SIZE) % BUFFER_SIZE;
		 
	 // Format timestamp to HH:MM:SS
    csv += formatTime(tempBuffer[idx].timestamp) + ",";
    csv += String(tempBuffer[idx].temperature, 1) + ",";
    csv += String(tempBuffer[idx].rate5min, 1) + ",";
    csv += String(tempBuffer[idx].rate30min, 1) + "\n";
	//csv += String(i*10) + "," + String(tempBuffer[idx].temperature, 1) + "\n";
  }
  server.send(200, "text/csv", csv);
}

void handleConfig()
 {
  if(server.hasArg("low") && server.hasArg("high"))
  {
    LOW_THRESH = server.arg("low").toFloat();
    HIGH_THRESH = server.arg("high").toFloat();
    prefs.putFloat("lowThresh", LOW_THRESH);
    prefs.putFloat("highThresh", HIGH_THRESH);
    server.send(200, "text/plain", "Thresholds updated");
  }
  else
  {
    server.send(400, "text/plain", "Invalid parameters");
  }
}

void handleWiFi()
 {
  if(server.hasArg("ssid") && server.hasArg("pass")) {
   
    String ssid = server.arg("ssid");
    String pass = server.arg("pass");
    
    if(ssid.length() == 0) {
														 
      server.send(400, "text/plain", "SSID cannot be empty");
      return;
    }
    
    prefs.putString("ssid", ssid);
    prefs.putString("pass", pass);
    prefs.end();  // Ensure data is committed
    
    server.send(200, "text/plain", "WiFi credentials updated. Reconnecting...");
    
    // Set flag to trigger reconnect in main loop
    restartFlag = true;
    restartTime = millis() + 2000;  // Reconnect after 2 seconds
  }
  else
  {
    server.send(400, "text/plain", "Missing parameters");
  }
 }

void handleSyncTime()
 {
  initTime();
  server.send(200, "text/plain", "Time synchronized: " + formatTime(getCurrentTime()));
 }

void blink_octet(int octet) 
 {
  if (octet ==10)
  {  
    digitalWrite(LED_MID, HIGH);
    delay(1000);
    digitalWrite(LED_MID, LOW);
    delay(pauseDuration);
   }
   else
   {
	
  for (int i = 0; i < octet; i++)
  {
    digitalWrite(LED_MID, HIGH);
    delay(shortBlinkDuration);
    digitalWrite(LED_MID, LOW);
    if (i < octet - 1)
	{
      delay(pauseDuration);
       }
	  }
     }
    }	

void initWiFi()
 {
  prefs.begin("thermoConfig", false);
  String ssid = prefs.getString("ssid", "");
  String pass = prefs.getString("pass", "");
  
  if(ssid == "")
  {
    WiFi.softAP(AP_SSID);
    Serial.println("AP Mode: " + String(AP_SSID));
    return;
  }

  WiFi.begin(ssid.c_str(), pass.c_str());
  Serial.print("Connecting to WiFi...");
  
  int attempts = 0;
  while(WiFi.status() != WL_CONNECTED && attempts < 20) {
									 
    delay(500);
    Serial.print(".");
    attempts++;
  }
  
  if(WiFi.status() == WL_CONNECTED) {
    Serial.println("\nConnected! IP: " + WiFi.localIP().toString());
	 // Initialize time after WiFi connection
    initTime();   
										 
  }
  else
  {
    WiFi.softAP(AP_SSID);
    Serial.println("\nFailed! Starting AP Mode");
  }
}

void reconnectWiFi()
 {
  Serial.println("Reconnecting to WiFi...");
  WiFi.disconnect();
  delay(1000);
  initWiFi();
  restartFlag = false;
}
void setup()
 {
  Serial.begin(115200);
  
  // Initialize hardware
  pinMode(LED_LOW, OUTPUT);
  pinMode(LED_MID, OUTPUT);
  pinMode(LED_HIGH, OUTPUT);
  initSPI();
  
  // Load saved settings
  prefs.begin("thermoConfig", false);
  LOW_THRESH = prefs.getFloat("lowThresh", 100.0);
  HIGH_THRESH = prefs.getFloat("highThresh", 175.0);
  prefs.end();
  
  // Start WiFi
  initWiFi();
IPAddress ipAddress = WiFi.localIP();
  int octet = ipAddress[3];

int val = octet;
int hundreds = val / 100;
val -= hundreds * 100;
int tens = val / 10;
val -= tens * 10;
int ones = val;

if (hundreds < 1) hundreds = 10;
 if (tens < 1) tens = 10;
 if (ones < 1) ones = 10;
			
  blink_octet(hundreds); 
  delay(1000); 
  blink_octet(tens); 
  delay(1000); 
  blink_octet(ones); 
  delay(1000); 

  // Setup web server
  server.on("/", handleRoot);
  server.on("/data", handleData);
  server.on("/csv", handleCSV);
  server.on("/config", handleConfig);
  server.on("/wifi", handleWiFi);
		   
  server.begin();
  
  Serial.println("HTTP server started");
}

void loop()
 {
    server.handleClient();
	delay(490); 
   
 // Handle WiFi reconnect if needed 
 if (restartFlag && millis() >= restartTime) {
								 
        reconnectWiFi();
    }
   
// Handle temperature readings   
   unsigned long currentTime = millis();

   TCCelsius = thermocouple.readCelsius();
   //float avgTCCelsius1 = avgTCCelsius + TCCelsius;
   //avgTCCelsius=avgTCCelsius1;
   avgTCCelsius=avgTCCelsius + TCCelsius;
   ++icnt;
      Serial.print("  temp ");
	Serial.print(TCCelsius);
      Serial.print('\n');
if (currentTime - lastTEMPTime >= TEMP_INTERVAL) {
    lastTEMPTime = currentTime;
    currentTemp = avgTCCelsius/icnt;
    avgTCCelsius = 0 ;   //james
    icnt = 0 ;
    Serial.print("  avtemp ");
	Serial.print(currentTemp);
      Serial.print('\n');
}

if (currentTime - lastReadingTime >= LOG_INTERVAL) {		
		lastReadingTime = currentTime;
        if (!isnan(currentTemp))
		{
			// Calculate rates
            updateRates();
	
    	// Store reading with rates and timestamp
			tempBuffer[bufferIndex] = {
			.timestamp = getCurrentTime(),
      .temperature = currentTemp,
			.rate5min = currentRate5min,
      .rate30min = currentRate30min
			};
					
			// Update buffer position				   
            bufferIndex = (bufferIndex + 1) % BUFFER_SIZE;
            if (readingsCount < BUFFER_SIZE) readingsCount++;
            
            // Update LEDs
            updateLEDs();
        }
    }
}
[/code

lbernstone
Posts: 1132
Joined: Mon Jul 22, 2019 3:20 pm

Re: Programing assistance, MAX31855 lags at temp over 1024

Postby lbernstone » Sat Dec 20, 2025 12:02 am

TL;DR
I would recommend 2 things:
1) Use a Ticker or timer to collect your sensor data in an async fashion. Having loop handle both data collection and web service can lead to slow, chained behavior. Let the OS decouple that for you.
2) Don't use floats unless you are intending to do some real maths with the data. Be realistic about what you want from the data- I doubt you care about the fractions of a degree on a 1200° kiln. Even so, multiplying by 10s so you keep just the precision you need will make anything you do with the number faster and avoid any ISR availability or endianness issues if you hand the data off.

Who is online

Users browsing this forum: Qwantbot and 2 guests