Skip to content

Commit

Permalink
initial version
Browse files Browse the repository at this point in the history
  • Loading branch information
sampsyo committed Dec 9, 2011
0 parents commit 3f19d81
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 0 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# BK Precision 1696

This is a Python library for communicating with the [BK Precision 1696][psup]
power supply via its serial interface. You can control the supply's parameters
and take voltage/current readings.

For example:

import psup
with psup.Supply() as sup:
sup.voltage(1.3)
volts, amps = sup.reading()
print '%f V, %f A' % (volts, amps)

For more details, see [my blog post][blog] about a setup using this library to
measure the dynamic power of a smartphone.

[psup]: http://www.bkprecision.com/products/model/1696/programmable-dc-power-supply-1-20vdc-0-999a.html
[blog]: http://www.cs.washington.edu/homes/asampson/blog/powermeasurement.html
104 changes: 104 additions & 0 deletions psup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
from __future__ import division
import serial
import glob
import time
import decimal

def _str2num(num, factor=10):
"""Takes a number in the supply's format, which always has one
decimal place, and returns a decimal number reflecting it.
"""
return decimal.Decimal(num) / factor
def _num2str(s, length=3, factor=10):
"""Turns a number, which may be a decimal, integer, or float,
and turns it into a supply-formatted string.
"""
dec = decimal.Decimal(s)
return ('%0' + str(length) + 'i') % int(dec * factor)
def _numfields(s, fields, factor=10):
"""Generates numbers of the given lengths in the string.
"""
pos = 0
for field in fields:
yield _str2num(s[pos:pos+field], factor)
pos += field

class Supply(object):
def __init__(self, ident=None):
if not ident:
# Discover a device.
devices = glob.glob('/dev/tty.PL*') # Mac OS X
devices += glob.glob('/dev/ttyUSB*') # Linux
ident = devices[0]
self.ser = serial.Serial(ident, 9600, 8, 'N', 1)

def command(self, code, param='', address='00'):
# Put this communication in an isolated little transaction.
self.ser.flushInput()
self.ser.flushOutput()

self.ser.write(code + address + param + "\r")
self.ser.flush()

out = None
while True:
# Read until CR.
resp = ''
while True:
char = self.ser.read()
resp += char
if char == '\r':
break

if resp == 'OK\r':
return out

if out is not None:
print 'received more than one line of output without OK!'
return resp

out = resp

def start(self):
self.command('SESS')
def close(self):
self.command('ENDS')
def voltage(self, volts):
self.command('VOLT', _num2str(volts))
def reading(self):
resp = self.command('GETD')
volts, amps = _numfields(resp, (4, 4), 1)
return volts/100, amps/1000
def maxima(self):
resp = self.command('GMAX')
return tuple(_numfields(resp, (3, 3)))
def settings(self):
resp = self.command('GETS')
return tuple(_numfields(resp, (3, 3)))
def enable(self):
"""Enable output."""
self.command('SOUT', '1')
def disable(self):
"""Enable output."""
self.command('SOUT', '0')

# Context manager.
def __enter__(self):
self.start()
return self
def __exit__(self, exc_type, exc_value, traceback):
self.close()

# Just some tests to show it's working.
if __name__ == '__main__':
with Supply() as sup:
time.sleep(0.5)
sup.voltage(1.3)
sup.enable()
time.sleep(0.5)
print 'Reading', sup.reading()
print 'Maxima', sup.maxima()
print 'Settings', sup.settings()
sup.disable()
print 'Disabled; reading', sup.reading()
time.sleep(0.5)

0 comments on commit 3f19d81

Please sign in to comment.