Introduction: Revive a Vacuum Former

I recently became the surprise owner of this CR Clarke Vacuum Former 1820. It was destined for the scrap heap, so instead it ended up in the back of my truck. This is my entry to the "Finish it already" contest because after a quick look it was clear that someone else started a repair project and never completed it.

Besides being an unfinished project, if I can bring this thing back to working order it gives me a chance to learn about the vacuum forming process and probably make some cool stuff along the way.

My goal for this instructable isn't necessarily to help you repair your own Vacuum Former 1820 (seems like the odds of anyone having one in this same situation is highly unlikely), but instead explain some of the techniques you can use to fix something that you may not be familiar with, or that someone else took apart and you don't have any information about how it goes back together.

Step 1: Inspection - What Are We Doing Here?

The first decision to make is what you plan to do with the thing. Ideally you do this before you own it.

  • Is it repairable? In this case I decided yes.
  • Is it worth repairing? Again, yes. I guessed that a couple hundred dollars would be enough to get it going again which seemed reasonable compared to the cost of a working or DIY vacuum former. It was clear the cost of having the manufacturer service it or provide replacement parts would be prohibitive, so I would be on my own.
  • What are you going to do with it? I'm going to use it to make cool stuff!

Second - figure out what condition it's in

  • Lots of corrosion on most of the connectors
  • Signs of a previous electrical problem (fire!)
  • Some of the heater control knobs damaged
  • Someone has already been messing with the wiring (this is probably the most difficult problem)
  • The heaters and vacuum pump appear to be in good shape.

Step 2: Reverse Engineering

Having never used this machine, the first thing to do was figure out how it was supposed to work before we can go about repairing it. Luckily the manufacturer is still in business, and even though this machine seems quite old I was able to find the manual for the current model online here: https://www.abbeon.com/ItemFiles/Manual/1820.pdf

There are also a few youtube videos demonstrating the usage of the machine, like this one:

From this information I could see that the operation is pretty simple:

  1. Heaters warm up in the back position
  2. A mold/form goes on the platen that moves up and down
  3. Plastic is clamped down over the platen
  4. Heaters pull out to warm up the plastic and also start a countdown timer
  5. When the timer reaches zero a buzzer turns on, and when the heater is put back the buzzer is turned off
  6. Vacuum pump is turned on and the platen is raised
  7. After the plastic hardens the platen is lowered and vacuum turned off

Behind the control panel is all analog/electromechanical: relays and switches only, nothing digital.

Step 3: Come Up With a Plan

Now that I had an idea how it is supposed to work, I needed to decide what to do. One important thing that I noted is that the machine runs on 220v AC, and all of the wiring in the control panel is at 220v (even the timer). Given the corroded and confusing state of the wires, I decided it would be far safer to throw that all away and start from scratch. An arduino was an obvious choice that will allow me to replace almost everything with low-voltage DC except for the heating elements themselves, and as a bonus it gives me the ability to test and troubleshoot on my bench without lower risk of electrocution.

Here's what I planned:

  • Add a 12v power supply
  • Replace the heater relays with new 12v relays
  • Replace the heat control knobs with potentiometers, reusing the existing knobs
  • Replace the timer with a rotary encoder and two seven-segment displays
  • Keep the 220v buzzer, using a relay to control it
  • Keep the 220v mains indicator and heater lights

The total cost of new relays, a power supply, a custom PCB, and all of the connectors and various electronic components worked out to about $100 which I think is perfectly reasonable.

Step 4: Designing a New Circuit

The design for this circuit was based on the decisions I made in the previous step. I recommend working backwards from the list of things you need to control in order to figure out what your circuit needs to include. I've got a pile of Arduino Nano clones around, so my main limitation was the number of pins available for input/output. Luckily the number of things that I needed to control matched up pretty well with this device, on something more complicated I might choose to use a MEGA, or maybe something else entirely.

The first diagram above was drawn with Digikey's Scheme-it which is really handy for sketching thing out like this: https://www.digikey.com/schemeit/project/.

I wanted my timer replacement to show the set time and the current countdown, so that meant a display that could show two numbers at once. I considered using an OLED or similar graphical display, but ultimately decided that seven segment displays would be the easiest to read. There are many ways to drive these from an arduino - driving them directly was not an option due to how many pins it would require, so I went with the TM1637 drivers so that each one only required two pins each. A rotary encoder is used to set and save the time for the timer.

The 12v relays are switched on/off with a N-Channel MOSFET (IRFZ44) because the Arduino is not designed to switch that high of voltage (also current). The ground connection for the relays is broken out on a separate pair of screw terminals - the point of this is to use a switch to open the circuit when we don't want the heaters on; this means that we don't have to worry about doing that in the firmware.

I lucked out this time and didn't make any mistakes on the circuit or PCB design, the first one I soldered up worked perfectly!

Step 5: Writing Firmware

I made an effort to put a lot of notes in this code so that it is easy to understand/troubleshoot in the future. The complete code likely won't be useful to anyone but me, but there may be some techniques in there that can be learned from (and I welcome feedback if you know better than I do!). I started with a list of what the code needed to do:

  1. Determine the heat setting level from the 4 heat knobs on the front
  2. Turn the heater relays on and off periodically
  3. Display a set time and countdown time on the 7-segment displays
  4. Set and save a new time that is input with the rotary encoder
  5. Start the timer when the heater is pulled out, sound a buzzer when it hits zero, and reset when the heater is put back

Important note about the heaters: the relays are only on and off, so different heat levels are accomplished by switching them on/off with a varying duty cycle. The arduino analogWrite() function does exactly this, but it is useless in this application because of the frequency. By default the PWM frequency is about 1000 hz, and if we switch our relays on/off this quickly they will be destroyed in short order. The manual for the vacuum former had a couple of hints about this, first was that the heaters should switch on/off about once per minute, and second that the minimum heat setting of "1" would provide about a 45% reduction in heat (compared to max)... this means that in addition to switching more slowly, we also have to vary the duty cycle between 55-100% not 0-100%.

The best advice I have is to write and test in small chunks: write a small part of the functionality, flash it to the device, and test it on your bench. Don't move on until you get it right. This makes troubleshooting much easier when you know that only a small amount has changed between the previously working version and the most recent test. Here is a brief video of the heater relays switching, I was able to test this on the bench without messing with AC mains voltage and without needing to have my computer next to the machine in the shop.

This may be easier to read if you paste it into the Arduino IDE, notepad++, or anything else with syntax highlighting.


//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
}


// if (buzzerOn) { //count up if the buzzer is on (timer has reached zero)
// display2.showNumberDecEx(elapsedMinutes * 100 + (elapsedSeconds % 60), 0x40, true); //count up. These were reset to 0 when the timer ran out
// }
// else {
// display2.showNumberDecEx((minutes - elapsedMinutes) * 100 + (seconds - (elapsedSeconds % 60)), 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.
}

Step 6: Testing

I moved the entire control panel onto my bench and was able to test everything right there. The moment of truth was plugging the thing into the wall, and fortunately no fire and no problems. I slowly tested each function one at a time and confirmed they all still worked exactly like on the bench, and that the heaters and vacuum pump also worked. Normally this is when you start troubleshooting, but I didn't have much go wrong at all - the only problem was that I swapped the plugs for the zone 3 and zone 4 potentiometer knobs - easy fix.

Once I confirmed that we weren't on fire and nobody was being electrocuted I had to give it a try. Being unfamiliar with the process and unprepared with a mold or materials I just grabbed whatever was handy and gave it a whirl.

Step 7: One Final Improvement and Done!

Since I don't have any experience with the vacuum forming process yet, I'd like to start small and be economical with plastic because I expect to make plenty of mistakes. The manufacturer of this machine still sells a reducing plate, but I'm sure it costs more than I will have invested in this whole deal... so I had a look at a couple photos and made one up myself.

Finish It Already Speed Challenge

Participated in the
Finish It Already Speed Challenge