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.

Reply via email to