Skip to content

Instantly share code, notes, and snippets.

@alexanderhiam
Last active August 29, 2015 13:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexanderhiam/9511261 to your computer and use it in GitHub Desktop.
Save alexanderhiam/9511261 to your computer and use it in GitHub Desktop.
Device Tree overlay and Python program for a simple thermostat BeagleBone cape: http://inspire.logicsupply.com/p/building-custom-cape.html
/* cape-thermostat-00A0.dts
* Mar. 2014
*
* Overlay for a simple thermostat cape.
*
* Created for Logic Supply's custom cape tutorial here:
* http://inspire.logicsupply.com/p/building-custom-cape.html
*/
/dts-v1/;
/plugin/;
/{
compatible = "ti,beaglebone", "ti,beaglebone-black";
part-number = "cape-thermostat";
version = "00A0";
/* state the resources this cape uses */
exclusive-use =
/* the pin header uses */
"P8.8", /* GPIO2_3 - switch control */
"P9.38", /* AIN3 - V threshold */
"P9.40", /* AIN1 - V temperature */
/* the hardware IP uses */
"gpio1_16",
"tscadc1",
"tscadc3";
fragment@0 {
/* Creates the pinmux configuration for the relay control pin */
target = <&am33xx_pinmux>;
__overlay__ {
thermostat_relay_pins: pinmux_thermostat_relay_pins {
pinctrl-single,pins = <
0x094 0x07 /* gpmc_oen_ren - PULL_DOWN | MODE7 (GPIO2_3) */
>;
};
};
};
fragment@1 {
/* Use pinmux-helper driver to configure switch control pin. */
target = <&ocp>; /* On-Chip Peripherals */
__overlay__ {
thermostat-pinmux {
compatible = "bone-pinmux-helper";
status="okay";
pinctrl-names = "default";
/* Use configuration defined in fragment@0 above: */
pinctrl-0 = <&thermostat_relay_pins>;
};
};
};
fragment@2 {
/* Enables the ADC and configures the two inputs used. */
target = <&ocp>;
__overlay__ {
#address-cells = <1>;
#size-cells = <1>;
tscadc {
/* Enable the ADC driver */
compatible = "ti,ti-tscadc";
reg = <0x44e0d000 0x1000>;
interrupt-parent = <&intc>;
interrupts = <16>;
ti,hwmods = "adc_tsc";
status = "okay";
adc {
/* enable AIN1 and AIN3 */
ti,adc-channels = <0 1 2 3 4 5 6 7>;
};
};
adc_helper: thermostat-AIN {
/* Configure scaling on the values and create sysfs interface. */
compatible = "bone-iio-helper";
vsense-name = "AIN1", "AIN3";
/* scale of 100000 gives readings in microVolts: */
vsense-scale = <100000 100000>;
status = "okay";
};
};
};
};
#!/usr/bin/env python
"""
simple-thermostat.py
Mar. 2014
Created for Logic Supply's custom cape tutorial here:
http://inspire.logicsupply.com/p/building-custom-cape.html
Uses a 1N4148 diode as a temperature sensor. Uses a GPIO pin and a relay
to switch an external load when the temperature crosses a threshold set
by a potentiometer.
"""
import os, collections, glob, time, atexit
# Set to True to have the relay engaged when the temperature falls below
# the set threshold:
SWITCH_WHEN_COLDER = True
# Or set to False to have the relay engaged when the temperature rises
# above the set threshold:
#SWITCH_WHEN_COLDER = False
# Used to avoid rapid switching when the temperature is right at the
# threshold. The relay will be engaged when it is this many degrees past
# the threshold, and will be disengaged once it has returned this many
# degrees past the threshold in the other direction:
HYSTERESIS = 2 # in degrees C
# The temperature will be sampled once every SAMPLE_INTERVAL seconds
SAMPLE_INTERVAL = 0.5 # in seconds
# Used to calibrate the measured temperature by offsetting it either
# positively or negatively:
TEMP_OFFSET = 0 # in degrees C
AIN_DIR = glob.glob('/sys/devices/ocp.*/thermostat-AIN.*')[0]
TEMPERATURE_FILE = '%s/AIN1' % AIN_DIR
THRESHOLD_FILE = '%s/AIN3' % AIN_DIR
RELAY_GPIO_NUM = '67' # GPIO2_3 = 2x32 + 3
GPIO_BASE_DIR = '/sys/class/gpio/gpio%s' % RELAY_GPIO_NUM
def switchOff():
""" Disengages the relay. """
with open('%s/value' % GPIO_BASE_DIR, 'wb') as f:
f.write('0')
def switchOn():
""" Engages the relay. """
with open('%s/value' % GPIO_BASE_DIR, 'wb') as f:
f.write('1')
def relayInit():
""" Initializes the relay pin using the sysfs driver and ensures it is
turned off. """
if not os.path.exists(GPIO_BASE_DIR):
# The control directory for this pin doesn't exist yet, which means it
# needs to be exported for userspace control:
with open('/sys/class/gpio/export', 'wb') as f:
f.write(RELAY_GPIO_NUM)
# Give the driver time to create the interface:
time.sleep(0.5)
# Set the pin as an output:
with open('%s/direction' % GPIO_BASE_DIR, 'wb') as f:
f.write('out')
# Make sure the relay is off:
switchOff()
# This ensures that the relay will be turned off when the program exits:
atexit.register(switchOff)
def getMicroVolts(ain_file):
""" Reads the value from the given AIN file, converts it to an integer
and returns it. """
with open(ain_file, 'rb') as f:
# Sometimes you end up trying to read the file while the kernel driver
# is writing to it, resulting in an IOError. To prevent this from
# crashing the program we try up to three times to read the file:
for i in range(3):
try:
uv = int(f.read())
# We got the value without an error, return:
return uv
except IOError:
continue
# If we make it here it's failed 3 times in a row, something else is
# probably not working.
raise Exception('could not read %s' % ain_file)
def uvToTemp(uv):
""" Converts a voltage in microvolts as measured on the diode, converts
to degrees C and returns. """
return -0.0005625535 * uv + 410.5644360774 + TEMP_OFFSET
def getTempC():
""" Returns the approximate temperature of the 1N4148 diode. """
uv = getMicroVolts(TEMPERATURE_FILE)
return uvToTemp(uv)
def getThreshold():
""" Returns the threshold temperature set by the potentiometer. """
uv = getMicroVolts(THRESHOLD_FILE)
# Because the potentometer has the same range and scaling as the voltage
# from the diode we can use the same function to convert its voltage into
# a temperature:
return uvToTemp(uv)
def run():
""" Runs the thermostat program until ctrl-c is pressed. """
relayInit()
engaged = False # Keep track of relay state for printing
try:
while(True):
temp = getTempC()
thresh = getThreshold()
# The thermostat logic:
if temp >= thresh + HYSTERESIS:
if SWITCH_WHEN_COLDER:
switchOff()
engaged = False
else:
switchOn()
engaged = True
elif temp <= thresh - HYSTERESIS:
if SWITCH_WHEN_COLDER:
switchOn()
engaged = True
else:
switchOff()
engaged = False
# Log the values to the terminal:
if engaged:
print "temp=%i C - thresh=%i C - relay engaged" % (temp, thresh)
else:
print "temp=%i C - thresh=%i C - relay disengaged" % (temp, thresh)
# Wait the set interval:
time.sleep(SAMPLE_INTERVAL)
except KeyboardInterrupt:
print "Exiting"
exit(0)
if __name__ == '__main__':
run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment