Final project

Final project - Derek Covill

Carbon ply - the open source carbon fibre curing oven

home

Here's the final result!

Here's a link to my final project slide and video presentation:

final project slide: presentation.png

final project video: presentation.mp4

And here are the files to download:

download a .zip file with all the CAD part+assembly files here.

And you can download the buttons.stl file here.

And you can download a .zip file with all the Eagle PCB design files here.

download the manual temperature control programme .ino file to download

download the 85 degree curing cycle programme .ino file here.

download the programme using PID to control temperature .ino file here.

Contents

Project Summary

Here are two final project videos, I wasn't sure which one to go with.

Here's what was I did, with links to respective weekly assignments:

Background

I was quite inspired by Denis Terwagne's 2017 final project which was a physics tinking kit, to provide a cleverly designed platform for students to play with various aspects relating to physics theory.
Denis Terwagne's 2017 final project
I teach product design and development, prototyping and CAD/CAE, but one of the areas I want to focus on in the coming years is electronic prototyping, so I would like my final project to be a electronic product design kit that supports students' learning and experimenting as they prototype with electronics. It would also be great to compile some useful resources in this area. My colleague, Dr Jerome Leary has produced a useful site for digital fabrication tools and something to complement this for our students would be ideal. I would like to produce some fabacademy style resources for my design project modules (DP401 beautiful project, DP402 durable project, DP403 usable project, DP404 digital fabrication methods, DP504 digital fabrication project).

The main one to focus on will be the DP403 project, and I'd like to develop a small customised PID controlled carbon fibre curing oven for small carbon fibre samples. It will be something like this (here's the link), only smaller since we only really work with small samples.

diy curing oven

Here are some samples we've produced lately using the large Carbolite oven. The problem we have at the moment is that the curing cycle for these carbon parts is a whole day (9-14 hours), so if we can have a small oven design specifically for the samples we need for our materials testing labs, then we can free up the oven for other projects, like forming shapes in acryllic etc.

vacuum bagging in the Carbolite oven

typical carbon fibre samples produced

Here are a few ducks we've recently moulded in a two part mould - we use this as a teaching exercise with our first year product designers. The idea is that we can do more of this kind/scale of work with my oven...we may even develop several ovens!

carbon fibre duck

uncured carbon fibre duck

here's an example of the typical curing cycle that we need for our prepreg samples. They can even go down to a cycle with a maximum temperature of only 80deg C, with still some very strong parts - the only down side is that the glass transition temperature of the parts will be lower if you cure it at a lower temperature curing cycle.

typical curing cycle for our carbon fibre samples

And there's also a low temperature curing cycle, which I will aim for with my oven.

Project scope

This project will produce a small carbon fibre curing oven that is built up in stages (ultimately as a learning kit) using spiral development to help students learn the basics of electronics, programming and heat power/energy. From this, they can learn the basics needed to help them design toasters, kettles, bread makers, thermostats, heaters, etc. The basic specification for this is that it should:

1. Connect a small heating device with a power supply and switch to heat up a small thermal mass.

2. The original idea was to use a potentiometer to control the amount of heat given off. This later changed to have buttons on an interface panel

3. Add a small motorised fan with switch and potentiometer to control cooling of the device, originally it was going to be something like the one below, but I soon realised that with higher temperatures (> 80 degrees C), this may be a problem so I've opted for a better, steel solution as shown later on.

12v fan

4. Develop a temperature measuring circuit to indicate when a set temperature is reached, originally I was going to use a thermister or thermocouple like the one shown here, but the easiest way to start with was to use an active temperature sensor. Later as further development I'd like to use a thermistor, and put this into a Wheatsone bridge to linearise the temperature output.

thermocouple

5. Integrate all the above steps with a relay to control the heater using a thermostat, here's an example relay circuit (although this was for a much higher voltage than I'll need as I'll only need about 24 V...I think):

example relay circuit

6. Possibly add a timing circuit to allow the system to be turned off after a set time and to programme a detailed curing cycle for carbon fibre. Here's an example timing circuit I can learn from:

example timing circuit

7. Later on, connect the system to the internet to control it using a basic web based input and also to allow for monitoring with live feedback.

8. Develop an app to set the temperature and time of the thermostat. https://www.figma.com might be useful for the UX/interface of the app. I was quite inspired by the TADO smart thermostat and smart radiators that I have now installed in my house.

9. Include PID control and develop an algorithm to improve the performance of the thermostat and turn it into a 'smart' thermostat with model predictive control. Include graphs, theory, calculators for energy/cost too. Here's an example of a project that I found where they developed their own PID controller:


Smart thermostat

I've recently installed a smart thermostat into my house (Tado), and have been really impressed by this. It has really got me thinking about how the algorithm works, but it's also made me realise that I can use this as a learning tool for my students to learn about heat, control, and electronics.

smart thermostat text
smart thermostat text

Early sketches, CAD and sketch models

here's a quick sketch of the carbon fibre curing oven.

here's a quick sketch of the inside of the carbon fibre curing oven. Note power supply should probably be 24V, not 12V.

and here's a quick sketch showing how the outside might look.

and how the layers might be constructed using a reflective insulation foil, layer of foam insulation, then a ply outer.

For the CAD week, I produced a basic assembly of crude parts (definately not to scale):

The Smart Thermostat Learning Kit Assembly

I then exploded the view, so all the parts can be seen.

I then thought I'd put this into a technical drawing to show the Bill of Materials (BOM).

Assembly
          drawing

Then later I redid this to be the correct scale:

Here's my first basic sketch model:

basic sketch model of my final project

basic sketch model of my final project - open lid

Early heating and control experiments

As part of the embedded programming, input, output, and interface weeks I tried to dovetail these exercises with my final project. So the output of my input+output weeks was a simple thermostatic control circuit, which included a thermistor to measure the temperature, a relay to turn on an LED and a heater if the temperature was below a preset value. Of course for my final project I will be using a much bigger heating element (240W=24V x 10A), but the principle is the same. I did all this using the board that I made in the input devices week.

the bongo board black and white

As part of the interface and applications programming week, I tried to start planning the workflow for my project. Here's the latest:

Also, here's where I've got to in loading idle2.7 and python2.7, opening the file, and generating the realtime graph of the serial data (which ultimately will be temperature):

It's given me a few things to think about, including:

Interface design

And you can download the buttons.stl file here.

PCB layout

and here is the eagle PCB board layout file

and here is the eagle PCB board schematic file

and here is the outline.png file

and here is the traces.png file

And you can download a .zip file with all the Eagle PCB design files here.

High power experiments

Now moving on to some simple experiments with a 200W (24V) heater I acquired. Note that I'm only using a 15V power supply here. What was very interesting here is that as the heating element heats up (above 150 degrees C), the current drops drastically, meaning that it stops putting heat power into the oven. My hypothesis is that this is because there is no air circulating, so if we put a fan in it should draw the heat away from the heater and hence put more heat into the oven!

And then to look at adding a fan to see how this influences the system...and yes, by adding the fan to take away heat from the surfaces of the heater, it allows the heater to draw more current and hence provide more heat power into the oven. i.e. I will definately need a fan!

Then I did a quick test to see how quickly this heating element in this setup would heat the inside of the oven. It turned out to be about 2 degrees/minute, which is all I will need for my oven. And this is with a lower voltage (perhaps as low as half of the power), with the door ajar and with only a little insulation. But the load is lower (ie no mould inside the oven to heat up), but even still I think this power should be suffient. Note that I also have a second 200W heater if needed, but if I include that also I will need to make sure the power supply can deal with the current of both.

Belt and pulley drive system?

The thinking here was that it would be great to use a steel fan to avoid any chance of it melting in the oven, and this might allow us to work at higher temperatures down the line. I also wanted to try this with some quick experiments while the existing metal fan was connected in the oven casing I had, just to see if I could learn something. So with the help of my trusty CAD and 3DP guru Ollie we came up with a simple 1:1 pulley drive system to test it out

while we could get it to drive by hand and with the low speed motor (10s of RPM), there is far too much friction here, and the tension needed would mean that we'd really need to step down the motor to a slower speed, increasing the torque...but we do need a high speed (1000's of RMP), so this doesn't seem like it will work at all.

Time to change direction here, and accept the lower temperature cycles which is fine, and go with a plastic fan. To start with I'll experiment with an impeller and 9-24V DC RS-385SA permanent magnet brushed motor I pulled off an old hair dryer that I reverse engineered in class last year. Here's another useful link for it, and you can also get it from ebay for about £3...about It spins at 21,000 RPM and is a beauty! The next step is to design the housing for this.

Designing the heating assembly

Basic measurement and control

The next step was to get the relay working to allow me to control the heating. I had a REAL panic here, a bit of a brain explosion, thinking that I needed a 24 V relay to control the 24 V heating/fan system. Actually, I only needed the little 5 V relay that I'd been using all along, since it's the 5 V input that's specified, that's what counts. The output can be almost anything, AC, DC, and a wide range of voltages.

Here's the one I SHOULDN'T use.

Then I needed to get the temperature measured, so I could then control the heating using a basic bang-bang thermostatic control programme.

I should note that I had REAL issues here with the LM35 active temperature sensor. I thought I was doing myself a favour by starting with this, since it should have given a nice analog temperature reading without much fuss. The opposite was true, the readings were all over the place, and it was fiddly to work with (the female leads didn't connect to the pins).

So after a few hours of messing around with those guys, I went back to the old thermistors. I used a 100k Ohm NTC EPCOS 3950 thermistor that I bought from ebay. They were good because they had longer wires. Here are the specs:

Glass Encapsulated for Long Term Stability & Reliability
Size: φ1.25mm×2.0mm
Resistance Accuracy: 1%
Temp. Range: −40°C to 270°C
Lead (Pb)-free and RoHS Compliant
1m wire
Head diameter: 1.2mm
Resistance: 100Kohm + / -1%
B value: 3950 + / -1%
Accuracy: + / -1%
Lead Length: 32 + /-5mm
Operating temperature range:-50~+260°C
Thermal time constant (τ): τ=10 - 17 s ( in still air)
Thermal dissipation constant (δ): δ=1.1 - 1.6mW/°C ( in still air)
Operating temperature range:-50~+260°C
Insulation resistance 50MΩ or over by DC500V megge (between glass and lead wire)

Programming the 0.96" I2C Serial 128X64 4-pin OLED LCD Display

It was a bit of a faff to get the OLED working, mostly because all the syntax was all completely new to me. It took about 3 hours from start to finish. I downloaded and added the U8g2 libraries (Sketch > include libraries > ZIP) from here.It also has some good examples in there, e.g. Printing hello world was the first one I used. Here is the code that worked for me:

/*
  HelloWorld.ino
  Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)
  Copyright (c) 2016, olikraus@gmail.com
  All rights reserved.
  Redistribution and use in source and binary forms, with or without modification, 
  are permitted provided that the following conditions are met:
  * Redistributions of source code must retain the above copyright notice, this list 
    of conditions and the following disclaimer.
    
  * Redistributions in binary form must reproduce the above copyright notice, this 
    list of conditions and the following disclaimer in the documentation and/or other 
    materials provided with the distribution.
  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
*/

#include 
#include 

#ifdef U8X8_HAVE_HW_SPI
#include 
#endif
#ifdef U8X8_HAVE_HW_I2C
#include 
#endif

U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, 3, 2);
//this is the constructor for my OLED. Note that the U8G2_R0, and U8X8_PIN_NONE are options that can vary the display e.g. rotation, starting point. Pin 3=SCL, Pin2=SDA. 
/*
  U8glib Example Overview:
    Frame Buffer Examples: clearBuffer/sendBuffer. Fast, but may not work with all Arduino boards because of RAM consumption
    Page Buffer Examples: firstPage/nextPage. Less RAM usage, should work with all Arduino boards.
    U8x8 Text Only Example: No RAM usage, direct communication with display controller. No graphics, 8x8 Text only.
    
  This is a page buffer example.    
*/

void setup(void) {

  u8g2.begin();  
}

void loop(void) {
  u8g2.firstPage();
  do {
    u8g2.setFont(u8g2_font_ncenB10_tr);
    u8g2.drawStr(0,24,"Hello World!");
  } while ( u8g2.nextPage() );
  //delay(1000);
}
            

Note that the following code is needed after the u8g2.begin(); in the void setup(). This allows us to print variables.

u8g2.enableUTF8Print();

I thought I'd take stock, I've now managed to get the screen working, displaying the current temperature, and the set temperature (currently this a set value defined in the code, but using buttons to set the set temperature is the next step!). Here's the code for this, noting that the OLED works, the LEDs come on/off as I'd like, the thermistor reads the temperature nicely, the relay

It's also worth noting that I had a problem with some of the LEDs not being as bright as the others, and it's because I didn't define them as OUTPUT pins in the setup!!! e.g. pinMode(powerledPin,OUTPUT);

Here's the code that's working so far.

#include 
#include 

#ifdef U8X8_HAVE_HW_SPI
#include 
#endif
#ifdef U8X8_HAVE_HW_I2C
#include 
#endif

U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, 3, 2);

const int relayPin = 12;
const int powerledPin = 13;
const int heaterledPin = 5;
const int programmedledPin = 10;

int powerled = HIGH ;
int heaterled = HIGH;
int programmedled = HIGH;
int setTemp = 35;

int ThermistorPin = A1;
int Vo;
float R1 = 10000;
float logR2, R2, T;
float c1 = 1.009249522e-03, c2 = 2.378405444e-04, c3 = 2.019202697e-07;
float powervoltage=5;//define the power supply voltage.
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  pinMode(relayPin,OUTPUT);
  pinMode(powerledPin,OUTPUT);
  pinMode(heaterledPin,OUTPUT);
  pinMode(programmedledPin,OUTPUT);
  u8g2.begin();  
  u8g2.enableUTF8Print();
}

// the loop routine runs over and over again forever:
void loop() {
    u8g2.firstPage();
  digitalWrite(powerledPin,HIGH);
  Vo = analogRead(ThermistorPin);
  R2 = R1 * (1023.0 / (float)Vo - 1.0);
  logR2 = log(R2);
  T = (1.0 / (c1 + c2*logR2 + c3*logR2*logR2*logR2));
  T = T - 273.15;

  Serial.print("Temperature: "); 
  Serial.println(T);
  
  // read the input on analog pin 0:
  //int sensorValue = analogRead(A1);
  // print out the value you read:
  //Serial.println(sensorValue);
  int counter=0;

  //If there is a big sound nearby, make the led light for one second.
  if(T LESS THAN SYMBOL setTemp)
  {
    digitalWrite(12,HIGH);
    digitalWrite(heaterledPin,HIGH);
    digitalWrite(programmedledPin,HIGH);
  }
  else {
   digitalWrite(12,LOW);    
    digitalWrite(heaterledPin,LOW);
}

do {
    u8g2.setFont(u8g2_font_ncenB10_tr);
    u8g2.drawStr(0,25,"Current: ");
    u8g2.setCursor(70, 25);
    u8g2.print(T);
    u8g2.drawStr(0,50,"Set: ");
    u8g2.setCursor(70, 50);
    u8g2.print(setTemp);
  } while ( u8g2.nextPage() );

  
   delay(2000);
}

Noting that I've now solved the LED brightness inconsistencies as mentioned above, by declaring the OUTPUT (and also now the INPUT) pins as OUTPUT/INPUT.

Programming the buttons

First things first, it's worth me noting what the button pins are:

mode button = pin A5; linked to LED = pin 13 (toggle between modes)
up button = pin A4
down button = pin A3
            

The first step here was the modify the generic arduino tutorial example for Debounce to include TWO buttons, one for my DOWN and one for my UP button. With a bit of tweaking I managed to get this to work, without too many hiccups. I did have a REAL fright though, as after trying to load a programme (one using the screen) the arduino IDE started to NOT recognise my board. But I re-burned the bootloader, and it seemed to start working again. Phew!

Here's the test code I used to get the counter to go up and down with the up/down buttons. THis could be easily transferred to my 'setTemp' variable, to define the target temperature for the oven when it's on the manual control setting.

// constants won't change. They're used here to set pin numbers:
const int downbuttonPin = A4;    // the number of the pushbutton pin
const int upbuttonPin = A3;    // the number of the pushbutton pin

const int ledPin = 13;      // the number of the LED pin

// Variables will change:
int ledState = HIGH;         // the current state of the output pin
int downbuttonState;             // the current reading from the input pin
int lastdownbuttonState = LOW;   // the previous reading from the input pin
int upbuttonState;             // the current reading from the input pin
int lastupbuttonState = LOW;   // the previous reading from the input pin
int counter;

// the following variables are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastdownDebounceTime = 0;  // the last time the output pin was toggled
unsigned long downdebounceDelay = 50;    // the debounce time; increase if the output flickers
unsigned long lastupDebounceTime = 0;  // the last time the output pin was toggled
unsigned long updebounceDelay = 50;    // the debounce time; increase if the output flickers


void setup() {
  pinMode(downbuttonPin, INPUT);
  pinMode(upbuttonPin, INPUT);
  pinMode(ledPin, OUTPUT);

  // set initial LED state
  digitalWrite(ledPin, ledState);
}

void loop() {
  // read the state of the switch into a local variable:
  int downreading = digitalRead(downbuttonPin);
  int upreading = digitalRead(upbuttonPin);

  // check to see if you just pressed the button
  // (i.e. the input went from LOW to HIGH), and you've waited long enough
  // since the last press to ignore any noise:

  // If the switch changed, due to noise or pressing:
  if (downreading != lastdownbuttonState) {
    // reset the debouncing timer
    lastdownDebounceTime = millis();
  }

  if ((millis() - lastdownDebounceTime) > downdebounceDelay) {
    // whatever the reading is at, it's been there for longer than the debounce
    // delay, so take it as the actual current state:

    // if the button state has changed:
    if (downreading != downbuttonState) {
      downbuttonState = downreading;
    
      // only toggle the LED if the new button state is HIGH
      if (downbuttonState == HIGH) {
        ledState = !ledState;
        counter=counter + 1;
        Serial.println(counter);
      }
    }
  }

  // set the LED:
  digitalWrite(ledPin, ledState);

  // save the reading. Next time through the loop, it'll be the lastdownbuttonState:
  lastdownbuttonState = downreading;


////UP BUTTON STARTS HERE
  if (upreading != lastupbuttonState) {
    // reset the debouncing timer
    lastupDebounceTime = millis();
  }

  if ((millis() - lastupDebounceTime) > updebounceDelay) {
    // whatever the reading is at, it's been there for longer than the debounce
    // delay, so take it as the actual current state:

    // if the button state has changed:
    if (upreading != upbuttonState) {
      upbuttonState = upreading;
    
      // only toggle the LED if the new button state is HIGH
      if (upbuttonState == HIGH) {
        ledState = !ledState;
        counter=counter - 1;
        Serial.println(counter);
      }
    }
  }

  // set the LED:
  digitalWrite(ledPin, ledState);
 

  // save the reading. Next time through the loop, it'll be the lastdownbuttonState:
  lastupbuttonState = upreading;
  
}

I then rolled this out to the wider programme to control the temperature using a simple thermostatic control programme, including the OLED screen too. I also updated the screen settings to not include a remaining time line. One issue I have is that it refreshes too quickly (40ms), and I can't slow this down (yet...it's late! maybe tomorrow). The issue is that I can't delay the programme, otherwise it will miss the event if I press the button....anyways, the rest of it seems to work well, reading temperature, turning on/off the relay, displaying it all on the OLED, buttons working to increase/decrease the Target temperature. Here is the .ino file to download and here's the working code in full:

/*
This programme uses the 85 degree low temperature curing cycle to programme the Atmega32U4 chip on the carbon ply carbon fibre curing oven.
*/

#include 
#include 

#ifdef U8X8_HAVE_HW_SPI
#include 
#endif
#ifdef U8X8_HAVE_HW_I2C
#include 
#endif

//constructor for the OLED screen
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, 3, 2);

//defining the constant variables for the pins
const int relayPin = 12;
const int powerledPin = 13;
const int heaterledPin = 5;
const int programmedledPin = 10;
const int modebuttonPin = A5;
const int downbuttonPin = A4;
const int upbuttonPin = A3;
const int ThermistorPin = A1;

//turning on the power LED and programmed LED
int powerled = HIGH ;
int heaterled = LOW;
int programmedled = HIGH;
int timeMins = 0;             //current time in minutes
int timeLeft = 900;           //amount of time left in programme. 

//variables for button press/debounce
int downbuttonState;             // the current reading from the input pin
int lastdownbuttonState = LOW;   // the previous reading from the input pin
int upbuttonState;             // the current reading from the input pin
int lastupbuttonState = LOW;   // the previous reading from the input pin

// the following variables are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastdownDebounceTime = 0;  // the last time the output pin was toggled
unsigned long downdebounceDelay = 50;    // the debounce time; increase if the output flickers
unsigned long lastupDebounceTime = 0;  // the last time the output pin was toggled
unsigned long updebounceDelay = 50;    // the debounce time; increase if the output flickers

//set initial target temperature
int setTemp = 20;              //may need to be float in some programmes?

//set initial variables used to calculate temperature
int Vo;
float R1 = 10000;
float logR2, R2, T;
float c1 = 1.009249522e-03, c2 = 2.378405444e-04, c3 = 2.019202697e-07;
float powervoltage=5;//define the power supply voltage.

//SETUP
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);

  //Define pins to be used as output
  pinMode(relayPin,OUTPUT);
  pinMode(powerledPin,OUTPUT);
  pinMode(heaterledPin,OUTPUT);
  pinMode(programmedledPin,OUTPUT);

  //Define pins to be used as input
   pinMode(modebuttonPin,INPUT);
   pinMode(downbuttonPin,INPUT);
   pinMode(upbuttonPin,INPUT);
   pinMode(ThermistorPin, INPUT);

   //initialise the u8g2 OLED screen
  u8g2.begin();  
  //allow printing of variables on the screen
  u8g2.enableUTF8Print();
}

// the loop routine runs over and over again forever:
void loop() {
  
  digitalWrite(powerledPin,HIGH);
  digitalWrite(programmedledPin,LOW);

  int downreading = digitalRead(downbuttonPin);
  int upreading = digitalRead(upbuttonPin);

  Vo = analogRead(ThermistorPin);
  R2 = R1 * (1023.0 / (float)Vo - 1.0);
  logR2 = log(R2);
  T = (1.0 / (c1 + c2*logR2 + c3*logR2*logR2*logR2));
  T = T - 273.15;
  //T=23.5;

  //Serial.print("Temperature: "); 
  //Serial.println(T);
  
  timeMins = millis()/1000/60;
  //setTemp = 20;
    
  Serial.println(setTemp);

      if(T (less than)setTemp) {
        digitalWrite(relayPin,HIGH);
        digitalWrite(heaterledPin,HIGH);
      }
      else {
       digitalWrite(relayPin,LOW);    
       digitalWrite(heaterledPin,LOW);
      }

      //display current and set temperatures
      u8g2.firstPage();
      do {
          u8g2.setFont(u8g2_font_ncenB10_tr);
          u8g2.drawStr(0,20,"Current: ");
          u8g2.setCursor(70, 20);
          u8g2.print(T,1);
          u8g2.drawStr(100,20,"C");
          u8g2.drawStr(0,40,"Target: ");
          u8g2.setCursor(70, 40);
          u8g2.print(setTemp);
          u8g2.drawStr(100,40,"C");
          //u8g2.drawStr(0,60,"Remain:");
          //u8g2.setCursor(70, 60);
          //u8g2.print(timeLeft);
          //u8g2.drawStr(100,60,"mins");
        } while (u8g2.nextPage());
        delay(40);
       
  //BUTTON PRESS SECTION
  // check to see if you just pressed the button
  // (i.e. the input went from LOW to HIGH), and youve waited long enough
  // since the last press to ignore any noise:

  // If the switch changed, due to noise or pressing:
  
  //DOWN BUTTON SECTION
  if (downreading != lastdownbuttonState) {
    // reset the debouncing timer
    lastdownDebounceTime = millis();
  }

  if ((millis() - lastdownDebounceTime) > downdebounceDelay) {
    // whatever the reading is at, it's been there for longer than the debounce
    // delay, so take it as the actual current state:

    // if the button state has changed:
    if (downreading != downbuttonState) {
      downbuttonState = downreading;
    
      // only toggle the LED if the new button state is HIGH
      if (downbuttonState == HIGH) {
        //ledState = !ledState;
        setTemp=setTemp - 1;
        Serial.println(setTemp);
        delay(1200);
      }
    }
  }

  // set the LED:
  //digitalWrite(ledPin, ledState);

  // save the reading. Next time through the loop, it'll be the lastdownbuttonState:
  lastdownbuttonState = downreading;


//UP BUTTON STARTS HERE
  if (upreading != lastupbuttonState) {
    // reset the debouncing timer
    lastupDebounceTime = millis();
  }

  if ((millis() - lastupDebounceTime) > updebounceDelay) {
    // whatever the reading is at, it's been there for longer than the debounce
    // delay, so take it as the actual current state:

    // if the button state has changed:
    if (upreading != upbuttonState) {
      upbuttonState = upreading;
    
      // only toggle the LED if the new button state is HIGH
      if (upbuttonState == HIGH) {
        //ledState = !ledState;
        setTemp=setTemp + 1;
        Serial.println(setTemp);
        delay(1200);
      }
    }
  }

  // set the LED:
  //digitalWrite(ledPin, ledState);
 

  // save the reading. Next time through the loop, itll be the lastdownbuttonState:
  lastupbuttonState = upreading;
  
    
}

The next step was to create a separate programme that was the 85degree low temperature curing cycle. I plan to integrate it with the above programme, using a hold button for 3 seconds to switch modes, but for the time being I'll keep them separate to keep the code manageable. Below is the full working code to go through the full 15-hour low-temperature curing cycle, and here is the .ino file to download.

/*
This programme uses the 85 degree low temperature curing cycle to programme the Atmega32U4 chip on the carbon ply carbon fibre curing oven.
*/

#include 
#include 

#ifdef U8X8_HAVE_HW_SPI
#include 
#endif
#ifdef U8X8_HAVE_HW_I2C
#include 
#endif

//constructor for the OLED screen
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, 3, 2);

//defining the constant variables for the pins
const int relayPin = 12;
const int powerledPin = 13;
const int heaterledPin = 5;
const int programmedledPin = 10;
const int modebuttonPin = A5;
const int downbuttonPin = A4;
const int upbuttonPin = A3;
const int ThermistorPin = A1;

//turning on the power LED and programmed LED
int powerled = HIGH ;
int heaterled = LOW;
int programmedled = HIGH;
int timeMins = 0;             //current time in minutes
int timeLeft = 900;           //amount of time left in programme. 

//set initial target temperature
int setTemp = 0;              //may need to be float in some programmes?

//set initial variables used to calculate temperature
int Vo;
float R1 = 10000;
float logR2, R2, T;
float c1 = 1.009249522e-03, c2 = 2.378405444e-04, c3 = 2.019202697e-07;
float powervoltage=5;//define the power supply voltage.

//SETUP
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);

  //Define pins to be used as output
  pinMode(relayPin,OUTPUT);
  pinMode(powerledPin,OUTPUT);
  pinMode(heaterledPin,OUTPUT);
  pinMode(programmedledPin,OUTPUT);

  //Define pins to be used as input
   pinMode(modebuttonPin,INPUT);
   pinMode(downbuttonPin,INPUT);
   pinMode(upbuttonPin,INPUT);
   pinMode(ThermistorPin, INPUT);

   //initialise the u8g2 OLED screen
  u8g2.begin();  
  //allow printing of variables on the screen
  u8g2.enableUTF8Print();
}

// the loop routine runs over and over again forever:
void loop() {
  
    
  digitalWrite(powerledPin,HIGH);
  digitalWrite(programmedledPin,HIGH);
  
  Vo = analogRead(ThermistorPin);
  R2 = R1 * (1023.0 / (float)Vo - 1.0);
  logR2 = log(R2);
  T = (1.0 / (c1 + c2*logR2 + c3*logR2*logR2*logR2));
  T = T - 273.15;
  //T=45;      //Just used for debugging

  Serial.print("Temperature: "); 
  Serial.println(T);
  
    timeMins = millis()/1000/60;
    setTemp = 20;
    
    //enter first ramp up to 70degC at 1deg/min increments
    while (setTemp less than 70){
      setTemp = setTemp+1;
      timeLeft=900-setTemp;
      Serial.println(setTemp);
      int counter = 1;
      
      //begin thermostatic loop with 1.2 second cycle
      while (counter less than= 50){
          if(T less than setTemp){
            digitalWrite(relayPin,HIGH);
            digitalWrite(heaterledPin,HIGH);
          }
          else {
           digitalWrite(relayPin,LOW);    
           digitalWrite(heaterledPin,LOW);
          }
    
          //display current and set temperatures
          u8g2.firstPage();
          do {
              u8g2.setFont(u8g2_font_ncenB10_tr);
              u8g2.drawStr(0,20,"Current: ");
              u8g2.setCursor(70, 20);
              u8g2.print(T,1);
              u8g2.drawStr(100,20,"'C");
              u8g2.drawStr(0,40,"Target: ");
              u8g2.setCursor(70, 40);
              u8g2.print(setTemp);
              u8g2.drawStr(100,40,"'C");
              u8g2.drawStr(0,60,"Remain:");
              u8g2.setCursor(70, 60);
              u8g2.print(timeLeft);
              u8g2.drawStr(100,60,"mins");
            } while (u8g2.nextPage());
            delay(1200);
            counter=counter + 1;
      }
    }

 //hold at 70 degrees until 4h50mins
    while (timeMins  less than 290){
      setTemp = 70;
      timeMins = millis()/1000/60;
      Serial.println(setTemp);
      int counter = 1;
      
      //begin thermostatic loop with 1.2 second cycle
      while (counter <= 50){
          if(T less than setTemp){
            digitalWrite(relayPin,HIGH);
            digitalWrite(heaterledPin,HIGH);
          }
          else {
           digitalWrite(relayPin,LOW);    
           digitalWrite(heaterledPin,LOW);
          }
    
          //display current and set temperatures
          u8g2.firstPage();
           do {
              u8g2.setFont(u8g2_font_ncenB10_tr);
              u8g2.drawStr(0,20,"Current: ");
              u8g2.setCursor(70, 20);
              u8g2.print(T,1);
              u8g2.drawStr(100,20,"'C");
              u8g2.drawStr(0,40,"Target: ");
              u8g2.setCursor(70, 40);
              u8g2.print(setTemp);
              u8g2.drawStr(100,40,"'C");
              u8g2.drawStr(0,60,"Remain:");
              u8g2.setCursor(70, 60);
              u8g2.print(timeLeft);
              u8g2.drawStr(100,60,"mins");
            } while (u8g2.nextPage());
            delay(1200);
            counter=counter + 1;
      }
    }

     //ramp up to 85degC until 4h58
     while (timeMins  less than 298){
      setTemp = setTemp+(2/12);
      timeMins = millis()/1000/60;
      Serial.println(setTemp);
      int counter = 1;
      
      //begin thermostatic loop with 1.2 second cycle
      while (counter  less than = 50){
          if(T less than setTemp){
            digitalWrite(relayPin,HIGH);
            digitalWrite(heaterledPin,HIGH);
          }
          else {
           digitalWrite(relayPin,LOW);    
           digitalWrite(heaterledPin,LOW);
          }
    
          //display current and set temperatures
          u8g2.firstPage();
         do {
              u8g2.setFont(u8g2_font_ncenB10_tr);
              u8g2.drawStr(0,20,"Current: ");
              u8g2.setCursor(70, 20);
              u8g2.print(T,1);
              u8g2.drawStr(100,20,"'C");
              u8g2.drawStr(0,40,"Target: ");
              u8g2.setCursor(70, 40);
              u8g2.print(setTemp);
              u8g2.drawStr(100,40,"'C");
              u8g2.drawStr(0,60,"Remain:");
              u8g2.setCursor(70, 60);
              u8g2.print(timeLeft);
              u8g2.drawStr(100,60,"mins");
            } while (u8g2.nextPage());
            delay(1200);
            counter=counter + 1;
      }
    }

  //hold at 85 degrees until 14h15mins
    while (timeMins less than 855){
      setTemp = 85;
      timeMins = millis()/1000/60;
      Serial.println(setTemp);
      int counter = 1;
      
      //begin thermostatic loop with 1.2 second cycle
      while (counter less than = 50){
          if(T less than setTemp){
            digitalWrite(relayPin,HIGH);
            digitalWrite(heaterledPin,HIGH);
          }
          else {
           digitalWrite(relayPin,LOW);    
           digitalWrite(heaterledPin,LOW);
          }
    
          //display current and set temperatures
          u8g2.firstPage();
        do {
              u8g2.setFont(u8g2_font_ncenB10_tr);
              u8g2.drawStr(0,20,"Current: ");
              u8g2.setCursor(70, 20);
              u8g2.print(T,1);
              u8g2.drawStr(100,20,"'C");
              u8g2.drawStr(0,40,"Target: ");
              u8g2.setCursor(70, 40);
              u8g2.print(setTemp);
              u8g2.drawStr(100,40,"'C");
              u8g2.drawStr(0,60,"Remain:");
              u8g2.setCursor(70, 60);
              u8g2.print(timeLeft);
              u8g2.drawStr(100,60,"mins");
            } while (u8g2.nextPage());
            delay(1200);
            counter=counter + 1;
      }
    }

   //natural cool until 15 hours
    while (timeMins less than 900){
      setTemp = 0;
      delay(60000);
      timeMins = millis()/1000/60;
    }
    setTemp = 0;
}

PID temperature control

Here is the first attempt at using PID temperature control using the PID library in the Arduino IDE.

And here's the code for this.

#include 
#define RelayPin 12

//Define Variables we'll be connecting to
double Setpoint, Input, Output;
int Vo;
float R1 = 10000;
float logR2, R2, T, rawdata;
float c1 = 1.009249522e-03, c2 = 2.378405444e-04, c3 = 2.019202697e-07;
float powervoltage=5;//define the power supply voltage.

const int ThermistorPin = A1;

//Specify the links and initial tuning parameters
PID myPID(andInput, andOutput, andSetpoint, 2, 5, 1, DIRECT);

int WindowSize = 5000;
unsigned long windowStartTime;
void setup()
{
  pinMode(RelayPin, OUTPUT);

  windowStartTime = millis();

  //initialize the variables we're linked to
  Setpoint = 30;

  //tell the PID to range between 0 and the full window size
  myPID.SetOutputLimits(0, WindowSize);

  //turn the PID on
  myPID.SetMode(AUTOMATIC);
}

void loop()
{
  
  Vo = analogRead(ThermistorPin);
  R2 = R1 * (1023.0 / (float)Vo - 1.0);
  logR2 = log(R2);
  T = (1.0 / (c1 + c2*logR2 + c3*logR2*logR2*logR2));
  T = T - 273.15;
  Input = T;
  myPID.Compute();

  /************************************************
     turn the output pin on/off based on pid output
   ************************************************/
  unsigned long now = millis();
  if (now - windowStartTime > WindowSize)
  { //time to shift the Relay Window
    windowStartTime += WindowSize;
  }
  if (Output > now - windowStartTime) digitalWrite(RelayPin, HIGH);
  else digitalWrite(RelayPin, LOW);
  delay(500);
  Serial.println(T);
}

And here's the direct comparison with bang-bang, which was noticably quicker to reach the set temperature of 30 degrees.

Here's a graph of the comparison, showing that a) the PID took MUCH longer to get up to the target temperature, and b) that the PID steady state value (with the current control variables) was actually not quite as good as the original bang-bang!

And here is the spreadsheet with the raw data for downloading.

Fabricating casing and assembling product

One of the most important functional tests was to get the PCB mounted with the front sub-panel. So I laser cut a thinner panel to mount the PCB on (3mm MDF), so it could be slotted into the main front panel (12mm).

This allowed me to test the buttons, the OLED, and how this all sat within the main casing assembly.

And you can download the buttons.stl file here.

Time to start cutting the panels for the casing, which I used 12mm MDF, with a simple finger jointed construction.

Then to test the front panel, to make sure the buttons slide in and out freely enough. I also printed some grey, more rubbery buttons that were all tied together with a plate at the back. This was too stiff, so when one button was pressed, all were pressed, so I ended up using these but cutting them into separate buttons.

Then to test the positioning and mounting of the PCB behind the front panel with regard to the mounting holes, the LED connecting tubes and the buttons too.

I had to adjust the front panel to get things to fit meaning I had to remove (for the time being) the text for the LEDs which was a shame, but is something I'll add back into mach 2.

Then I created a simple mount to sit behind the OLED to stop the pins from bending and to protect it from being pushed back behind the front panel. Here it is screwed in behind the OLED.

Front panel testing to make sure things can move uninhibited.

Then to put all the panels together. Our G-clamps wouldn't go around the length and girth of the casing, so I used some reasonable masses to press down and hold it together while it set with PVA.

Then to test that the buttons, to make sure it all works together, and the PCB holds in place when the buttons are pressed against it. Few!

Once the front panel and the route for the cabling had been fully tested I could glue it all together and voila!

I then did some basic functional tests to check that the mould we firstly want to test that it fits inside and can be connected to the vacuum pump with some room to spare.

And you can download a .zip file with all the CAD part+assembly files in here.

Full bill of materials (BOM) and parts made

Raw materials and components, sources and costs...Bill of Materials

Classification Part name Source Cost per item Number required
ELECTRONICS ATMega32U4 FABLAB INVENTORY £3.32 1
RED LEDS: 1206 FABLAB INVENTORY £0.24 3
22 OHM RESISTOR: RES-US1206FAB FABLAB INVENTORY £0.007 2
499 OHM 1206 RESISTOR FABLAB INVENTORY £0.007 3
RES-US1206FAB 680 OHM RESISTOR FABLAB INVENTORY £0.007 1
10K OHM RESISTOR1206 RESISTOR FABLAB INVENTORY £0.007 3
10uF CAP-US1206FAB CAPACITORS FABLAB INVENTORY £0.24 1
1uF CAP-US1206FAB CAPACITORS FABLAB INVENTORY £0.24 2
0.1uF CAP-US1206FAB CAPACITORS FABLAB INVENTORY £0.24 1
18pF CAP-US1206FAB CAPACITORS FABLAB INVENTORY £0.24 2
CSM-7X-DU 16 MHZ CRYSTAL FABLAB INVENTORY £0.88 1
MINI-B USB CONNECTOR FABLAB INVENTORY £0.77 1
CONNECTORS (FTDI) FABLAB INVENTORY £0.02 1
SPST_TACT-EVQQ2 BUTTONS FABLAB INVENTORY £0.34 3
24V 10 A RELAY EBAY £3.23 1
6 pin OLED SCREEN amazon £4.95 1
HEATING SYSTEM MAO5 - 5 V DC FAN EBAY £4.49 1
24 V DC, 10 A HEATING ELEMENT EBAY £4.88 1
24 V DC, 10 A Universal Regulated Switching Power Supply INDUSTRY MALL - EBAY - https://www.ebay.co.uk/itm/181881169501 £12.99 1
3pin active temperature sensor ebay £0.96 1
MAIN CASING 13 MM HIGH TEMPERATURE INSULATION, 6 X 61cm x 366cm x 13mm EBAY £30.00 1
MAIN CASING CARBON FIBRE VINYL COATING EBAY £6.26 1

what parts and systems will be made? and what processes will be used? Fabrication processes and materials

Sub assembly Part name Material Fabrication process Number required
MAIN_CASING FRONT_CONTROL_PANEL 2 MM PLY LASER CUT 1
FRONT_CONTROL_PANEL 2 MM PLY LASER CUT 1
FRONT_TOP_PANEL 2 MM PLY LASER CUT 1
FRONT_BOTTOM_PANEL 2 MM PLY LASER CUT 1
SIDE_PANEL 2 MM PLY LASER CUT 2
BACK_PANEL 2 MM PLY LASER CUT 1
TOP_PANEL 2 MM PLY LASER CUT 1
BOTTOM_PANEL 2 MM PLY LASER CUT 1
BUTTONS SILICONE CAST 1 (3 BUTTONS)
FEET PLA 3D PRINTED 4
HEATING SYSTEM 2 MM PLY LASER CUT 1

I thought it would be a nice touch to make a few 3D logos to put on the final product, using a variety of techniques, so I did this as part of the moulding and casting week, but I also made a 3D printed version.

Future developments

I really want to expand this to build more of this myself in the future, and one of the things I'd love to try is to make my own heating elements by winding Nichrome wire. Here are a couple of really great video resources. THe second video is of someone who built their own foundry - lots of good lessons from there!

I might also include other things into the final project as part of this suite of resources:

some links:

Licencing statement

(c) June 2018. This work may be reproduced, modified, distributed, performed, and displayed for any purpose, but must acknowledge "Carbon Ply". Copyright is retained and must be preserved. The work is provided as is; no warranty is provided, and users accept all liability.

Acknowledgements

A big thank you to all who have supported me throughout this journey this year: it has been enriching, enjoyable and challenging. So a big thank you to...

Back to top