Vacuum Former Done!

New circuit board arrived, firmware written, all wiring complete, and it’s working! I posted some info in an instructable here: https://www.instructables.com/id/Revive-a-Vacuum-Former/

The rest of this post is for future-me if I need to troubleshoot my work and can’t find it on my hard drive:

//Replacement controller for Clarke Vacuum Former 1820
//Elliotmade 4/27/2020
//Manual for the machine: https://www.abbeon.com/ItemFiles/Manual/1820.pdf

/*
 * Components attached to the arduino:
 * 4 heater relays (12v, using an IRFZ44 n-channel mosfet to switch these
 * 1 buzzer relay
 * 4 heater potentiometers
 * 2 7-segment displays each using a TM1637 driver
 * 1 Rotary encoder with button
 * 2 existing switches from the machine for the heater location
 * 
 * Operation should be similar to this:
 * Power on the machine, switch heaters on and off slowly according to power level set on the knobs
 * Timer set value is stored in eeprom, displayed on the first 7-segment display
 * Changing the timer set value is done with the rotary encoder
 * Extending the heater triggers a switch and the timer should count down on the second display
 * When the timer reaches zero the buzzer should be turned on
 * Retracting the heater triggers a switch and the timer should be reset and buzzer turned off
 * 
 * Pay attention to the heater position switches and which order they get pushed in
 * States for the timer:
 *  Not running, buzzer not triggered (normal idle status, assume we started from here)
 *  Running, buzzer not triggered (heater extended)
 *  Running, buzzer triggered (heater extended, but time has run out)
 *  Not running, buzzer not triggered (heater retracted, timer is reset and buzzer is canceled)
 */

//////////////////////////////////////////////libraries////////////////////////////////////////////////////////////
#include <RotaryEncoder.h>
//http://www.mathertel.de/Arduino/RotaryEncoderLibrary.aspx
//https://github.com/mathertel/RotaryEncoder

#include "OneButton.h"
//https://github.com/mathertel/OneButton

#include <SimpleTimer.h>
//https://github.com/jfturcot/SimpleTimer

#include <TM1637Display.h>
//https://github.com/avishorp/TM1637

#include <EEPROMex.h>
//https://github.com/thijse/Arduino-EEPROMEx

//////////////////////////////////////////Pins////////////////////////////////////////////////////////////////
const int encoderButton = A1;
const int encoderA = A2;
const int encoderB = A3;
const int timerStart = 7; //D7
const int timerReset = 6; //D6
const int buzzer = 8; //D8
const int dispClock1 = 2; //D2
const int dispClock2 = 4; //D4
const int dispData1 = 3; //D3
const int dispData2 = 5; //D5
const int heatRelay[4] = {12,11,10,9}; //D9-D12
const int heatKnob[4] = {A4,A5,A6,A7};



///////////////////////////////////////////Constants///////////////////////////////////////////////////////////////
//This will impact the duration of the on/off cycles for the heaters.  Milliseconds.  Heaters should stay on for the duty cycle percent, then off for 100-duty cycle
const unsigned long heatIntervalMult = 1000; 

//read heater knobs and update heaters every (milliseconds)
const int updateHeatInterval = 500;

//minimum duty cycle for heaters (old controls were approx. 55% according to the manual "power reduction is 45% on setting 1"
const int minDutyPct = 55;

//thresholds for knob to identify zero and max heat (0-1023)
const int minThreshold = 30;
const int maxThreshold = 1000;

const byte heaterCount = 4;

//eeprom memory locations
const int minAddress = 10;
const int secAddress = 20;
const int memBase = 350;

////////////////////////////////////////////Variables//////////////////////////////////////////////////////////////

bool heatStatus[4] = {false,false,false,false};   //current on/off status of a heater
int heatSetting[4] = {0,0,0,0};                   //current heat setting 0-100 percent, updated when the knobs are read
unsigned long heatLastChange[4] = {0,0,0,0};      //last time in milliseconds that the heater was turned on
bool timerRunning = false;                        //state of the timer
bool buzzerOn = false;                            //state of the buzzer
bool minutesMode = false;                         //change behavior of encoder based on this
int minutes;                                      //timer set time
int seconds;                                      //timer set time
int elapsedMinutes = 0;                           //timer current countdown time
int elapsedSeconds = 0;                           //timer current countdown time
bool rollover = false;                            //used to help display the countdown

//Initialize some things
SimpleTimer countdownTimer;
RotaryEncoder encoder(encoderA, encoderB);
OneButton startSwitch(timerStart, true);
OneButton resetSwitch(timerReset, true);
OneButton encoderButt(encoderButton, true);
TM1637Display display1(dispClock1, dispData1);
TM1637Display display2(dispClock2, dispData2);



void setup() { ////////////////////////////////////////Setup///////////////////////////////////////////////////
//Serial for debugging
Serial.begin(9600);

Serial.println("Setup Start");

//interrupts for encoder
PCICR |= (1 << PCIE1);    // This enables Pin Change Interrupt 1 that covers the Analog input pins or Port C.
PCMSK1 |= (1 << PCINT10) | (1 << PCINT11);  // This enables the interrupt for pin 2 and 3 of Port C.

// Set Up EEPROM
EEPROM.setMemPool(memBase, EEPROMSizeNano);

//pins
pinMode(buzzer, OUTPUT);
for (byte i = 0; i <= heaterCount - 1; i++) {
  pinMode(heatRelay[i], OUTPUT);
  pinMode(heatKnob[i], INPUT);
}

//button functions
startSwitch.attachClick(startTime);
resetSwitch.attachClick(resetTime);
encoderButt.attachClick(encoderClick);
encoderButt.attachLongPressStart(saveTime);

//Load the stored timer value
minutes = EEPROM.readInt(minAddress);
seconds = EEPROM.readInt(secAddress);


//displays
display1.setBrightness(2);
display2.setBrightness(2);
display1.showNumberDec(8008);
display2.showNumberDecEx(8008,0x40,true);
delay(50);

updateDisplay1();

countdownTimer.setInterval(1000,incrimentTimer); //make timer count every second
countdownTimer.setInterval(updateHeatInterval,readKnobs);
countdownTimer.setInterval(250,updateHeatStatus);
countdownTimer.setInterval(250,updateHeatRelays);

Serial.println("Setup Complete");
} //////////////////////////////////////////////End Setup/////////////////////////////////////////////////////





void loop() { ////////////////////////////////////Loop/////////////////////////////////////////////////////
  //monitor buttons
  startSwitch.tick();
  resetSwitch.tick();
  encoderButt.tick();
  readEncoder();
  countdownTimer.run();
  checkTimer();
  updateDisplay1();
  updateDisplay2();
  updateBuzzer();
} //////////////////////////////////////////////End Loop/////////////////////////////////////////////////////


void encoderClick() { //When the encoder button is clicked, change from minutes to seconds for timer adjustment
 
  minutesMode = !minutesMode; //toggle time setting modes
  Serial.print("Mode: ");
  Serial.println(minutesMode);
}

void readEncoder() {
      static int pos = 0;
      int newPos = encoder.getPosition();
      

      if (!timerRunning) { //only let the set time be changed when the timer is not counting down
        if (pos != newPos) {
  
        
          if (minutesMode) {
          minutes = minutes + (newPos - pos);
          
          }
          else {
            seconds = seconds + (newPos - pos);
            if (seconds == 60) {
              seconds = 0;
              minutes++;
            }
            if (seconds < 0) {
              seconds = 59;
              minutes--;
            }
          }
          pos = newPos;
  
          minutes = constrain(minutes,0,59);
          seconds = constrain(seconds,0,59);
          
          Serial.print("Minutes: ");
          Serial.print(minutes);
          Serial.print(" Seconds: ");
          Serial.println(seconds);
        }
      }
      else {
        encoder.setPosition(pos); //reset the encoder, as if it didn't move while the timer was counting
      }
}

void startTime() { //When the heater is extended, start counting down
  if (!timerRunning) {
    timerRunning = true;
    elapsedSeconds = 0;
    elapsedMinutes = 0;
    Serial.println("Timer Started");
  }
}

void resetTime() { //When the heater is retracted, stop counting down, rest the timer, and shut off the buzzer
  if (timerRunning) {
    timerRunning = false;
    buzzerOn = false;
    elapsedMinutes = 0;
    elapsedSeconds = 0;
  }
  updateBuzzer();
}

void saveTime() { //When the encoder knob is held down, save the set time to EEPROM
  EEPROM.writeInt(minAddress, minutes);
  EEPROM.writeInt(secAddress, seconds);
  Serial.println("Timer setting saved to EEPROM");
}

void incrimentTimer() { //update the elapsed time variables after each second passes
  if (timerRunning) {
    elapsedSeconds++;  
    if (elapsedSeconds == seconds && rollover == false) {  //used to help display differently if the original number of seconds have passed
        rollover = true;
        elapsedSeconds = 60;
        elapsedMinutes++;
    }
    if (elapsedSeconds == 60) {
      elapsedSeconds = 0;
      elapsedMinutes++;
    }
  }
}

void checkTimer() { //If the timer is running, sound the buzzer once it has reached zero
  if (elapsedMinutes >= minutes && (elapsedSeconds % 60) >= seconds && !buzzerOn) {
    Serial.println("Timer has reached zero");
    buzzerOn = true; //turn on the buzzer
    elapsedSeconds = 0; //reset the elapsed time so it can be displayed counting up easily
    elapsedMinutes = 0;
  }
}

void updateBuzzer() { //Turn buzzer on/off based on the variable
  if (buzzerOn) {
    digitalWrite(buzzer, LOW);
    //Serial.println("Buzzer On");
  }
  else {
    digitalWrite(buzzer, HIGH);
    //Serial.println("Buzzer Off");
  }
}

void updateDisplay1() {
  display1.showNumberDecEx(minutes * 100 + seconds, 0x40, true);  //Set time should always show on display 1
}

void updateDisplay2() {
  if (timerRunning) {
    if (buzzerOn) { //count up if the buzzer is on (timer has reached zero)
      display2.showNumberDecEx((elapsedMinutes) * 100 + (elapsedSeconds), 0x40, true); //count up.  These were reset to 0 when the timer ran out
    }
    else if (rollover == true) {  //count down from 60 seconds
      display2.showNumberDecEx((minutes - elapsedMinutes) * 100 + (60 - elapsedSeconds), 0x40, true); //count down
    }
    
    else { //count down from the original number of seconds
      display2.showNumberDecEx((minutes - elapsedMinutes) * 100 + (seconds - elapsedSeconds), 0x40, true); //count down
    }
  }
  else {
    display2.showNumberDecEx(minutes * 100 + seconds, 0x40, true); //show the same thing on 2 as 1
  }

}

void readKnobs() { //read knob position and update the heat setting array
  for (byte j = 0; j <= heaterCount - 1; j++) {

    if (analogRead(heatKnob[j]) < minThreshold) {
      heatSetting[j] = 0;
    }
    else if (analogRead(heatKnob[j]) > maxThreshold) {
      heatSetting[j] = 100;
    }
    else {
      heatSetting[j] = map(analogRead(heatKnob[j]),0,1023,minDutyPct * 10,maxThreshold)/10;
    }

  
  }
//Serial.println(" ");
}

void updateHeatStatus() { //update the on/off array for each heater based on the knob position
  //calculate the time to the next change
unsigned long curMillis = millis();

  for (byte m = 0; m <= heaterCount - 1; m++) {
    if (heatSetting == 0 && heatStatus[m] == true) {          //fully off if the knob is at the minimum position
      heatStatus[m] = false;
      heatLastChange[m] = curMillis;
    }
    else if (heatSetting == 100 && heatStatus[m] == false) {   //fully on if the knob is at the max position
      heatStatus[m] = true;
      heatLastChange[m] = curMillis;
    }
    
    else if (heatStatus[m] == true) {
      if (heatLastChange[m] + heatSetting[m] * heatIntervalMult < curMillis) {  //if the heater is currently on, wait until last change time + (duty cycle * multiplier) seconds have gone by then turn it off
        heatStatus[m] = false;
        heatLastChange[m] = curMillis;
      }
    }
    else {  //if the heater is currently off, wait until last change time + (100 - duty cycle * multiplier) seconds have gone by then turn it on
      if (heatLastChange[m] + (100 - heatSetting[m]) * heatIntervalMult < curMillis) {  //if the heater is currently off, wait until last change time + (100 - duty cycle) * multiplier seconds have gone by then turn it off
        heatStatus[m] = true;
        heatLastChange[m] = curMillis;
      }
    }


  }

  
}

void updateHeatRelays() { //Turn heaters relays on or off
  for (byte k = 0; k <= heaterCount - 1; k++) {
  
    if(heatStatus[k] == true) {
      digitalWrite(heatRelay[k], HIGH);
    }
    else { 
      digitalWrite(heatRelay[k], LOW);
    }
}


}

ISR(PCINT1_vect) { // The Interrupt Service Routine for Pin Change Interrupt 1
  encoder.tick(); // just call tick() to check the state.
}

Leave a Reply

Your email address will not be published. Required fields are marked *