User Manual: Reactor Interface and Control System

User Manual: Reactor Interface and Control System
User Manual:
Reactor Interface and Control System
By Gregory M. Mullen
[email protected]
v1.0 – January 15, 2016
Table of Contents
1. Introduction
1
2. Hardware
2-4
3. Software
5
4. Flow Control
6-7
5. Temperature Control
8-9
6. Using the Web Interface
10
7. Starting up the Control System
11
8. Control Programs
a. Overview
12-13
b. Flow Control Code
14-16
c. Temperature Control Code
17-20
d. Web Interface Code
21-27
e. Sample Status and Configuration Files
28-29
1
Introduction
The purpose of this system is to provide remote flow and temperature control of the fixed-bed
flow reactor reactor with a user interface updated via a web browser. The system was designed with
ease of operation in mind. Using it to control the reactor is very simple and can be learned in a matter of
minutes. The flow rates for four different gases and the temperature of the reactor can all be controlled
through the browser interface. Any device on the University of Texas network can connect to this
interface provided the user has the proper login credentials, making remote control of the reactor
possible.
In contrast to the ease of operation, setup and modification of the system is a different beast
altogether. To fully comprehend the machinery and methods by which the system functions requires
competency in a number of different areas. Altering the hardware or software of the system is,
therefore, likely to come with a steep learning curve. Here we’ll talk a bit about some of the basic skills
you will need to learn if you plan to make a modification.
The reactor interface system involves a number of electronic components that function
together. To modify these systems requires some understanding of wiring/soldering, general design of
electric circuits, communication protocols for stacked computer chips, and general design and
fabrication of structural hardware (drilling, using a Dremel, etc).
Additionally, some proficiency in coding is required to understand and modify the control
system software. The most extensively programming language employed in this system is Python. Basic
proficiency in HTML, CSS, and JavaScript are also required to fully understand the software packages
that run the system. Unix is used to interact with the system for launching the web interface program
and carrying out some basic maintenance functions. Therefore, some knowledge of Unix should be
attained by even light users.
In subsequent sections, we will go into more detail regarding the design of the control systems.
If the system is up and running, and the reader simply wants to learn how to use the web interface, go
ahead and jump to section 6, “Using the Web Interface.” Once the system is set up and running, it
should continue to work indefinitely unless the software crashes or power is lost to the control unit.
By Gregory M. Mullen
2
Hardware
The hardware on which the system runs is separated into three key
facets – flow control, temperature control, and total system interface
control.
The flow control hardware consists of the mass flow controllers
(MFCs) themselves and the MFC interface unit. The MFCs (MKS model
M100B) are the components that physically contact the gases that are
supplied to the reactor and regulate flow of these gases to the reactor
system. The reactor has five MFCs in total and the system can control up to
four at a time. These controllers have maximum flow rates of 10, 100, 200,
MKS M100B mass
flow controller
and 500 standard cm3/min (sccm). The lower the maximum flow rate, the higher resolution that unit
has. The MFC interface unit (MKS Type 247 Power
Supply/Readout) interacts with each MFC to set the desired
parameters for operation. This unit connects into each
controller individually and sends signals to control their
flow rates and on/off statuses. This device also interacts
MKS Type 247 Power Supply/Readout
with the total system interface control.
The temperature of the reactor is controlled with an Applied Test Systems 3210 tube furnace.
The furnace is an electrically heated via 120 VAC, and
does not come with a built-in control system. If plugged
into the wall outlet directly, the unit outputs its
maximum power and the furnace reaches a very high
temperature (which would also be likely to break it). A
separate system for temperature control is required for
proper operation. This is generally achieved via a PID
controller, which in our system is implemented via the
total system interface control. The AC power supply
Applied Test Systems 3210 Tube Furnace
chord of the furnace is connected to a peripheral control box containing a solid state relay that acts as
an electrical switch for regulating the furnace’s power. The relay switches the furnace on and off to
By Gregory M. Mullen
3
ramp the temperature or hold the reactor at a constant temperature. The parameters for this control
are provided by the total system interface.
The total system interface control unit
allows the operator of the reactor to communicate
with the MFCs and the temperature control system.
It is the brain through which the entire system
operates. At the core of the total system interface
control unit is a Raspberry Pi Model B+ computer
board. This component, along with the aid of a few
peripheral chips, takes inputs from the user (sent
Raspberry Pi Model B+ Computer Board
via the web browser) and processes them as needed into a package that the other components can
understand. All of these components are housed in an aluminum box that is located below the reactor.
Ethernet, serial, and thermocouple cables connect into this box and login credentials for connecting to
the system are taped to it as well.
A number of peripheral computer chips are used to perform various functions required to read
and generate signals used in the operation of the control system. Several chips are used to perform flow
control operations. These chips operate via the Raspberry Pi’s I2C bus. MCP4725 digital to analog
converter chips process digital signals from the Pi to generate a specified analog voltage output, which is
used to control the flow setting for each MFC. A TCA9548A Multiplexer chip works in tandem with the
MCP4725 chips and provides for simultaneous operation of four flow control units. This component is
necessary because the chips on the I2C bus are differentiated via an address line, and the MCP4725
chips that we use here all share the same address. If the multiplexer were not used, the MCP4725 chips
would interfere with one another. The I2C channels of the MCP4725 units are connected into the
multiplexer, which switches amongst each unit to alter settings individually.
In addition to the I2C components, a chip utilizing the Raspberry Pi’s SPI bus is also used in the
system. We use this protocol to address a K-type thermocouple amplifier chip (MAX31855). This chip is
an analog to digital converter that processes the voltage generated by the thermocouple that probes
the temperature of the furnace and convert it to a digital format that the Pi can read.
By Gregory M. Mullen
4
Overall Reactor System
By Gregory M. Mullen
5
Software
A number of control programs were developed specifically for this system
by GMM. These programs are discussed in sections to come. In this section we
discuss the software packages that are required to startup and run the Raspberry
Pi. All software used to set up this system is freely available through the internet
and open source (I believe).
The operating system on which the Raspberry Pi is build is called Raspbian
Raspbian
(available for download here: https://www.raspberrypi.org/downloads/raspbian/). The OS image was
written onto an SD card prior to initial startup of the Pi. Guides for OS image writing are available on the
internet (e.g. https://www.raspberrypi.org/documentation/installation/installing-images/README.md).
These guides should be referenced if a new SD card is required – i.e. in the case of total system failure or
a new system build.
After setup and initial configuration (for which walkthroughs are also readily available on the
internet), a number of programs were installed onto the Pi’s SD card before operation of the system
could begin. Flask is a program that provides API for generating a web interface within a Python script.
All of the control programs used in this system were written in Python, and Flask offers a convenient
means of summoning the web interface. Documentation can be found here: http://flask.pocoo.org/
Graphite is used to plot data collected by the control programs. The reactor temperature is
probably the most useful piece of data that can be accessed via Graphite as implemented in this system.
This program is useful, but not necessary for operating the reactor. Lately, it has been acting glitchy and
frequently crashing. I have mostly abandoned using it, but if it becomes required in the future, it is
available pending a fix. Documentation can be found here: http://graphite.wikidot.com/
Apache is software that renders web pages. It is used in tandem with Graphite to generate the
page with graphable data for view in the web browser. Documentation can be found here:
https://httpd.apache.org/
I also used a number of Python libraries in developing the control programs that will be
discussed in the sections to follow (RPi.GPIO, Adafruit_GPIO, Adafruit_Max31855, Adafruit_MCP4725,
and Adafruit_I2C). I believe that these libraries can all be pulled from the reactor interface software
archives saved on lab computers and on the Raspberry Pi, itself.
By Gregory M. Mullen
6
Flow control
Flow control is conducted by the MFC interface and regulated by the total system interface. In
order to use the web interface to regulate control, the On/Off switches on the interface unit for each of
the four channels must be set to Rem, and the Set Pt. Flow switches must be set to Ext. Additionally, the
scaling controls on the back of the unit should each be set to 100. See the user manual for the MKS Type
247 Power Supply/Readout if you are still confused.
Control of the flow set point for each channel is conducted by providing an analog voltage
between 0 and 5 V to the proper pin on the MFC interface unit’s serial connection (see “Connectors and
Cables” section of the MKS Type 247D instruction manual for more information). The flow setting for
each controller is determined by linear translation of the voltage signal applied to that controller’s pin.
This voltage acts as a linear scaling factor between 0 sccm, which corresponds to 0V, and the maximum
flow rate for the specified controller, which corresponds to 5V. For example, a 2.5 V signal set to the 100
sccm maximum controller produces a set point of 50 sccm, and a 2.5 V signal set to the 200 sccm
maximum controller produces a set point of 100 sccm.
Setting the flow rate for a given channel is not sufficient to activate flow through that channel.
On/off control of each MFC is conducted via a different pin on the interface unit’s serial connection. A
binary on/off signal voltage (high or low) is sent from the Pi to the appropriate pin for each channel to
set the flow for that channel on or off.
Additionally, each gas requires a specific correction factor in order for the MFC to deliver the
proper flow rate. Reference the MKS M100B instruction manual for further discussion of gas correction
factors (GCFs) and equations for determining them based on the gas composition. The GCF scales the
voltage output to the controller interface in order to achieve the desired flow rate for each gas.
***Note: because we set the GCF from the web interface and not from the flow controller interface
unit, the flow rate displayed on the interface unit itself is not correct unless the GCF for that gas
happens to be 1.0.
Interacting with the flow control program occurs via the web interface. Four channels with
on/off switches, maximum flow rate settings, gas correction factor settings, and flow rate settings are
displayed on the left-hand side of the interface – an example of which is shown in the image below. By
By Gregory M. Mullen
7
setting these values accordingly and clicking the submit button located on the right-hand side of the
interface, the system will deliver the desired parameters to each MFC.
Flow control toolbar for channel #1 (screenshot from web interface)
By Gregory M. Mullen
8
Temperature control
Control of the furnace temperature is conducted primarily by the Raspberry Pi board itself via a
PID control scheme implemented in Python that employs pulse width modulation of the furnace’s
power. The system applies power to the furnace during regularly spaced “pulses.” The percent of time
during which power is applied to the furnace for each pulse is determined via the PID algorithm. By
optimizing the pulse frequency and PID parameters, the temperature of the furnace can be regulated to
within 1 °C.
PID parameters can be determined by a number of techniques. GMM employed the Process
Reaction Method to determine the initial parameters for the system. Steps for carrying out this process
are here:
https://controls.engin.umich.edu/wiki/index.php/PIDTuningClassical
The calculated parameters are as follows:
P = 35.6
I = 4.5
D = 30
PWM frequency = 0.5
If the control allotted by these parameters does not meet the user’s desired specifications,
adjustments via a different tuning technique or trial and error are suggested.
The temperature control program launches automatically upon starting up the web interface.
Changing the set point is accomplished by entering the desired temperature into the temperature
setting field bar and pressing the submit button. You will notice a furnace control switch above the field
bar for the temperature setting, which can be seen in the image below. This switch should always be left
on. The function of this switch was initially intended to shut off power to the furnace entirely (acting as
an override). However, as implemented in the temperature control code, the system can still receive
power in the off position. This glitch could be fixed by changing a few lines of this code, but the system
can work without the change as well. Since the change was not crucial at the point of the software’s
implementation, the glitch was allowed to remain. To switch the furnace off, set the temperature to any
By Gregory M. Mullen
9
value below room temperature. In doing so, power will not be supplied to the furnace and the heating
will effectively be off.
Temperature control toolbar (screenshot from web interface)
The temperature control program also provides functionality for linear temperature ramping.
Due to the means with which this feature was implemented, the ramping feature only works for
temperature increases. If a lower temperature is entered into the set point field with ramping activated,
the system will approach that set point as fast as possible, acting as if the ramping feature is not
activated. If ramping for a temperature decrease is required by the user, the temperature control code
will need to be modified to include this functionality (i.e. you will need to learn some Python and do it
yourself).
Using the ramping feature is straightforward enough that it likely does not require explanation.
Toggle the temperature ramping switch to on, set the desired final temperature setting and the ramp
rate, and then press the submit button. The temperature will ramp linearly and should track quite well,
although the accuracy of the fit will depend somewhat on the ramp rate and temperature of the system.
By Gregory M. Mullen
10
Using the web interface
Accessing the web interface is the most simple and straightforward way is to interact with the
system. This can be achieved as follows:
1. Open a web browser. Chrome and Firefox both seem to work without issue, but I can’t confirm
that this will be the case with every web browser.
2. Enter the IP address for the Raspberry Pi followed by “:8000” into address bar (e.g.
146.6.118.130:8000)
3. At this point you will be prompted to enter login credentials. They should be taped to the
control box under the reactor furnace and MFCs.
4. The system control interface should load immediately after logging in. Wait a few seconds for
the flow set points and current temperature to load. They will appear on the right-hand side of
web browser window.
5. To set the flow parameters, ensure the maximum sccm for each MFC to be used is correct.
These parameters are specific to the MFCs, themselves. The hardware is mounted beneath the
furnace. Each MFC should be labelled with its maximum flow rate. Set the gas correction factor
based on gas flowing through each controller. Set the desired flow rate, and then press submit
to implement these settings.
6. To set the temperature, enter the desired set point into the temperature setting input bar.
Ensure the furnace control switch is set to on. Set the temperature ramping switch to on if
desired and set the ramp rate. Press the submit button to implement these settings.
This procedure should work as written if the web interface is running and the user is connecting
locally on campus via a lab computer or WIFI. If the web interface does not load, skp to the next section
entitled, “Starting up the Control System,” and reset the system.
One of the most powerful features of the reactor control system is that it enables remote access
via any device with an internet connection, as long as that device can access the UT VPN network. After
connecting to the VPN, the reactor can be accessed by the same procedure discussed above.
By Gregory M. Mullen
11
Starting up the Control System
Once the web interface is started, the reactor control software should continue to run without
interruption unless connection is disrupted to the Raspberry Pi (internet or power) or in the event of an
unforeseen glitch. However, in the event of such an issue, the Raspberry Pi must be accessed via a SSH
connection. It should then be restarted before relaunching the web interface. A standard connection
procedure would be conducted as follows:
1. Open a SSH client
2. Determine the IP address of the Raspberry Pi on the campus network (currently 146.6.118.130,
but this is not a static IP and may change without warning). The Pi Finder program is a
convenient tool that can be used to determine this IP address, although it could fail if another Pi
become connected to the campus network. Information about this program can be found here:
https://learn.adafruit.com/the-adafruit-raspberry-pi-finder/overview
3. Log in with the username that is displayed on the total system interface control unit. The means
of login varies a bit depending on the SSH software you are using, but you will have to enter the
username one way or another.
4. Enter the password. It is also displayed on the control box if you don’t already know it.
5. Follow any prompts that display during startup. Eventually you should be directed to the home
directory.
Once connected, you will have to navigate via Unix commands. The system can be shut down by
entering “sudo shutdown now” into the command prompt or restarted by entering “sudo shutdown –r
now” and waiting a few mintues. After restarting, the SSH connection will close and you will have to
follow the steps listed above to connect once again.
To start up the reactor interface software, navigate to the folder named “program” and then
enter the “scripts” subdirectory. This directory contains the code files required to run the software. The
software is initiated by launching the web interface program. To do so, enter the command “sudo
python web_interface.py” Some dialog should appear and after a few seconds, the interface control
panel will be available via the web browser.
By Gregory M. Mullen
12
Control Programs
Overview
The control system operates via three primary sets of code written by GMM. One of these
programs launches and controls the web interface, another reads settings and generates signals for flow
control, and the final reads settings and runs pulse width modulated PID control of the reactor furnace.
The code that launches the web interface acts as the top-level code for controlling the system.
While starting up the interface and initializing settings, the web interface code starts the flow control
and temperature control programs as subprocesses. Reading through the code below should provide
insight into the full machinery with which the web interface program operates. At a high level, the web
interface code performs a few key functions. It renders the web browser that the operator uses to
interact with the reactor. The code also updates status files, which are used by the subprocesses, any
time a change is made with settings submitted by the operator from the web interface and periodically
updates the web interface status bar with the current flow rate settings, flow controller on/off statuses,
and reactor temperature.
The flow control program operates by reading set points, on/off statuses, GCFs, and MFC
maximum values for each flow channel from a status file, processing these values to determine the
proper voltages to apply to the set point pins and on/off pins of the interface unit, and updating the
values at the Raspberry Pi. Flow rate settings are communicated to peripheral chips on the I2C bus in
this program as well. This process repeats every 5 seconds to keep the values current.
The temperature control program reads the temperature of the furnace, generates parameters
for control of the furnace via pulse width modulation PID control, and uses the Raspberry Pi to generate
a binary voltage signal for implementation of that control in tandem with the solid state relay in the
temperature control unit. The reactor temperature is read via a chip operating on the SPI bus as
discussed previously in the “Hardware” section, which is implemented in the program as well. This
temperature is used in an algorithm that determines the PID settings, which is housed in a separate
library. The PID library determines the duty cycle for the pulse width modulation, or the percent of time
during each pulse that the solid state relay unit is sent a high voltage signal from the Pi. When high
voltage is received, the solid state relay is turned on, providing current to the furnace and heating the
By Gregory M. Mullen
13
reactor. The PID library employed within the temperature control program is available for
viewing/download here:
http://code.activestate.com/recipes/577231-discrete-pid-controller/
The temperature ramping feature as implemented in the control program works by gradually
changing the set point for the furnace over time based on linear interpolation between the initial
temperature when the submit button was pressed on the web interface and the new desired set point
temperature. The temperature control program also updates every 5 seconds.
In addition to the three control programs discussed, the reactor interface software also uses a
number of text files for setting hardware configuration information and live status settings for use in the
control programs. The configuration file (config.txt) stores assignments for the Raspberry Pi GPIO pins
and PID control settings. The pin assignments are hard-wired into the physical control units themselves.
They should not be altered unless the hardware for the system is modified. PID settings are subject to
change given the user’s control needs as discussed previously in the temperature control section. A
status file is generated for each control program. These files (status, status_flow_control, status_html,
and status_temp_control) keep settings required for implementation of each program up to date. Each
program reads its relevant status file and implements settings accordingly. The web interface updates all
status files to reflect the user’s desired settings.
By Gregory M. Mullen
14
Flow Control Code
#! /usr/bin/python
import
import
import
import
import
import
import
sys, platform, re, serial
subprocess
os
time
RPi.GPIO as GPIO
datetime
ConfigParser
from Adafruit_MCP4725 import MCP4725
from shutil import copyfile
from Adafruit_I2C import Adafruit_I2C
from socket import socket
#set working directory to where "flow_control.py" is
abspath = os.path.abspath(__file__)
dname = os.path.dirname(abspath)
os.chdir(dname)
#read values from the config file
config = ConfigParser.ConfigParser()
config.read("config.txt")
DEBUG = int(config.get('main','DEBUG'))
ch_address_list = ['channel addresses', 0x01, 0x02, 0x04, 0x08] #addresses for
channels on multiplexer
on_off_pin_list = ['on off pin assignments'] #initialize pin configuration list global
num_controllers = 4
def configureGPIO(num_controllers):
global on_off_pin_list
config = ConfigParser.ConfigParser()
config.read("config.txt")
GPIO.setmode(GPIO.BCM)
i = 1
while i <= num_controllers:
on_off_pin = int(config.get('main','ch_' + str(i) + '_on_off_pin'))
GPIO.setup(on_off_pin, GPIO.OUT)
subprocess.Popen("echo " + str(on_off_pin) + " > /sys/class/gpio/export",
shell=True)
on_off_pin_list.append(on_off_pin)
i += 1
#necessary settings and functions for graphite
CARBON_SERVER = '0.0.0.0'
CARBON_PORT = 2003
def sendData(message):
sock = socket()
try:
sock.connect( (CARBON_SERVER,CARBON_PORT) )
except:
print "Couldn't connect to %(server)s on port %(port)d, is carbon running?" %
{ 'server':CARBON_SERVER, 'port':CARBON_PORT }
sock.sendall(message)
By Gregory M. Mullen
15
#set all flow rates to 0 and all flow controllers to off on start
def initializeFlowRates(num_controllers):
i = 1
while i <= num_controllers:
file = ConfigParser.ConfigParser()
file.read("status")
set_pt = 'ch_' + str(i) + '_set_pt'
on_off = 'ch_' + str(i) + '_on_off_pin'
file.set('set',set_pt,0)
file.set('on_off', on_off, 'off')
with open('status', 'wb') as configfile:
file.write(configfile)
i += 1
copyfile('status','status_flow_control')
copyfile('status','status_html')
copyfile('status','status_temp_control')
#set all gcfs to 1.00 on start
def initializeGCFs(num_controllers):
i = 1
while i <= num_controllers:
file = ConfigParser.ConfigParser()
file.read("status")
gcf = 'ch_' + str(i) + '_gcf'
file.set('gcf',gcf,1.00)
with open('status', 'wb') as configfile:
file.write(configfile)
i += 1
#gauge factor for adjusting scaling factor
def getGaugeFactor(maximum):
check = str(maximum)
if int(check[0]) == 1:
f = float(100)
return f
if int(check[0]) == 2:
f = float(200)
return f
if int(check[0]) == 5:
f = float(50)
return f
else:
return 0
print 'incorrect entry for maximum flow rate'
def setFlowRate(dac,maximum,value,gcf):
volt = float(value) / float(maximum) * 5 / float(gcf)
val = int(volt / 5.3 * 4095)
dac.setVoltage(val)
#check on/off status of controller
def getFlowState(pin):
flowStatus = int(subprocess.Popen("cat /sys/class/gpio/gpio" + str(pin) +
"/value", shell=True, stdout=subprocess.PIPE).stdout.read().strip())
return flowStatus
By Gregory M. Mullen
16
configureGPIO(num_controllers)
initializeFlowRates(num_controllers)
initializeGCFs(num_controllers)
def run():
global ch_address_list, num_controllers
while True:
#set flow rates
file = ConfigParser.ConfigParser()
file.read("status_flow_control")
i = 1
while i <= num_controllers:
try:
set_pt = float(file.get('set','ch_' + str(i) + '_set_pt'))
# set flow control chip on multiplexer
i2ccommand = "sudo i2cset -y 1 0x70 0x00 " + str(ch_address_list[i])
output = subprocess.call(i2ccommand,shell=True)
#set flow rate
dac = MCP4725(0x62)
max = float(file.get('maximum','ch_' + str(i) + '_max'))
gcf = float(file.get('gcf','ch_' + str(i) + '_gcf'))
setFlowRate(dac,max,set_pt,gcf)
lines = []
lines.append("%s %s %d" % ('set','ch_' + str(i) +
'_set_pt.setPt',str(set_pt),now))
message = '\n'.join(lines) + '\n'
sendData(message)
except:
pass
i += 1
#set on/off
i = 1
while i <= num_controllers:
try:
on_off_status = file.get('on_off','ch_' + str(i) + '_on_off_pin')
if on_off_status == 'on':
GPIO.output(on_off_pin_list[i], False)
elif on_off_status == 'off':
GPIO.output(on_off_pin_list[i], True)
else:
print 'It broke.'
except:
pass
i += 1
time.sleep(5)
if __name__ == "__main__":
run()
By Gregory M. Mullen
17
Temperature Control Code
#! /usr/bin/python
import
import
import
import
import
import
import
sys, platform, re, serial
subprocess
os
time
RPi.GPIO as GPIO
datetime
ConfigParser
from PID import PID
from socket import socket
import Adafruit_GPIO.SPI as SPI
import Adafruit_MAX31855.MAX31855 as MAX31855
#set working directory to where "temp_control_daemon.py" is
abspath = os.path.abspath(__file__)
dname = os.path.dirname(abspath)
os.chdir(dname)
#read values from the config file
config = ConfigParser.ConfigParser()
config.read("config.txt")
DEBUG = int(config.get('main','DEBUG'))
furnace_pin = int(config.get('main','furnace_pin'))
clk_pin = int(config.get('main','clk_pin'))
cs_pin = int(config.get('main','cs_pin'))
do_pin = int(config.get('main','do_pin'))
#initialize furnace parameters
GPIO.setmode(GPIO.BCM)
GPIO.setup(furnace_pin, GPIO.OUT)
subprocess.Popen("echo " + str(furnace_pin) + " > /sys/class/gpio/export", shell=True)
heater = GPIO.PWM(furnace_pin, float(config.get('main','pwm_frequency')))
#initialize SPI for thermocouple amplifier
def initializeSPI():
config = ConfigParser.ConfigParser()
config.read("config.txt")
clk_pin = int(config.get('main','clk_pin'))
cs_pin = int(config.get('main','cs_pin'))
do_pin = int(config.get('main','do_pin'))
sensor = MAX31855.MAX31855(clk_pin, cs_pin, do_pin)
return sensor
sensor = initializeSPI()
#initialize PID
def initializePID():
config = ConfigParser.ConfigParser()
config.read("config.txt")
p = float(config.get('main','p'))
i = float(config.get('main','i'))
d = float(config.get('main','d'))
pid_param = PID(p,i,d)
return pid_param
#determine if furnace is on or off - this is only used when on/off control is used
By Gregory M. Mullen
18
def getHeaterState():
heaterStatus = int(subprocess.Popen("cat /sys/class/gpio/gpio" + str(furnace_pin)
+ "/value", shell=True, stdout=subprocess.PIPE).stdout.read().strip())
if heaterStatus == 1:
#heating
return 0
elif heaterStatus == 0:
#idle
return 1
else:
#broken
return 2
#self explanatory
def getCurrentTemp():
now = int( time.time() )
temp = sensor.readTempC()
now = int( time.time() )
lines = []
lines.append("%s %s %d" % ('thermocouple.tempRead',str(temp),now))
message = '\n'.join(lines) + '\n' #all lines must end in a newline
return message
#for on/off control - we are currently using PID instead of this
def heatOnOff(tempSet):
heaterStatus = getHeaterState()
temp = sensor.readTempC()
if heaterStatus == 0:
if temp < tempSet - hysteresis:
GPIO.output(furnace_pin, True)
elif heaterStatus == 1:
if temp > tempSet + hysteresis:
GPIO.output(furnace_pin, False)
else:
print "It broke."
#self explanatory
def updatePID(temp,target):
global heater, pid_param
pid_param.setPoint(target)
pid = pid_param.update(temp)
if pid >= 100:
pid = 100.0
elif pid <= 0:
pid = 0.0
heater.ChangeDutyCycle(pid)
now = int( time.time() )
lines = []
lines.append("%s %s %d" % ('PID.pid',str(pid),now))
message = '\n'.join(lines) + '\n' #all lines must end in a newline
return message
#necessary settings and functions for graphite
CARBON_SERVER = '0.0.0.0'
CARBON_PORT = 2003
By Gregory M. Mullen
19
def sendData(message):
sock = socket()
try:
sock.connect( (CARBON_SERVER,CARBON_PORT) )
except:
print "Couldn't connect to %(server)s on port %(port)d, is carbon running?" %
{ 'server':CARBON_SERVER, 'port':CARBON_PORT }
sock.sendall(message)
def run():
global heater, pid_param
# initialize status file with setpoint temperature - make 20 C always to start
time.sleep(5)
file = ConfigParser.ConfigParser()
file.read("status")
try:
file.set('temperature', 'temp', 20.0)
file.set('on_off', 'furnace_pin', 'off')
except:
pass
with open('status', 'wb') as configfile:
file.write(configfile)
with open('status_html', 'wb') as configfile:
file.write(configfile)
#set heater pin for pulse width modulation with specified frequency
heater.start(10) #start at 10% duty cycle
# initialize PID
pid_param = initializePID()
targetTemp = 20.0
while True:
# open status file and read furnace setting and target temperature
file = ConfigParser.ConfigParser()
file.read("status_temp_control")
status = file.get('on_off','furnace_pin')
temp = sensor.readTempC()
now = time.time()
if status == 'on':
targetTemp0 = float(file.get('temperature','temp'))
if targetTemp0 != targetTemp: #reinitialize PID if temperature set point
is changed
pid_param = initializePID()
targetTemp = float(file.get('temperature','temp'))
else:
pass
#ramping section
ramping = file.get('temperature','ramping')
if ramping == 'on':
ramp_start = float(file.get('temperature','ramp_start'))
ramp_rate = float(file.get('temperature','ramp_rate'))
temp0 = float(file.get('temperature','temp0'))
temp_diff = (now - ramp_start)/60 * ramp_rate
targetTempNow = temp0 + temp_diff
if targetTempNow < targetTemp:
message1 = updatePID(temp,targetTempNow)
sendData(message1)
lines = []
By Gregory M. Mullen
20
lines.append("%s %s %d" %
('target_temp.targetTemp',str(targetTempNow),now))
message = '\n'.join(lines) + '\n' #all lines must end in a newline
sendData(message)
else:
message1 = updatePID(temp,targetTemp)
sendData(message1)
lines = []
lines.append("%s %s %d" %
('target_temp.targetTemp',str(targetTemp),now))
message = '\n'.join(lines) + '\n'
sendData(message)
else:
message1 = updatePID(temp,targetTemp)
sendData(message1)
lines = []
lines.append("%s %s %d" %
('target_temp.targetTemp',str(targetTemp),now))
message = '\n'.join(lines) + '\n'
sendData(message)
else:
pass
# send temperature values to graphite
lines = []
lines.append("%s %s %d" % ('thermocouple.tempRead',str(temp),now))
message = '\n'.join(lines) + '\n' #all lines must end in a newline
sendData(message)
time.sleep(5)
if __name__ == "__main__":
run()
By Gregory M. Mullen
21
Web Interface
#!/usr/bin/python
import os
import subprocess
import re
import ConfigParser
import time
from flow_control import getFlowState
from flask import Flask, request, session, g, redirect, url_for, \
abort, render_template, flash, jsonify
from flask.ext.basicauth import BasicAuth
import Adafruit_GPIO.SPI as SPI
import Adafruit_MAX31855.MAX31855 as MAX31855
abspath = os.path.abspath(__file__)
dname = os.path.dirname(abspath)
os.chdir(dname)
app = Flask(__name__)
#set secret key - I don't know what this does, but it seems necessary
app.secret_key = ‘'
#this section controls the security features for login
app.config['BASIC_AUTH_USERNAME'] = ''
app.config['BASIC_AUTH_PASSWORD'] = ''
basic_auth = BasicAuth(app)
@app.route('/secret')
@basic_auth.required
def secret_view():
return render_template('secret.html')
app.config['BASIC_AUTH_FORCE'] = True
#set number of flow controllers on system
num_controllers = 4
#configure on/off pins
config = ConfigParser.ConfigParser()
config.read('config.txt')
on_off_pin_list = ['on off pin assignments']
i = 1
while i <= num_controllers:
pin_string = 'ch_' + str(i) + '_on_off_pin'
on_off_pin = int(config.get('main',pin_string))
on_off_pin_list.append(on_off_pin)
i += 1
furnace_pin = int(config.get('main','furnace_pin'))
#initialize SPI for thermocouple amplifier
def initializeSPI():
config = ConfigParser.ConfigParser()
config.read("config.txt")
clk_pin = int(config.get('main','clk_pin'))
cs_pin = int(config.get('main','cs_pin'))
do_pin = int(config.get('main','do_pin'))
sensor = MAX31855.MAX31855(clk_pin, cs_pin, do_pin)
By Gregory M. Mullen
22
return sensor
sensor = initializeSPI()
#start the flow control and temp control scripts as a subprocesses
subprocess.Popen("/usr/bin/python flow_control.py", shell=True)
subprocess.Popen("/usr/bin/python temp_control_daemon.py", shell=True)
#useful logic test we will use later
def RepresentsFloat(s):
try:
float(s)
return True
except ValueError:
return False
#checks on/off status of flow controllers and furnace
def getWhatsOn(num_controllers):
global on_off_pin_list, furnace_pin
status_list = []
status_string_list = []
i = 1
while i <= num_controllers:
status = getFlowState(on_off_pin_list[i])
status_list.append(status)
if status == 0:
statusString = "Channel #" + str(i) + ": <p id=\"channelOn\"> ON </p>"
status_string_list.append(statusString)
elif status == 1:
statusString = "Channel #" + str(i) + ": <p id=\"channel\"> OFF </p>"
status_string_list.append(statusString)
i += 1
return status_string_list[0] + status_string_list[1] + status_string_list[2] +
status_string_list[3]
#functions for setting parameters
def setOnOff(ch):
status = 'off'
f = 'onoffswitch' + str(ch)
if f in request.form:
status = 'on'
return status
def setSetPt(ch):
set_string = 'set_point_' + str(ch) + '_target'
set = request.form[set_string]
newSetPt = set.upper()
match = RepresentsFloat(newSetPt)
if match:
return newSetPt
else:
return 0
def setMax(ch):
max_string = 'set_point_' + str(ch) + '_max'
max = request.form[max_string]
newMax = max.upper()
match = RepresentsFloat(newMax)
if match:
return newMax
else:
return 0
By Gregory M. Mullen
23
def setGCF(ch):
gcf_string = 'gcf_' + str(ch)
gcf = request.form[gcf_string]
newGCF = gcf.upper()
match = RepresentsFloat(newGCF)
if match:
return newGCF
else:
return 0
def setTemp():
temp = request.form['temp']
newTemp = temp.upper()
match = RepresentsFloat(newTemp)
if match:
return newTemp
else:
return 0
def setRamp():
ramp_rate = request.form['ramp_rate']
newRamp = ramp_rate.upper()
match = RepresentsFloat(newRamp)
if match:
return newRamp
else:
return 0
@app.route('/')
def my_form():
file = ConfigParser.ConfigParser()
file.read("status_html")
ch_1_set_pt = float(file.get('set','ch_1_set_pt'))
ch_2_set_pt = float(file.get('set','ch_2_set_pt'))
ch_3_set_pt = float(file.get('set','ch_3_set_pt'))
ch_4_set_pt = float(file.get('set','ch_4_set_pt'))
ch_1_max = float(file.get('maximum','ch_1_max'))
ch_2_max = float(file.get('maximum','ch_2_max'))
ch_3_max = float(file.get('maximum','ch_3_max'))
ch_4_max = float(file.get('maximum','ch_4_max'))
ch_1_gcf = float(file.get('gcf','ch_1_gcf'))
ch_2_gcf = float(file.get('gcf','ch_2_gcf'))
ch_3_gcf = float(file.get('gcf','ch_3_gcf'))
ch_4_gcf = float(file.get('gcf','ch_4_gcf'))
temp = float(file.get('temperature','temp'))
ramp_rate = float(file.get('temperature','ramp_rate'))
status1 = file.get('on_off','ch_1_on_off_pin')
status2 = file.get('on_off','ch_2_on_off_pin')
status3 = file.get('on_off','ch_3_on_off_pin')
status4 = file.get('on_off','ch_4_on_off_pin')
status5 = file.get('on_off','furnace_pin')
status6 = file.get('temperature','ramping')
if status1 == "off":
checked1 = ""
elif status1 == "on":
checked1 = "checked=\"checked\""
else:
checked1 = "Something broke"
if status2 == "off":
checked2 = ""
By Gregory M. Mullen
24
elif status2 == "on":
checked2 = "checked=\"checked\""
else:
checked2 = "Something broke"
if status3 == "off":
checked3 = ""
elif status3 == "on":
checked3 = "checked=\"checked\""
else:
checked3 = "Something broke"
if status4 == "off":
checked4 = ""
elif status4 == "on":
checked4 = "checked=\"checked\""
else:
checked4 = "Something broke"
if status5 == "off":
checked5 = ""
elif status5 == "on":
checked5 = "checked=\"checked\""
else:
checked5 = "Something broke"
if status6 == "off":
checked6 = ""
elif status6 == "on":
checked6 = "checked=\"checked\""
else:
checked6 = "Something broke"
whatsOn = getWhatsOn(num_controllers)
return render_template("index.html", whatsOn = whatsOn, \
ch_1_set_pt = ch_1_set_pt,
ch_2_set_pt = ch_2_set_pt,
ch_3_set_pt = ch_3_set_pt,
ch_4_set_pt = ch_4_set_pt,
ch_1_max = ch_1_max, \
ch_2_max = ch_2_max, \
ch_3_max = ch_3_max, \
ch_4_max = ch_4_max, \
ch_1_gcf = ch_1_gcf, \
ch_2_gcf = ch_2_gcf, \
ch_3_gcf = ch_3_gcf, \
ch_4_gcf = ch_4_gcf, \
temp = temp, \
ramp_rate = ramp_rate, \
checked1 = checked1, \
checked2 = checked2, \
checked3 = checked3, \
checked4 = checked4, \
checked5 = checked5, \
checked6 = checked6)
@app.route("/", methods=['POST'])
def my_form_post():
global num_controllers
By Gregory M. Mullen
\
\
\
\
25
file = ConfigParser.ConfigParser()
file.read("status")
#on/off status control
i = 1
while i <= num_controllers:
status = setOnOff(i)
ch_string = 'ch_' + str(i) + '_on_off_pin'
try:
file.set('on_off', ch_string, status)
except:
pass
i += 1
statusFurnace = setOnOff(5) # this works because the furnace is set to
onoffswitch5
try:
file.set('on_off', 'furnace_pin', statusFurnace)
except:
pass
statusRamp = setOnOff(6) # this works because the ramping control is set to
onoffswitch6
try:
file.set('temperature', 'ramping', statusRamp)
except:
pass
#set point control
i = 1
while i <= num_controllers:
set_pt = setSetPt(i)
ch_string = 'ch_' + str(i) + '_set_pt'
try:
file.set('set', ch_string, set_pt)
except:
pass
i += 1
#max sccm identity control
i = 1
while i <= num_controllers:
max = setMax(i)
ch_string = 'ch_' + str(i) + '_max'
try:
file.set('maximum', ch_string, max)
except:
pass
i += 1
#gcf control
i = 1
while i <= num_controllers:
gcf = setGCF(i)
ch_string = 'ch_' + str(i) + '_gcf'
try:
file.set('gcf', ch_string, gcf)
except:
pass
i += 1
#temperature control
By Gregory M. Mullen
26
temp = setTemp()
previousTemp = float(file.get('temperature','temp'))
try:
file.set('temperature', 'temp', temp)
except:
pass
#if temperature has changed write timestamp into status file
if temp != previousTemp:
time_start = time.time()
temp0 = sensor.readTempC()
try:
file.set('temperature', 'ramp_start', time_start)
file.set('temperature', 'temp0', temp0)
except:
pass
#ramping
ramp_rate = setRamp()
try:
file.set('temperature', 'ramp_rate', ramp_rate)
except:
pass
ramp_status = setOnOff(6)
try:
file.set('temperature', 'ramping', ramp_status)
except:
pass
#rewrite status file and write status files for html code, flow_control, and
temp_control subprocesses
with open('status', 'wb') as configfile:
file.write(configfile)
with open('status_html', 'wb') as configfile:
file.write(configfile)
with open('status_flow_control', 'wb') as configfile:
file.write(configfile)
with open('status_temp_control', 'wb') as configfile:
file.write(configfile)
return redirect(url_for('my_form'))
#functions for updating web interface
@app.route('/_liveFlowSetPt1', methods= ['GET'])
def updateFlowSetPt1():
file = ConfigParser.ConfigParser()
file.read("status_html")
ch_1_set_pt = file.get('set','ch_1_set_pt')
return ch_1_set_pt
@app.route('/_liveFlowSetPt2', methods= ['GET'])
def updateFlowSetPt2():
file = ConfigParser.ConfigParser()
file.read("status_html")
ch_2_set_pt = file.get('set','ch_2_set_pt')
return ch_2_set_pt
@app.route('/_liveFlowSetPt3', methods= ['GET'])
def updateFlowSetPt3():
By Gregory M. Mullen
27
file = ConfigParser.ConfigParser()
file.read("status_html")
ch_3_set_pt = file.get('set','ch_3_set_pt')
return ch_3_set_pt
@app.route('/_liveFlowSetPt4', methods= ['GET'])
def updateFlowSetPt4():
file = ConfigParser.ConfigParser()
file.read("status_html")
ch_4_set_pt = file.get('set','ch_4_set_pt')
return ch_4_set_pt
@app.route('/_liveWhatsOn', methods= ['GET'])
def updateWhatsOn():
global num_controllers
return getWhatsOn(num_controllers)
@app.route('/_liveTemp', methods= ['GET'])
def updateTemp():
temp = sensor.readTempC()
return str(round(temp,1))
if __name__ == "__main__":
app.run("0.0.0.0", port=8000)
By Gregory M. Mullen
28
Sample Status and Configuration Files
status, status_flow_control, status_html, or status_temp_control
[set]
ch_1_set_pt
ch_2_set_pt
ch_3_set_pt
ch_4_set_pt
[maximum]
ch_1_max =
ch_2_max =
ch_3_max =
ch_4_max =
=
=
=
=
48.0
0.0
36.0
69.0
100.0
500.0
200.0
100.0
[on_off]
ch_1_on_off_pin =
ch_2_on_off_pin =
ch_3_on_off_pin =
ch_4_on_off_pin =
furnace_pin = on
[gcf]
ch_1_gcf
ch_2_gcf
ch_3_gcf
ch_4_gcf
=
=
=
=
off
off
on
off
0.9
1.0
1.24
0.69
[temperature]
temp = 20.0
ramping = on
ramp_rate = 10.0
ramp_start = 1453061630.51
temp0 = 516.0
By Gregory M. Mullen
29
config.txt
[main]
#dictates logging
DEBUG = 0
#sets pin assignments for on/off pins and outputs (from controller)
ch_1_on_off_pin = 4
ch_2_on_off_pin = 5
ch_3_on_off_pin = 6
ch_4_on_off_pin = 17
#pin assignment for furnace temperature controller
furnace_pin = 21
#pid parameters
p = 35.6
i = 4.5
d = 30
pwm_frequency = 0.5
#pin assignments for spi
clk_pin = 13
cs_pin = 19
do_pin = 26
By Gregory M. Mullen
Was this manual useful for you? yes no
Thank you for your participation!

* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project

Download PDF

advertisement