I am new to weewx and know enough about python to be dangerous. My
weather station setup is totally custom with an arduino reading off the
shelf sensors and feeding it via serial every 2.5 seconds to my Ubuntu
14.04 PC. So I am working on a custom driver using the ws1 and simulator
as examples. I believe my issue is that when the driver goes to read the
serial line with readline(), it is reading a null string. In other words,
its not waiting on the data to arrive -not applying the timeout. I have
this highlighted in the log file which shows the null string. This causes
the program to error out when it attempts to parse the null string.
With a standalone python script reading and parsing the same serial input,
it works fine. But doesnt when applied to the driver. I overlooking
something basic I am sure. But at the moment I am lost in the weeds. If
anyone could point me in the right direction I would greatly appreciate it.
"""Driver for Arduino weather stations.
"""
from __future__ import with_statement
import syslog
import time
import weewx.drivers
DRIVER_NAME = 'arduino'
DRIVER_VERSION = '0.01'
def loader(config_dict, _):
return arduinoDriver(**config_dict[DRIVER_NAME])
def confeditor_loader():
return arduinoConfEditor()
DEFAULT_SER_PORT = '/dev/ttyUSB0'
PACKET_SIZE = 90
DEBUG_READ = 2
def logmsg(level, msg):
syslog.syslog(level, 'arduino: %s' % msg)
def logdbg(msg):
logmsg(syslog.LOG_DEBUG, msg)
def loginf(msg):
logmsg(syslog.LOG_INFO, msg)
def logerr(msg):
logmsg(syslog.LOG_ERR, msg)
class arduinoDriver(weewx.drivers.AbstractDevice):
"""weewx driver that communicates with an arduino weather station
mode - Communication mode - Serial.
[Required. Default is serial]
port - Serial port.
[Required. Default is /dev/ttyUSB0 for serial]
max_tries - how often to retry serial communication before giving up.
[Optional. Default is 5]
wait_before_retry - how long to wait, in seconds, before retrying after
a failure.
[Optional. Default is 10]
timeout - The amount of time, in seconds, before the connection fails if
there is no response.
[Optional. Default is 4]
debug_read - The level of message logging. The higher this number, the
more
information is logged.
[Optional. Default is 0]
"""
def __init__(self, **stn_dict):
loginf('driver version is %s' % DRIVER_VERSION)
con_mode = stn_dict.get('mode', 'serial').lower()
if con_mode == 'serial':
port = stn_dict.get('port', DEFAULT_SER_PORT)
else:
raise ValueError("Invalid driver connection mode %s" % con_mode)
self.max_tries = int(stn_dict.get('max_tries', 5))
self.wait_before_retry = float(stn_dict.get('wait_before_retry',
10))
timeout = int(stn_dict.get('timeout', 4))
self.last_rain = None
loginf('using %s port %s' % (con_mode, port))
global DEBUG_READ
DEBUG_READ = int(stn_dict.get('debug_read', DEBUG_READ))
self.station = StationSerial(port, timeout=timeout)
self.station.open()
def closePort(self):
if self.station is not None:
self.station.close()
self.station = None
@property
def hardware_name(self):
return "arduino"
def genLoopPackets(self):
while True:
packet = {'dateTime': int(time.time() + 0.5),
'usUnits': weewx.US}
readings = self.station.get_readings_with_retry(self.max_tries,
self.wait_before_retry)
data = StationData.parse_readings(readings)
packet.update(data)
self._augment_packet(packet)
yield packet
def _augment_packet(self, packet):
# calculate the rain delta from rain total
if self.last_rain is not None:
packet['rain'] = packet['long_term_rain'] - self.last_rain
else:
packet['rain'] = None
self.last_rain = packet['long_term_rain']
#
===========================================================================
#
# Station data class - parses and validates data from the device
#
#
===========================================================================
#
class StationData(object):
def __init__(self):
pass
@staticmethod
def validate_string(buf):
if len(buf) >= PACKET_SIZE:
raise weewx.WeeWxIOError("Unexpected buffer length %d" %
len(buf))
return buf
@staticmethod
def parse_readings(raw):
data={}
for e in raw.split(','):
k, v=e.split('=')
data[k]=float(v)
return data
#
===========================================================================
#
# Station Serial class - Gets data through a serial port
#
#
===========================================================================
#
class StationSerial(object):
def __init__(self, port, timeout=4):
self.port = port
self.baudrate = 9600
self.timeout = timeout
self.serial_port = None
def __enter__(self):
self.open()
return self
def __exit__(self, _, value, traceback): # @UnusedVariable
self.close()
def open(self):
import serial
logdbg("open serial port %s" % self.port)
self.serial_port = serial.Serial(self.port, self.baudrate,
timeout=self.timeout)
def close(self):
if self.serial_port is not None:
logdbg("close serial port %s" % self.port)
self.serial_port.close()
self.serial_port = None
def get_readings(self):
while True:
buf = self.serial_port.readline()
break
if DEBUG_READ >= 2:
logdbg("bytes: '%s'" % ' '.join(["%0.2X" % ord(c) for c in
buf]))
#buf = buf.strip()
return buf
def get_readings_with_retry(self, max_tries=5, wait_before_retry=10):
import serial
for ntries in range(0, max_tries):
try:
buf = self.get_readings()
StationData.validate_string(buf)
return buf
except (serial.serialutil.SerialException, weewx.WeeWxIOError),
e:
loginf("Failed attempt %d of %d to get readings: %s" %
(ntries + 1, max_tries, e))
time.sleep(wait_before_retry)
else:
msg = "Max retries (%d) exceeded for readings" % max_tries
logerr(msg)
raise weewx.RetriesExceeded(msg)
class arduinoConfEditor(weewx.drivers.AbstractConfEditor):
@property
def default_stanza(self):
return """
[arduino]
# This section is for the ADS WS1 series of weather stations.
# Driver mode - tcp, udp, or serial
mode = serial
# If serial, specify the serial port device. (ex. /dev/ttyS0,
/dev/ttyUSB0,
# or /dev/cuaU0)
# If TCP, specify the IP address and port number. (ex.
192.168.36.25:3000)
port = /dev/ttyUSB0
# The amount of time, in seconds, before the connection fails if there
is
# no response
timeout = 3
# The driver to use:
driver = weewx.drivers.arduino
"""
def prompt_for_settings(self):
print "How is the station connected? tcp, udp, or serial."
con_mode = self._prompt('mode', 'serial')
con_mode = con_mode.lower()
if con_mode == 'serial':
print "Specify the serial port on which the station is
connected, "
"for example: /dev/ttyUSB0 or /dev/ttyS0."
port = self._prompt('port', '/dev/ttyUSB0')
elif con_mode == 'tcp' or con_mode == 'udp':
print "Specify the IP address and port of the station. For "
"example: 192.168.36.40:3000."
port = self._prompt('port', '192.168.36.40:3000')
print "Specify how long to wait for a response, in seconds."
timeout = self._prompt('timeout', 4)
return {'mode': con_mode, 'port': port, 'timeout': timeout}
Log file:
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Initializing weewx version
3.6.2
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Using Python 2.7.6 (default,
Oct 26 2016, 20:30:19) #012[GCC 4.8.4]
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Platform
Linux-3.13.0-83-generic-x86_64-with-Ubuntu-14.04-trusty
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Using configuration file
/etc/weewx/weewx.conf
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Initializing engine
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Loading station type arduino
(weewx.arduino)
Feb 4 10:16:15 Aragorn weewx[23197]: arduino: driver version is 0.01
Feb 4 10:16:15 Aragorn weewx[23197]: arduino: using serial port
/dev/ttyUSB0
Feb 4 10:16:15 Aragorn weewx[23197]: arduino: open serial port /dev/ttyUSB0
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Loading service
weewx.engine.StdTimeSynch
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Finished loading service
weewx.engine.StdTimeSynch
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Loading service
weewx.engine.StdConvert
Feb 4 10:16:15 Aragorn weewx[23197]: engine: StdConvert target unit is 0x1
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Finished loading service
weewx.engine.StdConvert
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Loading service
weewx.engine.StdCalibrate
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Finished loading service
weewx.engine.StdCalibrate
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Loading service
weewx.engine.StdQC
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Finished loading service
weewx.engine.StdQC
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Loading service
weewx.wxservices.StdWXCalculate
Feb 4 10:16:15 Aragorn weewx[23197]: wxcalculate: The following values
will be calculated: barometer=prefer_hardware, windchill=software,
dewpoint=software, appTemp=prefer_hardware, rainRate=software,
windrun=prefer_hardware, heatindex=software, maxSolarRad=prefer_hardware,
humidex=prefer_hardware, pressure=prefer_hardware,
inDewpoint=prefer_hardware, ET=prefer_hardware, altimeter=prefer_hardware,
cloudbase=prefer_hardware
Feb 4 10:16:15 Aragorn weewx[23197]: wxcalculate: The following algorithms
will be used for calculations: altimeter=aaNOAA, maxSolarRad=RS
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Finished loading service
weewx.wxservices.StdWXCalculate
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Loading service
weewx.engine.StdArchive
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Archive will use data binding
wx_binding
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Record generation will be
attempted in 'hardware'
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Using archive interval of 300
seconds (specified in weewx configuration)
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Use LOOP data in hi/low
calculations: 1
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Using binding 'wx_binding' to
database 'weewx.sdb'
Feb 4 10:16:15 Aragorn weewx[23197]: manager: Starting backfill of daily
summaries
Feb 4 10:16:15 Aragorn weewx[23197]: manager: Daily summaries up to date
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Finished loading service
weewx.engine.StdArchive
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Loading service
weewx.restx.StdStationRegistry
Feb 4 10:16:15 Aragorn weewx[23197]: restx: StationRegistry: Registration
not requested.
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Finished loading service
weewx.restx.StdStationRegistry
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Loading service
weewx.restx.StdWunderground
Feb 4 10:16:15 Aragorn weewx[23197]: restx: Wunderground: Posting not
enabled.
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Finished loading service
weewx.restx.StdWunderground
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Loading service
weewx.restx.StdPWSweather
Feb 4 10:16:15 Aragorn weewx[23197]: restx: PWSweather: Posting not
enabled.
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Finished loading service
weewx.restx.StdPWSweather
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Loading service
weewx.restx.StdCWOP
Feb 4 10:16:15 Aragorn weewx[23197]: restx: CWOP: Posting not enabled.
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Finished loading service
weewx.restx.StdCWOP
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Loading service
weewx.restx.StdWOW
Feb 4 10:16:15 Aragorn weewx[23197]: restx: WOW: Posting not enabled.
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Finished loading service
weewx.restx.StdWOW
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Loading service
weewx.restx.StdAWEKAS
Feb 4 10:16:15 Aragorn weewx[23197]: restx: AWEKAS: Posting not enabled.
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Finished loading service
weewx.restx.StdAWEKAS
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Loading service
weewx.engine.StdPrint
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Finished loading service
weewx.engine.StdPrint
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Loading service
weewx.engine.StdReport
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Finished loading service
weewx.engine.StdReport
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Starting up weewx version
3.6.2
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Station does not support
reading the time
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Starting main packet loop.
Feb 4 10:16:15 Aragorn weewx[23197]: arduino: bytes: '00 0A'
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Main loop exiting. Shutting
engine down.
Feb 4 10:16:15 Aragorn weewx[23197]: arduino: close serial port
/dev/ttyUSB0
Feb 4 10:16:15 Aragorn weewx[23197]: engine: Caught unrecoverable
exception in engine:
Feb 4 10:16:15 Aragorn weewx[23197]: **** need more than 1 value to
unpack
Feb 4 10:16:15 Aragorn weewx[23197]: **** Traceback (most recent call
last):
Feb 4 10:16:15 Aragorn weewx[23197]: **** File
"/usr/share/weewx/weewx/engine.py", line 847, in main
Feb 4 10:16:15 Aragorn weewx[23197]: **** engine.run()
Feb 4 10:16:15 Aragorn weewx[23197]: **** File
"/usr/share/weewx/weewx/engine.py", line 186, in run
Feb 4 10:16:15 Aragorn weewx[23197]: **** for packet in
self.console.genLoopPackets():
Feb 4 10:16:15 Aragorn weewx[23197]: **** File
"/usr/share/weewx/weewx/arduino.py", line 107, in genLoopPackets
Feb 4 10:16:15 Aragorn weewx[23197]: **** data =
StationData.parse_readings(readings)
Feb 4 10:16:15 Aragorn weewx[23197]: **** File
"/usr/share/weewx/weewx/arduino.py", line 143, in parse_readings
Feb 4 10:16:15 Aragorn weewx[23197]: **** k, v=e.split('=')
Feb 4 10:16:15 Aragorn weewx[23197]: **** ValueError: need more than
1 value to unpack
Feb 4 10:16:15 Aragorn weewx[23197]: **** Exiting.
--
You received this message because you are subscribed to the Google Groups
"weewx-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.