From 33655a7f679e79b01ed73d52d5a39683dbaf669c Mon Sep 17 00:00:00 2001
From: Matthew Wall
Date: Thu, 13 Mar 2014 16:54:21 +0000
Subject: [PATCH] clean up usb initialization and shutdown for fousb, ws28xx,
and te923. remove more dead code from ws28xx.
---
bin/weewx/drivers/fousb.py | 57 +++++------
bin/weewx/drivers/te923.py | 62 ++++++------
bin/weewx/drivers/ws28xx.py | 182 ++++++++++++++++++++++--------------
docs/changes.txt | 2 +
docs/usersguide.htm | 27 +++---
5 files changed, 176 insertions(+), 154 deletions(-)
diff --git a/bin/weewx/drivers/fousb.py b/bin/weewx/drivers/fousb.py
index 57f3d3a0..bffcc551 100644
--- a/bin/weewx/drivers/fousb.py
+++ b/bin/weewx/drivers/fousb.py
@@ -221,7 +221,7 @@ import weewx.abstractstation
import weewx.units
import weewx.wxformulas
-DRIVER_VERSION = '1.5'
+DRIVER_VERSION = '1.6'
def loader(config_dict, engine):
altitude_m = weewx.units.getAltitudeM(config_dict)
@@ -587,27 +587,9 @@ class FineOffsetUSB(weewx.abstractstation.AbstractStation):
max_tries: How many times to try before giving up.
[Optional. Default is 3]
- data_format: Format for data from the station
- [Optional. Default is 1080, automatically changes to 3080 as needed]
-
- vendor_id: The USB vendor ID for the station.
- [Optional. Default is 1941]
-
- product_id: The USB product ID for the station.
- [Optional. Default is 8021]
-
device_id: The USB device ID for the station. Specify this if there
are multiple devices of the same type on the bus.
[Optional. No default]
-
- interface: The USB interface.
- [Optional. Default is 0]
-
- usb_endpoint: The IN_endpoint for reading from USB.
- [Optional. Default is 0x81]
-
- usb_read_size: Number of bytes to read from USB.
- [Optional. Default is 32 and is the same for all FineOffset devices]
"""
self.altitude = stn_dict['altitude']
@@ -618,17 +600,18 @@ class FineOffsetUSB(weewx.abstractstation.AbstractStation):
self.timeout = float(stn_dict.get('timeout', 15.0))
self.wait_before_retry = float(stn_dict.get('wait_before_retry', 30.0))
self.max_tries = int(stn_dict.get('max_tries', 3))
- self.interface = int(stn_dict.get('interface', 0))
- self.vendor_id = int(stn_dict.get('vendor_id', '0x1941'), 0)
- self.product_id = int(stn_dict.get('product_id', '0x8021'), 0)
self.device_id = stn_dict.get('device_id', None)
- self.usb_endpoint = int(stn_dict.get('usb_endpoint', '0x81'), 0)
- self.usb_read_size = int(stn_dict.get('usb_read_size', '0x20'), 0)
- self.data_format = stn_dict.get('data_format', '1080')
self.pressure_offset = stn_dict.get('pressure_offset', None)
if self.pressure_offset is not None:
self.pressure_offset = float(self.pressure_offset)
+ self.data_format ='1080'
+ self.vendor_id = 0x1941
+ self.product_id = 0x8021
+ self.usb_interface = 0
+ self.usb_endpoint = 0x81
+ self.usb_read_size = 0x20
+
# avoid USB activity this many seconds each side of the time when
# console is believed to be writing to memory.
self.avoid = 3.0
@@ -694,17 +677,24 @@ class FineOffsetUSB(weewx.abstractstation.AbstractStation):
if not dev:
logcrt("Cannot find USB device with Vendor=0x%04x ProdID=0x%04x Device=%s" % (self.vendor_id, self.product_id, self.device_id))
raise weewx.WeeWxIOError("Unable to find USB device")
+
self.devh = dev.open()
- # Detach any old claimed interfaces
+ if not self.devh:
+ raise weewx.WeeWxIOError("Open USB device failed")
+
+ # be sure kernel does not claim the interface
try:
- self.devh.detachKernelDriver(self.interface)
- except:
- pass
+ self.devh.detachKernelDriver(self.usb_interface)
+ except Exception, e:
+ loginf('Detach kernel driver failed: %s' % e)
+
+ # attempt to claim the interface
try:
- self.devh.claimInterface(self.interface)
+ self.devh.claimInterface(self.usb_interface)
except usb.USBError, e:
self.closePort()
- logcrt("Unable to claim USB interface: %s" % e)
+ logcrt("Unable to claim USB interface %s: %s" %
+ (self.usb_interface, e))
raise weewx.WeeWxIOError(e)
def closePort(self):
@@ -712,10 +702,7 @@ class FineOffsetUSB(weewx.abstractstation.AbstractStation):
self.devh.releaseInterface()
except:
pass
- try:
- self.devh.detachKernelDriver(self.interface)
- except:
- pass
+ self.devh = None
def _find_device(self):
"""Find the vendor and product ID on the USB."""
diff --git a/bin/weewx/drivers/te923.py b/bin/weewx/drivers/te923.py
index 6e69f7b8..4081d336 100644
--- a/bin/weewx/drivers/te923.py
+++ b/bin/weewx/drivers/te923.py
@@ -161,7 +161,7 @@ import weewx.abstractstation
import weewx.units
import weewx.wxformulas
-DRIVER_VERSION = '0.8'
+DRIVER_VERSION = '0.9'
DEBUG_READ = 0
DEBUG_DECODE = 0
DEBUG_PRESSURE = 0
@@ -678,13 +678,11 @@ class Station(object):
READ_LENGTH = 0x8
def __init__(self, vendor_id=0x1130, product_id=0x6801,
- dev_id=None, ifc=None, cfg=None, memory_size='small'):
+ dev_id=None, memory_size='small'):
self.vendor_id = vendor_id
self.product_id = product_id
self.device_id = dev_id
- self.interface = ifc
- self.configuration = cfg
- self.handle = None
+ self.devh = None
self.elevation = None
self.latitude = None
@@ -708,62 +706,56 @@ class Station(object):
return dev
return None
- def open(self):
+ def open(self, interface=0):
dev = self._find(self.vendor_id, self.product_id, self.device_id)
if not dev:
logcrt("Cannot find USB device with VendorID=0x%04x ProductID=0x%04x DeviceID=%s" % (self.vendor_id, self.product_id, self.device_id))
- raise weewx.WeeWxIOError("Unable to find station on USB")
+ raise weewx.WeeWxIOError('Unable to find station on USB')
- if self.configuration is None:
- self.configuration = dev.configurations[0]
- if self.interface is None:
- self.interface = self.configuration.interfaces[0][0]
- self.handle = dev.open()
+ self.devh = dev.open()
+ if not self.devh:
+ raise weewx.WeeWxIOError('Open USB device failed')
+ # be sure kernel does not claim the interface
try:
- self.handle.detachKernelDriver(self.interface)
- except Exception:
- pass
+ self.devh.detachKernelDriver(interface)
+ except Exception, e:
+ loginf('Detach kernel driver failed: %s' % e)
+ # attempt to claim the interface
try:
- self.handle.setConfiguration(self.configuration)
- self.handle.claimInterface(self.interface)
- self.handle.setAltInterface(self.interface)
+ self.devh.claimInterface(interface)
+ self.devh.setAltInterface(interface)
except usb.USBError, e:
self.close()
- logcrt("Unable to claim USB interface %s of configuration %s: %s" %
- (self.interface, self.configuration, e))
+ logcrt("Unable to claim USB interface %s: %s" % (interface, e))
raise weewx.WeeWxIOError(e)
def close(self):
try:
- self.handle.releaseInterface()
+ self.devh.releaseInterface()
except Exception:
pass
- try:
- self.handle.detachKernelDriver(iface)
- except Exception:
- pass
- self.handle = None
+ self.devh = None
# The te923tool does reads in chunks with pause between. According to
# the wview implementation, after sending the read command the device will
# send back 32 bytes of data within 100 ms. If not, the command was not
# properly received.
#
- # FIXME: must be a better way to know when there is no more data
+ # FIXME: must be a better way to know when there are no more data
def _raw_read(self, addr, timeout=50):
reqbuf = [0x05, 0xAF, 0x00, 0x00, 0x00, 0x00, 0xAF, 0xFE]
reqbuf[4] = addr / 0x10000
reqbuf[3] = (addr - (reqbuf[4] * 0x10000)) / 0x100
reqbuf[2] = addr - (reqbuf[4] * 0x10000) - (reqbuf[3] * 0x100)
reqbuf[5] = (reqbuf[1] ^ reqbuf[2] ^ reqbuf[3] ^ reqbuf[4])
- ret = self.handle.controlMsg(requestType=0x21,
- request=usb.REQ_SET_CONFIGURATION,
- value=0x0200,
- index=0x0000,
- buffer=reqbuf,
- timeout=timeout)
+ ret = self.devh.controlMsg(requestType=0x21,
+ request=usb.REQ_SET_CONFIGURATION,
+ value=0x0200,
+ index=0x0000,
+ buffer=reqbuf,
+ timeout=timeout)
if ret != 8:
raise BadRead('Unexpected response to data request: %s != 8' % ret)
@@ -772,8 +764,8 @@ class Station(object):
rbuf = []
try:
while time.time() - start_ts < 5:
- buf = self.handle.interruptRead(self.ENDPOINT_IN,
- self.READ_LENGTH, timeout)
+ buf = self.devh.interruptRead(self.ENDPOINT_IN,
+ self.READ_LENGTH, timeout)
if buf:
nbytes = buf[0]
if nbytes > 7 or nbytes > len(buf)-1:
diff --git a/bin/weewx/drivers/ws28xx.py b/bin/weewx/drivers/ws28xx.py
index c93d772e..a8d87b1e 100644
--- a/bin/weewx/drivers/ws28xx.py
+++ b/bin/weewx/drivers/ws28xx.py
@@ -873,7 +873,7 @@ import weewx.units
import weewx.wxengine
import weewx.wxformulas
-DRIVER_VERSION = '0.25'
+DRIVER_VERSION = '0.26'
# flags for enabling/disabling debug verbosity
DEBUG_WRITES = 0
@@ -982,6 +982,12 @@ class WS28xx(weewx.abstractstation.AbstractStation):
each will have a unique device identifier. Use this identifier
to indicate which device should be used.
[Optional. Default is None]
+
+ serial: The transceiver serial number. If there are multiple
+ devices with the same vendor and product IDs on the bus, each will
+ have a unique serial number. Use the serial number to indicate which
+ transceiver should be used.
+ [Optional. Default is None]
"""
self.altitude = stn_dict['altitude']
@@ -993,6 +999,7 @@ class WS28xx(weewx.abstractstation.AbstractStation):
self.vendor_id = int(stn_dict.get('vendor_id', '0x6666'), 0)
self.product_id = int(stn_dict.get('product_id', '0x5555'), 0)
self.device_id = stn_dict.get('device_id', None)
+ self.serial = stn_dict.get('serial', None)
self.pressure_offset = stn_dict.get('pressure_offset', None)
if self.pressure_offset is not None:
self.pressure_offset = float(self.pressure_offset)
@@ -1086,7 +1093,7 @@ class WS28xx(weewx.abstractstation.AbstractStation):
self._service = CCommunicationService(self.cache_file)
self._service.setup(self.frequency,
self.vendor_id, self.product_id, self.device_id,
- comm_interval=self.comm_interval)
+ self.serial, comm_interval=self.comm_interval)
self._service.startRFThread()
def shutDown(self):
@@ -1199,9 +1206,6 @@ class WS28xx(weewx.abstractstation.AbstractStation):
def get_transceiver_id(self):
return self._service.DataStore.getDeviceID()
- def get_battery(status, hardware):
- return 0
-
# The following classes and methods are adapted from the implementation by
# eddie de pieri, which is in turn based on the HeavyWeather implementation.
@@ -2794,42 +2798,72 @@ class sHID(object):
self.timeout = 1000
self.last_dump = None
- def open(self, vid, pid, did):
- device = self._find_device(vid, pid, did)
+ def open(self, vid, pid, did, serial):
+ device = self._find_device(vid, pid, did, serial)
if device is None:
- logcrt('Cannot find USB device with Vendor=0x%04x ProdID=0x%04x Device=%s' % (vid, pid, did))
- raise weewx.WeeWxIOError('Unable to find USB device')
+ logcrt('Cannot find USB device with Vendor=0x%04x ProdID=0x%04x Device=%s Serial=%s' % (vid, pid, did, serial))
+ raise weewx.WeeWxIOError('Unable to find transceiver on USB')
self._open_device(device)
def close(self):
self._close_device()
- def _find_device(self, vid, pid, did):
+ def _find_device(self, vid, pid, did, serial):
for bus in usb.busses():
for dev in bus.devices:
if dev.idVendor == vid and dev.idProduct == pid:
if did is None or dev.filename == did:
- loginf('found transceiver on USB bus=%s device=%s' % (bus.dirname, dev.filename))
- return dev
+ if serial is None:
+ loginf('found transceiver at bus=%s device=%s' %
+ (bus.dirname, dev.filename))
+ return dev
+ else:
+ handle = dev.open()
+ try:
+ buf = shid.readCfg(handle, 0x1F9, 7)
+ sn = str("%02d"%(buf[0]))
+ sn += str("%02d"%(buf[1]))
+ sn += str("%02d"%(buf[2]))
+ sn += str("%02d"%(buf[3]))
+ sn += str("%02d"%(buf[4]))
+ sn += str("%02d"%(buf[5]))
+ sn += str("%02d"%(buf[6]))
+ if str(serial) == sn:
+ loginf('found transceiver at bus=%s device=%s serial=%s' % (bus.dirname, dev.filename, sn))
+ return dev
+ else:
+ loginf('skipping transceiver with serial %s (looking for %s)' % (sn, serial))
+ finally:
+ del handle
return None
- def _open_device(self, device, interface=0, configuration=1):
- self._device = device
- self._configuration = device.configurations[0]
- self._interface = self._configuration.interfaces[0][0]
- self._endpoint = self._interface.endpoints[0]
- self.devh = device.open()
- loginf('manufacturer: %s' % self.devh.getString(device.iManufacturer,30))
- loginf('product: %s' % self.devh.getString(device.iProduct,30))
- loginf('interface: %d' % self._interface.interfaceNumber)
+ def _open_device(self, dev, interface=0):
+ self.devh = dev.open()
+ if not self.devh:
+ raise weewx.WeeWxIOError('Open USB device failed')
- # detach any old claimed interfaces
+ loginf('manufacturer: %s' % self.devh.getString(dev.iManufacturer,30))
+ loginf('product: %s' % self.devh.getString(dev.iProduct,30))
+ loginf('interface: %d' % interface)
+
+ # be sure kernel does not claim the interface
try:
- self.devh.detachKernelDriver(self._interface.interfaceNumber)
- except Exception:
- pass
+ self.devh.detachKernelDriver(interface)
+ except Exception, e:
+ loginf('Detach kernel driver failed: %s' % e)
+
+ # attempt to claim the interface
+ try:
+ logdbg('claiming USB interface %d' % interface)
+ self.devh.claimInterface(interface)
+ self.devh.setAltInterface(interface)
+ except usb.USBError, e:
+ self._close_device()
+ logcrt('Unable to claim USB interface %s: %s' % (interface, e))
+ raise weewx.WeeWxIOError(e)
# FIXME: this seems to be specific to ws28xx?
+ # FIXME: check return values
usbWait = 0.05
self.devh.getDescriptor(0x1, 0, 0x12)
time.sleep(usbWait)
@@ -2837,39 +2871,19 @@ class sHID(object):
time.sleep(usbWait)
self.devh.getDescriptor(0x2, 0, 0x22)
time.sleep(usbWait)
-
- # attempt to claim the interface
- try:
- if platform.system() is 'Windows':
- loginf('set USB device configuration to %d' % configuration)
- self.devh.setConfiguration(configuration)
- logdbg('claiming USB interface %d' % interface)
- self.devh.claimInterface(interface)
- self.devh.setAltInterface(interface)
- except usb.USBError, e:
- self._close_device()
- raise weewx.WeeWxIOError(e)
-
- # FIXME: this seems to be specific to ws28xx?
- # FIXME: check return value
- self.devh.controlMsg(
- usb.TYPE_CLASS + usb.RECIP_INTERFACE,
- 0x000000a, [], 0x0000000, 0x0000000, 1000)
- time.sleep(0.05)
+ self.devh.controlMsg(usb.TYPE_CLASS + usb.RECIP_INTERFACE,
+ 0xa, [], 0x0, 0x0, 1000)
+ time.sleep(usbWait)
self.devh.getDescriptor(0x22, 0, 0x2a9)
time.sleep(usbWait)
def _close_device(self):
try:
- logdbg('release USB interface')
+ logdbg('releasing USB interface')
self.devh.releaseInterface()
except Exception:
pass
- try:
- logdbg('detach kernel driver')
- self.devh.detachKernelDriver(self._interface.interfaceNumber)
- except Exception:
- pass
+ self.devh = None
def setTX(self):
buf = [0]*0x15
@@ -3074,6 +3088,38 @@ class sHID(object):
logdbg('%s: %s%s' % (cmd, pad, strbuf))
self.last_dump = None
+ def readCfg(self, handle, addr, numBytes):
+ while numBytes:
+ buf=[0xcc]*0x0f #0x15
+ buf[0] = 0xdd
+ buf[1] = 0x0a
+ buf[2] = (addr >>8) & 0xFF
+ buf[3] = (addr >>0) & 0xFF
+ handle.controlMsg(usb.TYPE_CLASS + usb.RECIP_INTERFACE,
+ request=0x0000009,
+ buffer=buf,
+ value=0x00003dd,
+ index=0x0000000,
+ timeout=1000)
+ buf = handle.controlMsg(requestType=usb.TYPE_CLASS |
+ usb.RECIP_INTERFACE | usb.ENDPOINT_IN,
+ request=usb.REQ_CLEAR_FEATURE,
+ buffer=0x15,
+ value=0x00003dc,
+ index=0x0000000,
+ timeout=1000)
+ new_data=[0]*0x15
+ if numBytes < 16:
+ for i in xrange(0, numBytes):
+ new_data[i] = buf[i+4]
+ numBytes = 0
+ else:
+ for i in xrange(0, 16):
+ new_data[i] = buf[i+4]
+ numBytes -= 16
+ addr += 16
+ return new_data
+
class CCommunicationService(object):
reg_names = dict()
@@ -3326,7 +3372,7 @@ class CCommunicationService(object):
changed = self.DataStore.StationConfig.testConfigChanged(cfgBuffer)
inBufCS = self.DataStore.StationConfig.getInBufCS()
if inBufCS == 0 or inBufCS != cs:
- logdbg('handleCurrentData: inBufCS of station not actual')
+ logdbg('handleCurrentData: inBufCS of station does not match')
self.DataStore.setRequestType(ERequestType.rtGetConfig)
self.setSleep(0.400,0.400)
newLength[0] = self.buildACKFrame(newBuffer, EAction.aGetConfig, cs, idx)
@@ -3610,18 +3656,11 @@ class CCommunicationService(object):
for r in self.reg_names:
self.shid.writeReg(r, self.reg_names[r])
- self.shid.execute(5)
- self.shid.setPreamblePattern(0xaa)
- self.shid.setState(0)
- time.sleep(1)
- self.shid.setRX()
-
def setup(self, frequency_standard,
- vendor_id, product_id, device_id,
+ vendor_id, product_id, device_id, serial,
comm_interval=3):
self.DataStore.setCommModeInterval(comm_interval)
- self.firstSleep, self.nextSleep = self.getSleep(comm_interval)
- self.shid.open(vendor_id, product_id, device_id)
+ self.shid.open(vendor_id, product_id, device_id, serial)
self.initTransceiver(frequency_standard)
self.DataStore.setTransceiverPresent(True)
@@ -3676,7 +3715,7 @@ class CCommunicationService(object):
def doRF(self):
try:
logdbg('setting up rf communication')
- self.doRFStartup()
+ self.doRFSetup()
logdbg('starting rf communication')
while self.running:
self.doRFCommunication()
@@ -3689,7 +3728,17 @@ class CCommunicationService(object):
finally:
logdbg('stopping rf communication')
- def doRFStartup(self):
+ # it is probably not necessary to have two setPreamblePattern invocations.
+ # however, HeavyWeatherPro seems to do it this way on a first time config.
+ # doing it this way makes configuration easier during a factory reset and
+ # when re-establishing communication with the station sensors.
+ def doRFSetup(self):
+ self.shid.execute(5)
+ self.shid.setPreamblePattern(0xaa)
+ self.shid.setState(0)
+ time.sleep(1)
+ self.shid.setRX()
+
self.shid.setPreamblePattern(0xaa)
self.shid.setState(0x1e)
time.sleep(1)
@@ -3729,15 +3778,6 @@ class CCommunicationService(object):
self.firstSleep = firstsleep
self.nextSleep = nextsleep
- # FIXME: are these values needed now?
- # comm_interval first next
- # 3 3.980 0.020
- # 5 5.980 0.020
- def getSleep(self, comInt):
- if comInt == 3:
- return 3.980,0.020
- return 5.980, 0.020
-
def timing(self):
s = self.firstSleep + self.nextSleep * (self.pollCount - 1)
return 'sleep=%s first=%s next=%s count=%s' % (
diff --git a/docs/changes.txt b/docs/changes.txt
index 22ec64d5..003e8374 100644
--- a/docs/changes.txt
+++ b/docs/changes.txt
@@ -21,6 +21,8 @@ Provide explicit pairing feedback using wee_config_ws28xx
Count wxengine restarts in logwatch.
+Cleaned up USB initialization for fousb, ws28xx, and te923 drivers.
+
2.6.2 02/16/14
diff --git a/docs/usersguide.htm b/docs/usersguide.htm
index b3556a4d..1e00084b 100644
--- a/docs/usersguide.htm
+++ b/docs/usersguide.htm
@@ -2274,20 +2274,20 @@ Configuration utility for WS-28xx weather stations.
Options:
-h, --help show this help message and exit
--config=FILE configuration file
- --check-transceiver check USB transceiver
+ --check-transceiver check USB transceiver
--pair pair the USB transceiver with a station console
- --info display weather station configuration
- --current get the current weather conditions
- --history-since=N display history records since N minutes ago
- --history=N display N history records
+ --info display weather station configuration
+ --current get the current weather conditions
+ --history-since=N display history records since N minutes ago
+ --history=N display N history records
--format=FORMAT format for history, one of raw, table, or dict
--maxtries=MAXTRIES maximum number of retries, 0 indicates no max
--debug display diagnostic information while running
Mutating actions will request confirmation before proceeding.
- The WS28xx driver is still in active development; only the
+
The WS28xx driver is still in active development; the
highlighted options
- are working.
+ are not yet working.
Pairing
The console and transceiver must be paired. Pairing ensures that your
@@ -2297,16 +2297,17 @@ Mutating actions will request confirmation before proceeding.
batteries.
There are two ways to pair the console and the transceiver:
- - The Weewx way. With weewx running, press and hold
- the [v] button on the console until you see 'PC' in the display. If
- the console pairs with the transceiver, 'PC' will go away within a
- second or two.
+ - The Weewx way. With weewx
+ running, press and hold the [v] button on the console until you see
+ 'PC' in the display. If the console pairs with the transceiver, 'PC'
+ will go away within a second or two.
- The HeavyWeather way. Follow the pairing
instructions that came with the station. You will have to run
HeavyWeather on a windows computer with the USB transceiver. After
HeavyWeather indicates the devices are paired, put the USB transceiver
- in your weewx computer and start weewx. Do not power cycle the
- station console or you will have to start over.
+ in your weewx computer and start weewx.
+ Do not power cycle the station console or you will have to start
+ over.
If the console does not pair, you will see messages in the log such as