First import
authorHeinz Junkes <junkes@fhi-berlin.mpg.de>
Fri, 14 Aug 2015 11:30:43 +0200
changeset 0 bd6bb22c6533
child 1 7029db7ac3db
First import
Makefile
configure/CONFIG
configure/CONFIG_SITE
configure/Makefile
configure/RELEASE
configure/RULES
configure/RULES.ioc
configure/RULES_DIRS
configure/RULES_TOP
iocBoot/Makefile
iocBoot/iockstm/Makefile
iocBoot/iockstm/pid.txt
iocBoot/iockstm/st.cmd
kstmApp/Db/4kStmEuro.sub
kstmApp/Db/M1900.db
kstmApp/Db/M1900.proto
kstmApp/Db/Makefile
kstmApp/Db/dht.db
kstmApp/Db/eurotherm2k.proto
kstmApp/Db/eurotherm2k.template
kstmApp/Db/misc.db
kstmApp/Db/mtm.db
kstmApp/Db/mtm.proto
kstmApp/Db/pgc2.db
kstmApp/Db/pgc2.proto
kstmApp/Makefile
kstmApp/src/Makefile
kstmApp/src/common_dht_read.c
kstmApp/src/common_dht_read.h
kstmApp/src/dbSubReadADC.c
kstmApp/src/dbSubReadDHT.c
kstmApp/src/dbSubReadDHT.dbd
kstmApp/src/kstmMain.cpp
kstmApp/src/pi_2_dht_read.c
kstmApp/src/pi_2_dht_read.h
kstmApp/src/pi_2_mmio.c
kstmApp/src/pi_2_mmio.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,31 @@
+# Makefile at top of application tree
+TOP = .
+include $(TOP)/configure/CONFIG
+
+# Directories to build, any order
+DIRS += configure
+DIRS += $(wildcard *Sup)
+DIRS += $(wildcard *App)
+DIRS += $(wildcard *Top)
+DIRS += $(wildcard iocBoot)
+
+# The build order is controlled by these dependency rules:
+
+# All dirs except configure depend on configure
+$(foreach dir, $(filter-out configure, $(DIRS)), \
+    $(eval $(dir)_DEPEND_DIRS += configure))
+
+# Any *App dirs depend on all *Sup dirs
+$(foreach dir, $(filter %App, $(DIRS)), \
+    $(eval $(dir)_DEPEND_DIRS += $(filter %Sup, $(DIRS))))
+
+# Any *Top dirs depend on all *Sup and *App dirs
+$(foreach dir, $(filter %Top, $(DIRS)), \
+    $(eval $(dir)_DEPEND_DIRS += $(filter %Sup %App, $(DIRS))))
+
+# iocBoot depends on all *App dirs
+iocBoot_DEPEND_DIRS += $(filter %App,$(DIRS))
+
+# Add any additional dependency rules here:
+
+include $(TOP)/configure/RULES_TOP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/configure/CONFIG	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,29 @@
+# CONFIG - Load build configuration data
+#
+# Do not make changes to this file!
+
+# Allow user to override where the build rules come from
+RULES = $(EPICS_BASE)
+
+# RELEASE files point to other application tops
+include $(TOP)/configure/RELEASE
+-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).Common
+ifdef T_A
+-include $(TOP)/configure/RELEASE.Common.$(T_A)
+-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A)
+endif
+
+CONFIG = $(RULES)/configure
+include $(CONFIG)/CONFIG
+
+# Override the Base definition:
+INSTALL_LOCATION = $(TOP)
+
+# CONFIG_SITE files contain other build configuration settings
+include $(TOP)/configure/CONFIG_SITE
+-include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).Common
+ifdef T_A
+ -include $(TOP)/configure/CONFIG_SITE.Common.$(T_A)
+ -include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A)
+endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/configure/CONFIG_SITE	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,36 @@
+# CONFIG_SITE
+
+# Make any application-specific changes to the EPICS build
+#   configuration variables in this file.
+#
+# Host/target specific settings can be specified in files named
+#   CONFIG_SITE.$(EPICS_HOST_ARCH).Common
+#   CONFIG_SITE.Common.$(T_A)
+#   CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A)
+
+# CHECK_RELEASE controls the consistency checking of the support
+#   applications pointed to by the RELEASE* files.
+# Normally CHECK_RELEASE should be set to YES.
+# Set CHECK_RELEASE to NO to disable checking completely.
+# Set CHECK_RELEASE to WARN to perform consistency checking but
+#   continue building even if conflicts are found.
+CHECK_RELEASE = YES
+
+# Set this when you only want to compile this application
+#   for a subset of the cross-compiled target architectures
+#   that Base is built for.
+#CROSS_COMPILER_TARGET_ARCHS = vxWorks-ppc32
+
+# To install files into a location other than $(TOP) define
+#   INSTALL_LOCATION here.
+#INSTALL_LOCATION=</absolute/path/to/install/top>
+
+# Set this when the IOC and build host use different paths
+#   to the install location. This may be needed to boot from
+#   a Microsoft FTP server say, or on some NFS configurations.
+#IOCS_APPL_TOP = </IOC's/absolute/path/to/install/top>
+
+# For application debugging purposes, override the HOST_OPT and/
+#   or CROSS_OPT settings from base/configure/CONFIG_SITE
+#HOST_OPT = NO
+#CROSS_OPT = NO
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/configure/Makefile	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,8 @@
+TOP=..
+
+include $(TOP)/configure/CONFIG
+
+TARGETS = $(CONFIG_TARGETS)
+CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS)))
+
+include $(TOP)/configure/RULES
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/configure/RELEASE	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,39 @@
+# RELEASE - Location of external support modules
+#
+# IF YOU MAKE ANY CHANGES to this file you must subsequently
+# do a "gnumake rebuild" in this application's top level
+# directory.
+#
+# The build process does not check dependencies against files
+# that are outside this application, thus you should do a
+# "gnumake rebuild" in the top level directory after EPICS_BASE
+# or any other external module pointed to below is rebuilt.
+#
+# Host- or target-specific settings can be given in files named
+#  RELEASE.$(EPICS_HOST_ARCH).Common
+#  RELEASE.Common.$(T_A)
+#  RELEASE.$(EPICS_HOST_ARCH).$(T_A)
+#
+# This file should ONLY define paths to other support modules,
+# or include statements that pull in similar RELEASE files.
+# Build settings that are NOT module paths should appear in a
+# CONFIG_SITE file.
+
+TEMPLATE_TOP=$(EPICS_BASE)/templates/makeBaseApp/top
+
+HOME=/home/epics/
+MODULES=$(HOME)/EPICS/modules
+SUPPORT=$(HOME)/EPICS/support
+
+# If using the sequencer, point SNCSEQ at its top directory:
+SNCSEQ=$(MODULES)/seq-2.2.2
+ASYN=$(SUPPORT)/asyn4-26
+STREAM=$(SUPPORT)/stream
+EUROTHERM2K=$(SUPPORT)/eurotherm2k-1-18
+
+# EPICS_BASE usually appears last so other apps can override stuff:
+EPICS_BASE=$(HOME)/EPICS/base
+
+# Set RULES here if you want to take build rules from somewhere
+# other than EPICS_BASE:
+#RULES=/path/to/epics/support/module/rules/x-y
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/configure/RULES	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,6 @@
+# RULES
+
+include $(CONFIG)/RULES
+
+# Library should be rebuilt because LIBOBJS may have changed.
+$(LIBNAME): ../Makefile
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/configure/RULES.ioc	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,2 @@
+#RULES.ioc
+include $(CONFIG)/RULES.ioc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/configure/RULES_DIRS	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,2 @@
+#RULES_DIRS
+include $(CONFIG)/RULES_DIRS
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/configure/RULES_TOP	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,3 @@
+#RULES_TOP
+include $(CONFIG)/RULES_TOP
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/iocBoot/Makefile	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,6 @@
+TOP = ..
+include $(TOP)/configure/CONFIG
+DIRS += $(wildcard *ioc*)
+DIRS += $(wildcard as*)
+include $(CONFIG)/RULES_DIRS
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/iocBoot/iockstm/Makefile	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,5 @@
+TOP = ../..
+include $(TOP)/configure/CONFIG
+ARCH = linux-x86_64
+TARGETS = envPaths
+include $(TOP)/configure/RULES.ioc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/iocBoot/iockstm/pid.txt	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,1 @@
+2436
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/iocBoot/iockstm/st.cmd	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,73 @@
+#!../../bin/linux-arm/kstm
+
+## You may have to change kstm to something else
+## everywhere it appears in this file
+
+< envPaths
+
+epicsEnvSet EUROTH01_INET 141.14.128.45:4001 
+epicsEnvSet EUROTH01_LINK EUROTH_01 
+
+epicsEnvSet EUROTH02_INET 141.14.128.45:4002 
+epicsEnvSet EUROTH02_LINK EUROTH_02 
+
+epicsEnvSet MTM_INET 141.14.128.45:4004 
+epicsEnvSet MTM_LINK MTM 
+
+epicsEnvSet M1900_INET 141.14.128.45:4003 
+epicsEnvSet M1900_LINK M1900 
+
+epicsEnvSet PGC2_MC_INET 141.14.128.45:4005 
+epicsEnvSet PGC2_MC_LINK PGC2_MC 
+
+epicsEnvSet PGC2_PC_INET 141.14.128.45:4006 
+epicsEnvSet PGC2_PC_LINK PGC2_PC 
+
+cd "${TOP}"
+
+epicsEnvSet "STREAM_PROTOCOL_PATH", "$(TOP)/db"
+
+## Register all support components
+dbLoadDatabase("dbd/kstm.dbd",0,0)
+kstm_registerRecordDeviceDriver(pdbbase) 
+
+drvAsynIPPortConfigure(${EUROTH01_LINK}, ${EUROTH01_INET}, 0, 0, 0)
+drvAsynIPPortConfigure(${EUROTH02_LINK}, ${EUROTH02_INET}, 0, 0, 0)
+drvAsynIPPortConfigure(${MTM_LINK}, ${MTM_INET}, 0, 0, 0)
+drvAsynIPPortConfigure(${M1900_LINK}, ${M1900_INET}, 0, 0, 0)
+drvAsynIPPortConfigure(${PGC2_MC_LINK}, ${PGC2_MC_INET}, 0, 0, 0)
+drvAsynIPPortConfigure(${PGC2_PC_LINK}, ${PGC2_PC_INET}, 0, 0, 0)
+
+# Set asyn trace IO format
+#define ASYN_TRACEIO_NODATA 0x0000
+#define ASYN_TRACEIO_ASCII 0x0001
+#define ASYN_TRACEIO_ESCAPE 0x0002
+#define ASYN_TRACEIO_HEX 0x0004
+#asynSetTraceIOMask ${PGC2_MC_LINK} 0 1
+
+# Set asyn trace masks
+# ASYN_TRACE_ERROR    0x0001
+# ASYN_TRACEIO_DEVICE 0x0002
+# ASYN_TRACEIO_FILTER 0x0004
+# ASYN_TRACEIO_DRIVER 0x0008
+# ASYN_TRACE_FLOW     0x0010
+#asynSetTraceMask ${PGC2_MC_LINK} 0 8
+
+epicsEnvSet ("streamDebug","1")
+
+## Load record instances
+dbLoadRecords("db/misc.db","P=FHI4KSTM, Q=MISC")
+
+#dbLoadRecords("db/dht.db","P=FHI4KSTM, Q=DHT")
+
+dbLoadTemplate("db/4kStmEuro.sub")
+
+dbLoadRecords("db/mtm.db","PORT=MTM, P=FHI4KSTM, Q=MTM")
+dbLoadRecords("db/M1900.db","PORT=M1900, P=FHI4KSTM, Q=M1900")
+dbLoadRecords("db/pgc2.db","PORT=PGC2_MC, P=FHI4KSTM, Q=PGC2_MC")
+dbLoadRecords("db/pgc2.db","PORT=PGC2_PC, P=FHI4KSTM, Q=PGC2_PC")
+
+iocInit()
+
+## Start any sequence programs
+#seq snckstm,"user=heinz"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/Db/4kStmEuro.sub	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,20 @@
+# Macros:
+#  P         Device prefix
+#  Q         Device suffix
+#  PORT      Asyn port name
+#  GAD       Global address number, normally 0
+#  LAD       Local address number, normally 1
+#  SPMAX     Maximum setpoint
+#  RRMAX     Maximum ramp rate
+#  SPCMD     Setpoint command, set to 'write' for floating point
+#  PREC      Setpoint precision
+#  EGU       Engineering units for ramp rate, default is C/s
+#  name      object and gui association name
+#  gda_name  Name to export to gda as
+#  gda_desc  Description for gda
+file db/eurotherm2k.template
+{
+pattern { P, Q, PORT, GAD, LAD, SPMAX, RRMAX, SPCMD, PREC, EGU, name, gda_name, gda_desc }
+    { "FHI4KSTM:", "EUROTH01", "EUROTH_01", "0", "1", "1000", "1", "writeint", "0", "C/s", "", "", "" }
+    { "FHI4KSTM:", "EUROTH02", "EUROTH_02", "0", "1", "1000", "1", "writeint", "0", "C/s", "", "", "" }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/Db/M1900.db	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,9 @@
+record(ai, "$(P):$(Q):temp")
+{
+field(SCAN, "5 second")
+field(DTYP,"stream")
+field(INP,"@M1900.proto read $(PORT) -1")
+field(EGU,"K")
+field(HOPR,"400")
+field(LOPR,"0")
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/Db/M1900.proto	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,2 @@
+Terminator = CR NUL;
+read { out "T"; in "T"; in "%f"; }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/Db/Makefile	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,35 @@
+TOP=../..
+include $(TOP)/configure/CONFIG
+#----------------------------------------
+#  ADD MACRO DEFINITIONS AFTER THIS LINE
+
+#----------------------------------------------------
+#  Optimization of db files using dbst (DEFAULT: NO)
+#DB_OPT = YES
+
+#----------------------------------------------------
+# Create and install (or just install) into <top>/db
+# databases, templates, substitutions like this
+#DB += xxx.db
+DB += dht.db
+DB += misc.db
+DB += mtm.db
+
+DB += 4kStmEuro.sub
+DB += eurotherm2k.proto
+DB += eurotherm2k.template
+
+DB += pgc2.db
+DB += pgc2.proto
+
+DB += M1900.db
+DB += M1900.proto
+
+#----------------------------------------------------
+# If <anyname>.db template is not named <anyname>*.template add
+# <anyname>_template = <templatename>
+
+include $(TOP)/configure/RULES
+#----------------------------------------
+#  ADD RULES AFTER THIS LINE
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/Db/dht.db	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,8 @@
+record(sub,"$(P):$(Q):R_temp")
+{
+    field(INAM,"dbSubReadDHTInit")
+    field(SNAM,"dbSubReadDHTProcess")
+    field(SCAN,"5 second")
+    field(INPA,"3")
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/Db/eurotherm2k.proto	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,96 @@
+## \file
+## Stream Device Protocol for eurotherm 2000 series EI Bisynch
+## \param GAD = First char of address, e.g. address = 1, GAD = 0, address = 12, GAD = 1
+## \param LAD = Second char of address, e.g. address = 1, LAD = 1, address = 12, LAD = 2
+
+locktimeout = 5000;
+
+## Unfortunately, this is designed with a checksum terminator.
+OutTerminator   = "";
+replytimeout = 200;
+## Setting a small readtimeout means that we can get data without needing a terminator
+readtimeout  = 100;
+extrainput   = Ignore;
+
+#STX = "\x02"
+#ETX = "\x03"
+#EOT = "\x04"
+#ENQ = "\x05"
+#ACK = "\x06"
+
+## Read value
+## \code
+## send: [EOT](GAD)(GAD)(LAD)(LAD)(CHAN)(C1)(C2)[ENQ]
+## reply: [STX](CHAN)(C1)(C2)<DATA>[ETX](BCC)
+## \endcode
+## - $1 = GAD
+## - $2 = LAD
+## - $3 = command mnemonic
+#############################################################################
+read { InTerminator = "\x03"; out "\x04\$1\$1\$2\$2\$3\x05"; in "\x02\$3%f"; }
+
+## Read value, but in hex
+readhex { InTerminator = "\x03"; out "\x04\$1\$1\$2\$2\$3\x05"; in "\x02\$3>%x"; }
+
+## NOTE: we rely on readtimeout for the in commands. 
+## Could use maxInput, but that screws up record initialisation (once inTerminator is set it can't be overwritten in the \@init handler)
+##
+## Write value
+## \code
+## send: [EOT](GAD)(GAD)(LAD)(LAD)[STX](CHAN)(C1)(C2)<DATA>[ETX](BCC)
+## reply: [ACK] or [NAK], discarded as no terminator
+## \endcode
+## - $1 = GAD
+## - $2 = LAD
+## - $3 = command mnemonic
+## - $4 = device prefix, \$(P)\$(Q)
+#############################################################################
+write {  InTerminator = ""; out "\x04\$1\$1\$2\$2\x02\$3%f\x03%6<xor>"; in "\x06"; @init{ read; }; @mismatch{ in "%(\$4:ERR.PROC)r"; }; }
+
+## Write a value in int rather than float
+writeint {  InTerminator = ""; out "\x04\$1\$1\$2\$2\x02\$3%i\x03%6<xor>"; in "\x06"; @init{ read; }; @mismatch{ in "%(\$4:ERR.PROC)r"; }; }
+
+
+### \brief Controller configuration mode
+##
+## This mode enable setting a number of controller configuration settings 
+## (see Communications Handbook PDF pages 5-25 onwards)
+##
+## We set the mode by sending a command 'IM2' and get out of the mode with the command 'IM0'
+## While in configuration mode the controller makes itself address 0 so any configuration commands
+## have to be sent to address = 0
+setConfMode {  InTerminator = ""; out "\x04\$1\$1\$2\$2\x02IM2\x03%6<xor>"; in "\x06"; @mismatch{ in "%(\$3:ERR.PROC)r"; }; }
+
+## Escape from the configuration mode in to normal operation mode
+clearConfMode {  InTerminator = ""; out "\x040000\x02IM0\x03%6<xor>"; in "\x06"; @mismatch{ in "%(\$1:ERR.PROC)r"; }; }
+
+## Get the current mode of the controller: 0=normal mode, 2=configuration mode
+getConfMode { InTerminator = "\x03"; out "\x0400%(\$1:ADDR.VAL)i%(\$1:ADDR.VAL)iIM\x05"; in "\x02IM%i."; }
+
+## \brief Set device precision.
+##
+## The number of decimal places in the controller is also the precision the data is returned when communicating over serial.
+## This parameter can only be set when in Configuration Mode (see above)
+setDecPlaces { InTerminator = ""; out "\x0400%(\$1:ADDR.VAL)i%(\$1:ADDR.VAL)i\x02QD%i\x03%6<xor>"; in "\x06"; @mismatch{ in "%(\$1:ERR.PROC)r"; }; }
+
+## Read the number of decimal places used in the controller
+getDecPlaces { InTerminator = "\x03"; out "\x0400%(\$1:ADDR.VAL)i%(\$1:ADDR.VAL)iQD\x05"; in "\x02QD%i."; }
+
+## The ramp rate time unit. Note this parameter can only be set when in Configuration Mode
+setRampRateUnit { InTerminator = ""; out "\x0400%(\$1:ADDR.VAL)i%(\$1:ADDR.VAL)i\x02QJ%i\x03%6<xor>"; in "\x06"; @mismatch{ in "%(\$1:ERR.PROC)r"; }; }
+
+## Read out the ramp rate time unit
+getRampRateUnit { InTerminator = "\x03"; out "\x0400%(\$1:ADDR.VAL)i%(\$1:ADDR.VAL)iQJ\x05"; in "\x02QJ%i."; }
+
+## \brief Write any ASCII string command to the device.
+## 
+## It will read the device address (although only the LAD part) from another record: \$1:ADDR.VAL
+addrWrite { InTerminator = "";     out "\x0400%(\$1:ADDR.VAL)i%(\$1:ADDR.VAL)i\x02%s\x03%6<xor>"; in "\x06";    @mismatch{ in "%(\$1:ERR.PROC)r"; }; }
+
+## \brief Read back any ASCII string paramter from the device.
+##
+## This will also read the device address from an external record.
+addrRead  { InTerminator = "\x03"; out "\x0400%(\$1:ADDR.VAL)i%(\$1:ADDR.VAL)i%s\x05"; in "\x02%(\$1:ADDR:RESP.VAL)6c"; }
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/Db/eurotherm2k.template	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,510 @@
+#! Generated by VisualDCT v2.6
+#! DBDSTART
+#! DBD("../../example/dbd/example.dbd")
+#! DBDEND
+
+
+# % macro, P, Device prefix
+# % macro, Q, Device suffix
+# % macro, PORT, Asyn port name
+# % macro, GAD, Global address number, normally 0
+# % macro, LAD, Local address number, normally 1
+# % macro, SPMAX, Maximum setpoint
+# % macro, RRMAX, Maximum ramp rate
+# % macro, SPCMD, Setpoint command, set to 'write' for floating point
+# % macro, PREC, Setpoint precision
+# % macro, EGU, Engineering units for ramp rate, default is C/s
+# % macro, name, object and gui association name
+# % macro, gda_name, Name to export to gda as
+# % macro, gda_desc, Description for gda
+# % gdatag,template,eurotherm2k,$(gda_name=),$(gda_desc=)
+# % gui, $(name=), edmembed, eurotherm2k_embed.edl, eurotherm=$(P)$(Q)
+# % gui, $(name=), edm, eurotherm2k.edl, eurotherm=$(P)$(Q)
+# % autosave 2 SCAN
+record(seq, "$(P)$(Q):UPDATE") {
+  field(DESC, "Update Temp Params")
+  field(DOL1, "1")
+  field(DOL2, "1")
+  field(DOL3, "1")
+  field(DOL4, "1")
+  field(LNK1, "$(P)$(Q):SP:RBV.PROC")
+  field(LNK2, "$(P)$(Q):RR:RBV.PROC")
+  field(LNK3, "$(P)$(Q):O:RBV.PROC")
+  field(LNK4, "$(P)$(Q):PV:RBV.PROC")
+  field(SCAN, "1 second")
+  field(PINI, "YES")
+  field(DOL5, "1")
+  field(LNK5, "$(P)$(Q):MAN:RBV_AI.PROC")
+}
+
+# % autosave 2 SCAN
+record(seq, "$(P)$(Q):PID") {
+  field(DESC, "Update PID Params")
+  field(DOL1, "1")
+  field(DOL2, "1")
+  field(DOL3, "1")
+  field(LNK1, "$(P)$(Q):P:RBV.PROC")
+  field(LNK2, "$(P)$(Q):I:RBV.PROC")
+  field(LNK3, "$(P)$(Q):D:RBV.PROC")
+  field(SCAN, "10 second")
+  field(PINI, "YES")
+}
+
+#% gdatag,pv,rw,$(gda_name=),SP
+record(ao, "$(P)$(Q):SP") {
+  field(DESC, "Setpoint")
+  field(DTYP, "stream")
+  field(FLNK, "$(P)$(Q):SP:RBV")
+  field(OUT, "@eurotherm2k.proto $(SPCMD=writeint)($(GAD),$(LAD),S1,$(P)$(Q)) $(PORT)")
+  field(PREC, "$(PREC=0)")
+  field(EGU, "C")
+  field(DRVH, "$(SPMAX=1000)")
+  field(DRVL, "0")
+  field(PINI, "YES")
+  field(SDIS, "$(P)$(Q):DISABLE")
+}
+
+# % gdatag,pv,ro,$(gda_name=),SPRBV
+# % archiver 10 Monitor
+record(ai, "$(P)$(Q):SP:RBV") {
+  field(DESC, "Setpoint Readback")
+  field(DTYP, "stream")
+  field(INP, "@eurotherm2k.proto read($(GAD),$(LAD),S1) $(PORT)")
+  field(PREC, "$(PREC=0)")
+  field(EGU, "C")
+  field(SDIS, "$(P)$(Q):DISABLE")
+}
+
+#% gdatag,pv,rw,$(gda_name=),RR
+record(ao, "$(P)$(Q):RR") {
+  field(DESC, "Ramprate")
+  field(DTYP, "stream")
+  field(FLNK, "$(P)$(Q):RR:RBV")
+  field(OUT, "@eurotherm2k.proto write($(GAD),$(LAD),RR,$(P)$(Q)) $(PORT)")
+  field(PREC, "3")
+  field(EGU, "$(EGU=C/s)")
+  field(DRVH, "$(RRMAX=1)")
+  field(DRVL, "0")
+  field(PINI, "YES")
+  field(SDIS, "$(P)$(Q):DISABLE")
+}
+
+# % gdatag,pv,ro,$(gda_name=),RRRBV
+# % archiver 10 Monitor
+record(ai, "$(P)$(Q):RR:RBV") {
+  field(DESC, "Ramprate Readback")
+  field(DTYP, "stream")
+  field(INP, "@eurotherm2k.proto read($(GAD),$(LAD),RR) $(PORT)")
+  field(PREC, "3")
+  field(EGU, "$(EGU=C/s)")
+  field(SDIS, "$(P)$(Q):DISABLE")
+}
+
+#% gdatag,pv,rw,$(gda_name=),O
+record(ao, "$(P)$(Q):O") {
+  field(DESC, "Output")
+  field(DTYP, "stream")
+  field(FLNK, "$(P)$(Q):O:RBV")
+  field(OUT, "@eurotherm2k.proto write($(GAD),$(LAD),OP,$(P)$(Q)) $(PORT)")
+  field(PREC, "1")
+  field(EGU, "%")
+  field(DRVH, "100")
+  field(DRVL, "-100")
+  field(PINI, "YES")
+  field(SDIS, "$(P)$(Q):DISABLE")
+}
+
+# % gdatag,pv,ro,$(gda_name=),ORBV
+# % archiver 1 Monitor
+record(ai, "$(P)$(Q):O:RBV") {
+  field(DESC, "Ouptut Readback")
+  field(DTYP, "stream")
+  field(INP, "@eurotherm2k.proto read($(GAD),$(LAD),OP) $(PORT)")
+  field(PREC, "1")
+  field(EGU, "%")
+  field(SDIS, "$(P)$(Q):DISABLE")
+}
+
+# % gdatag,pv,ro,$(gda_name=),PVRBV
+# % archiver 1 Monitor
+record(ai, "$(P)$(Q):PV:RBV") {
+  field(DESC, "Temperature Readback")
+  field(DTYP, "stream")
+  field(INP, "@eurotherm2k.proto read($(GAD),$(LAD),PV) $(PORT)")
+  field(PREC, "1")
+  field(EGU, "C")
+  field(SDIS, "$(P)$(Q):DISABLE")
+}
+
+#% gdatag,pv,rw,$(gda_name=),P
+record(ao, "$(P)$(Q):P") {
+  field(DESC, "P Param")
+  field(DTYP, "stream")
+  field(FLNK, "$(P)$(Q):P:RBV")
+  field(OUT, "@eurotherm2k.proto writeint($(GAD),$(LAD),XP,$(P)$(Q)) $(PORT)")
+  field(PREC, "0")
+  field(VAL, "150")
+  field(PINI, "YES")
+  field(SDIS, "$(P)$(Q):DISABLE")
+}
+
+# % gdatag,pv,ro,$(gda_name=),PRBV
+# % archiver 10 Monitor
+record(ai, "$(P)$(Q):P:RBV") {
+  field(DESC, "P Param Readback")
+  field(DTYP, "stream")
+  field(INP, "@eurotherm2k.proto read($(GAD),$(LAD),XP) $(PORT)")
+  field(PREC, "0")
+  field(SDIS, "$(P)$(Q):DISABLE")
+}
+
+#% gdatag,pv,rw,$(gda_name=),I
+record(ao, "$(P)$(Q):I") {
+  field(DESC, "I Param")
+  field(DTYP, "stream")
+  field(FLNK, "$(P)$(Q):I:RBV")
+  field(OUT, "@eurotherm2k.proto writeint($(GAD),$(LAD),TI,$(P)$(Q)) $(PORT)")
+  field(PREC, "0")
+  field(VAL, "28")
+  field(PINI, "YES")
+  field(SDIS, "$(P)$(Q):DISABLE")
+}
+
+# % gdatag,pv,ro,$(gda_name=),IRBV
+# % archiver 10 Monitor
+record(ai, "$(P)$(Q):I:RBV") {
+  field(DESC, "I Param Readback")
+  field(DTYP, "stream")
+  field(INP, "@eurotherm2k.proto read($(GAD),$(LAD),TI) $(PORT)")
+  field(PREC, "0")
+  field(SDIS, "$(P)$(Q):DISABLE")
+}
+
+#% gdatag,pv,rw,$(gda_name=),D
+record(ao, "$(P)$(Q):D") {
+  field(DESC, "D Param")
+  field(DTYP, "stream")
+  field(FLNK, "$(P)$(Q):D:RBV")
+  field(OUT, "@eurotherm2k.proto writeint($(GAD),$(LAD),TD,$(P)$(Q)) $(PORT)")
+  field(PREC, "0")
+  field(VAL, "4")
+  field(PINI, "YES")
+  field(SDIS, "$(P)$(Q):DISABLE")
+}
+
+# % gdatag,pv,ro,$(gda_name=),DRBV
+# % archiver 10 Monitor
+record(ai, "$(P)$(Q):D:RBV") {
+  field(DESC, "D Param Readback")
+  field(DTYP, "stream")
+  field(INP, "@eurotherm2k.proto read($(GAD),$(LAD),TD) $(PORT)")
+  field(PREC, "0")
+  field(SDIS, "$(P)$(Q):DISABLE")
+}
+
+# % gdatag,pv,rw,$(gda_name=),ERR
+record(mbbi, "$(P)$(Q):ERR") {
+  field(DESC, "Error message")
+  field(DTYP, "stream")
+  field(INP, "@eurotherm2k.proto readhex($(GAD),$(LAD),EE) $(PORT)")
+  field(ZRVL, "0")
+  field(ZRST, "No Error")
+  field(ONVL, "1")
+  field(ONST, "Invalid Param")
+  field(TWVL, "2")
+  field(TWST, "Read Only")
+  field(THVL, "7")
+  field(THST, "Incorrect Msg")
+  field(FRVL, "8")
+  field(FRST, "Limit Error")
+  field(PINI, "YES")
+  field(SDIS, "$(P)$(Q):DISABLE")
+}
+
+# % gdatag,binary,rw,$(gda_name=),DISABLE
+record(bo, "$(P)$(Q):DISABLE") {
+  field(DESC, "Disable comms")
+  field(PINI, "YES")
+  field(VAL, "1")
+  field(OMSL, "supervisory")
+  field(ZNAM, "Enabled")
+  field(ONAM, "Disabled")
+}
+
+record(bo, "$(P)$(Q):MAN") {
+  field(DESC, "Manual mode enable")
+  field(DTYP, "stream")
+  field(SDIS, "$(P)$(Q):DISABLE")
+  field(FLNK, "$(P)$(Q):MAN:RBV_AI")
+  field(OUT, "@eurotherm2k.proto writeint($(GAD),$(LAD),mA,$(P)$(Q)) $(PORT)")
+  field(ZNAM, "Automatic")
+  field(ONAM, "Manual")
+  field(PINI, "YES")
+}
+
+record(ai, "$(P)$(Q):MAN:RBV_AI") {
+  field(DESC, "Manual mode readback")
+  field(DTYP, "stream")
+  field(SDIS, "$(P)$(Q):DISABLE")
+  field(INP, "@eurotherm2k.proto read($(GAD),$(LAD),mA) $(PORT)")
+  field(FLNK, "$(P)$(Q):MAN:RBV")
+}
+
+# % archiver 10 Monitor
+record(bi, "$(P)$(Q):MAN:RBV") {
+  field(DESC, "Manual mode readback")
+  field(DTYP, "Soft Channel")
+  field(INP, "$(P)$(Q):MAN:RBV_AI")
+  field(ZNAM, "Automatic")
+  field(ONAM, "Manual")
+}
+
+record(mbbi, "$(P)$(Q):CONF:MODE") {
+  field(DTYP, "stream")
+  field(INP, "@eurotherm2k.proto getConfMode($(P)$(Q)) $(PORT)")
+  field(ZRVL, "0")
+  field(ONVL, "2")
+  field(ZRST, "operational")
+  field(ONST, "configuration")
+}
+
+record(longin, "$(P)$(Q):ADDR") {
+  field(FLNK, "$(P)$(Q):CONF:DLY")
+}
+
+record(longout, "$(P)$(Q):ADDR:DEVICE") {
+  field(VAL, "$(LAD)")
+  field(OUT, "$(P)$(Q):ADDR PP")
+}
+
+record(bo, "$(P)$(Q):CONF:SETMODE") {
+  field(DTYP, "stream")
+  field(FLNK, "$(P)$(Q):CONF:ZEROADDR")
+  field(OUT, "@eurotherm2k.proto setConfMode($(GAD),$(LAD),$(P)$(Q)) $(PORT)")
+  field(ZNAM, "0")
+  field(ONAM, "1")
+}
+
+record(bo, "$(P)$(Q):CONF:CLEARMODE") {
+  field(DTYP, "stream")
+  field(FLNK, "$(P)$(Q):ADDR:DEVICE")
+  field(OUT, "@eurotherm2k.proto clearConfMode($(P)$(Q)) $(PORT)")
+  field(ZNAM, "0")
+  field(ONAM, "1")
+}
+
+record(longout, "$(P)$(Q):CONF:ZEROADDR") {
+  field(VAL, "0")
+  field(OUT, "$(P)$(Q):ADDR PP")
+}
+
+record(stringout, "$(P)$(Q):ADDR:WR") {
+  field(DESC, "write cmd")
+  field(DTYP, "stream")
+  field(OUT, "@eurotherm2k.proto addrWrite($(P)$(Q)) $(PORT)")
+  field(PINI, "NO")
+}
+
+record(stringout, "$(P)$(Q):ADDR:RD") {
+  field(DESC, "Read parameter")
+  field(DTYP, "stream")
+  field(OUT, "@eurotherm2k.proto addrRead($(P)$(Q)) $(PORT)")
+}
+
+record(stringin, "$(P)$(Q):ADDR:RESP") {
+  field(DESC, "Read response")
+}
+
+record(mbbi, "$(P)$(Q):CONF:DECP") {
+  field(DTYP, "stream")
+  field(INP, "@eurotherm2k.proto getDecPlaces($(P)$(Q)) $(PORT)")
+  field(ZRVL, "0")
+  field(ONVL, "1")
+  field(ZRST, "nnnn.")
+  field(ONST, "nnn.n")
+  field(DESC, "decimal places")
+  field(TWVL, "2")
+  field(TWST, "nn.nn")
+}
+
+record(mbbo, "$(P)$(Q):CONF:SET:DECP") {
+  field(DESC, "decimal places")
+  field(DTYP, "stream")
+  field(OUT, "@eurotherm2k.proto setDecPlaces($(P)$(Q)) $(PORT)")
+  field(ZRVL, "0")
+  field(ONVL, "1")
+  field(TWVL, "2")
+  field(ZRST, "nnnn.")
+  field(ONST, "nnn.n")
+  field(TWST, "nn.nn")
+}
+
+record(fanout, "$(P)$(Q):CONF:READ") {
+  field(FLNK, "$(P)$(Q):CONF:DECP")
+  field(LNK1, "$(P)$(Q):CONF:RREGU")
+}
+
+record(mbbi, "$(P)$(Q):CONF:RREGU") {
+  field(DTYP, "stream")
+  field(INP, "@eurotherm2k.proto getRampRateUnit($(P)$(Q)) $(PORT)")
+  field(ZRVL, "0")
+  field(ONVL, "1")
+  field(ZRST, "sec")
+  field(ONST, "min")
+  field(DESC, "decimal places")
+  field(TWVL, "2")
+  field(TWST, "hour")
+}
+
+record(mbbo, "$(P)$(Q):CONF:SET:RREGU") {
+  field(DESC, "decimal places")
+  field(DTYP, "stream")
+  field(OUT, "@eurotherm2k.proto setRampRateUnit($(P)$(Q)) $(PORT)")
+  field(ZRVL, "0")
+  field(ONVL, "1")
+  field(TWVL, "2")
+  field(ZRST, "sec")
+  field(ONST, "min")
+  field(TWST, "hour")
+}
+
+
+record(seq, "$(P)$(Q):CONF:DLY") {
+  field(SELM, "Specified")
+  field(SELL, "1")
+  field(DLY1, "5.0")
+  field(DOL1, "1")
+  field(LNK1, "$(P)$(Q):CONF:MODE.PROC CA MS")
+}
+
+#! Further lines contain data used by VisualDCT
+#! View(206,1478,1.1)
+#! Record("$(P)$(Q):UPDATE",960,222,0,0,"$(P)$(Q):UPDATE")
+#! Field("$(P)$(Q):UPDATE.LNK1",16777215,1,"$(P)$(Q):UPDATE.LNK1")
+#! Link("$(P)$(Q):UPDATE.LNK1","$(P)$(Q):SP:RBV.PROC")
+#! Field("$(P)$(Q):UPDATE.LNK2",16777215,1,"$(P)$(Q):UPDATE.LNK2")
+#! Link("$(P)$(Q):UPDATE.LNK2","$(P)$(Q):RR:RBV.PROC")
+#! Field("$(P)$(Q):UPDATE.LNK3",16777215,1,"$(P)$(Q):UPDATE.LNK3")
+#! Link("$(P)$(Q):UPDATE.LNK3","$(P)$(Q):O:RBV.PROC")
+#! Field("$(P)$(Q):UPDATE.LNK4",16777215,1,"$(P)$(Q):UPDATE.LNK4")
+#! Link("$(P)$(Q):UPDATE.LNK4","$(P)$(Q):PV:RBV.PROC")
+#! Field("$(P)$(Q):UPDATE.LNK5",16777215,1,"$(P)$(Q):UPDATE.LNK5")
+#! Link("$(P)$(Q):UPDATE.LNK5","$(P)$(Q):MAN:RBV_AI.PROC")
+#! Record("$(P)$(Q):PID",1080,1258,0,1,"$(P)$(Q):PID")
+#! Field("$(P)$(Q):PID.LNK1",16777215,1,"$(P)$(Q):PID.LNK1")
+#! Link("$(P)$(Q):PID.LNK1","$(P)$(Q):P:RBV.PROC")
+#! Field("$(P)$(Q):PID.LNK2",16777215,1,"$(P)$(Q):PID.LNK2")
+#! Link("$(P)$(Q):PID.LNK2","$(P)$(Q):I:RBV.PROC")
+#! Field("$(P)$(Q):PID.LNK3",16777215,1,"$(P)$(Q):PID.LNK3")
+#! Link("$(P)$(Q):PID.LNK3","$(P)$(Q):D:RBV.PROC")
+#! Record("$(P)$(Q):SP",1200,224,0,0,"$(P)$(Q):SP")
+#! Field("$(P)$(Q):SP.FLNK",16777215,1,"$(P)$(Q):SP.FLNK")
+#! Link("$(P)$(Q):SP.FLNK","$(P)$(Q):SP:RBV")
+#! Field("$(P)$(Q):SP.SDIS",16777215,1,"$(P)$(Q):SP.SDIS")
+#! Link("$(P)$(Q):SP.SDIS","$(P)$(Q):DISABLE.VAL")
+#! Record("$(P)$(Q):SP:RBV",1480,240,0,0,"$(P)$(Q):SP:RBV")
+#! Field("$(P)$(Q):SP:RBV.PROC",16777215,0,"$(P)$(Q):SP:RBV.PROC")
+#! Field("$(P)$(Q):SP:RBV.SDIS",16777215,1,"$(P)$(Q):SP:RBV.SDIS")
+#! Link("$(P)$(Q):SP:RBV.SDIS","$(P)$(Q):DISABLE.VAL")
+#! Record("$(P)$(Q):RR",1180,564,0,0,"$(P)$(Q):RR")
+#! Field("$(P)$(Q):RR.FLNK",16777215,1,"$(P)$(Q):RR.FLNK")
+#! Link("$(P)$(Q):RR.FLNK","$(P)$(Q):RR:RBV")
+#! Field("$(P)$(Q):RR.SDIS",16777215,1,"$(P)$(Q):RR.SDIS")
+#! Link("$(P)$(Q):RR.SDIS","$(P)$(Q):DISABLE.VAL")
+#! Record("$(P)$(Q):RR:RBV",1500,500,0,0,"$(P)$(Q):RR:RBV")
+#! Field("$(P)$(Q):RR:RBV.PROC",16777215,0,"$(P)$(Q):RR:RBV.PROC")
+#! Field("$(P)$(Q):RR:RBV.SDIS",16777215,1,"$(P)$(Q):RR:RBV.SDIS")
+#! Link("$(P)$(Q):RR:RBV.SDIS","$(P)$(Q):DISABLE.VAL")
+#! Record("$(P)$(Q):O",1180,824,0,0,"$(P)$(Q):O")
+#! Field("$(P)$(Q):O.FLNK",16777215,1,"$(P)$(Q):O.FLNK")
+#! Link("$(P)$(Q):O.FLNK","$(P)$(Q):O:RBV")
+#! Field("$(P)$(Q):O.SDIS",16777215,1,"$(P)$(Q):O.SDIS")
+#! Link("$(P)$(Q):O.SDIS","$(P)$(Q):DISABLE.VAL")
+#! Record("$(P)$(Q):O:RBV",1480,740,0,0,"$(P)$(Q):O:RBV")
+#! Field("$(P)$(Q):O:RBV.PROC",16777215,0,"$(P)$(Q):O:RBV.PROC")
+#! Field("$(P)$(Q):O:RBV.SDIS",16777215,1,"$(P)$(Q):O:RBV.SDIS")
+#! Link("$(P)$(Q):O:RBV.SDIS","$(P)$(Q):DISABLE.VAL")
+#! Record("$(P)$(Q):PV:RBV",1460,980,0,0,"$(P)$(Q):PV:RBV")
+#! Field("$(P)$(Q):PV:RBV.PROC",16777215,0,"$(P)$(Q):PV:RBV.PROC")
+#! Field("$(P)$(Q):PV:RBV.SDIS",16777215,1,"$(P)$(Q):PV:RBV.SDIS")
+#! Link("$(P)$(Q):PV:RBV.SDIS","$(P)$(Q):DISABLE.VAL")
+#! Record("$(P)$(Q):P",1320,1231,0,0,"$(P)$(Q):P")
+#! Field("$(P)$(Q):P.FLNK",16777215,1,"$(P)$(Q):P.FLNK")
+#! Link("$(P)$(Q):P.FLNK","$(P)$(Q):P:RBV")
+#! Field("$(P)$(Q):P.SDIS",16777215,1,"$(P)$(Q):P.SDIS")
+#! Link("$(P)$(Q):P.SDIS","$(P)$(Q):DISABLE.VAL")
+#! Record("$(P)$(Q):P:RBV",1580,1234,0,0,"$(P)$(Q):P:RBV")
+#! Field("$(P)$(Q):P:RBV.PROC",16777215,0,"$(P)$(Q):P:RBV.PROC")
+#! Field("$(P)$(Q):P:RBV.SDIS",16777215,1,"$(P)$(Q):P:RBV.SDIS")
+#! Link("$(P)$(Q):P:RBV.SDIS","$(P)$(Q):DISABLE.VAL")
+#! Record("$(P)$(Q):I",1300,1511,0,0,"$(P)$(Q):I")
+#! Field("$(P)$(Q):I.FLNK",16777215,1,"$(P)$(Q):I.FLNK")
+#! Link("$(P)$(Q):I.FLNK","$(P)$(Q):I:RBV")
+#! Field("$(P)$(Q):I.SDIS",16777215,1,"$(P)$(Q):I.SDIS")
+#! Link("$(P)$(Q):I.SDIS","$(P)$(Q):DISABLE.VAL")
+#! Record("$(P)$(Q):I:RBV",1580,1514,0,0,"$(P)$(Q):I:RBV")
+#! Field("$(P)$(Q):I:RBV.PROC",16777215,0,"$(P)$(Q):I:RBV.PROC")
+#! Field("$(P)$(Q):I:RBV.SDIS",16777215,1,"$(P)$(Q):I:RBV.SDIS")
+#! Link("$(P)$(Q):I:RBV.SDIS","$(P)$(Q):DISABLE.VAL")
+#! Record("$(P)$(Q):D",1300,1731,0,0,"$(P)$(Q):D")
+#! Field("$(P)$(Q):D.FLNK",16777215,1,"$(P)$(Q):D.FLNK")
+#! Link("$(P)$(Q):D.FLNK","$(P)$(Q):D:RBV")
+#! Field("$(P)$(Q):D.SDIS",16777215,1,"$(P)$(Q):D.SDIS")
+#! Link("$(P)$(Q):D.SDIS","$(P)$(Q):DISABLE.VAL")
+#! Record("$(P)$(Q):D:RBV",1560,1734,0,0,"$(P)$(Q):D:RBV")
+#! Field("$(P)$(Q):D:RBV.PROC",16777215,0,"$(P)$(Q):D:RBV.PROC")
+#! Field("$(P)$(Q):D:RBV.SDIS",16777215,1,"$(P)$(Q):D:RBV.SDIS")
+#! Link("$(P)$(Q):D:RBV.SDIS","$(P)$(Q):DISABLE.VAL")
+#! Record("$(P)$(Q):ERR",1480,2234,0,0,"$(P)$(Q):ERR")
+#! Field("$(P)$(Q):ERR.SDIS",16777215,1,"$(P)$(Q):ERR.SDIS")
+#! Link("$(P)$(Q):ERR.SDIS","$(P)$(Q):DISABLE.VAL")
+#! Record("$(P)$(Q):DISABLE",1920,1040,0,1,"$(P)$(Q):DISABLE")
+#! Field("$(P)$(Q):DISABLE.VAL",16777215,0,"$(P)$(Q):DISABLE.VAL")
+#! Record("$(P)$(Q):MAN",1300,1971,0,1,"$(P)$(Q):MAN")
+#! Field("$(P)$(Q):MAN.FLNK",16777215,1,"$(P)$(Q):MAN.FLNK")
+#! Link("$(P)$(Q):MAN.FLNK","$(P)$(Q):MAN:RBV_AI")
+#! Field("$(P)$(Q):MAN.SDIS",16777215,1,"$(P)$(Q):MAN.SDIS")
+#! Link("$(P)$(Q):MAN.SDIS","$(P)$(Q):DISABLE.VAL")
+#! Record("$(P)$(Q):MAN:RBV_AI",1560,1954,0,0,"$(P)$(Q):MAN:RBV_AI")
+#! Field("$(P)$(Q):MAN:RBV_AI.SDIS",16777215,1,"$(P)$(Q):MAN:RBV_AI.SDIS")
+#! Link("$(P)$(Q):MAN:RBV_AI.SDIS","$(P)$(Q):DISABLE.VAL")
+#! Field("$(P)$(Q):MAN:RBV_AI.FLNK",16777215,1,"$(P)$(Q):MAN:RBV_AI.FLNK")
+#! Link("$(P)$(Q):MAN:RBV_AI.FLNK","$(P)$(Q):MAN:RBV")
+#! Field("$(P)$(Q):MAN:RBV_AI.VAL",16777215,1,"$(P)$(Q):MAN:RBV_AI.VAL")
+#! Field("$(P)$(Q):MAN:RBV_AI.PROC",16777215,0,"$(P)$(Q):MAN:RBV_AI.PROC")
+#! Record("$(P)$(Q):MAN:RBV",1920,1934,0,0,"$(P)$(Q):MAN:RBV")
+#! Field("$(P)$(Q):MAN:RBV.INP",16777215,0,"$(P)$(Q):MAN:RBV.INP")
+#! Link("$(P)$(Q):MAN:RBV.INP","$(P)$(Q):MAN:RBV_AI.VAL")
+#! Record("$(P)$(Q):CONF:MODE",420,1660,0,1,"$(P)$(Q):CONF:MODE")
+#! Field("$(P)$(Q):CONF:MODE.PROC",16777215,1,"$(P)$(Q):CONF:MODE.PROC")
+#! Record("$(P)$(Q):ADDR",720,1370,0,1,"$(P)$(Q):ADDR")
+#! Field("$(P)$(Q):ADDR.VAL",16777215,0,"$(P)$(Q):ADDR.VAL")
+#! Field("$(P)$(Q):ADDR.FLNK",16777215,1,"$(P)$(Q):ADDR.FLNK")
+#! Link("$(P)$(Q):ADDR.FLNK","$(P)$(Q):CONF:DLY")
+#! Record("$(P)$(Q):ADDR:DEVICE",420,1436,0,0,"$(P)$(Q):ADDR:DEVICE")
+#! Field("$(P)$(Q):ADDR:DEVICE.OUT",16777215,1,"$(P)$(Q):ADDR:DEVICE.OUT")
+#! Link("$(P)$(Q):ADDR:DEVICE.OUT","$(P)$(Q):ADDR.VAL")
+#! Record("$(P)$(Q):CONF:SETMODE",120,1154,0,1,"$(P)$(Q):CONF:SETMODE")
+#! Field("$(P)$(Q):CONF:SETMODE.FLNK",16777215,1,"$(P)$(Q):CONF:SETMODE.FLNK")
+#! Link("$(P)$(Q):CONF:SETMODE.FLNK","$(P)$(Q):CONF:ZEROADDR")
+#! Record("$(P)$(Q):CONF:CLEARMODE",120,1394,0,1,"$(P)$(Q):CONF:CLEARMODE")
+#! Field("$(P)$(Q):CONF:CLEARMODE.FLNK",16777215,1,"$(P)$(Q):CONF:CLEARMODE.FLNK")
+#! Link("$(P)$(Q):CONF:CLEARMODE.FLNK","$(P)$(Q):ADDR:DEVICE")
+#! Record("$(P)$(Q):CONF:ZEROADDR",420,1256,0,0,"$(P)$(Q):CONF:ZEROADDR")
+#! Field("$(P)$(Q):CONF:ZEROADDR.OUT",16777215,1,"$(P)$(Q):CONF:ZEROADDR.OUT")
+#! Link("$(P)$(Q):CONF:ZEROADDR.OUT","$(P)$(Q):ADDR.VAL")
+#! Record("$(P)$(Q):ADDR:WR",40,908,0,1,"$(P)$(Q):ADDR:WR")
+#! Record("$(P)$(Q):ADDR:RD",260,921,0,1,"$(P)$(Q):ADDR:RD")
+#! Record("$(P)$(Q):ADDR:RESP",460,930,0,1,"$(P)$(Q):ADDR:RESP")
+#! Record("$(P)$(Q):CONF:DECP",360,438,0,0,"$(P)$(Q):CONF:DECP")
+#! Record("$(P)$(Q):CONF:SET:DECP",660,418,0,1,"$(P)$(Q):CONF:SET:DECP")
+#! Record("$(P)$(Q):CONF:READ",20,556,0,1,"$(P)$(Q):CONF:READ")
+#! Field("$(P)$(Q):CONF:READ.FLNK",16777215,1,"$(P)$(Q):CONF:READ.FLNK")
+#! Link("$(P)$(Q):CONF:READ.FLNK","$(P)$(Q):CONF:DECP")
+#! Field("$(P)$(Q):CONF:READ.LNK1",16777215,1,"$(P)$(Q):CONF:READ.LNK1")
+#! Link("$(P)$(Q):CONF:READ.LNK1","$(P)$(Q):CONF:RREGU")
+#! Record("$(P)$(Q):CONF:RREGU",360,658,0,0,"$(P)$(Q):CONF:RREGU")
+#! Record("$(P)$(Q):CONF:SET:RREGU",660,658,0,1,"$(P)$(Q):CONF:SET:RREGU")
+#! Record("$(P)$(Q):CONF:DLY",760,1674,0,1,"$(P)$(Q):CONF:DLY")
+#! Field("$(P)$(Q):CONF:DLY.LNK1",16777215,0,"$(P)$(Q):CONF:DLY.LNK1")
+#! Link("$(P)$(Q):CONF:DLY.LNK1","$(P)$(Q):CONF:MODE.PROC")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/Db/misc.db	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,9 @@
+record(calc, "$(P):$(Q):graphUpdate")
+{
+field(DESC, "Just a counter to update graphs")
+field(SCAN, "1 second")
+field(INPA, "$(P):$(Q):graphUpdate.VAL  NPP NMS")
+field(INPB, "1")
+field(CALC, "B?A+B:B")
+field(EGU, "rels")
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/Db/mtm.db	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,36 @@
+record(ai, "$(P):$(Q):thickness")
+{
+field(SCAN, "I/O Intr")
+field(DTYP,"stream")
+field(INP,"@mtm.proto read($(P):$(Q):sampleNr) $(PORT) -1")
+field(EGU,"nm")
+field(HOPR,"10")
+field(LOPR,"-15")
+field (FLNK,"$(P):$(Q):thicknessAVG")
+}
+
+record(longin, "$(P):$(Q):sampleNr")
+{
+  field (FLNK,"$(P):$(Q):sampleNrMDN")
+}
+
+record(compress,"$(P):$(Q):thicknessAVG")
+{
+  field(DESC, "thickness average")
+  field(INP,"$(P):$(Q):thickness.VAL NPP NMS")
+  field(ALG,"N to 1 Average")
+  field(NSAM,"1")
+  field(N,"10")
+  field(HOPR,"10")
+  field(LOPR,"-25")
+  field(EGU,"nm")
+}
+
+record(compress,"$(P):$(Q):sampleNrMDN")
+{
+  field(DESC, "thickness average")
+  field(INP,"$(P):$(Q):sampleNr.VAL NPP NMS")
+  field(ALG,"N to 1 Median")
+  field(NSAM,"1")
+  field(N,"10")
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/Db/mtm.proto	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,2 @@
+Terminator = CR NUL LF;
+read {  in "%(\$1)d,%f"; }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/Db/pgc2.db	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,42 @@
+record(ai, "$(P):$(Q):dumm")
+{
+field(SCAN, "I/O Intr")
+field(DTYP,"stream")
+field(INP,"@pgc2.proto read($(P):$(Q)) $(PORT) -1")
+field(EGU,"mBar")
+field(HOPR,"1")
+field(LOPR,"1E-11")
+field(TPRO,"1")
+}
+
+record(calc, "$(P):$(Q):pressure1")
+{ 
+field(CALC,"A*10^B")
+field(EGU,"mBar")
+field(HOPR,"1")
+field(LOPR,"1E-11")
+}
+
+record(calc, "$(P):$(Q):pressure2")
+{
+field(CALC,"A*10^B")
+field(EGU,"mBar")
+field(HOPR,"1")
+field(LOPR,"1E-11")
+}
+
+record(calc, "$(P):$(Q):pressure3")
+{
+field(CALC,"A*10^B")
+field(EGU,"mBar")
+field(HOPR,"1")
+field(LOPR,"1E-11")
+}
+
+record(calc, "$(P):$(Q):pressure4")
+{
+field(CALC,"A*10^B")
+field(EGU,"mBar")
+field(HOPR,"1")
+field(LOPR,"1E-11")
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/Db/pgc2.proto	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,4 @@
+Terminator = CR NUL LF;
+read { ExtraInput = Ignore;
+       in "::::%(\$1:pressure1.A)f%(\$1:pressure1.B)d %(\$1:pressure2.A)f%(\$1:pressure2.B)d %(\$1:pressure3.A)f%(\$1:pressure3.B)d %(\$1:pressure4.A)f%(\$1:pressure4.B)d %f";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/Makefile	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,8 @@
+TOP = ..
+include $(TOP)/configure/CONFIG
+DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *src*))
+DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Src*))
+DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *db*))
+DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Db*))
+include $(TOP)/configure/RULES_DIRS
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/src/Makefile	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,55 @@
+TOP=../..
+
+include $(TOP)/configure/CONFIG
+#----------------------------------------
+#  ADD MACRO DEFINITIONS AFTER THIS LINE
+#=============================
+
+#=============================
+# Build the IOC application
+
+PROD_IOC = kstm
+# kstm.dbd will be created and installed
+DBD += kstm.dbd
+
+# kstm.dbd will be made up from these files:
+kstm_DBD += base.dbd
+
+# Include dbd files from all support applications:
+#kstm_DBD += xxx.dbd
+kstm_DBD += dbSubReadDHT.dbd 
+kstm_DBD += asyn.dbd
+kstm_DBD += drvAsynIPPort.dbd
+kstm_DBD += stream.dbd
+
+# Add all the support libraries needed by this IOC
+#kstm_LIBS += xxx 
+kstm_LIBS += stream 
+kstm_LIBS += asyn 
+
+# kstm_registerRecordDeviceDriver.cpp derives from kstm.dbd
+kstm_SRCS += kstm_registerRecordDeviceDriver.cpp
+
+#Link locally-provided code into the IOC application
+kstm_SRCS += common_dht_read.c
+kstm_SRCS += pi_2_mmio.c
+kstm_SRCS += pi_2_dht_read.c
+kstm_SRCS += dbSubReadDHT.c
+#kstm_SRCS += dbSubReadADC.c
+
+# Build the main IOC entry point on workstation OSs.
+kstm_SRCS_DEFAULT += kstmMain.cpp
+kstm_SRCS_vxWorks += -nil-
+
+# Add support from base/src/vxWorks if needed
+#kstm_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary
+
+# Finally link to the EPICS Base libraries
+kstm_LIBS += $(EPICS_BASE_IOC_LIBS)
+
+#===========================
+
+include $(TOP)/configure/RULES
+#----------------------------------------
+#  ADD RULES AFTER THIS LINE
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/src/common_dht_read.c	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,67 @@
+// Copyright (c) 2014 Adafruit Industries
+// Author: Tony DiCola
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+#include <errno.h>
+#include <sched.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "common_dht_read.h"
+
+void busy_wait_milliseconds(uint32_t millis) {
+  // Set delay time period.
+  struct timeval deltatime;
+  deltatime.tv_sec = millis / 1000;
+  deltatime.tv_usec = (millis % 1000) * 1000;
+  struct timeval walltime;
+  // Get current time and add delay to find end time.
+  gettimeofday(&walltime, NULL);
+  struct timeval endtime;
+  timeradd(&walltime, &deltatime, &endtime);
+  // Tight loop to waste time (and CPU) until enough time as elapsed.
+  while (timercmp(&walltime, &endtime, <)) {
+    gettimeofday(&walltime, NULL);
+  }
+}
+
+void sleep_milliseconds(uint32_t millis) {
+  struct timespec sleep;
+  sleep.tv_sec = millis / 1000;
+  sleep.tv_nsec = (millis % 1000) * 1000000L;
+//  while (clock_nanosleep(CLOCK_MONOTONIC, 0, &sleep, &sleep) && errno == EINTR);
+  while (nanosleep(&sleep, &sleep) && errno == EINTR);
+}
+
+void set_max_priority(void) {
+  struct sched_param sched;
+  memset(&sched, 0, sizeof(sched));
+  // Use FIFO scheduler with highest priority for the lowest chance of the kernel context switching.
+  sched.sched_priority = sched_get_priority_max(SCHED_FIFO);
+  sched_setscheduler(0, SCHED_FIFO, &sched);
+}
+
+void set_default_priority(void) {
+  struct sched_param sched;
+  memset(&sched, 0, sizeof(sched));
+  // Go back to default scheduler with default 0 priority.
+  sched.sched_priority = 0;
+  sched_setscheduler(0, SCHED_OTHER, &sched);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/src/common_dht_read.h	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,51 @@
+// Copyright (c) 2014 Adafruit Industries
+// Author: Tony DiCola
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+#ifndef COMMON_DHT_READ_H
+#define COMMON_DHT_READ_H
+
+#include <stdint.h>
+
+// Define errors and return values.
+#define DHT_ERROR_TIMEOUT -1
+#define DHT_ERROR_CHECKSUM -2
+#define DHT_ERROR_ARGUMENT -3
+#define DHT_ERROR_GPIO -4
+#define DHT_SUCCESS 0
+
+// Define sensor types.
+#define DHT11 11
+#define DHT22 22
+#define AM2302 22
+
+// Busy wait delay for most accurate timing, but high CPU usage.
+// Only use this for short periods of time (a few hundred milliseconds at most)!
+void busy_wait_milliseconds(uint32_t millis);
+
+// General delay that sleeps so CPU usage is low, but accuracy is potentially bad.
+void sleep_milliseconds(uint32_t millis);
+
+// Increase scheduling priority and algorithm to try to get 'real time' results.
+void set_max_priority(void);
+
+// Drop scheduling priority back to normal/default.
+void set_default_priority(void);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/src/dbSubReadADC.c	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,66 @@
+#include <stdio.h>
+#include <string.h>
+
+#include <dbDefs.h>
+#include <registryFunction.h>
+#include <subRecord.h>
+#include <aSubRecord.h>
+#include <epicsExport.h>
+
+#include "pi_2_dht_read.h"
+
+int dbSubReadDHTDebug;
+
+static long dbSubReadDHTInit(subRecord *precord)
+{
+    if (dbSubReadDHTDebug)
+        printf("Record %s called dbSubReadDHTInit(%p)\n",
+               precord->name, (void*) precord);
+    return 0;
+}
+
+static long dbSubReadDHTProcess(subRecord *precord)
+{
+char cmd[256];
+char buf[256];
+double dumm;
+
+    if (dbSubReadDHTDebug)
+        printf("Record %s called dbSubReadDHTProcess(%p)\n",
+               precord->name, (void*) precord);
+
+
+ pi_2_dht_read(int type, int pin, float* humidity, float* temperature)
+
+
+ precord->val = dumm;
+ printf(" val = %lf\n", precord->val );
+
+    return 0;
+}
+
+static long myAsubInit(aSubRecord *precord)
+{
+    if (mySubDebug)
+        printf("Record %s called myAsubInit(%p)\n",
+               precord->name, (void*) precord);
+    return 0;
+}
+
+static long myAsubProcess(aSubRecord *precord)
+{
+    if (mySubDebug)
+        printf("Record %s called myAsubProcess(%p)\n",
+               precord->name, (void*) precord);
+    return 0;
+}
+
+/* Register these symbols for use by IOC code: */
+
+epicsExportAddress(int, mySubDebug);
+epicsRegisterFunction(mySubInit);
+epicsRegisterFunction(mySubProcess);
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/src/dbSubReadDHT.c	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,46 @@
+#include <stdio.h>
+#include <string.h>
+
+#include <dbDefs.h>
+#include <registryFunction.h>
+#include <subRecord.h>
+#include <aSubRecord.h>
+#include <epicsExport.h>
+
+#include "pi_2_dht_read.h"
+
+int dbSubReadDHTDebug = 1;
+
+static long dbSubReadDHTInit(subRecord *precord)
+{
+    if (dbSubReadDHTDebug)
+        printf("Record %s called dbSubReadDHTInit(%p)\n",
+               precord->name, (void*) precord);
+    return 0;
+}
+
+static long dbSubReadDHTProcess(subRecord *precord)
+{
+float humidity, temperature;
+int type = 22;
+int ret;
+int pin = 7;
+
+    if (dbSubReadDHTDebug)
+        printf("Record %s called dbSubRreadHTProcess(%p)\n",
+               precord->name, (void*) precord);
+
+ ret = pi_2_dht_read(type, pin, &humidity, &temperature);
+ printf(" ret : %d, humidity = %f, temperature = %f\n", ret, humidity, temperature);
+ precord->val = (double)temperature;
+ printf(" val = %lf\n", precord->val );
+
+    return 0;
+}
+
+/* Register these symbols for use by IOC code: */
+
+epicsExportAddress(int, dbSubReadDHTDebug);
+epicsRegisterFunction(dbSubReadDHTInit);
+epicsRegisterFunction(dbSubReadDHTProcess);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/src/dbSubReadDHT.dbd	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,3 @@
+variable(dbSubReadDHTDebug)
+function(dbSubReadDHTInit)
+function(dbSubReadDHTProcess)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/src/kstmMain.cpp	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,23 @@
+/* kstmMain.cpp */
+/* Author:  Marty Kraimer Date:    17MAR2000 */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "epicsExit.h"
+#include "epicsThread.h"
+#include "iocsh.h"
+
+int main(int argc,char *argv[])
+{
+    if(argc>=2) {    
+        iocsh(argv[1]);
+        epicsThreadSleep(.2);
+    }
+    iocsh(NULL);
+    epicsExit(0);
+    return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/src/pi_2_dht_read.c	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,162 @@
+// Copyright (c) 2014 Adafruit Industries
+// Author: Tony DiCola
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "pi_2_dht_read.h"
+#include "pi_2_mmio.h"
+
+// This is the only processor specific magic value, the maximum amount of time to
+// spin in a loop before bailing out and considering the read a timeout.  This should
+// be a high value, but if you're running on a much faster platform than a Raspberry
+// Pi or Beaglebone Black then it might need to be increased.
+#define DHT_MAXCOUNT 32000
+
+// Number of bit pulses to expect from the DHT.  Note that this is 41 because
+// the first pulse is a constant 50 microsecond pulse, with 40 pulses to represent
+// the data afterwards.
+#define DHT_PULSES 41
+
+int pi_2_dht_read(int type, int pin, float* humidity, float* temperature) {
+  // Validate humidity and temperature arguments and set them to zero.
+  if (humidity == NULL || temperature == NULL) {
+    return DHT_ERROR_ARGUMENT;
+  }
+  *temperature = 0.0f;
+  *humidity = 0.0f;
+
+  // Initialize GPIO library.
+  if (pi_2_mmio_init() < 0) {
+    return DHT_ERROR_GPIO;
+  }
+
+  // Store the count that each DHT bit pulse is low and high.
+  // Make sure array is initialized to start at zero.
+  int pulseCounts[DHT_PULSES*2] = {0};
+
+  // Set pin to output.
+  pi_2_mmio_set_output(pin);
+
+  // Bump up process priority and change scheduler to try to try to make process more 'real time'.
+  set_max_priority();
+
+  // Set pin high for ~500 milliseconds.
+  pi_2_mmio_set_high(pin);
+  sleep_milliseconds(500);
+
+  // The next calls are timing critical and care should be taken
+  // to ensure no unnecssary work is done below.
+
+  // Set pin low for ~20 milliseconds.
+  pi_2_mmio_set_low(pin);
+  busy_wait_milliseconds(20);
+
+  // Set pin at input.
+  pi_2_mmio_set_input(pin);
+  // Need a very short delay before reading pins or else value is sometimes still low.
+volatile int i;
+  for (i = 0; i < 50; ++i) {
+  }
+
+  // Wait for DHT to pull pin low.
+  uint32_t count = 0;
+  while (pi_2_mmio_input(pin)) {
+    if (++count >= DHT_MAXCOUNT) {
+      // Timeout waiting for response.
+      set_default_priority();
+      return DHT_ERROR_TIMEOUT;
+    }
+  }
+
+  // Record pulse widths for the expected result bits.
+  int j;
+  for ( j=0; j < DHT_PULSES*2; j+=2) {
+    // Count how long pin is low and store in pulseCounts[i]
+    while (!pi_2_mmio_input(pin)) {
+      if (++pulseCounts[i] >= DHT_MAXCOUNT) {
+        // Timeout waiting for response.
+        set_default_priority();
+        return DHT_ERROR_TIMEOUT;
+      }
+    }
+    // Count how long pin is high and store in pulseCounts[i+1]
+    while (pi_2_mmio_input(pin)) {
+      if (++pulseCounts[i+1] >= DHT_MAXCOUNT) {
+        // Timeout waiting for response.
+        set_default_priority();
+        return DHT_ERROR_TIMEOUT;
+      }
+    }
+  }
+
+  // Done with timing critical code, now interpret the results.
+
+  // Drop back to normal priority.
+  set_default_priority();
+
+  // Compute the average low pulse width to use as a 50 microsecond reference threshold.
+  // Ignore the first two readings because they are a constant 80 microsecond pulse.
+  uint32_t threshold = 0;
+  int k;
+  for (k=2; i < DHT_PULSES*2; k+=2) {
+    threshold += pulseCounts[k];
+  }
+  threshold /= DHT_PULSES-1;
+
+  // Interpret each high pulse as a 0 or 1 by comparing it to the 50us reference.
+  // If the count is less than 50us it must be a ~28us 0 pulse, and if it's higher
+  // then it must be a ~70us 1 pulse.
+  uint8_t data[5] = {0};
+  int l;
+  for ( l=3; l < DHT_PULSES*2; l+=2) {
+    int index = (l-3)/16;
+    data[index] <<= 1;
+    if (pulseCounts[l] >= threshold) {
+      // One bit for long pulse.
+      data[index] |= 1;
+    }
+    // Else zero bit for short pulse.
+  }
+
+  // Useful debug info:
+  //printf("Data: 0x%x 0x%x 0x%x 0x%x 0x%x\n", data[0], data[1], data[2], data[3], data[4]);
+
+  // Verify checksum of received data.
+  if (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) {
+    if (type == DHT11) {
+      // Get humidity and temp for DHT11 sensor.
+      *humidity = (float)data[0];
+      *temperature = (float)data[2];
+    }
+    else if (type == DHT22) {
+      // Calculate humidity and temp for DHT22 sensor.
+      *humidity = (data[0] * 256 + data[1]) / 10.0f;
+      *temperature = ((data[2] & 0x7F) * 256 + data[3]) / 10.0f;
+      if (data[2] & 0x80) {
+        *temperature *= -1.0f;
+      }
+    }
+    return DHT_SUCCESS;
+  }
+  else {
+    return DHT_ERROR_CHECKSUM;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/src/pi_2_dht_read.h	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,32 @@
+// Copyright (c) 2014 Adafruit Industries
+// Author: Tony DiCola
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+#ifndef PI_2_DHT_READ_H
+#define PI_2_DHT_READ_H
+
+#include "../common_dht_read.h"
+
+// Read DHT sensor connected to GPIO pin (using BCM numbering).  Humidity and temperature will be 
+// returned in the provided parameters. If a successfull reading could be made a value of 0 
+// (DHT_SUCCESS) will be returned.  If there was an error reading the sensor a negative value will
+// be returned.  Some errors can be ignored and retried, specifically DHT_ERROR_TIMEOUT or DHT_ERROR_CHECKSUM.
+int pi_2_dht_read(int sensor, int pin, float* humidity, float* temperature);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/src/pi_2_mmio.c	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,71 @@
+// Copyright (c) 2014 Adafruit Industries
+// Author: Tony DiCola
+// Based on code from Gert van Loo & Dom: http://elinux.org/RPi_Low-level_peripherals#GPIO_Code_examples
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "pi_2_mmio.h"
+
+#define GPIO_BASE_OFFSET 0x200000
+#define GPIO_LENGTH 4096
+
+volatile uint32_t* pi_2_mmio_gpio = NULL;
+
+int pi_2_mmio_init(void) {
+  if (pi_2_mmio_gpio == NULL) {
+    // Check for GPIO and peripheral addresses from device tree.
+    // Adapted from code in the RPi.GPIO library at:
+    //   http://sourceforge.net/p/raspberry-gpio-python/
+    FILE *fp = fopen("/proc/device-tree/soc/ranges", "rb");
+    if (fp == NULL) {
+      return MMIO_ERROR_OFFSET;
+    }
+    fseek(fp, 4, SEEK_SET);
+    unsigned char buf[4];
+    if (fread(buf, 1, sizeof(buf), fp) != sizeof(buf)) {
+      return MMIO_ERROR_OFFSET;
+    }
+    uint32_t peri_base = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3] << 0;
+    uint32_t gpio_base = peri_base + GPIO_BASE_OFFSET;
+    fclose(fp);
+
+    int fd = open("/dev/mem", O_RDWR | O_SYNC);
+    if (fd == -1) {
+      // Error opening /dev/mem.  Probably not running as root.
+      return MMIO_ERROR_DEVMEM;
+    }
+    // Map GPIO memory to location in process space.
+    pi_2_mmio_gpio = (uint32_t*)mmap(NULL, GPIO_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, fd, gpio_base);
+    close(fd);
+    if (pi_2_mmio_gpio == MAP_FAILED) {
+      // Don't save the result if the memory mapping failed.
+      pi_2_mmio_gpio = NULL;
+      return MMIO_ERROR_MMAP;
+    }
+  }
+  return MMIO_SUCCESS;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kstmApp/src/pi_2_mmio.h	Fri Aug 14 11:30:43 2015 +0200
@@ -0,0 +1,62 @@
+// Copyright (c) 2014 Adafruit Industries
+// Author: Tony DiCola
+// Based on code from Gert van Loo & Dom: http://elinux.org/RPi_Low-level_peripherals#GPIO_Code_examples
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+// Simple fast memory-mapped GPIO library for the Raspberry Pi.
+#ifndef PI_2_MMIO_H
+#define PI_2_MMIO_H
+
+#include <stdint.h>
+
+#define MMIO_SUCCESS 0
+#define MMIO_ERROR_DEVMEM -1
+#define MMIO_ERROR_MMAP -2
+#define MMIO_ERROR_OFFSET -3
+
+volatile uint32_t* pi_2_mmio_gpio;
+
+int pi_2_mmio_init(void);
+
+static inline void pi_2_mmio_set_input(const int gpio_number) {
+  // Set GPIO register to 000 for specified GPIO number.
+  *(pi_2_mmio_gpio+((gpio_number)/10)) &= ~(7<<(((gpio_number)%10)*3));
+}
+
+static inline void pi_2_mmio_set_output(const int gpio_number) {
+  // First set to 000 using input function.
+  pi_2_mmio_set_input(gpio_number);
+  // Next set bit 0 to 1 to set output.
+  *(pi_2_mmio_gpio+((gpio_number)/10)) |=  (1<<(((gpio_number)%10)*3));
+}
+
+static inline void pi_2_mmio_set_high(const int gpio_number) {
+  *(pi_2_mmio_gpio+7) = 1 << gpio_number;
+}
+
+static inline void pi_2_mmio_set_low(const int gpio_number) {
+  *(pi_2_mmio_gpio+10) = 1 << gpio_number;
+}
+
+static inline uint32_t pi_2_mmio_input(const int gpio_number) {
+  return *(pi_2_mmio_gpio+13) & (1 << gpio_number);
+}
+
+#endif