Last active
August 29, 2015 13:57
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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"; | |
}; | |
}; | |
}; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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