Building a Digital Vaporizer (Brain Dump)

tuttle

Well-Known Member
jackalope said:
Thanks for posting that info. Knowing that will save me a lot of time and money.

I'm surprised that heating elements are so expensive. Even the ceramic elements are simple components and I feel like the only reason they're so expensive is because I'm not trying to buy 1000 of them at once.

I wonder if there is some househod appliance I can pull a ceramic element out of.
The 60w Hakko heater, part number A1177, is $16 and is pretty available.
 
tuttle,

jackalope

Well-Known Member
Good point. I guess I should move past the sticker shock on the elements and concentrate on finding the best trade-off of reliability and price.

HD, do you have any recommendations?
 
jackalope,

Hippie Dickie

The Herbal Cube
Manufacturer
Sorry, jack, no since i roll my own ... 24" of 16 gauge nichrome 80 wire wrapped around the outside of a glass tube, with k-type thermocouple probe input to a PIC processor configured for pulse width modulation for temperature control ... all outside the vapor-air path.
 
Hippie Dickie,
  • Like
Reactions: Swampy

skippymcware

Well-Known Member
jack,

if you are willing to have your heater outside the airpath, there are lots of options. Using bare nichrome wire is probably the best cheapest and offers the most design flexibility. Without the bells and whistles, very powerful cartridge heaters can be had for less than $20 new. Also, the Purple days uses a ceramic power resistor, if I remember right. Those things are very affordable and get the job done.

Sorry I have been MIA. My life just got several times busier.

On another note, that ideal looking heater curve I posted up there was actually generated when I tuned a code with a serious error in it. Strangely, the result came out almost perfect except that the final temperature was offset. I have since fixed the code, and have a more normal overshoot, but without the offset. Sometimes I wonder if I should have just left it.

Lastly, I am now elbow deep working with pic microcontrollers. Turns out the lab I just started work in uses them. Funny how that works. My only immediate comments are that programming the PICs directly, as opposed to using something like arduino, gives the programmer much more power, but the PIC development environment is awful and too feature laden. The latter part is easy to fix, though. Just don't use it.
 
skippymcware,

Hippie Dickie

The Herbal Cube
Manufacturer
are you using MPLAB IDE? it ain't so bad ... comparied to what else is out there. i wrote better (in a previous life), but it gets the job done.
 
Hippie Dickie,

skippymcware

Well-Known Member
I am using the PIC CSS IDE at the moment. As I said before, it is pissing me off. There are far too many buttons. They might be useful to me one day, but all I want is to monitor the serial port, compile my code, and program the chip. Maybe one day I will figure out what the other 800 buttons do.
 
skippymcware,

Bubar

Well-Known Member
Yo! Fuck CSS! MikroC FTW! Just pirate it. It's got a shit ton of built-in routines.
 
Bubar,

skippymcware

Well-Known Member
Here is the code I am running for those interested. Knock yourselves out. A side note: you have to reprogram the stock boot loader on the arduino chips in order to run the wdt, and I have not done that yet. All the other bells and whistles are in there, though. I wouldn't suggest using this regularly until that safety is setup, however.

Lastly, I am running a graphical user interface with the PID controller that is built in processing (check on wiki if you have never heard of it. It is great.). This gui lets me change the tuning parameters and graph the output without rebooting the chip or interrupting the process. The nicest part is I can change the tuning parameters and watch the thing oscillate. This makes behavior like overshoot and oscillation stand out much quicker. Anyway here is a link:

http://brettbeauregard.com/blog/2009/07/pid-front-end-v02/



//Vape 0.7 On/Off

//libraries
#include <PID_Beta6.h>
#include <Bounce.h>

//pin definitions
#define YLED 2 //Yellow LED connected to pin 2
#define RLED 3 //Red component of LED connected to pwm pin 3
#define GLED 4 //Green component of LED connected to pwm pin 4
#define BLED 5 //Blue component of LED connected to pwm pin 5
#define OUT 6 //Heater output on pin 6
#define POW 7 //power button input on pin 7
#define BTN1 8 //Button 1 input on pin 8
#define BTN2 9 //Button 2 input on pin 9
#define BTN3 10 //Button 3 input on pin 10
#define CS 11 //
#define SO 12 //serial for MAX6675
#define SCK 13 //clock out for MAX6675

//MAX6675 temperature variables
float offset_tc = 0; //Temperature compensation error
int samples = 5;
int bad_tc = 0; //flag for dead thermocouple

//PID varibles
double input, output, setpoint;
int cycle_time = 250;
double default_setpoint = 430;
int highest_setpoint = 500;
int lowest_setpoint = 360;

PID pid(&input, &output, &setpoint, 20,285,2.5); //pid object

//button variables
int btnpress1 = 0; //temp variable for button press recognition
int btnpress2 = 0; //temp variable for button press recognition
int btnpress3 = 0; //temp variable for button press recognition
int powpress = 0; //temp variable for button press recognition
int button_flag = 0; //prevents multiple recognitions of a single button press (1 while button is pressed, 0 otherwise)

Bounce bouncer1 = Bounce(BTN1, 5); //debouncer object for button 1
Bounce bouncer2 = Bounce(BTN2, 5); //debouncer object for button 2
Bounce bouncer3 = Bounce(BTN3, 5); //debouncer object for button 3
Bounce bouncerpow = Bounce(POW, 5); //debouncer object for power button

//power on variables
int power_flag = 0; //determines if program runs in power on loop or power off loop
int start_flag = 0; //stores wether or not the startup LED test sequence has run

//time variables
unsigned long counter; //length of power button press
unsigned long start_time = 0; //time that current loop started
unsigned long serialTime; //this will help us know when to talk with processing

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void setup()
{
pinMode(RLED,OUTPUT); //sets the digital pin as output
pinMode(GLED,OUTPUT); //sets the digital pin as output
pinMode(BLED,OUTPUT); //sets the digital pin as output
pinMode(YLED,OUTPUT); //sets the ditital pin as output
pinMode(OUT,OUTPUT); //sets the digital pin as output
pinMode(POW,INPUT); //sets the digital pin as input (powers unit on and off)
pinMode(BTN1,INPUT); //sets the digital pin as input (adjusts setpoint -10 degrees F)
pinMode(BTN2,INPUT); //sets the digital pin as input (resets setpoint to default_setpoint)
pinMode(BTN3,INPUT); //sets the digital pin as input (adjusts setpoint +10 degrees F)
pinMode(CS,OUTPUT); //sets the digital pin as input (MAX6675)
pinMode(SO,INPUT); //sets the digital pin as output (MAX6675)
pinMode(SCK,OUTPUT); //sets the digital pin as input (MAX6675)

Serial.begin(9600); //starts serial communication with computer

//Initialize PID variables
input = 0;
output = 0;
setpoint = default_setpoint;

pid.SetOutputLimits(0,cycle_time); //sets output range
pid.SetInputLimits(0,1873); //sets input range
pid.SetSampleTime(250); //PID samples every 250 ms
pid.SetMode(AUTO); //turn on the PID
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void loop()
{
/*
/////////////////////////////////////////////////////////////Power Off Logic///////////////////////////////////////////////
//power on routine that waits for power a button press of a certain length
if(power_flag == 0) {
start_time = millis(); //set cycle start time

//turn LED's off
digitalWrite(YLED,LOW);
digitalWrite(RLED,LOW);
digitalWrite(GLED,LOW);

//check for power button press
bouncerpow.update();
powpress = bouncerpow.read();

if(powpress == HIGH) {
counter = millis();
while(powpress == HIGH) {
bouncerpow.update();
powpress = bouncerpow.read();
digitalWrite(YLED,HIGH);
digitalWrite(BLED,LOW);
if(millis() - counter > 3000) {
power_flag = 1;
digitalWrite(YLED,LOW);
powpress = LOW;
counter = 0;
}
}
} else {
counter = 0;
digitalWrite(YLED,LOW);

for(int i = 1; i < 255; i++) {
if(digitalRead(POW) == HIGH) {
break;
}
analogWrite(BLED, i);
delay(7);
}
for(int i = 255; i > 1; i--) {
if(digitalRead(POW) == HIGH) {
break;
}
analogWrite(BLED,i);
delay(7);
}
}
}
*/

////////////////////////////////////////////////////////Power On Logic////////////////////////////////////////////////////////////////
//power on routing that runs once the power button has been pressed for a defined period of time
//if(power_flag == 1) {
start_time = millis(); //set cycle start time

//*******************************************************Startup Routine*************************************************************
//startup sequence that executres if the unit has just been turned on
if(start_flag == 0)
{
digitalWrite(BLED, HIGH);
delay(1000);
digitalWrite(BLED, LOW);
digitalWrite(GLED, HIGH);
delay(1000);
digitalWrite(GLED, LOW);
digitalWrite(RLED, HIGH);
delay(1000);
digitalWrite(RLED, LOW);
digitalWrite(YLED, HIGH);
delay(500);
digitalWrite(YLED, LOW);
delay(500);
digitalWrite(YLED, HIGH);
delay(500);
digitalWrite(YLED, LOW);
delay(500);
digitalWrite(YLED, HIGH);
delay(500);
digitalWrite(YLED, LOW);
delay(500);
start_flag++;
}

//*****************************************************************Read Temperature from MAX6675***********************************************
input = 0; //clear input

//reading heater temp from MAX6675
for(int i=samples; i>0; i--) {

digitalWrite(CS,LOW); // Enable device

/* Cycle the clock for dummy bit 15 */
digitalWrite(SCK,HIGH);
digitalWrite(SCK,LOW);

/* Read bits 14-3 from MAX6675 for the Temp
Loop for each bit reading the value and
storing the final value in 'input'
*/
for (int i=11; i>=0; i--){
digitalWrite(SCK,HIGH); // Set Clock to HIGH
input += digitalRead(SO) << i; // Read data and add it to our variable
digitalWrite(SCK,LOW); // Set Clock to LOW
}

/* Read the TC Input inp to check for TC Errors */
digitalWrite(SCK,HIGH); // Set Clock to HIGH
bad_tc = digitalRead(SO); // Read data
digitalWrite(SCK,LOW); // Set Clock to LOW

digitalWrite(CS,HIGH); //Disable Device
}

input /= samples; // Divide the value by the number of samples to get the average

/*
Keep in mind that the temp that was just read is on the digital scale
from 0C to 1023.75C at a resolution of 2^12. We now need to convert
to an actual readable temperature. Now multiply by 0.25 for deg C.
The final value is converted to an int and returned at x10 power.
*/

input += offset_tc; // Insert the calibration error value

/* Output 999.9 if there is a TC error, otherwise convert to deg F and make temp equal input*/
if(bad_tc == 1) {
input = 999.9;
} else {
input = ((input*0.25) * (9.0/5.0)) + 32.0;;
}

//exectures for bad thermocouple
if(input > 999) {
digitalWrite(BLED,HIGH);
delay(1000);
digitalWrite(BLED,LOW);
digitalWrite(RLED,HIGH);
delay(1000);
digitalWrite(RLED,LOW);
digitalWrite(BLED,HIGH);
delay(1000);
digitalWrite(BLED,LOW);
digitalWrite(RLED,HIGH);
delay(1000);
digitalWrite(RLED,LOW);
power_flag = 0; //kicks back out to power off state
}

//****************************************************Temperature Indication Logic****************************************************
//executes if temp is low
if(input < setpoint - 5) {
digitalWrite(BLED,HIGH);
} else {
digitalWrite(BLED,LOW);
}
//executes if temp is within window
if(input >= setpoint -5 && input <= setpoint + 5) {
digitalWrite(GLED,HIGH);
} else {
digitalWrite(GLED,LOW);
}
//executes if temp is high
if(input > setpoint + 5) {
digitalWrite(RLED,HIGH);
} else {
digitalWrite(RLED,LOW);
}

//****************************************************Button Logic*****************************************************
//button debouncer updates
bouncer1.update();
bouncer2.update();
bouncer3.update();
bouncerpow.update();

//button press checks
btnpress1 = bouncer1.read();
btnpress2 = bouncer2.read();
btnpress3 = bouncer3.read();
powpress = bouncerpow.read();

//power off routine that waits for a power button press of a certain length
if(powpress == HIGH) {
counter = millis(); //counter is reset in the no button pressed routine below
while(powpress == HIGH) {
bouncerpow.update();
powpress = bouncerpow.read();
Serial.println(millis() - counter);
digitalWrite(YLED,HIGH);
if(millis() - counter > 3000) {
power_flag = 0;
digitalWrite(YLED,LOW);
powpress = LOW;
}
}
}
//executes when a button is first pressed, but bypasses once buttonflag is HIGH
if(button_flag == 0) {
//button 1 logic decreases setpoint
if(btnpress1 == HIGH) {
digitalWrite(YLED,HIGH);
setpoint -= 10;
button_flag = 1;
}
//button 2 logic resets setpoint to default, but also executres reset if setpoint is out of acceptable window
if(btnpress2 == HIGH || setpoint < lowest_setpoint || setpoint > highest_setpoint) {
digitalWrite(YLED,HIGH);
setpoint = default_setpoint;
button_flag = 1;
}
//button 3 logic increases setpoint
if(btnpress3 == HIGH) {
digitalWrite(YLED,HIGH);
setpoint += 10;
button_flag = 1;
}
}
//executes only if no button is pressed
if(btnpress1 + btnpress2 + btnpress3 + powpress == 0) {
digitalWrite(YLED,LOW);
button_flag = 0;
counter = 0;
}

//**************************************************PID Logic*******************************************************************
pid.Compute(); //give the PID the opportunity to compute if needed

//turn the output on for the portion of the cycle time specified by output
while(output > millis() - start_time) {
digitalWrite(OUT,HIGH);
}
//turn power off for the remainder of the cycle time not demanded by output
while(cycle_time > millis() - start_time) {
digitalWrite(OUT,LOW);
}

//*************************************************Processing********************************************************************
//send-receive with processing if it's time
if(millis()>serialTime)
{
SerialReceive();
SerialSend();
serialTime+=500;
}
//}
}


/********************************************
* Serial Communication functions / helpers
********************************************/


union { // This Data structure lets
byte asBytes[24]; // us take the byte array
float asFloat[6]; // sent from processing and
} // easily convert it to a
foo; // float array



// getting float values from processing into the arduino
// was no small task. the way this program does it is
// as follows:
// * a float takes up 4 bytes. in processing, convert
// the array of floats we want to send, into an array
// of bytes.
// * send the bytes to the arduino
// * use a data structure known as a union to convert
// the array of bytes back into an array of floats

// the bytes coming from the arduino follow the following
// format:
// 0: 0=Manual, 1=Auto, else = ? error ?
// 1-4: float setpoint
// 5-8: float input
// 9-12: float output
// 13-16: float P_Param
// 17-20: float I_Param
// 21-24: float D_Param
void SerialReceive()
{

// read the bytes sent from Processing
int index=0;
byte Auto_Man = -1;
while(Serial.available()&&index<25)
{
if(index==0) Auto_Man = Serial.read();
else foo.asBytes[index-1] = Serial.read();
index++;
}

// if the information we got was in the correct format,
// read it into the system
if(index==25 && (Auto_Man==0 || Auto_Man==1))
{
setpoint=double(foo.asFloat[0]);
//Input=double(foo.asFloat[1]); // * the user has the ability to send the
// value of "Input" in most cases (as
// in this one) this is not needed.
if(Auto_Man==0) // * only change the output if we are in
{ // manual mode. otherwise we'll get an
output=double(foo.asFloat[2]); // output blip, then the controller will
} // overwrite.

double p, i, d; // * read in and set the controller tunings
p = double(foo.asFloat[3]); //
i = double(foo.asFloat[4]); //
d = double(foo.asFloat[5]); //
pid.SetTunings(p, i, d); //

if(Auto_Man==0) pid.SetMode(MANUAL);// * set the controller mode
else pid.SetMode(AUTO); //
}
Serial.flush(); // * clear any random data from the serial buffer
}



// unlike our tiny microprocessor, the processing ap
// has no problem converting strings into floats, so
// we can just send strings. much easier than getting
// floats from processing to here no?
void SerialSend()
{
Serial.print("PID ");
Serial.print(setpoint);
Serial.print(" ");
Serial.print(input);
Serial.print(" ");
Serial.print(output);
Serial.print(" ");
Serial.print(pid.GetP_Param());
Serial.print(" ");
Serial.print(pid.GetI_Param());
Serial.print(" ");
Serial.print(pid.GetD_Param());
Serial.print(" ");
if(pid.GetMode()==AUTO) Serial.println("Automatic");
else Serial.println("Manual");
}
 
skippymcware,

skippymcware

Well-Known Member
hey all,

been super busy. made some progress...



that is with the 300W coil heater. I have the dc cartridge heater, i just wanted to make sure I had my controller working with this one first. I will make a test housing over the next week and a half. Then I can test the unit with the proto board model of the circuit.

have to try and get this thing on a board in the next month or two. i will keep you updated.
 
skippymcware,

gotlucy

Well-Known Member
looking forward to seeing more progress! i think your determination and goal is quite admirable.
 
gotlucy,

skippymcware

Well-Known Member
Thanks. Strangely, working on this project has become how I relax. I have a little more progress for you.

So, I dove into setting up the power pwm for the heater control. First off, I can't believe I didn't burn anything important out doing this. The arduino and max6675 are pretty resilient little chips. The other good news is I had spares for those the components that fell into the other category, and, despite the occasional f-bomb, I ended up making quite a bit of progress.

Here is the general setup:


The heater is on the left in the vice. The mosfet and terminal block are in a proto board in the other vice. Two reasons for that. First, I had no more room on my breadboard. Second, I was afraid it would get too hot. It actually stays very cool. The 12VDC power supply is in the back there. Just a radshack unit. It puts out 15 amps, so I can't complain. I will switch to batteries once everything else is smoothed over. Also, you can see I still have the arduino board on there. The main reason is so I can use this:



I have mentioned this before, but here you can see how helpful it is. The green line is setpoint, and the red is temp. The blue is pwm signal. You can see that I have increased the setpoint by 50 degrees twice over this run, and the heater responded. I haven't tuned the parameters yet, but I think it is going to be a slower on heat up and cool down than the big heater. Less surface area to cool with, I guess.

Here are two pictures of the whole setup:



Between the oscilloscope on my pwm output and signal, and the processing usb interface, it makes it much harder to f something up. One time I turned around to do something, and before I knew it, the heater was 1000 degrees. Speaking of the oscilloscope, I figured some peoples would be interesting in seeing these:

Here is the pwm signal line displayed on the scope. The peaks are so narrow because it is asking for a very small duty cycle:


Here is another one with a bigger duty cycle:



Lastly, here is a comparison of what is coming out of the mosfet with the pwm signal going to it:


Two interesting things here. First, there is a time constant associated with the voltage drop. Ideally it should be square like the upper signal. This isn't a problem at the pwm frequency I am using now, but it may become a problem once I bump it up to get it fully out of the audible range. You can't here it now if there is music on, but it is annoying in a silent room. The other interesting thing is the very large overshoot and oscillation of the voltage when it is turned back on. That spikey peak is almost 30V. I can't imagine this is good for components, but there is a chance that it might not matter. I am pretty new to all this.

Alright, its late. Later,
-skippy
 
skippymcware,
  • Like
Reactions: lazylathe

cebe11

Well-Known Member
Wow, no idea how I stumbled upon the bud toaster and all these other great ideas but it made my year!
Thanks guys. I'm a mech eng by trade that specializes in heat transfer processes etc.
Need to do some studying up before I can contribute much but will help as i can. Also got my Arduino on the way so am anxiously watching everyones progress.

Question:

I see lots of references to the original Eterra patent but am not very succesful in finding it. Does anyone have a copy? The number? Even the holders name of the patent so i can go back and start at the beginning?

Thanks all

Excellent website.

My wife already hates you guys, but she does love the vape so it pays off in the end

Happy new year to all!
 
cebe11,

Hippie Dickie

The Herbal Cube
Manufacturer
skippy - just an observation ... those megabyte jpgs make this thread take forever to load.

Cebe -- i never saw the patent itself, just a post by the Eterra dude that described his vape.
 
Hippie Dickie,

Mark

John Brown
We need a spoiler tag, it's like a quote but you have to click to expand.
But I'm sure he knows about the codes, heck it's his thread. :ko:
 
Mark,

vap999

Well-Known Member
cebe11 said:
Wow, no idea how I stumbled upon the bud toaster and all these other great ideas but it made my year!
Question:

I see lots of references to the original Eterra patent but am not very succesful in finding it. Does anyone have a copy? The number? Even the holders name of the patent so i can go back and start at the beginning?
See U.S. patents 4,141,369 ,and also 4,303,083. [Try these links: http://patft.uspto.gov/netacgi/nph-...&S1=burruss.INNM.&OS=in/burruss&RS=IN/burruss and http://patft.uspto.gov/netacgi/nph-...&S1=burruss.INNM.&OS=in/burruss&RS=IN/burruss

See the inventor's Web site at http://flashevap.com/. Most of the Web pages appear not to have been updated for well over a decade.
For more information on the Eterra, again legacy/dated Web pages, more recently apparently made or sold by someone else, see http://www.lightwell.net/classic.html.
 
vap999,
  • Like
Reactions: Swampy

bluecar5556

Well-Known Member
That voltage spike you are seeing is the magnetic field collapsing probably from something being switched off. The solution is using a capacitor in series to absorb the collapsed magnetic field.
 
bluecar5556,
Top Bottom