clean up usb initialization and shutdown for fousb, ws28xx, and te923. remove more dead code from ws28xx.

This commit is contained in:
Matthew Wall
2014-03-13 16:54:21 +00:00
parent 5523cbbf59
commit 33655a7f67
5 changed files with 176 additions and 154 deletions

View File

@@ -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."""

View File

@@ -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:

View File

@@ -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' % (

View File

@@ -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

View File

@@ -2274,20 +2274,20 @@ Configuration utility for WS-28xx weather stations.
Options:
-h, --help show this help message and exit
--config=FILE configuration file
<span class='highlight'> --check-transceiver</span> check USB transceiver
--check-transceiver check USB transceiver
--pair pair the USB transceiver with a station console
<span class='highlight'> --info</span> display weather station configuration
<span class='highlight'> --current</span> 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
<span class='highlight'> --history-since=N display history records since N minutes ago</span>
<span class='highlight'> --history=N display N history records</span>
--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.</pre>
<p>The WS28xx driver is still in active development; only the
<p>The WS28xx driver is still in active development; the
<span class='highlight'>&nbsp;highlighted&nbsp;</span> options
are working.</p>
are not yet working.</p>
<h3>Pairing</h3>
<p>The console and transceiver must be paired. Pairing ensures that your
@@ -2297,16 +2297,17 @@ Mutating actions will request confirmation before proceeding.</pre>
batteries.</p>
<p>There are two ways to pair the console and the transceiver:</p>
<ol>
<li><strong>The Weewx way.</strong> With <span class="code">weewx</span> 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.</li>
<li><strong>The Weewx way.</strong> With <span class="code">weewx</span>
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.</li>
<li><strong>The HeavyWeather way.</strong> 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 <span class="code">weewx</span> computer and start weewx. Do not power cycle the
station console or you will have to start over.</li>
in your <span class="code">weewx</span> computer and start weewx.
Do not power cycle the station console or you will have to start
over.</li>
</ol>
<p>If the console does not pair, you will see messages in the log such as