Added Deborah Pickett's additions, which allow changing and monitoring extra channels on the Vantage. Also, allows setting calibration values in the console.

This commit is contained in:
Tom Keffer
2014-09-25 14:35:48 +00:00
parent 6f879839a0
commit df09ddddf9
2 changed files with 358 additions and 3 deletions

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/python
#
# Copyright (c) 2012 Tom Keffer <tkeffer@gmail.com>
#
@@ -13,7 +13,6 @@
import optparse
import sys
import syslog
import time
import weewx.drivers.vantage
import weeutil.weeutil
@@ -23,6 +22,8 @@ description = """Configures the Davis Vantage weather station."""
usage="""%prog: [config_file] [--help] [--info] [--clear]
[--set-interval=SECONDS] [--set-altitude=FEET] [--set-barometer=inHg]
[--set-bucket=CODE] [--set-rain-year-start=MM]
[--set-calibration=VARIABLE,OFFSET]
[--set-transmitter-type=CHANNEL,TYPE,TEMP,HUM]
[--set-time] [--set-dst=[AUTO|ON|OFF]]
[--set-tz-code=TZCODE] [--set-tz-offset=HHMM]
[--set-lamp=[ON|OFF]] [--dump] [--logger_summary=FILE] [--start | --stop]"""
@@ -57,6 +58,15 @@ def main():
"Specify '0' for 0.01 inches; '1' for 0.2 MM; '2' for 0.1 MM")
parser.add_option("--set-rain-year-start", type=int, dest="set_rain_year_start", metavar="MM",
help="Set the rain year start (1=Jan, 2=Feb, etc.).")
parser.add_option("--set-calibration", type=str, dest="set_calibration", metavar="VARIABLE,OFFSET",
help="Set the onboard calibration for VARIABLE "\
"(inTemp, outTemp, extraTemp<1-7>, inHumid, outHumid, extraHumid<1-7>, "\
"soilTemp<1-4>, leafTemp<1-4>, windDir) to OFFSET (Fahrenheit, %, degrees)")
parser.add_option("--set-transmitter-type", type=str, dest="set_transmitter_type", metavar="CHANNEL,TYPE,TEMP,HUM",
help="Set the transmitter type for CHANNEL (1-8), "\
"TYPE (0=iss, 1=temp, 2=hum, 3=temp_hum, 4=wind, "\
"5=rain, 6=leaf, 7=soil, 8=leaf_soil, 9=sensorlink, 10=none), "\
"as extra TEMP station and extra HUM station (both 1-7, if applicable)")
parser.add_option("--set-time", action="store_true", dest="set_time", help="Set the onboard clock to the current time.")
parser.add_option("--set-dst", dest="set_dst", help="Set DST to 'ON', 'OFF', or 'AUTO'", metavar="AUTO|ON|OFF")
parser.add_option("--set-tz-code", type=int, dest="set_tz_code",
@@ -98,6 +108,10 @@ def main():
set_bucket(station, options.set_bucket)
if options.set_rain_year_start is not None:
set_rain_year_start(station, options.set_rain_year_start)
if options.set_calibration is not None:
set_calibration(station, options.set_calibration)
if options.set_transmitter_type is not None:
set_transmitter_type(station, options.set_transmitter_type)
if options.set_time:
set_time(station)
if options.set_dst:
@@ -181,6 +195,23 @@ def info(station, dest=sys.stdout):
except:
pass
# Add transmitter types for each channel, if we can:
transmitter_list = None
try:
transmitter_list = station.getStnTransmitters()
print >>dest, " TRANSMITTERS: "
for transmitter_id in range(0, 8):
transmitter_type = transmitter_list[transmitter_id]["transmitter_type"]
print >>dest, " Channel %d: %s %s" % (
transmitter_id + 1, transmitter_type,
"(active)" if transmitter_list[transmitter_id]["listen"] == 1 else "(ignored)")
if transmitter_type in ['temp', 'temp_hum']:
print >>dest, " As extra temperature %d" % transmitter_list[transmitter_id]["temp"]
if transmitter_type in ['hum', 'temp_hum']:
print >>dest, " As extra humidity %d" % transmitter_list[transmitter_id]["hum"]
print >>dest, ""
except:
pass
# Add reception statistics if we can:
try:
@@ -212,6 +243,46 @@ def info(station, dest=sys.stdout):
except:
pass
# Add temperature/humidity/wind calibration if we can.
try:
calibration_dict = station.getStnCalibration()
print >> dest, """ OTHER CALIBRATION DATA:
Wind direction: %(wind)+.0f deg
Inside Temperature: %(inTemp)+.1f F
Inside Humidity: %(inHumid)+.0f%%
Outside Temperature: %(outTemp)+.1f F
Outside Humidity: %(outHumid)+.0f%%""" % calibration_dict
if transmitter_list is not None:
# Only print the calibrations for channels that we are listening to.
for extraTemp in range(1, 8):
for transmitter_id in range(0, 8):
transmitter_type = transmitter_list[transmitter_id]["transmitter_type"]
if transmitter_type in ['temp', 'temp_hum'] and \
extraTemp == transmitter_list[transmitter_id]["temp"]:
print >> dest, " Extra Temperature %d: %+.1f F" \
% (extraTemp, calibration_dict["extraTemp%d" % extraTemp])
for extraHumid in range(1, 8):
for transmitter_id in range(0, 8):
transmitter_type = transmitter_list[transmitter_id]["transmitter_type"]
if transmitter_type in ['hum', 'temp_hum'] and \
extraHumid == transmitter_list[transmitter_id]["hum"]:
print >> dest, " Extra Humidity %d: %+.1f F" \
% (extraHumid, calibration_dict["extraHumid%d" % extraHumid])
for transmitter_id in range(0, 8):
transmitter_type = transmitter_list[transmitter_id]["transmitter_type"]
if transmitter_type in ['soil', 'leaf_soil']:
for soil in range(1, 5):
print >> dest, " Soil Temperature %d: %+.1f F" \
% (soil, calibration_dict["soilTemp%d" % soil])
for transmitter_id in range(0, 8):
transmitter_type = transmitter_list[transmitter_id]["transmitter_type"]
if transmitter_type in ['leaf', 'leaf_soil']:
for leaf in range(1, 5):
print >> dest, " Leaf Temperature %d: %+.1f F" % (leaf, calibration_dict["leafTemp%d" % leaf])
print >> dest, ""
except:
raise
def set_interval(station, new_interval_seconds):
"""Set the console archive interval."""
@@ -328,6 +399,100 @@ def set_rain_year_start(station, rain_year_start):
elif ans == 'n':
print "Nothing done."
def set_calibration(station, calibration_list):
"""Set the on-board calibration for a temperature, humidity or wind direction variable."""
(variable, offset_str) = calibration_list.split(',')
# These variables may be calibrated.
temp_variables = ['inTemp', 'outTemp' ] + \
['extraTemp%d' % i for i in range(1, 8)] + \
['soilTemp%d' % i for i in range(1, 5)] + \
['leafTemp%d' % i for i in range(1, 5)]
humid_variables = ['inHumid', 'outHumid'] + \
['extraHumid%d' % i for i in range(1, 8)]
# Wind direction can also be calibrated.
if variable == "windDir":
offset = int(offset_str)
if not -359 < offset < 359:
print >>sys.stderr, "Wind direction calibration %d is out of range." % (offset)
else:
ans = None
while ans not in ['y', 'n']:
print "Proceeding will set calibration for wind direction to %+d." % (offset)
ans = raw_input("Are you sure you want to proceed (y/n)? ")
if ans == 'y' :
try:
station.setCalibrationWindDir(offset)
except StandardError, e:
print >>sys.stderr, "Unable to set new wind calibration. Reason:\n\t****", e
else:
print "Wind direction calibration now set to %+d." % (offset)
elif variable in temp_variables:
offset = float(offset_str)
if not -12.8 < offset < 12.7:
print >>sys.stderr, "Temperature calibration %+.1f is out of range." % (offset)
else:
ans = None
while ans not in ['y', 'n']:
print "Proceeding will set calibration for temperature %s to %.1f." % (variable, offset)
ans = raw_input("Are you sure you want to proceed (y/n)? ")
if ans == 'y' :
try:
station.setCalibrationTemp(variable, offset)
except StandardError, e:
print >>sys.stderr, "Unable to set new temperature calibration. Reason:\n\t****", e
else:
print "Temperature calibration %s now set to %+.1f." % (variable, offset)
elif variable in humid_variables:
offset = int(offset_str)
if not -100 < offset < 100:
print >>sys.stderr, "Humidity calibration %+d is out of range." % (offset)
else:
ans = None
while ans not in ['y', 'n']:
print "Proceeding will set calibration for humidity %s to %+d." % (variable, offset)
ans = raw_input("Are you sure you want to proceed (y/n)? ")
if ans == 'y' :
try:
station.setCalibrationHumid(variable, offset)
except StandardError, e:
print >>sys.stderr, "Unable to set new humidity calibration. Reason:\n\t****", e
else:
print "Humidity calibration %s now set to %+d." % (variable, offset)
else:
print >>sys.stderr, "Unknown variable %s" % (variable)
def set_transmitter_type(station, transmitter_list):
"""Set the transmitter type for one of the eight channels."""
transmitter_list = map((lambda x: int(x) if x != "" else None), transmitter_list.split(','))
channel = transmitter_list[0]
transmitter_type = transmitter_list[1]
extra_temp = transmitter_list[2] if len(transmitter_list) > 2 else None
extra_hum = transmitter_list[3] if len(transmitter_list) > 3 else None
transmitter_type_name = weewx.drivers.vantage.Vantage.transmitter_type_dict[transmitter_type]
if transmitter_type_name in ['temp', 'temp_hum'] and extra_temp not in range(1,8):
print "Transmitter type %s requires extra_temp in range 1-7'" % transmitter_type_name
return
if transmitter_type_name in ['hum', 'temp_hum'] and extra_hum not in range(1,8):
print "Transmitter type %s requires extra_hum in range 1-7'" % transmitter_type_name
return
ans = None
while ans not in ['y', 'n']:
print "Proceeding will set channel %d to type %d (%s)." % (channel, transmitter_type, transmitter_type_name)
ans = raw_input("Are you sure you want to proceed (y/n)? ")
if ans == 'y' :
try:
station.setTransmitterType(channel, transmitter_type, extra_temp, extra_hum)
except StandardError, e:
print >>sys.stderr, "Unable to set transmitter type. Reason:\n\t****", e
else:
print "Transmitter %d now set to type %d." % (channel, transmitter_type)
else:
print "Nothing done."
def set_time(station):
print "Setting time on console..."
station.setTime()

View File

@@ -338,6 +338,9 @@ class Vantage(weewx.abstractstation.AbstractStation):
wind_unit_dict = {0:'mile_per_hour', 1:'meter_per_second', 2:'km_per_hour', 3:'knot'}
wind_cup_dict = {0:'small', 1:'large'}
rain_bucket_dict = {0: "0.01 inches", 1: "0.2 MM", 2: "0.1 MM"}
transmitter_type_dict = {0: 'iss', 1: 'temp', 2: 'hum', 3: 'temp_hum', 4: 'wind',
5: 'rain', 6: 'leaf', 7: 'soil', 8: 'leaf_soil',
9: 'sensorlink', 10: 'none'}
def __init__(self, **vp_dict) :
"""Initialize an object of type Vantage.
@@ -371,12 +374,13 @@ class Vantage(weewx.abstractstation.AbstractStation):
"""
# TODO: These values should really be retrieved dynamically from the VP:
self.iss_id = int(vp_dict.get('iss_id', 1))
self.model_type = 2 # = 1 for original VantagePro, = 2 for VP2
# These come from the configuration dictionary:
self.wait_before_retry= float(vp_dict.get('wait_before_retry', 1.2))
self.max_tries = int(vp_dict.get('max_tries' , 4))
self.iss_id = vp_dict.get('iss_id', None)
if self.iss_id is not None: self.iss_id = int(self.iss_id)
self.save_monthRain = None
self.max_dst_jump = 7200
@@ -789,6 +793,100 @@ class Vantage(weewx.abstractstation.AbstractStation):
syslog.syslog(syslog.LOG_NOTICE, "vantage: Lamp set to '%s'" % onoff)
def setTransmitterType(self, new_channel, new_transmitter_type, new_extra_temp, new_extra_hum):
"""Set the transmitter type for one of the eight channels."""
# Default value just for tidiness.
new_temp_hum_bits = 0xFF
# Check arguments are consistent.
if new_channel not in range(0, 8):
raise weewx.ViolatedPrecondition("Invalid channel %d" % new_channel)
if new_transmitter_type not in range(0, 11):
raise weewx.ViolatedPrecondition("Invalid transmitter type %d" % new_transmitter_type)
if Vantage.transmitter_type_dict[new_transmitter_type] in ['temp', 'temp_hum']:
if new_extra_temp not in range(1, 9):
raise weewx.ViolatedPrecondition("Invalid extra temperature number %d" % new_extra_temp)
# Extra temp is origin 0.
new_temp_hum_bits = new_temp_hum_bits & 0xF0 | (new_extra_temp - 1)
if Vantage.transmitter_type_dict[new_transmitter_type] in ['hum', 'temp_hum']:
if new_extra_hum not in range(1, 9):
raise weewx.ViolatedPrecondition("Invalid extra humidity number %d" % new_extra_hum)
# Extra humidity is origin 1.
new_temp_hum_bits = new_temp_hum_bits & 0x0F | (new_extra_hum << 4)
# Preserve top nibble, is related to repeaters.
old_type_bits = self._getEEPROM_value(0x19 + (new_channel-1)*2)[0]
new_type_bits = old_type_bits & 0xF0 | new_transmitter_type
# Transmitter type 10 is "none"; turn off listening too.
usetx = 1 if new_transmitter_type != 10 else 0
old_usetx_bits = self._getEEPROM_value(0x17)[0]
new_usetx_bits = old_usetx_bits & ~(1 << (new_channel - 1)) | usetx * (1 << new_channel - 1)
# Tell the console to put two bytes in hex location 0x19 or 0x1B or ... depending on channel.
self.port.send_data("EEBWR %X 02\n" % (0x19 + (new_channel-1)*2))
# Follow it up with the data:
self.port.send_data_with_crc16(chr(new_type_bits) + chr(new_temp_hum_bits), max_tries=1)
# Tell the console to put one byte in hex location 0x17
self.port.send_data("EEBWR 17 01\n")
# Follow it up with the data:
self.port.send_data_with_crc16(chr(new_usetx_bits), max_tries=1)
# Then call NEWSETUP to get it to stick:
self.port.send_data("NEWSETUP\n")
self._setup()
syslog.syslog(syslog.LOG_NOTICE, "vantage: Transmitter type for channel %d set to %d (%s)" %(
new_channel, new_transmitter_type, Vantage.transmitter_type_dict[new_transmitter_type]))
def setCalibrationWindDir(self, offset):
"""Set the on-board wind direction calibration."""
if offset < -359 or offset > 359:
raise weewx.ViolatedPrecondition("Offset %d out of range [-359, 359]." % offset)
bytes = struct.pack("<h", offset)
# Tell the console to put two bytes in hex location 0x4D
self.port.send_data("EEBWR 4D 02\n")
# Follow it up with the data:
self.port.send_data_with_crc16(bytes, max_tries=1)
syslog.syslog(syslog.LOG_NOTICE, "vantage: Wind calibration set to %d" % (offset))
def setCalibrationTemp(self, variable, offset):
"""Set an on-board temperature calibration."""
# Offset is in tenths of degree Fahrenheit.
if offset < -12.8 or offset > 12.7:
raise weewx.ViolatedPrecondition("Offset %.1f out of range [-12.8, 12.7]." % offset)
byte = struct.pack("b", int(round(offset*10)))
variable_dict = { 'outTemp': 0x34 }
for i in range(1, 8): variable_dict['extraTemp%d' % i] = 0x34 + i
for i in range(1, 5): variable_dict['soilTemp%d' % i] = 0x3B + i
for i in range(1, 5): variable_dict['leafTemp%d' % i] = 0x3F + i
if variable == "inTemp":
# Inside temp is special, needs ones' complement in next byte.
complement_byte = struct.pack("B", ~int(round(offset*10)) & 0xFF)
self.port.send_data("EEBWR 32 02\n")
self.port.send_data_with_crc16(byte + complement_byte, max_tries=1)
elif variable in variable_dict:
# Other variables are just sent as-is.
self.port.send_data("EEBWR %X 01\n" % variable_dict[variable])
self.port.send_data_with_crc16(byte, max_tries=1)
else:
raise weewx.ViolatedPrecondition("Variable name %s not known" % variable)
syslog.syslog(syslog.LOG_NOTICE, "vantage: Temperature calibration %s set to %.1f" % (variable, offset))
def setCalibrationHumid(self, variable, offset):
"""Set an on-board humidity calibration."""
# Offset is in percentage points.
if offset < -100 or offset > 100:
raise weewx.ViolatedPrecondition("Offset %d out of range [-100, 100]." % offset)
byte = struct.pack("b", offset)
variable_dict = { 'inHumid': 0x44, 'outHumid': 0x45 }
for i in range(1, 8): variable_dict['extraHumid%d' % i] = 0x45 + i
if variable in variable_dict:
self.port.send_data("EEBWR %X 01\n" % variable_dict[variable])
self.port.send_data_with_crc16(byte, max_tries=1)
else:
raise weewx.ViolatedPrecondition("Variable name %s not known" % variable)
syslog.syslog(syslog.LOG_NOTICE, "vantage: Humidity calibration %s set to %d" % (variable, offset))
def clearLog(self):
"""Clear the internal archive memory in the Vantage."""
for unused_count in xrange(self.max_tries):
@@ -858,6 +956,75 @@ class Vantage(weewx.abstractstation.AbstractStation):
return (stnlat, stnlon, man_or_auto, dst, gmt_or_zone, zone_code, gmt_offset)
def getStnTransmitters(self):
""" Get the types of transmitters on the eight channels."""
transmitters = [ ]
use_tx = self._getEEPROM_value(0x17)[0]
transmitter_data_2 = self._getEEPROM_value(0x19, "8H")
transmitter_type_list = [Vantage.transmitter_type_dict[ushort & 0x0F] for ushort in transmitter_data_2]
listen_list = [(use_tx >> transmitter_id) & 1 for transmitter_id in range(8)]
transmitter_data = self._getEEPROM_value(0x19, "16B")
for transmitter_id in range(8):
transmitter_type = Vantage.transmitter_type_dict[transmitter_data[transmitter_id * 2] & 0x0F]
transmitter = {"transmitter_type" : transmitter_type,
"listen" : (use_tx >> transmitter_id) & 1 }
if transmitter_type in ['temp', 'temp_hum']:
# Extra temperature is origin 0.
transmitter['temp'] = (transmitter_data[transmitter_id * 2 + 1] & 0xF) + 1
if transmitter_type in ['hum', 'temp_hum']:
# Extra humidity is origin 1.
transmitter['hum'] = transmitter_data[transmitter_id * 2 + 1] >> 4
transmitters.append(transmitter)
return transmitters
def getStnCalibration(self):
""" Get the temperature/humidity/wind calibrations built into the console. """
(inTemp, inTempComp, outTemp,
extraTemp1, extraTemp2, extraTemp3, extraTemp4, extraTemp5, extraTemp6, extraTemp7,
soilTemp1, soilTemp2, soilTemp3, soilTemp4, leafTemp1, leafTemp2, leafTemp3, leafTemp4,
inHumid,
outHumid, extraHumid1, extraHumid2, extraHumid3, extraHumid4, extraHumid5, extraHumid6, extraHumid7,
wind) = self._getEEPROM_value(0x32, "<27bh")
# inTempComp is 1's complement of inTemp.
if inTemp + inTempComp != -1:
syslog.syslog(syslog.LOG_ERR, "vantage: Inconsistent EEPROM calibration values")
return None;
# Temperatures are in tenths of a degree F; Humidity in 1 percent.
return {
"inTemp": inTemp / 10,
"outTemp": outTemp / 10,
"extraTemp1": extraTemp1 / 10,
"extraTemp2": extraTemp2 / 10,
"extraTemp3": extraTemp3 / 10,
"extraTemp4": extraTemp4 / 10,
"extraTemp5": extraTemp5 / 10,
"extraTemp6": extraTemp6 / 10,
"extraTemp7": extraTemp7 / 10,
"soilTemp1": soilTemp1 / 10,
"soilTemp2": soilTemp2 / 10,
"soilTemp3": soilTemp3 / 10,
"soilTemp4": soilTemp4 / 10,
"leafTemp1": leafTemp1 / 10,
"leafTemp2": leafTemp2 / 10,
"leafTemp3": leafTemp3 / 10,
"leafTemp4": leafTemp4 / 10,
"inHumid": inHumid,
"outHumid": outHumid,
"extraHumid1": extraHumid1,
"extraHumid2": extraHumid2,
"extraHumid3": extraHumid3,
"extraHumid4": extraHumid4,
"extraHumid5": extraHumid5,
"extraHumid6": extraHumid6,
"extraHumid7": extraHumid7,
"wind": wind
}
def startLogger(self):
self.port.send_command("START\n")
@@ -928,6 +1095,29 @@ class Vantage(weewx.abstractstation.AbstractStation):
_loop_map['monthRain'] = _loop_map['yearRain'] = _val100
_loop_map['rainRate'] = _big_val100
# Try to guess the ISS ID for gauging reception strength.
if self.iss_id is None:
stations = self.getStnTransmitters()
# Wind retransmitter is best candidate.
for station_id in range(0, 8):
if stations[station_id]['transmitter_type'] == 'wind':
self.iss_id = station_id + 1 # Origin 1.
break
else:
# ISS is next best candidate.
for station_id in range(0, 8):
if stations[station_id]['transmitter_type'] == 'iss':
self.iss_id = station_id + 1 # Origin 1.
break
else:
# On Vue, can use VP2 ISS, which reports as "rain"
for station_id in range(0, 8):
if stations[station_id]['transmitter_type'] == 'rain':
self.iss_id = station_id + 1 # Origin 1.
break
else:
self.iss_id = 1 # Pick a reasonable default.
def _getEEPROM_value(self, offset, v_format="B"):
"""Return a list of values from the EEPROM starting at a specified offset, using a specified format"""