Helene Moorman 2015-02-22 20:38:05 -08:00
commit fee6ed6c0c
9 zmienionych plików z 103 dodań i 334 usunięć

Wyświetl plik

@ -1,6 +0,0 @@
Raspberry Pi controlled Kiln
============================
This project implements a wifi-accessible temperature controller for a ceramic firing kiln.
Please see the BOM for all the parts we purchased for this project. Additional circuit diagrams and 3D models will be forthcoming.

Wyświetl plik

@ -3,7 +3,7 @@
#define PIN_STEP2 8
#define PIN_STEP3 7
#define PIN_STEP4 6
#define PIN_AUXTEMP A1
#define PIN_AUXTEMP 2
#define PIN_TEMP_CS 4
#define PIN_LOADCELL A3
#define PIN_FLAME_A A2
@ -12,8 +12,17 @@
#define STEP_SPEED 275//in steps per second
#define TEMP_UPDATE 250 //milliseconds
#define MOTOR_TIMEOUT 5000 //milliseconds
#define AUX_UPDATE 1000 //milliseconds
#define MOTOR_TIMEOUT 60000 //milliseconds
#define NUM_AUXTEMP 2
#define NO_PORTB_PINCHANGES
#define NO_PORTC_PINCHANGES
#define DISABLE_PCINT_MULTI_SERVICE
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Stepper.h>
#include <Wire.h>
#include <SPI.h>
@ -28,7 +37,7 @@ struct Status {
float main_temp;
float ambient;
float weight;
float aux_temp[2];
float aux_temp[NUM_AUXTEMP];
} status;
uint8_t* status_data = (uint8_t*) &status;
@ -36,22 +45,34 @@ const float step_interval = 1. / STEP_SPEED * 1000.; //milliseconds
//intermediate variables
Adafruit_MAX31855 thermo(PIN_TEMP_CS);
Stepper stepper(2048, PIN_STEP1, PIN_STEP3, PIN_STEP2, PIN_STEP4);
Stepper stepper(2048, PIN_STEP4, PIN_STEP2, PIN_STEP3, PIN_STEP1);
pushbutton reglimit = pushbutton(PIN_REGLIMIT, 5);
OneWire oneWire(PIN_AUXTEMP);
DallasTemperature sensors(&oneWire);
DeviceAddress aux_addr[NUM_AUXTEMP];
char i2c_command;
float next_step;
unsigned long next_temp;
unsigned long next_aux = 0;
unsigned char motor_active = false;
unsigned long stepper_target = 0;
unsigned int n_clicks = 0; //Number of full rotations
unsigned long num_aux = 0;
int n_clicks = 0; //Number of full rotations
boolean limit_state = false;
unsigned long limit_last;
void setup() {
//setup ignition mosfet
pinMode(PIN_IGNITE, OUTPUT);
digitalWrite(PIN_IGNITE, LOW);
status.ignite = 0.;
status.flame = false;
status.weight = 0.;
status.aux_temp[0] = 0.;
status.aux_temp[1] = 0.;
status.aux_temp[0] = -1.;
status.aux_temp[1] = -1.;
sensors.begin();
//Setup I2C
Wire.begin(0x08);
@ -61,33 +82,32 @@ void setup() {
//Set up regulator stepper
status.motor = 0;
//setup regulator limit switch
pinMode(PIN_REGLIMIT, INPUT);
digitalWrite(PIN_REGLIMIT, HIGH);
//setup ignition mosfet
pinMode(PIN_IGNITE, OUTPUT);
digitalWrite(PIN_IGNITE, LOW);
status.ignite = false;
//set initial temperature
//set initial thermocouple temperature
delay(500);
update_temp();
next_temp = millis() + TEMP_UPDATE;
//Setup auxtemp ds18b20 sensors
num_aux = sensors.getDeviceCount();
num_aux = NUM_AUXTEMP < num_aux ? NUM_AUXTEMP : num_aux;
for (int i = 0; i < num_aux; i++) {
sensors.getAddress(aux_addr[i], i);
sensors.setResolution(aux_addr[i], 12);
status.aux_temp[i] = 0.;
}
if (num_aux > 0) {
sensors.setWaitForConversion(false);
sensors.requestTemperatures();
next_aux = millis() + AUX_UPDATE;
}
}
int dir;
unsigned long now;
void loop() {
now = millis();
if (digitalRead(PIN_REGLIMIT) == LOW) {
if (limit_last == 0) {
limit_last = millis();
} else if ((millis() - limit_last) > 5) {
n_clicks += dir;
limit_last = 0;
}
}
reglimit.update();
status.aux_temp[1] = reglimit.n_clicks;
if (stepper_target != status.motor && now > next_step) {
dir = status.motor < stepper_target ? 1 : -1;
@ -95,7 +115,7 @@ void loop() {
//Limit switch tripped
if (stepper_target == 0) {
if (n_clicks == 0)
if (reglimit.n_clicks == 0)
status.motor = 0;
} else {
status.motor += dir;
@ -119,6 +139,12 @@ void loop() {
next_temp += TEMP_UPDATE;
}
//update auxtemp
if (num_aux > 0 && now > next_aux) {
update_aux();
next_aux += AUX_UPDATE;
}
//check flame status
}
@ -134,9 +160,14 @@ void update_temp() {
thermo.readAll(status.main_temp, status.ambient);
}
void update_aux() {
for (int i = 0; i < num_aux; i++) {
status.aux_temp[i] = sensors.getTempC(aux_addr[i]);
}
sensors.requestTemperatures();
}
void i2c_update() {
//update temperatures
if (i2c_command == 'M') {
Wire.write((byte*) &(status.motor), 4);
} else if (i2c_command == 'I') {
@ -170,7 +201,8 @@ void i2c_action(int nbytes) {
set_regulator(*((unsigned int*) buffer));
break;
case 'I':
digitalWrite(PIN_IGNITE, buffer[0]);
analogWrite(PIN_IGNITE, buffer[0]);
status.ignite = buffer[0];
break;
}

Wyświetl plik

@ -1,7 +1,7 @@
class pushbutton : public CallBackInterface
{
public:
uint8_t n_clicks;
int n_clicks;
uint8_t pin;
unsigned int interval;
unsigned long last;
@ -11,8 +11,6 @@ class pushbutton : public CallBackInterface
n_clicks = 0;
last = 0;
init();
state = digitalRead(pin);
};
void cbmethod() {
last = millis();
@ -31,12 +29,11 @@ class pushbutton : public CallBackInterface
private:
int dir;
boolean state;
void init () {
pinMode(pin, INPUT);
digitalWrite(pin, HIGH);
PCintPort::attachInterrupt(pin, this, FALLING);
PCintPort::attachInterrupt(pin, this, CHANGE);
};
};

Wyświetl plik

@ -1,174 +0,0 @@
#define PIN_IGNITE 10
#define PIN_STEP1 9
#define PIN_STEP2 8
#define PIN_STEP3 7
#define PIN_STEP4 6
#define PIN_AUXTEMP A1
#define PIN_TEMP_CS 4
#define PIN_LOADCELL A3
#define PIN_FLAME_A A2
#define PIN_FLAME_D 1
#define PIN_REGLIMIT 5
#define STEP_SPEED 275//in steps per second
#define TEMP_UPDATE 250 //milliseconds
#define MOTOR_TIMEOUT 5000 //milliseconds
#define NO_PORTB_PINCHANGES
#define NO_PORTC_PINCHANGES
#define DISABLE_PCINT_MULTI_SERVICE
#include <Stepper.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_MAX31855.h>
#include <ooPinChangeInt.h>
#include "pushbutton.h"
struct Status {
unsigned char ignite;
unsigned char flame;
unsigned int motor;
float main_temp;
float ambient;
float weight;
float aux_temp[2];
} status;
uint8_t* status_data = (uint8_t*) &status;
const float step_interval = 1. / STEP_SPEED * 1000.; //milliseconds
//intermediate variables
Adafruit_MAX31855 thermo(PIN_TEMP_CS);
Stepper stepper(2048, PIN_STEP4, PIN_STEP2, PIN_STEP3, PIN_STEP1);
pushbutton reglimit = pushbutton(PIN_REGLIMIT, 5);
char i2c_command;
float next_step;
unsigned long next_temp;
unsigned char motor_active = false;
unsigned long stepper_target = 0;
int n_clicks = 0; //Number of full rotations
boolean limit_state = false;
unsigned long limit_last;
void setup() {
status.flame = false;
status.weight = 0.;
status.aux_temp[0] = 0.;
status.aux_temp[1] = 0.;
//Setup I2C
Wire.begin(0x08);
Wire.onRequest(i2c_update);
Wire.onReceive(i2c_action);
//Set up regulator stepper
status.motor = 0;
//setup ignition mosfet
pinMode(PIN_IGNITE, OUTPUT);
digitalWrite(PIN_IGNITE, LOW);
status.ignite = false;
//set initial temperature
delay(500);
update_temp();
next_temp = millis() + TEMP_UPDATE;
}
int dir;
unsigned long now;
void loop() {
now = millis();
reglimit.update();
status.aux_temp[0] = reglimit.n_clicks;
if (stepper_target != status.motor && now > next_step) {
dir = status.motor < stepper_target ? 1 : -1;
stepper.step(dir);
//Limit switch tripped
if (stepper_target == 0) {
if (reglimit.n_clicks == 0)
status.motor = 0;
} else {
status.motor += dir;
}
next_step += step_interval;
}
//put motor to sleep after timeout
if (motor_active && (now - next_step) > MOTOR_TIMEOUT) {
digitalWrite(PIN_STEP1, LOW);
digitalWrite(PIN_STEP2, LOW);
digitalWrite(PIN_STEP3, LOW);
digitalWrite(PIN_STEP4, LOW);
motor_active = false;
}
//update temperature
if (now > next_temp) {
update_temp();
next_temp += TEMP_UPDATE;
}
//check flame status
}
void set_regulator(unsigned long pos) {
motor_active = true;
reglimit.setDir(status.motor < pos ? 1 : -1);
if (stepper_target == status.motor)
next_step = millis(); //Start stepping immediately
stepper_target = pos;
}
void update_temp() {
thermo.readAll(status.main_temp, status.ambient);
}
void i2c_update() {
//update temperatures
if (i2c_command == 'M') {
Wire.write((byte*) &(status.motor), 4);
} else if (i2c_command == 'I') {
Wire.write((byte*) &(status.ignite), 1);
} else if (i2c_command == 'T') {
Wire.write((byte*) &(status.main_temp), 4);
} else if (i2c_command == 'F') {
Wire.write((byte*) &(status.flame), 1);
} else {
Wire.write(status_data, sizeof(struct Status));
}
i2c_command = 0;
}
byte buffer[32];
void i2c_action(int nbytes) {
i2c_command = Wire.read();
int i = 0;
while (Wire.available()) {
buffer[i++] = Wire.read();
}
if (nbytes == 1) {
return; //Command already stored, no arguments
}
switch (i2c_command) {
case 'M':
set_regulator(*((unsigned int*) buffer));
break;
case 'I':
analogWrite(PIN_IGNITE, buffer[0]);
status.ignite = buffer[0];
break;
}
i2c_command = 0;
}

Wyświetl plik

@ -1,39 +0,0 @@
#include "protocol.h"
char Comm::buffer[BUFFER_LENGTH+1];
int Comm::_nacts;
char Comm::_commands[MAX_ACTIONS];
char* (*Comm::_actions[MAX_ACTIONS])(int, char*);
char Comm::_current_cmd;
int Comm::_current_len;
Comm::Comm(int addr) {
Wire.begin(addr);
Wire.onReceive(_handle_request);
Wire.onRequest(_handle_response);
}
int Comm::action(char cmd, char* (*func)(int, char*)) {
if (_nacts >= MAX_ACTIONS)
return 1;
_actions[_nacts] = func;
return 0;
}
void Comm::_handle_request(int nbytes) {
_current_cmd = Wire.read();
_current_len = nbytes-1;
for (int i = 0; i < nbytes-1; i++) {
buffer[i] = Wire.read();
}
}
void Comm::_handle_response() {
for (int i = 0; i < MAX_ACTIONS; i++) {
if (_commands[i] == _current_cmd) {
_actions[i](_current_len, buffer);
}
}
Wire.write(buffer);
}

Wyświetl plik

@ -1,25 +0,0 @@
#ifndef PROTOCOL_H
#define PROTOCOL_H
#include "Wire.h"
#define MAX_ACTIONS 16
class Comm {
private:
static char buffer[BUFFER_LENGTH+1];
static int _nacts;
static char _commands[MAX_ACTIONS];
static char* (*_actions[MAX_ACTIONS])(int, char*);
static char _current_cmd;
static int _current_len;
static void _handle_request(int);
static void _handle_response(void);
public:
Comm(int addr);
int action(char, char* (*)(int, char*));
};
#endif //PROTOCOL_H

Wyświetl plik

@ -1,39 +0,0 @@
class pushbutton : public CallBackInterface
{
public:
int n_clicks;
uint8_t pin;
unsigned int interval;
unsigned long last;
pushbutton (uint8_t _pin, unsigned int _interval): pin(_pin) , interval(_interval) {
dir = 0;
n_clicks = 0;
last = 0;
init();
};
void cbmethod() {
last = millis();
};
void update() {
if (last != 0 && (millis() - last) > interval) {
n_clicks += dir;
last = 0;
}
}
void setDir(int d) {
dir = d;
}
private:
int dir;
void init () {
pinMode(pin, INPUT);
digitalWrite(pin, HIGH);
PCintPort::attachInterrupt(pin, this, CHANGE);
};
};

Wyświetl plik

@ -127,6 +127,7 @@ class Profile(threading.Thread):
self.pid = PID.PID(Kp, Ki, Kd)
self.callback = callback
self.running = True
self.duty_cycle = False
self.start()
@property
@ -142,8 +143,8 @@ class Profile(threading.Thread):
self.running = False
def run(self):
_next = time.time()+self.interval
while not self.completed and self.running:
now = time.time()
ts = self.elapsed
#find epoch
for i in range(len(self.schedule)-1):
@ -157,13 +158,25 @@ class Profile(threading.Thread):
temp = self.therm.temperature.temp
if temp == -1:
continue #skip invalid temperature readings
pid_out = self.pid.update(temp)
if pid_out < 0: pid_out = 0
if pid_out > 1: pid_out = 1
self.regulator.set(pid_out)
elif temp - setpoint > 10:
self.regulator.off()
self.duty_cycle = True
pid_out = -1
elif self.duty_cycle:
if temp - setpoint < -5:
self.regulator.ignite()
self.duty_cycle = False
pid_out = -1
else:
pid_out = self.pid.update(temp)
if pid_out < 0: pid_out = 0
if pid_out > 1: pid_out = 1
self.regulator.set(pid_out)
if self.callback is not None:
self.callback(temp, setpoint, pid_out)
time.sleep(self.interval - (time.time()-now))
sleep = _next - time.time()
if sleep > 0:
time.sleep(sleep)
_next += self.interval

Wyświetl plik

@ -244,10 +244,10 @@ class Regulator(threading.Thread):
pass
class Breakout(object):
def __init__(self, addr, maxsteps=6500, minsteps=2300):
def __init__(self, addr, maxsteps=6500, minsteps=((2600, 0), (2300, 15)) ):
import breakout
self.device = breakout.Breakout(addr)
self.min = minsteps
self.min_interp = minsteps
self.max = maxsteps
def exit():
@ -255,31 +255,41 @@ class Breakout(object):
self.off()
atexit.register(exit)
def ignite(self, start=2500, delay=5):
@property
def min(self):
temp = self.device.status.aux_temp0
if temp > self.min_interp[1][1]:
return self.min_interp[1][0]
elif temp <= self.min_interp[0][1]:
return self.min_interp[0][0]
else:
mrange = self.min_interp[0][0] - self.min_interp[1][0]
trange = self.min_interp[1][1] - self.min_interp[0][1]
mix = (temp - self.min_interp[0][1]) / float(trange)
return mrange * mix + self.min_interp[1][0]
def ignite(self, start=2400):
logger.info("Igniting system")
self.device.ignite = 127
time.sleep(2)
self.device.ignite = 255
time.sleep(delay)
self.device.motor = start
while self.device.motor != start:
time.sleep(.1)
self.device.motor = self.min
#self.device.ignite = 127
@property
def output(self):
out = (self.device.motor - self.min) / float(self.max - self.min)
m = self.min
out = (self.device.motor - m) / float(self.max - m)
if out < 0:
return -1
return out
def set(self, value):
m = self.min
if self.device.motor == 0:
raise ValueError('Must ignite first')
if not 0 <= value <= 1:
raise ValueError('Must give value between 0 and 1')
self.device.motor = int((self.max - self.min)*value + self.min)
self.device.motor = int((self.max - m)*value + m)
def off(self):
self.device.motor = 0