WS2300(1)ws2300WS2300(1)NAMEws2300 - LaCrosse WS-2300 weather station tool
SYNOPSISws2300 help-measures
ws2300 tty-device measure[=value|=reset] ...
ws2300 tty-device display fields [url [sample [save [insert]]]]
ws2300 tty-device record config-file-name [pid-file-name [email-address
[recovery-file-name [port]]]]
DESCRIPTION
Ws2300 manipulates the LaCrosse WS-2300 weather station via its RS232
interface. It can read and write values, and can continuously log data
from WS-2300 to a file or SQL database.
The first form prints a list of values (measures) in the WS-2300 that
ws2300 can read and set. The second form is used to read and set the
measures individually. The third form is used capturing a single sam‐
ple to a log file or SQL database. The fourth form causes ws2300 to
become a daemon continuously saving samples to log files and databases.
tty-device is the name of the tty device the WS-2300's serial interface
is connected to. It can also have the form ws2300://host:port, which
means share the port a ws2300 daemon that is running on host and lis‐
tening to port is using.
WARNING
This program was developed with no input from the manufacturer,
LaCrosse. LaCrosse has said that using non-standard software such as
this program with the weather station may damage it, and such damage is
not covered by the warranty.
In the past LaCrosse has threatened to legally pursue people who pub‐
lish the protocols used by WS-2300. As the source code to this program
documents the protocols publishing it may put you at risk.
READING, SETTING AND RESETTING MEASURES
Measures can be displayed in a human readable format and set in an
interactive fashion using the command line.
help-measures
Prints a table of known measures. Measures have two names - a
short one and a long one. You can use either, but be sure to
quote the spaces in the long names to protect them from the
shell. Other information in the table are the units used by the
measure and the is position of the measure value in the
WS-2300's memory map. Eg 300:1 means the measure lives at
address 300 (hex) and is 1 nybble (4 bits) long. 310.3 means
the measure lives at address 300 (hex), is a 1 bit quantity and
is stored in bit 3 (1 << 3). If it isn't obvious, the measures
cd, ct and cw, which print various renditions of the date of the
computer running ws2300, aren't read from the weather station.
measure
Putting one or more measure names on the command line displays
their current values. In addition to measure names you can sup‐
ply hexaddress:length to display the raw nybbles at hexaddress,
and history: recno[-recno] to display a [range] of history
records starting at recno. Recno is 0 for the most recently
written record, 1 for the next most recently written record and
so on.
=value|=reset
Appending =value to a measure sets the measure rather than dis‐
playing it. The value is supplied in the same form it is dis‐
played by the read command. If a value contains spaces protect
them from the shell by quoting them. Reset can be supplied
instead of a value for min/max measure's and their associated
times. This will set a min/max measure to the current reading
or its time to the current station time. History and raw memory
writes are also supported. The length may be omitted when when
doing raw writes as it can be derived from the data. The raw
nybbles are always displayed singularly, but you can combine
them to specify a large number. Their order is reversed when
you combine them as the WS-2300 stores numbers in little endian
order, so 1,2,3 is equivalent to 321. If commands to both dis‐
play and set values are given on the command line all display
commands are executed first.
CAPTURING DATA
When used in the third and fourth forms, ws2300 captures weather data
from the WS-2300 and outputs it in a format suitable for logging or
inserting it into an SQL database. Rapidly changing data (eg wind
velocity) needs to be captured frequently in order to build up an accu‐
rate picture of what happened. This can generate a lot of data. To
reduced the amount of data that needs to be stored Ws2300 can aggregate
samples taken over a period of time and write them to a single record.
display
Capture one record of data and exit. These options follow dis‐
play:
fields The list of values to be stored for each record. Sepa‐
rate the values using the character '!'. These values
are actually Python expressions that are evaluated each
time a sample is taken. They are described in the sec‐
tion "FIELD EXPRESSIONS" below.
url Where the data should be saved. If it has the format
csv:filename or ssv:filename the fields are appended to a
file as comma (csv) or space (ssv) separated values, one
line per record. If filename is omitted they are printed
on stdout instead. If url has the format CSV:filename or
SSV:filename the file is overwritten with the new data
rather than being appended to. Otherwise the url speci‐
fies the name of a database ws2300 must connect to to
save the data. It must have the format sqlmodule:key‐
word=value,... Sqlmodule is the name of a Python module
that implements the Python Database API, and key‐
word=value are the keyword arguments that will be passed
to that modules "connect()" method. The "EXAMPLES" sec‐
tion has url's for the more common open source SQL data‐
bases. If omitted url defaults to csv, so comma sepa‐
rated values are written to stdout.
sample Samples are taken every sample seconds. Be aware that at
best, when using a cable connection, the WS-2300 updates
its internal data every 8 seconds. If the sampling fre‐
quency is faster than the WS-2300 can supply them then
samples will be skipped. If omitted it defaults to 1
second.
save Records are saved every save seconds. This can't be
smaller than sample. Making this N times larger than
sample means N samples will be taken per record. Records
are always saved at pre-determined times - at save second
intervals from the start of the day. Thus the first
record saved may have less samples than normal. If omit‐
ted it defaults to 1 second.
insert The SQL statement that will be executed to insert a row
into the database. This should be omitted for csv and
ssv urls. The statement must use the '?'s as place hold‐
ers for the values to be inserted, and the '?'s must
match the values in fields both in order and number.
record Continuously capture weather data. Ie, what display does once
record does continuously. These options follow record:
config-file-name
This option is mandatory. It is the name of a file that
specifies what to record and where to put it. This
information is described in the same way it is to dis‐
play. You put the same command line options display
takes, ie fields, url, sample, save, and insert, into
config-file-name in the same order. Each option must
start on a new line but can be split over multiple lines
by starting successive lines with white space. Unlike
display, record can save to several databases at once.
Each field, url, etc definition is separated with blank
lines or comments. A comment is a line starting with a #
character.
pid-file-name
If supplied and is not the character '.' (decimal point)
the program runs in the background and writes its mes‐
sages to the syslog LOG_DAEMON. The pid of the back‐
ground process is written to pid-file-name if it is sup‐
plied and isn't '.' (decimal point) or '-' (minus).
email-address
This is a comma separated list of address to send email
to if the program exits abnormally. /usr/sbin/sendmail
is used to send the email. It is supplied by most 'nix
SMTP servers. If this is a '.' (decimal point) no email
is sent.
recovery-file-name
If not '.' (decimal point) samples that haven't been
saved to disk are written to this file. When the program
is next started it reads all unwritten samples from the
recovery file, saving them when the time comes. If a
recovery file is not used all unsaved data is lost when
the daemon is restarted. If the configuration is changed
such that new fields are added that aren't in the saved
data then their values are taken from the current weather
station readings. This heuristic doesn't work well if
daemon is restarted a long time after changing the con‐
figuration file.
port If not '.' (decimal point) ws2300 will make its serial
port available to other instances of ws2300 via this TCP
port. The other instances use the ws2300://host:port
syntax for tty-device to access it. As communications to
the ws2300 is very slow this feature can't be used too
heavily.
FIELD EXPRESSIONS
The expressions in the fields parameter are Python expressions. The
following variables are available. In the variable names only the
short forms of the measure names may be used. The variables always
return floating point numbers. In the case of dates and times this
represents the number of seconds since midnight 1970-01-01, UTC.
endtime
The time the save period finishes. Times recorded when a
sample is taken such as cw and sw are unpredictable in
that they depend on when sampling was started and when
how long the WS-2300 takes to respond. The endtime on
the other hand is always exactly the time the save period
is due to finish, regardless of when samples where taken
or how long it took to read them. For example, if sam‐
ples are being saved every hour then this value will
always be exactly on the hour, and thus the minutes and
seconds will be 0.
None An SQL null.
starttime
The time the save period started. For the first sample
saved this will be the time ws2300 was started. There‐
after it always equals the previous endtime.
ws.measure[0]
The first sample taken for measure.
ws.measure[-1]
The last sample taken for measure.
ws.measure.avg
The average (arithmetic mean) of the samples taken for
measure.
ws.measure.cnt
The number of the samples taken for measure.
ws.measure.max
The maximum sample taken for measure.
ws.measure.median
The median of the samples taken for measure.
ws.measure.min
The minimum sample taken for measure.
ws.measure.std
The standard deviation of samples taken for measure.
ws.wv.dir, ws.wv.speed
The measure wv (wind velocity) is special. It is a vec‐
tor that has two components: direction and speed. The
aggregates (first, last, average, etc) are stored sepa‐
rately. Thus ws.wv.dir.avg is the average wind velocity
direction. To get a feel for what the wind velocity is,
imagine an air molecule at the sensor at the start of the
recording period. It gets blown around by the wind dur‐
ing the recording period, and at the end of the recording
period we take note of its position. The speed and
direction it would take to get from the sensor to that
position in a straight line over the recording period is
its average wind velocity. All the other aggregates are
calculated the same way its scalar cousins, ws (wind
speed) and w0 (wind direction).
The following Python modules are also available:
datetime
math
re
string
time
All Python's builtin methods are available. In addition these func‐
tions can be used:
db.DateFromTicks(ticks)
Convert a time field into something acceptable for a DATE
field in the SQL database being used.
db.TimeFromTicks(ticks)
Convert a time field into something acceptable for a TIME
field in the SQL database being used.
db.TimespanFromTicks(ticks)
Convert a time field into something acceptable for a
TIMESPAN field in the SQL database being used.
select('table.field', whereclause)
Return a value from an SQL table. In csv and ssv url's
this function always returns 0. The value from the first
row found by this select clause is returned:
select field from table where whereclause
generator('table.field', whereclause)
Identical to select, except the field in the database is
incremented after each call. In csv and ssv url's this
function returns the number of records saved so far.
EXTENDING ws2300
Ws2300 can be extended in two ways. Both require the ability to pro‐
gram in Python. The simple way is to write your own Python Database
API module. This is not as complex as it sounds as ws2300 doesn't
actually use much of the API. Here is a example that appends the
records as lines containing '|' separated fields to a text file:
mydb.py:
class Db:
def __init__(self, filename):
self.handle = file(filename, "a")
def cursor(self): return self
def commit(self): pass
def close(self): self.handle.close()
#
# Called by ws2300 each to write each new record.
# data is a tuple containing the fields, ie the
# result of evaluating the Python expressions
# given on the command line.
#
def execute(self, sql, data):
record = '|'.join([str(field) for field in data])
self.handle.write(record+"\n")
def connect(filename=None):
return Db(filename)
To use your module simply stick it in the current directory, and pass a
url like "mydb:filename='weather.txt'" to ws2300.
To do really radical things ws2300.py can be used as a module. Things
defined in there are:
SerialPort
A class defining the abstract interface to a serial port. Port‐
ing ws2300 to Windows for example would be just a case of imple‐
menting this interface under Windows and arranging it to be used
instead of LinuxSerialPort.
LinuxSerialPort
A class implementing SerialPort for Linux. After creating one
of these using LinuxSerialPort(tty_device), call open() and call
close() when you are finished with it.
Ws2300 A class that implements the protocol used by the WS-2300.
Ws2300(serialPort) creates an instance of this class. You pass
it an open SerialPort. read_safe(address,nybble_count) reads
nybble_count nybbles starting at address and returns them in a
tuple of int's. Similarly, write_safe(address,data) writes the
nybbles in the tuple data to address.
Measure
Is a class that defines a measure. Instances have the fields:
address, the address of the measure in the Ws2300's memory map,
conv, an instance of Conversion defining the type of data, id,
the short name of the measure, and name the long name of the
measure. The class variable Measure.IDS is a mapping from a
measures short name to its Measure instance.
Conversion
Is a class that defines a data type stored by the WS-2300.
Instances have the fields: description, a string describing the
type, nybble_count, the number of nybbles occupied by the data
type on the WS-2300, and units, the SI units used to represent
the measure. Instances have the functions: binary2value(nyb‐
bles) converts the raw nybbles returned by Ws2300.read_safe()
into the number used to represent the measure in Python,
value2binary(value) converts the Python number used to represent
the measure into nybbles that can be written by
Ws2300.write_safe(), str(value) converts the Python value into a
human readable string, and parse(str) converts the string pro‐
duced by str() to its Python value.
HistoryMeasure
is a class defining a history measure. It has a separate class
because history is unusual in two ways. Unlike say indoor tem‐
perature which has a single occurence there are many history
records, and whereas an indoor temperature is represented by a
single number a history record contains multiple values. The
class method HistoryMeasure.set_constants(ws2300) must be called
prior to use to initialise the class. The constructor Histo‐
ryMeasure(record_number) creates a Measure for the passed his‐
tory record number. The instance method binary2value(nybbles)
returns a HistoryConversion.HistoryRecord instance which con‐
tains the properties temp_indoor, temp_outdoor. pressure_abso‐
lute, humidity_indoor, humidity_outdoor, rain, wind_speed and
wind_direction.
read_measures(ws2300,measures)
Identical to the Python expression [ws2300.read_data(m.address,
m.conv.nybble_count) for m in measures], except its more effi‐
cient.
A complete program that would print the indoor temperature and humidity
reported by the WS-2300, followed by the same things from the first 10
history records.
#!/usr/bin/python
import ws2300
serialPort = ws2300.LinuxSerialPort("/dev/ttyS0")
try:
ws = ws2300.Ws2300(serialPort)
measures = [
ws2300.Measure.IDS["it"],
ws2300.Measure.IDS["ih"]]
data = ws2300.read_measurements(ws, measures)
hist_measures = [ws2300.HistoryMeasure(recno) for recno in range(10)]
hist_data = ws2300.read_measurements(ws, hist_measures)
finally:
serialPort.close()
print [
m.conv.binary2value(d)
for m, d in zip(measures, data)]
for recno in range(len(hist_measures)):
history_record = hist_measures[recno].conv.binary2value(hist_data[recno])
print history_record.temp_indoor, history_record.humidity_indoor
EXAMPLES
In these examples the WS-2300 is assumed to be connected to the serial
port /dev/ttyS0.
ws2300 help-measures
Prints out all known measures. This is the first thing you
should run.
ws2300 /dev/ttyS0 'indoor temp' 'indoor humidity'
Displays the indoor temperature and humidity in a nice readable
format.
ws2300 /dev/ttyS0 it ih
Same as the previous example, but uses the short measure names.
ws2300 /dev/ttyS0 'lcd backlight=on'
Sets the LCD Backlight bit on in the WS-2300's memory. As you
might guess, this turns on the backlight.
ws2300 /dev/ttyS0 sd=2005-12-25 st=12:00:00
Set the weather station's idea of the date and time to 12 noon
Christmas day, 2005.
ws2300 /dev/ttyS0 sd=$(date +%Y-%d-%m) st=$(date +%H-%M-%S)
If entered on a unix/linux machines command prompt this with set
the stations idea of the date and time to the computers time.
ws2300 /dev/ttyS0 wch=reset wchw=reset
Reset the wind chill maximum. You should always reset both the
value and the time.
ws2300 /dev/ttyS0 54d:1
Display the connection type nybble, which happens to live at
address 54d.
ws2300 /dev/ttyS0 54d=0,2,3
Set nybbles at addresses 54d, 54e and 54f to 0, 2 and 3 respec‐
tively. DON'T DO THIS!
ws2300 /dev/ttyS0 54d=0,32
Identical to the previous example. DON'T DO THIS!
ws2300 /dev/ttyS0 history:0
Display the most recent history record.
ws2300 /dev/ttyS0 history:0-174
Display all history records.
ws2300 /dev/ttyS0 display ws.cw[0]!ws.sw
Print to stdout the computers time and the current station time
as as floating pointing numbers representing the number of sec‐
onds since the start of 1970-01-01, UTC. Probably not what you
wanted.
ws2300 /dev/ttyS0 display 'db.TimestampFromTicks(ws.cw[0])!db.Times‐
tampFromTicks(ws.sw)'
Print to stdout the computers time and the current station time
as text, UTC. Still probably not what you wanted.
ws2300 /dev/ttyS0 display 'db.TimestampFromTicks(ws.cw[0]-time.time‐
zone)!db.TimestampFromTicks(ws.sw-time.timezone)'
Print to stdout the computers time and the current station time
as text, local time.
ws2300 /dev/ttyS0 display 'ws.it[0]!ws.ws[0]'
Print to stdout the indoor temperature in degrees Celsius, and
the wind speed in meters per second.
ws2300 /dev/ttyS0 display 'ws.it[0]*9/5+32!ws.ws[0]/1609.344*3600'
Print to stdout the indoor temperature in degrees Fahrenheit,
and the wind speed in miles per hour.
ws2300 /dev/ttyS0 display ws.wv.dir.avg!ws.wv.speed.avg csv 8 300
Print to stdout the average wind velocity over a 5 minute
period, sampling every 8 seconds.
ws2300 /dev/ttyS0 display ws.wv.dir.avg!ws.wv.speed.avg 'kinterbasdb:
dsn="127.0.0.1:/home/rstuart/weather.db", user="sysdba", password="sys‐
dba", dialect=3' 8 900 'insert into weather values (?,?)'
Display the average wind velocity over a 15 minute period, sam‐
pling every 8 seconds, to the table 'weather' in a firebird
database running on the local machine and stored in the file
/home/rstuart/weather.gdb.
ws2300 /dev/ttyS0 display ws.wv.dir.avg!ws.wv.speed.avg 'pyPgSql.PgSql:
host="127.0.0.1", port=5432, database="weather", user="me", pass‐
word="my"' 8 900 'insert into weather values (?,?)'
Display the average wind velocity over a 15 minute period, sam‐
pling every 8 seconds, to the table 'weather' in a postgresql
database running on the local machine and stored in the database
weather.
ws2300 /dev/ttyS0 display ws.wv.dir.avg!ws.wv.speed.avg '_mysql:
host="127.0.0.1", port=5432, db="weather", user="me", passwd="my"' 8
900 'insert into weather values (?,?)'
Display the average wind velocity over a 15 minute period, sam‐
pling every 8 seconds, to the table 'weather' in a MySql data‐
base running on the local machine and stored in the database
weather.
ws2300 /dev/ttyS0 record /etc/ws2300/ws2300.conf /var/run/ws2300.pid
root /var/lib/ws2300/ws2300.recovery 8192
Run in the background recording the data specified in the file
/etc/ws2300/ws2300.conf. Write the PID of the background
process to /var/run/ws2300.pid. If something goes wrong (eg the
WS-2300 fails to response or a database update fails) send email
to root before exiting. Save unwritten data to
/var/lib/ws2300/ws2300.recovery, so that if the daemon is
restarted no data is lost. Make the serial port available to
other instances of ws2300 via TCP socket 8192. If those other
instances were running on the same machine they would access the
serial port by using ws2300://127.0.0.1:8192 for the tty-device.
If this is the contents of /etc/ws2300/ws2300.conf then three
sets of data would be captured, as described in the comments.
#
# This first capture spec writes the data to csv text
# file, overwriting it for each new data sample. The
# data is actually written to a temporary file which
# is then moved to the correct filename so it someone
# reading it never sees 1/2 written data. This format
# easily digested by CGI scripts to generate web pages.
#
db.TimestampFromTicks(ws.cw[-1]-time.timezone) !
db.TimestampFromTicks(ws.sw[-1]-time.timezone) !
ws.ot[-1] ! ws.oh[-1] !
ws.wv.speed.avg ! ws.wv.dir.avg !
ws.rd[-1] ! ws.pa[-1]
CSV:/var/lib/ws2300/current-weather.csv
8
8
#
# This capture spec captures all weather data every hour.
# It is written to an SQL database.
#
db.TimestampFromTicks(starttime) ! db.TimestampFromTicks(endtime) !
db.TimestampFromTicks(ws.sw[-1]) ! db.TimestampFromTicks(ws.cw[-1]) !
ws.sw.cnt !
ws.ot.avg ! ws.oh.avg !
ws.rt[-1] ! ws.pa.avg !
ws.it.avg ! ws.ih.avg !
ws.wv.speed.avg ! ws.wv.speed.std !
ws.wv.dir.avg ! ws.wv.dir.std !
ws.ws.avg
kinterbasdb:
dsn="127.0.0.1:/var/lib/ws2300/weather.gdb",
user="sysdba", password="sysdba",
dialect=3
8
3600
insert into weather_all values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
#
# This capture spec captures the wind velocity data
# every 5 minutes and writes it to an SQL database.
# Wind velocity changes rapidly, so I like to keep a
# close eye on it.
#
db.TimestampFromTicks(starttime) ! db.TimestampFromTicks(endtime) !
ws.ws.cnt ! ws.ws.avg !
ws.wv.speed.avg ! ws.wv.speed.std !
ws.wv.dir.avg ! ws.wv.dir.std
kinterbasdb:
dsn="127.0.0.1:/var/lib/ws2300/weather.gdb",
user="sysdba", password="sysdba",
dialect=3
8
300
insert into weather_wind values (?,?,?,?,?,?,?,?)
SEE ALSO
http://www.heavyweather.info/english_uk/english_uk_2300.html
LaCrosse's home page for the WS-2300.
http://www.webmet.com/met_monitoring/62.html
Covers the math for to aggregating wind velocities.
http://www.lavrsen.dk/twiki/bin/view/Open2300/WebHome
http://sf.net/projects/open2300
The code base ws2300 was derived from. Open2300 was written by
Kenneth Lavrsen. If ws2300 doesn't suit your needs perhaps
open2300 will.
memory_map_2300.txt
The WS-2300's memory map. It was put together by reverse engi‐
neering the WS-2300. This was done mainly on the German lan‐
guage Weather Station Forum, http://www.wetterstationsfo‐
rum.de/phpBB/viewforum.php?f=28. This file should be included
with the ws2300 distribution.
AUTHOR
Russell Stuart, <russell-ws2300@stuart.id.au>.
Version 1.9 May 2014 WS2300(1)