Safety parameter not doing what I would like (SOLVED)

UPDATE
Thank you to all who have commented down below you were all of great help
and I appreciate all of your input and comments.

Ultimatly I have marked post#6 as the answer to my intended needs, so, a big thank you to
@LarryD for the contributed code that he supplied which after some modification ended up
being what helped me out.... Again a big thank you to you all for commenting!

Good day All!

Hope you are all well? In my neck o' the woods it's a bit chilly right moew.

So, again I need some help from the brilliant minds around this forum.

I am busy with my pulltruder project, all is progressing very well for the limited amount
of time I have. I am at the point where the temps are getting high and I would like
some safety parameters.

I have a function that I would like to have triggered when a certain threshold is
crossed which should start a timer of 60seconds the time is "overTempTime" and is
configured as "overTempTime = 1000L * 60L;" If this is not correct please help me to correct this?

So, the function should be triggered as soon as the actual temp is above the the setpoint,
when it goes below the timer should stop and reset, it takes about 40Seconds for the temp
to stabalize so 60s should be enough. But if there is thermal runaway I would like the mosfet to cut power to the heater cartridge.

"activate_Heating" and "avgTemp" have been defined higher up, and "activate_Heating"
is also another function used higher up in the code (Both global definitions), as this code is already several 100 lines I will not bore you with the whole thing unless you would like to see it.

Now, I do know that this is because of how I have structured it so I know this is my fault.
At the moment this code is not triggering. Any help welcome please, Thank you.

void safetyAbort () {

  unsigned long overTemptimer = millis(); // Start over temp timer

  if ((activate_Heating) && avgTemp >= set_temperature) {
  }
  if (millis() - overTemptimer >= overTemptime) {
    activate_Heating = !activate_Heating;                       // Turn off the hot end heating element.
    activate_Heating = false;                                   // Keep Heating element turned off inside this function once turned off.

    lcd.clear ();
    lcd.setCursor (0, 2);
    lcd.print ("!!HEATING ERROR!!");
    lcd.setCursor (0, 3);
    lcd.print ("POWER CYCLE");

  }
}

What does this do for you ?


  if ((activate_Heating) && avgTemp >= set_temperature) 
  {
  }


How does this work ?


unsigned long overTemptimer = millis(); // Start over temp timer

  if (millis() - overTemptimer >= overTemptime) 
 {
    activate_Heating = !activate_Heating;                       // Turn off the hot end heating element.
    activate_Heating = false;                                   // Keep Heating element turned off inside this function once turned off.

    lcd.clear ();
    lcd.setCursor (0, 2);
    lcd.print ("!!HEATING ERROR!!");
    lcd.setCursor (0, 3);
    lcd.print ("POWER CYCLE");

  }

:thinking:

Hi @LarryD

 if ((activate_Heating) && avgTemp >= set_temperature) 
  {
  }

what I would like is, if the active_heating function is on and avgTemp is grater than the set_temperature then it must start the timer.

unsigned long overTemptimer = millis(); // Start over temp timer

  if (millis() - overTemptimer >= overTemptime) 
 {
    activate_Heating = !activate_Heating;                       // Turn off the hot end heating element.
    activate_Heating = false;                                   // Keep Heating element turned off inside this function once turned off.

    lcd.clear ();
    lcd.setCursor (0, 2);
    lcd.print ("!!HEATING ERROR!!");
    lcd.setCursor (0, 3);
    lcd.print ("POWER CYCLE");

  }

The above bit must check the current time and then compare that to the over temp time,
if it is less keep counting till it is more than or equal to overTemptime and then
switch OFF the element, prevent it from being switched back on,
and print the messages on the LCD.

I know it has to be a cockup with how I have it written, it has to be..

Thanks for any help.

What do you think needs to go between the braces ?

{
       ?
}

We are guessing how this function is being called.

void safetyAbort ()

One option is to place this into loop( )


  if (thisTimer == ENABLED && millis() - overTemptimer >= overTemptime) 
 {
    activate_Heating = !activate_Heating;                       // Turn off the hot end heating element.
    activate_Heating = false;                                   // Keep Heating element turned off inside this function once turned off.

    lcd.clear ();
    lcd.setCursor (0, 2);
    lcd.print ("!!HEATING ERROR!!");
    lcd.setCursor (0, 3);
    lcd.print ("POWER CYCLE");

  }


In the Arduino IDE, use Ctrl T or CMD T to format your code then copy the complete sketch.
Use the < CODE / > icon from the ‘posting menu’ to attach the copied sketch.

1 Like

@LarryD

Ok, Posting in the code below, yes function is being called inside the loop. I thought I was using the code tags? Am I not using them correctly?

Have not made the changes will wait for you to revert once you have gone through it to get a better understanding. ISR is written for the ATmega 4809 Arduino nano Again, thank you,

/* Disclaimer!!!!!!!

   GAP RECORDINGS Pulltruder - Firmware V1.0
   If you find this project useful remember to
   give credit to the designers please.

   This is NOT intended to be used outside the scope
   of it's intended design, you consent to using this
   code and it's entirety AT YOUR OWN RISK!

   There are various sources that code snippets were used from.
   Some of this coding is mine but some of it comes from
   other libraries and other youtubers such as but
   not limited to:

   Electronoobs https://www.youtube.com/@ELECTRONOOBS,
   JRT3D, https://www.youtube.com/@JRT3D/videos
   Thank you to Mike McCauly and Patrick Wasp
   for the AccelStepper Library.
   Website: http://www.airspayce.com/mikem/arduino/AccelStepper/
   Github: https://github.com/waspinator/AccelStepper

   Finally, thank you to red_car on the Arduino Forum for help
   with the final, but all important safty coding for this project.
   https://forum.arduino.cc/u/red_car/summary

   Other coding and sources are linked in futher down!

   FINALLY, THIS PROJECT AND IT'S CODING WERE DEVELOPED AROUND
   THE ARDUINO NANO EVERY, IF YOU WANT TO USE A PLAIN NANO
   YOU WILL NEED TO REFER TO ELECTRONOOBS PROGRAMING AND ADAPT
   FROM THERE. I AM BY NO MEANS A KNOWLAGABLE PROGRAMMER!!!!!

*/

/* UI Setup: NOTES!!
    In these lines below you can set the temperature (Line 41) that you want
    the heating block to heat to. And then at (Line 42) you can set the speed
    that the motor must turn/run at. This is a (HIGHLY NON) scientific approach
    to getting the right speed for the correct extrusion width. The next (lines 44 /45)
    are for the fan hysteries, YOU MUST USE FLOATING POINT NUMERICS PLEASE!
    Failing to do this will lead to fans not running. Also ALLOW AT LEAST 2.00
    degrees centigrade between on and off so that your fans do not cycle or chatter.
*/

//UI SETUP !!!!!!NB!!!!!! PLEASE SET UP THE NEXT FEW LINES ACCORDING TO YOUR NEEDS!! Thank you.

float set_temperature = 200;     // Default temperature setpoint. Find what works and preset this here.
int pullingSpeed = 2000;         // Set speed for my motor running at 62.5kHz PWM with
/////////////////////////////////// 1/16th steps this works out to 300mm or 30cm / minute.
float setFanON = 50.00;          // Here you set the FAN ON temp.
float setFanOFF = 35.00;         // Here you can set theFAN OFF temp.


/*
   The Proccessor or PWM speed sketch is from @HiboTronix
   It can be found here: https://hibotronix.co.uk/electronics/arduino-nano-every-atmega4809-pwm-code
   NB!!!This sketch DIRECTLY INFLUENCES STEPPER SPEED! If your stepper is set to microstepping be
   forewarned that you will experiance LOWER speeds than when running @ full steps! The advantage of
   microstepping here is a more "LINEAR" pulling or extruding of the filament. The step pin is driven
   @ PWM frequency which means that at 970Hz you might only see a max of around 1000 stepps / second.

*/


/* Defining PWM Frequency */
#define PWM_NORMAL 0 // 16MHz CPU = 970Hz PWM,   20MHz CPU = 1221Hz PWM
#define PWM_MEDIUM 1 // 16MHz CPU = 31250Hz PWM, 20MHz CPU = 39063Hz PWM
#define PWM_FAST 2   // 16MHz CPU = 62500Hz PWM, 20MHz CPU = 78125Hz PWM


/* Configuring LCD Type and Address */
#include <Wire.h>                     // You will need to include this library.
#include <LiquidCrystal_I2C.h>       // And this one.
LiquidCrystal_I2C lcd(0x27, 20, 4);  // Sometimes the adress is not 0x27. Change to 0x3f if it dosn't work.


/*
   Thermistors NEED a library if you are using the 100k glass bead type from a 3D printer the below library "should" work.
   Original sketch from Andrei @ electronoobs.com
   Thermistor library also from him. PID config ALSO from Andrei
*/
#include <thermistor.h>           // Download it here: https://electronoobs.com/eng_arduino_thermistor.php
thermistor therm1(A0, 0);         // Connect thermistor on (pin) A0, (Thermistor) 0 represents TEMP_SENSOR_0 ( configuration.h for more)


/* Configuring I/O for Fans, Heater Buttons and more */
int PWM_Fan = 6;      // Pin for PWM signal to the fan MOSFET driver
int PWM_pin = 3;      // Pin for PWM signal to the heater MOSFET driver In all fairness this device will need a heatsink
int LED = 13;         // Pin for visual status of motor engaged or running
int heaterPWM = 255;  // Setting up Max PWM for Heater cartridge, this setting can also be used to limit current draw from the PSU (Setting 0 -255)


//Variables
float temperature_read = 0.0;
float PID_error = 0.0;
float previous_error = 0.0;
float elapsedTime, Time, timePrev;
int TEMP_CHECK_INTERVAL = 2000;
float PID_value = 0.0;
float last_set_temperature = 0.0;
float newTemp;
float newPID;
float currTemp;
float currPID;
float overTemptime = 1000L * 60L;

//Smoothing Temp Readings
const int numReadings = 10;
int readings[numReadings];
int readIndex = 0;
float avgTemp;
int thisReading;


// Min and Max motor speed (PWM Frequency dependant)
const int max_speed = 12000; // Max Posible speed @ specified PWM frequency.
const int min_speed = 0;     // Minimum speed = off.


// Stepper Driver pin allocation.
const int EN = 2;    // Enable disable pin to Stepper driver.
const int STEP = 5;  // Stepping pin to Stepper driver.
const int DIR = 4;   // Direction pin to Stepper driver.


// Button States for heater and stepper de/activation and run out detection.
const int but1 = 7;    // Stepper Run / Stop button.
const int but2 = 10;   // Heating on / off button.
const int Micro1 = 11; // Filament runout sensor. (still to implement Hardware)

bool but1_state = true;
bool activate_stepper = false;

bool but2_state = true;
bool activate_Heating = false;
bool heating_active = true;
bool reached_target_temp = false;

int currMicro1state = HIGH;
int prevMicro1state = HIGH;



/*
   AccelStepper is maintained and written by AirSpayce.com

   @https://www.airspayce.com/mikem/arduino/AccelStepper/

   Make sure to read all Calls and functions. This is NOT my software.
*/

#include <AccelStepper.h>
// Define a stepper and the pins it will use
AccelStepper stepper1(1, STEP, DIR); // (Type of driver: with 2 pins, STEP, DIR).

//PID Constants
//////////////////////////////////////////////////////////
int kp = 95.0;   int ki = 30.0;   int kd = 5.0;
//////////////////////////////////////////////////////////

int PID_p = 0;    int PID_i = 0;    int PID_d = 0;
float last_kp = 0;
float last_ki = 0;
float last_kd = 0;

int PID_values_fixed = 0;



void setup() {

  // set_PWM(PWM_NORMAL); // Uncomment for Normal PWM frequency.
  // set_PWM(PWM_MEDIUM); // Uncomment for Medium PWM frequency.
  set_PWM(PWM_FAST);      // Uncomment for Fast PWM frequency.

  Serial.begin (115200);

  pinMode(EN, OUTPUT);              // Setting the EN pinmode to output.
  digitalWrite(EN, HIGH);           // Writing the EN pin to Disable the stepper driver output.
  stepper1.setMaxSpeed(max_speed);  // Setting the "max Speed" Parameter for the Stepper library.
  pinMode(but1, INPUT_PULLUP);      // Setting the pin mode for button1.
  pinMode(but2, INPUT_PULLUP);      // Setting the pin mode for button2.
  pinMode (Micro1, INPUT_PULLUP);   // Setting the pin mode for MicroSwitch 1 (filament runout sensor).
  pinMode(LED, OUTPUT);             // Setting the pin mode for the LED.
  digitalWrite(LED, LOW);           // Setting the initial status of the LED to off.
  pinMode(PWM_pin, OUTPUT);         // Setting the pin mode for the heater pin.
  analogWrite(PWM_pin, 0);          // Setting the heater pin to off.
  pinMode (PWM_Fan, OUTPUT);        // Setting the pin mode for the fan pin.


  TCB0.INTCTRL |= TCB_CAPT_bm;      // Enabling Timer B0's Interrupt.
  attachInterrupt(digitalPinToInterrupt(but1), stepRun, FALLING); //Attaching interrupt to motor Run/Stop button.
  attachInterrupt(digitalPinToInterrupt(but2), butt_Heating, FALLING); // Attaching itnterrupt to Heating On/Off Button.


  lcd.init();       // Initialising LCD communication.
  lcd.backlight();  // Switching on the backlight.
  lcd.clear();      // Clearing whatever might be on the LCD.


  // Setting up and printing stationary text, this saves on processing time
  // and program memory as this does not have to be written over and over.
  lcd.setCursor(0, 0);      // Setting the cursor pozition, row 0.
  lcd.print("TA: ");        // Printing text.
  lcd.setCursor (10, 0);    // Setting the cursor pozition, row 0.
  lcd.print ("TS: ");       // Printing text.
  lcd. setCursor (0, 1);    // Setting the cursor pozition, row 1.
  lcd.print ("Speed: ");    // Printing text.
  lcd.setCursor(12, 1);     // Setting the cursor pozition, row 1.
  lcd.print("PID: ");       // Printing text.
  lcd.setCursor (0, 2);     // Setting the cursor pozition, row 2.
  lcd.print ("Motor:");     // Printing text.
  lcd.setCursor (12, 2);    // Setting the cursor pozition, row 2.
  lcd.print ("Fan: ");      // Printing text.
  lcd.setCursor (0, 3);     // Setting the cursor pozition, row 3.
  lcd.print ("Heating: ");  // Printing text.



  /*Clearing/ zeroing posible readings*/
  readings[thisReading] = 0;
}


void loop() {

  runOut();          // Filament runout function.
  butt_Heating();    // Button ON/OFF for heating.
  stepRun();         // Stepper run function.
  fan_Control();
  heating_routine(); //
  PID_Heating();     // Temperature function.
  lcdPrint();        // LCD Printing function.
  // serialPrint();
  safetyAbort ();

}



// Checking the filament sensor.
void runOut() { // GAP code

  currMicro1state = digitalRead(Micro1);                        // Setting the place holder to read a value from an Input.

  if (currMicro1state == LOW && prevMicro1state == HIGH) {      // If this is the state then do the following:
    lcd.setCursor(12, 3);                                       // Setting the Cursor position.
    lcd.print (" Extrude");                                     // Write this to the screen for this function.
  }
  else if (currMicro1state == HIGH && prevMicro1state == LOW) { // Otherwise, if this is the state then do the following:
    lcd.setCursor(12, 3);                                       // Setting the Cursor position.
    lcd.print ("!RUNOUT!");                                     // Write this to the screen for this function.
    activate_Heating = !activate_Heating;                       // Turn off the hot end heating element.
    activate_Heating = false;                                   // Keep Heating element turned off inside this function once turned off.
  } prevMicro1state = currMicro1state;                          // The previous Micro switch state is now the current state.

}



void butt_Heating() { // Chat GPT-3 Assisted code

  heating_active = true;
  if (!digitalRead(but2) && but2_state) {       // Are we reading the digital input as triggered?
    but2_state = false;                         // If not.
    activate_Heating = !activate_Heating;       // The active heating function will remain disabled.
  }

  else if (digitalRead(but2) && !but2_state) {  // If we are reading the digital input as triggered,
    but2_state = true;                          // Then the state will be LOW.
  }

  if (activate_Heating) {                       // If the active heating function is called,
    // check if the temperature has reached the target temperature
    if (!reached_target_temp) {
      // code to check temperature and wait until it reaches target temperature
      reached_target_temp = true;               //when temperature reaches target
    }
    (analogWrite(PWM_pin, PID_value));          // continue with heating code
  } else {
    // heating is not active, set reached_target_temp = false
    reached_target_temp = false;
    analogWrite(PWM_pin, 0);                    // The PWM pin for the heater is off!
  }
}


// Run or Stop the Stepper motor.
void stepRun() {

  if (!digitalRead(but1) && but1_state) {                    // Are we reading the digital input?
    but1_state = false;                                      // If not.
    activate_stepper = !activate_stepper;                    // The active Stepper function will remain disabled.
  }

  else if (digitalRead(but1) && !but1_state) {               // If the active Stepping function is called,
    but1_state = true;                                       // The step pin for the Stepper will be pullsed with the Pulling Value.
  }

  if (activate_stepper) {                                    // If the stepper function is active.
    digitalWrite(LED, HIGH);                                 // Enable the LED for visual confirmation of the command.
    digitalWrite(EN, LOW);                                   // Enable the Stepper dirver module.
    stepper1.setSpeed(pullingSpeed);                         // Set Stepper 1's speed to the pulling value.
    //stepper1.runSpeed();                                   // The speed (PWM) pulse value is handeled inside the ISR.
  }
  else                                                       // Otherwise,
  {
    digitalWrite(EN, HIGH);                                  // Disable the Stepper dirver module.
    digitalWrite(LED, LOW);                                  // Disable the LED for visual confirmation of the command.
    stepper1.setSpeed(0);                                    // Set Stepper 1's speed to the stop value.
    stepper1.runSpeed();                                     // is set to 0.
  }
}

// Fan Control.
void fan_Control() {
  if (avgTemp > setFanON) {
    analogWrite(PWM_Fan, 255);
  } else if (avgTemp < setFanOFF) {
    analogWrite(PWM_Fan, 0);
  }
}
// Function to check if heating is taking place or not and to see if temp is reached or not before calling the PID_Heating Function
void heating_routine() {

  unsigned long start_time = millis();                    // store the start time

  while (!reached_target_temp) {
    PID_Heating();                                        // check temperature and update PID values
    if (temperature_read >= set_temperature) {            // check if temperature has reached target
      reached_target_temp = true;
      break;
    }
    if (millis() - start_time >= TEMP_CHECK_INTERVAL) {   // check if TEMP_CHECK_INTERVAL has passed
      break;
    }
  }
}


// Calculating PID values and temp readings. Added fan control.
void PID_Heating() {
  // First we read the real value of temperature
  temperature_read = therm1.analog2temp(); // read temperature

  //Next we calculate the error between the setpoint and the real value
  PID_error = set_temperature - temperature_read + 4; //GAP (+6 I found this setting the error point to high  +2 was to low)
  //Calculate the P value
  PID_p = 0.01 * kp * PID_error;
  //Calculate the I value in a range on +-4
  PID_i = 0.01 * PID_i + (ki * PID_error);


  //For derivative we need real time to calculate speed change rate
  timePrev = Time;  // The previous time is stored before the actual time read
  Time = millis();  // Actual time read
  elapsedTime = (Time - timePrev) / 1000;
  //Now we can calculate the D calue
  PID_d = 0.01 * kd * ((PID_error - previous_error) / elapsedTime);
  //Final total PID value is the sum of P + I + D
  PID_value = PID_p + PID_i + PID_d;


  //We define PWM range between 0 and 255
  PID_value = min(heaterPWM, max(0, PID_value));


  previous_error = PID_error;       // Remember to store the previous error for next loop.

  // read from the sensor:
  readings[readIndex] = temperature_read;

  // advance to the next position in the array:
  readIndex = (readIndex + 1) % numReadings;

  int  total = 0;
  for (thisReading = 0; thisReading < numReadings; thisReading++) {
    total += readings[thisReading];
  }

  avgTemp = total / numReadings;



}

// Printing info to LCD.
void lcdPrint() {

  currTemp = temperature_read;           // Setting place holder to read from temp function
  if (newTemp != currTemp ) {            // If the new temp is not = to the current temp do the following:

    //   lcd.setCursor(0, 0);            // Printed in setup
    //    lcd.print("TA: ");             // Printed in setup

    lcd.setCursor(4, 0);
    lcd.print ("    ");
    lcd.setCursor(4, 0);                 // Setting Cursor Position
    lcd.print(avgTemp , 0);              // Writing new Temp Characters.


    //   lcd.setCursor (10, 0);          // Printed in setup
    //   lcd.print ("TS: ");             // Printed in setup
    lcd.setCursor(14, 0);                // Setting Cursor Position
    lcd.print(set_temperature, 0);       // Writing new SET Temp Characters.

  } newTemp = currTemp;                  // Setting new temp to the current temp


  lcd.setCursor (7, 1);                  // Setting Cursor Position
  lcd.print(pullingSpeed);               // Writing set motor Speed.


  currPID = PID_value;                   // Setting PID place holder to read actual PID Value.

  if (newPID != currPID) {               // If the new PID value is not = to the current PID value, do the following:
    // lcd.setCursor(12, 1);             // Printed in setup.
    //  lcd.print("PID: ");              // Printed in setup.
    lcd.setCursor(17, 1);                // Setting Cursor Position.
    lcd.print ("   ");                   // Used to clear provius PID Characters.
    lcd.setCursor(17, 1);                // Resetting Cursor to write new Characters.
    lcd.print(currPID, 0);               // Writing new PID Characters.

  } newPID = currPID;                    // New PID value is now the current PID value.

  if (digitalRead(LED) == HIGH) {        // If the LED is ON.
    // lcd.setCursor (0, 2);             // Printed in setup.
    // lcd.print ("Motor:");             // Printed in setup.
    lcd.setCursor (7, 2);                // Setting Cursor Position.
    lcd.print ("Run ");                  // Writing text for that set function.
  } else {                               // Otherwise if the function is:
    //  lcd.setCursor (0, 2);            // Printed in setup
    //  lcd.print ("Motor: ");           // Printed in setup
    lcd.setCursor (7, 2);                // Setting Cursor Position.
    lcd.print ("Stop");                  // Writing text for that set function.
  }
  if (avgTemp > setFanON ) {             // If the average temperature is higher than this value.
    //  lcd.setCursor (12, 2);           // Printed in setup.
    //  lcd.print ("Fan: ");             // Printed in setup.
    lcd.setCursor (17, 2);               // Setting Cursor Position.
    lcd.print ("On ");                   // Writing text for that set function.
  }

  if (avgTemp < setFanOFF) {             // If the average temperature is lower than this value.
    //  lcd.setCursor (12, 2);           //Printed in setup.
    //   lcd.print ("Fan: ");            //Printed in setup.
    lcd.setCursor (17, 2);               // Setting Cursor Position.
    lcd.print ("Off");                   // Writing text for that set function.
  }

  if (activate_Heating) {                // If the heating function is active.
    //  lcd.setCursor (0, 3);            // Printed in setup.
    //  lcd.print ("Heating: ");         // Printed in setup.
    lcd.setCursor (9, 3);                // Setting Cursor Position.
    lcd.print ("On ");                   // Writing text for that set function.
  } else {                               // Otherwise if the function is:
    // lcd.setCursor (0, 3);             // Printed in setup.
    //  lcd.print ("Heating: ");         // Printed in setup.
    lcd.setCursor (9, 3);                // Setting Cursor Position.
    lcd.print ("Off");                   // Writing text for that set function.
  }
}

void serialPrint() {

  Serial.println (avgTemp, 0);
  Serial.println (set_temperature, 0);
  Serial.println (currPID, 0);

}

void safetyAbort () {

  unsigned long overTemptimer = millis(); // Start over temp timer

  if ((activate_Heating) && avgTemp >= set_temperature) {
  }
  if (millis() - overTemptimer >= overTemptime) {
    activate_Heating = !activate_Heating;                       // Turn off the hot end heating element.
    activate_Heating = false;                                   // Keep Heating element turned off inside this function once turned off.

    lcd.clear ();
    lcd.setCursor (0, 2);
    lcd.print ("!!HEATING ERROR!!");
    lcd.setCursor (0, 3);
    lcd.print ("POWER CYCLE");

  }
}

//PWM function to set Frequency of PWM output pins.
void set_PWM(int option) {
  cli();  // Disable Interrupts
  switch (option) {
    case PWM_MEDIUM:
      TCB0_CTRLA = (TCB_CLKSEL_CLKDIV2_gc) | (TCB_ENABLE_bm);
      TCB1_CTRLA = (TCB_CLKSEL_CLKDIV2_gc) | (TCB_ENABLE_bm);
      TCB2_CTRLA = (TCB_CLKSEL_CLKDIV2_gc) | (TCB_ENABLE_bm);
      break;
    case PWM_FAST:
      TCB0_CTRLA = (TCB_CLKSEL_CLKDIV1_gc) | (TCB_ENABLE_bm);
      TCB1_CTRLA = (TCB_CLKSEL_CLKDIV1_gc) | (TCB_ENABLE_bm);
      TCB2_CTRLA = (TCB_CLKSEL_CLKDIV1_gc) | (TCB_ENABLE_bm);
      break;
    case PWM_NORMAL:
    default:
      TCB0_CTRLA = (TCB_CLKSEL_CLKTCA_gc) | (TCB_ENABLE_bm);
      TCB1_CTRLA = (TCB_CLKSEL_CLKTCA_gc) | (TCB_ENABLE_bm);
      TCB2_CTRLA = (TCB_CLKSEL_CLKTCA_gc) | (TCB_ENABLE_bm);
  }
  sei();  // Enable Interrupts
}

// ISR for buttons.
ISR(TCB0_INT_vect) {
  TCB0.INTFLAGS  =  TCB_CAPT_bm; //Clear the interrupt flag
  stepper1.runSpeed();           // Set Stepper 1's speed (PWM) to the pulling value.
}
/*End of Program */

The problem was we could not see your complete sketch.


Don't have the time to go thru the complete sketch.


Here is the idea that I was suggesting, you will need to make changes to your sketch:


#define ENABLED        true
#define DISABLED       false

bool safetyAbortFlag = DISABLED;


//timing stuff
unsigned long overTemptimer;


//********************************************^************************************************
void setup()
{
  //other code goes here

} //END of setup()


//********************************************^************************************************
void loop()
{
  //*************************************
  //other code goes here

  
  //*************************************         o v e r   t e m p   T I M E R
  //if this  "over temp TIMER" is enabled has it expired ?
  if (safetyAbortFlag == ENABLED && millis() - overTemptimer >= overTemptime)
  {
    //disable this TIMER
    safetyAbortFlag = DISABLED;

    //do your stuff
    activate_Heating = !activate_Heating;                       // Turn off the hot end heating element.
    activate_Heating = false;                                   // Keep Heating element turned off inside this function once turned off.

    lcd.clear ();
    lcd.setCursor (0, 2);
    lcd.print ("!!HEATING ERROR!!");
    lcd.setCursor (0, 3);
    lcd.print ("POWER CYCLE");
  }

}  //END of   loop()


//********************************************^************************************************
void safetyAbort()
{
  //if we are not currently timing . . . 
  if (safetyAbortFlag == DISABLED && activate_Heating && avgTemp >= set_temperature)
  {
    //enable the "over temp TIMER"
    safetyAbortFlag = ENABLED;

    //start "over temp TIMER"
    overTemptimer = millis(); 
  }

}  //END of   safetyAbort()





Note:
During the timing phase, to stop said TIMER, you can clear the TIMER flag anywhere in your code.

i.e.
safetyAbortFlag = DISABLED;

Hi, Larry.

No problem, sorry for not showing the whole thing but I did say it was a couple of 100 lines.

Thank you for taking the time to help. I will implement the changes and try it out.

Again, thank you very much.

In the code offered you, the idea is to enable a TIMER only when it is needed.

i.e.

//enable the "over temp TIMER"
safetyAbortFlag = ENABLED;


Once the Flag is enabled, we restart (start) the TIMER

//start "over temp TIMER"
overTemptimer = millis();


To prevent the function from restarting the TIMER, we add :

//if we are not currently timing . . .
if (safetyAbortFlag == DISABLED && activate_Heating && avgTemp >= set_temperature)


Finally, when the Flag is enabled, we check to see if the TIMER has expired.

//if this "over temp TIMER" is enabled has it expired ?
if (safetyAbortFlag == ENABLED && millis() - overTemptimer >= overTemptime)

This immediately caught my eye. I believe you only need one of these statements. Also, you have an empty if statement:

I hope that this is the error and it solves your problem. If not, I will see if I can locate any more errors.
All best, V

1 Like

Hi Larry,

I implemented the changes as you suggested, I had a few hiccups,
but was able to sort them.

I moved all the code into one function.
As the code is implemented now it will stop ALL functionality until the unit
is power cycled as I want it to be, same deal as when a 3d Printer has heating
problems and the whole unit stops until it is switched off and back on.

If you see something that can be "streamlined" please do let me know.
Thank you for the help!
It is very much appreciated.....


/*
   Written with help from "LarryD" on the Arduino forum
*/
void safetyAbort() { 
  /*
    If the safetyAbort function has run and been tripped do not
    let any of the other functions run until unit is powercycled
  */
  if (safetyAbortFlag == ENABLED && (activate_Heating = !activate_Heating) && avgTemp < set_temperature) {

    activate_Heating = !activate_Heating;                       // Turn off the hot end heating element.
    activate_Heating = false;                                   // Keep Heating element turned off inside this function once turned off.
    activate_stepper = !activate_stepper;                       // Keep Stepper motor turned off
    lcd.clear ();
    lcd.setCursor (0, 2);
    lcd.print ("!!HEATING ERROR!!");
    lcd.setCursor (0, 3);
    lcd.print ("POWER CYCLE");
  }

  /*
     safetyAbort Sanity Checks
  */
  if (safetyAbortFlag == ENABLED && (activate_Heating) && avgTemp <= set_temperature) {
    safetyAbortFlag = DISABLED;
  }

  /*
     If safetyAbort is triggered
  */
  if (safetyAbortFlag == DISABLED && (activate_Heating) && avgTemp > set_temperature)
  {
    //enable the "over temp TIMER"
    safetyAbortFlag = ENABLED;

    //start "over temp TIMER"
    overTemptimer = millis();
  }

  /*
     O v e r  T e m p  T I M E R
  */
  //if this  "over temp TIMER" is enabled has it expired ?
  if (safetyAbortFlag == ENABLED && millis() - overTemptimer >= overTemptime) {

    //keep this TIMER enabled
    safetyAbortFlag = ENABLED;

    //do your stuff
    activate_Heating = !activate_Heating;                       // Turn off the hot end heating element.
    activate_Heating = false;                                   // Keep Heating element turned off inside this function once turned off.
    activate_stepper = !activate_stepper;                       // Keep stepper motor turned off

    lcd.clear ();
    lcd.setCursor (0, 2);
    lcd.print ("!!HEATING ERROR!!");
    lcd.setCursor (0, 3);
    lcd.print ("POWER CYCLE");
  }
}  //END of safetyAbort()


if (safetyAbortFlag == ENABLED && (activate_Heating = !activate_Heating) && avgTemp < set_temperature) {

What do you think this does ?

(activate_Heating = !activate_Heating)


if (safetyAbortFlag == ENABLED && millis() - overTemptimer >= overTemptime) {

//keep this TIMER enabled
safetyAbortFlag = ENABLED;

Not sure what you need (please explain), you probably want: :thinking:
safetyAbortFlag = DISABLED;


Hi Larry..

Yes, I saw that, have adapted the code, stepper was also not behaving as expected.

What I want is: if the flag was enabled and the temp has come down
the unit must not be able to be reheated without being power cycled.

What was happening without that if statement was that once the temps came down
once the temps came down I could push the heating button and the unit would start reheating and ignore the set error. If that error state has been set and run i do not want the unit to be able to reheat with out being reset.

Thank you for the heads up!


/*
   Written with help from "LarryD" on the Arduino forum
*/
void safetyAbort() { 
  /*
    If the safetyAbort function has run and been tripped do not
    let any of the other functions run until unit is powercycled
  */
  if (safetyAbortFlag == ENABLED && avgTemp < set_temperature) {

    activate_Heating = !activate_Heating;                       // Turn off the hot end heating element.
    activate_Heating = false;                                   // Keep Heating element turned off inside this function once turned off.
    but1_state = false;                                     
    activate_stepper = !activate_stepper;                       // The active Stepper function will remain disabled.
    digitalWrite(EN, HIGH);                                     // Writing the EN pin to Disable the stepper driver output.
    analogWrite(PWM_pin, 0);                                    // Setting the heater pin to off.
   
    lcd.clear ();
    lcd.setCursor (0, 2);
    lcd.print ("!!HEATING ERROR!!");
    lcd.setCursor (0, 3);
    lcd.print ("POWER CYCLE");
  }

  /*
     safetyAbort Sanity Checks
  */
  if (safetyAbortFlag == ENABLED && (activate_Heating) && avgTemp <= set_temperature) {
    safetyAbortFlag = DISABLED;
  }

  /*
     If safetyAbort is triggered
  */
  if (safetyAbortFlag == DISABLED && (activate_Heating) && avgTemp > set_temperature)
  {
    //enable the "over temp TIMER"
    safetyAbortFlag = ENABLED;

    //start "over temp TIMER"
    overTemptimer = millis();
  }

  /*
     O v e r  T e m p  T I M E R
  */
  //if this  "over temp TIMER" is enabled has it expired ?
  if (safetyAbortFlag == ENABLED && millis() - overTemptimer >= overTemptime) {

    //keep this TIMER enabled
    safetyAbortFlag = ENABLED;

    //do your stuff
    activate_Heating = !activate_Heating;                       // Turn off the hot end heating element.
    activate_Heating = false;                                   // Keep Heating element turned off inside this function once turned off.
    but1_state = false;                                      
    activate_stepper = !activate_stepper;                       // The active Stepper function will remain disabled.
    digitalWrite(EN, HIGH);                                     // Writing the EN pin to Disable the stepper driver output.
    analogWrite(PWM_pin, 0);                                    // Setting the heater pin to off.
    
    lcd.clear ();
    lcd.setCursor (0, 2);
    lcd.print ("!!HEATING ERROR!!");
    lcd.setCursor (0, 3);
    lcd.print ("POWER CYCLE");
  }
}  //END of safetyAbort()

Hope this makes sense?
Regards

This

activate_Heating = !activate_Heating;
activate_Heating = false;

is the same as this

activate_Heating = false;


This

if (safetyAbortFlag == ENABLED && (activate_Heating) && avgTemp <= set_temperature)
{
safetyAbortFlag = DISABLED;
}

can be changed to:

if (activate_Heating && avgTemp <= set_temperature)
{
safetyAbortFlag = DISABLED;
}


  //if this  "over temp TIMER" is enabled has it expired ?
  if (safetyAbortFlag == ENABLED && millis() - overTemptimer >= overTemptime) {

    //keep this TIMER enabled
    safetyAbortFlag = ENABLED;
. . .

Leaving safetyAbortFlag ENABLED and when the TIMER is expired, makes the code between { } run over and over.

consider

const byte PinHeat = LED_BUILTIN;
const byte PinTemp = A0;
const byte PinBut  = A1;

byte butLst;

const int TempThresh = 500;

const unsigned long MsecTempTimeout = 5000;
unsigned long msecPeriod;
unsigned long msecLst;

enum { Off = HIGH, On = LOW };

enum { HtrOff, HtrOn, HtrTimeout };
int state = Off;

// -----------------------------------------------------------------------------
void
loop (void)
{
    // timer
    unsigned long msec = millis ();
    if (HtrOn == state && msec - msecLst >= msecPeriod) {
        state = HtrTimeout;
        digitalWrite (PinHeat, Off);
        Serial.println (" timeout");
    }

    // thermostat
    int temp = analogRead (PinTemp);
    if (TempThresh > temp && HtrOff == state)  {
        state      = HtrOn;
        msecLst    = msec;
        msecPeriod = MsecTempTimeout;
        digitalWrite (PinHeat, On);
        Serial.println (" heater on");
    }
    else if (HtrOff != state && TempThresh < temp)  {
        state = HtrOff;
        digitalWrite (PinHeat, Off);
        Serial.println (" heater off");
    }

    // check button for heater reset
    byte but = digitalRead (PinBut);
    if (butLst != but)  {
        butLst = but;
        delay (20);         // debounce

        if (LOW == but)     // button pressed
            state = HtrOff;
    }
}

void
setup (void)
{
    Serial.begin (9600);

    pinMode (PinHeat, OUTPUT);
    digitalWrite (PinHeat, Off);

    pinMode (PinBut,  INPUT_PULLUP);
    butLst = digitalRead (PinBut);
}

Hi,

Is this to streamline the code or is this for a specific part?
Sorry if this is a stupid question I am not the sharpest tool in the shed
when it comes to all the programming.

Thanks in advance!

@LarryD

Hi Larry,

So, the below code is what I have done, and this is working now.
Below this I will address your above question.

/*
   Written with help from "LarryD" on the Arduino forum
*/
void safetyAbort() {
  /*
    If the safetyAbort function has run and been tripped do not
    let any of the other functions run until unit is powercycled
  */
  if (safetyAbortFlag == ENABLED && avgTemp < set_temperature) {
    
    activate_stepper = false;             // Stop stepper and keep it off
                                          
    activate_Heating = false;             // Stop heating and keep it off

    digitalWrite(EN, HIGH);                // Writing the EN pin to Disable the stepper driver output.
    analogWrite(PWM_pin, 0);               // Setting the heater pin to off.

    lcd.clear();
    lcd.setCursor(0, 2);
    lcd.print("!!HEATING ERROR!!");
    lcd.setCursor(0, 3);
    lcd.print("POWER CYCLE");
  }

  /*
     safetyAbort Sanity Checks
  */
  if (safetyAbortFlag == ENABLED && (activate_Heating) && avgTemp <= set_temperature) {
    safetyAbortFlag = DISABLED;
  }

  /*
     If safetyAbort is triggered
  */
  if (safetyAbortFlag == DISABLED && (activate_Heating) && avgTemp > set_temperature) {
    //enable the "over temp TIMER"
    safetyAbortFlag = ENABLED;

    //start "over temp TIMER"
    overTemptimer = millis();
  }

  /*
     O v e r  T e m p  T I M E R
  */
  //if this  "over temp TIMER" is enabled has it expired ?
  if (safetyAbortFlag == ENABLED && millis() - overTemptimer >= overTemptime) {

    //keep this TIMER enabled
    safetyAbortFlag = ENABLED;

    activate_stepper = false;              // Stop stepper and keep it off

    activate_Heating = false;              // Stop heating and keep it off

    digitalWrite(EN, HIGH);                // Writing the EN pin to Disable the stepper driver output.
    analogWrite(PWM_pin, 0);               // Setting the heater pin to off.

    lcd.clear();
    lcd.setCursor(0, 2);
    lcd.print("!!HEATING ERROR!!");
    lcd.setCursor(0, 3);
    lcd.print("POWER CYCLE");
  }
}  //END of safetyAbort()

The reason I changed that specific part to "enabled" rather than leave it at "disabled" was because:

After the function had been triggered it would stop the heating and stepper functions,

but, after the hotend had cooled sufficiently the heating and stepping could be re-enabled via the buttons even though the abort has been run and triggered, this is not what is desired.

I want the whole system to stop, the operation must be aborted and the the system must be
power cycled for the error to be cleared, one must not be able to access any of the functions till this has been done.

Is there another way to do this? Changing that safetyAbortFlag to enabled there, was the only thing I found that would get the desired effect. Can this be done in another way?
If that safetyAbort Function has indeed been triggered that the system will be halted?

Regards Charles

seemed like your code was becoming overcomplicated. or am i missing something?

Hi Greg.

If it is possible to condense the code down yes please,
but, as I stated I am by no means the sharpest tool
in the shed when it comes to coding.

So, if this could help could you please explain the code to me?

I would need to understand how it works in order to be able to
implement it further down into my existing code.

The actual issue for me is getting an abort function to work properly
for the sake of safety.

Thank you for your time.

const byte PinHeat = LED_BUILTIN;
const byte PinTemp = A0;
const byte PinBut  = A1;

byte butLst;

const int TempThresh = 500;

const unsigned long MsecTempTimeout = 5000;
unsigned long msecPeriod;
unsigned long msecLst;

enum { Off = HIGH, On = LOW };

enum { HtrOff, HtrOn, HtrTimeout };
int state = Off;

// -----------------------------------------------------------------------------
void
loop (void)
{

the following is a basic thermostat, if the temperature is less than (not <=) the threshold and heater is off, turn the heater on. but it also needs to capture the current timestamp, msecLst, sets a timer period and sets a state variable indicating the heater is on

    // thermostat
    int temp = analogRead (PinTemp);
    if (TempThresh > temp && HtrOff == state)  {
        state      = HtrOn;
        msecLst    = msec;
        msecPeriod = MsecTempTimeout;
        digitalWrite (PinHeat, On);
        Serial.println (" heater on");
    }

the following turns the heater off if the timer exceeds the threshold

    else if (HtrOff != state && TempThresh < temp)  {
        state = HtrOff;
        digitalWrite (PinHeat, Off);
        Serial.println (" heater off");
    }

the following checks if the time period since when the heater was turned on exceeds the time period. it turns the heater off but also indicate that the timeout occured

    // timer
    unsigned long msec = millis ();
    if (HtrOn == state && msec - msecLst >= msecPeriod) {
        state = HtrTimeout;
        digitalWrite (PinHeat, Off);
        Serial.println (" timeout");
    }

checks if a button is pressed and unfortunately only sets the state to off -- which is a bug if the state is not timeout !!

this can easily be corrected by checking that the state is HtrTimeout before setting it HtrOff

    // check button for heater reset
    byte but = digitalRead (PinBut);
    if (butLst != but)  {
        butLst = but;
        delay (20);         // debounce

        if (LOW == but)     // button pressed
            state = HtrOff;
    }
}

besides configuring the pins, setup() makes sure the timer if off and captures the current button state

void
setup (void)
{
    Serial.begin (9600);

    pinMode (PinHeat, OUTPUT);
    digitalWrite (PinHeat, Off);

    pinMode (PinBut,  INPUT_PULLUP);
    butLst = digitalRead (PinBut);
}