Commit 90424fe0 authored by aloz77's avatar aloz77
Browse files

First commit to Github

parent 9fb4a937
* Based on wwsr - Wireless Weather Station Reader
* Michael Pendec (
* Svend Skafte (
* Adam Pribyl (
* Bradley Jarvis (
* Lukas Zvonar (
*Petr Zitny (
* Alexey Ozerov (
* 2011-12-20 some code cleaning and restructure, read_period, km/h wind speed
* 2011-12-21 introduce ws_parse(), struct wrecord
* 2011-12-22 flexible read period, server url, curl post data
* 2011-12-25 add inches, mph, utc, introduce ws_format(), urlencode, additional url
* 2011-12-26 readd rain 60 min calculation
* 2011-12-28 read config vars from cfg file, multiple add_url, date DD.MM.YYYY and time HH:MM formats
* 2011-12-29 read read_period from WS, add rain from 0h calculation
* 2012-01-01 fix error handling in main(), handle ws_submit feedback, get lasttime, submit multiple positions, submit error
* 2012-01-02 submit last error text to frewe-server
* 2012-01-03 add support for, more accurate time calculation for position shift, accurate cfg read
* 2012-01-05 don't encode : in HH:MM
* 2012-01-05 rewrite read_cfg(), add station type, support WH3080, calibration, read data_count
* 2012-01-08 check data_count when looking for old records, plausi check for age, read illumination & UV
* 2012-01-14 Better check for invalid values, skip record if too many errors, fix rain calibration
* 2012-01-15 Double read the data from USB device
* 2012-01-16 Fix time calculation for submission to frewe-server
* 2012-01-19 Fix linebreak after OK problem on frewe-server responce
* 2012-01-24 Integrate weather service URLs, fix bad data prevents position loop problem
* 2012-01-25 Read weather service access data from cfg
* 2012-01-26 No formatting in ws_parse, different errorstrings for different services
* 2012-01-27 Fix address calculation on address overflow
* 2012-01-28 Handle signals, fix ws_close(), fix address calculation again, multiple tries on USB read problems, recognize sensor loss, station type %K
* 2012-01-28 Don't handle SIGHUP
* 2012-02-01 Use http_fetcher instead of curl (saves 200 KB, however is not IPv6 capable)
* 2012-02-03 Resubmit data also to known weather services
* 2012-02-04 Handle DOS encoded lines in cfg file, read alarm config, introduce ws_alarm, clean up some code
* 2012-02-05 Configure emails for errors and alarms in cfg file, enable multiple alarms of each type, fix memory in URLencode
* 2012-02-09 Fix UV value output
* 2012-02-11 Add Sauerlandwetter
* 2012-02-23 Don't drop record if sensors are lost, NB: total rain, indoor values and abs. pressure (?) are usable even if sensors are lost
info: frewe.ws_dump - 0xD6C0: 0x05 0x38 0xEE 0x00 0xFF 0xFF 0xFF 0xEE 0x27 0xFF 0xFF 0xFF 0x8C 0x6B 0x03 0x40
info: frewe.ws_dump - 0xD6D0: 0x05 0x38 0xEE 0x00 0x35 0x67 0x00 0xEC 0x27 0x11 0x18 0x00 0x08 0x6B 0x03 0x00
* 2012-02-24 Only resend data to services which support it, don't resend if sensor lost (sensor lost = good data)
* 2012-03-06 Handle HighIndoorTemp and LowIndoorTemp Alarms
* 2012-04-04 Continue sending to other weather services on error
* 2012-05-24 Don't run out of available data on rain calculation
* 2012-06-11 Flush the stdout (enable data logging into a file), use stderr for debug and info output, replaced
* 2012-06-13 Include age of last record last_age in record datetime, pos60 and pos0h calculation, fix rainday calculation for old records
* 2012-06-15 Ignore get lasttime errors if frewe-server is not available
* 2012-07-27 make unit conversions in ws_format() instead of ws_parse()
* 2012-07-27 WH3080: Transmit illumination in W/m instead of Lux to WUnderground, PWS, Awekas, METOffice
* 2012-09-01 Remove, add Wetternetz Sachsen
* 2013-03-16 Remove Sauerlandwetter, rename time_interval to read_period, increase http timeout to 15 sec, print version info with -H
* 2013-03-16 Trying to fix double submissions with -59 logical seconds, later changed back to -1
* 2013-04-11 Fix SIGSERV by getlasttime from wrong input, fix pause calculation (int overflow)
* 2013-04-25 Fix pause calculation again
* 2013-05-23 Fix pause calculation again (use usleep), use getaddrinfo in http_fetcher.c, better validation rainday and rainhour
* 2013-06-22 Trying to fix stability issue when FreweServer fails, ignore FreweServer submission fault
* 2013-07-01 Fix stability issue within logger function (use strcpy instead of strcat)
* 2013-07-12 Try resetting USB port on errno 62 when setting alt device
* 2013-10-13 New interface URL for Wetternetz Sachsen (2.1)
* 2014-08-10 Add fhem file
* 2015-03-25 Send UTC time to frewe-server
* 2015-04-24 Handle lasttime as UTC
* 2015-08-20 Don't write fhem.txt for older records
* TODO: Handle rain counter overflow
// #define _XOPEN_SOURCE 1 /* was needed for strptime? */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <signal.h>
#include <usb.h>
#include <time.h>
#include <math.h>
#include <elf.h>
#include <openssl/md5.h>
#include "http_fetcher.h"
#define PROGRAM_VERSION "1.18"
#define DEFAULT_VENDOR 0x1941
#define DEFAULT_PRODUCT 0x8021
#define DEFAULT_FORMAT (char *)"\ntime: %N\nage: %a min\nin humidity: %h %%\nout humidity: %H %%\nin temperature: %I C\nout temperature: %O C\ndewpoint temperature: %E C\nwindchill temperature: %C C\nwind speed: %W km/h\nwind gust: %G km/h\nwind direction: %D\npressure: %P hPa\nrel. pressure: %L hPa\nrain total: %R mm\nrain 60 min: %S mm\nrain since 0h: %T mm\nillumination: %M lux\nUV: %U\n\n"
// Look for memory map
#define WS_MIN_ENTRY_ADDR 0x0100
#define WS_MAX_ENTRY_ADDR 0x10000
#define MAX_ALARMS 20
#define MAX_ADD_URLS 10
// extern double round (double __x) __attribute__ ((__nothrow__)) __attribute__ ((__const__));
int ws_open(usb_dev_handle **dev,uint16_t vendor,uint16_t product,int reset_done);
int ws_close(usb_dev_handle **dev);
int ws_read(usb_dev_handle *dev,uint16_t address,uint8_t *data,uint16_t size);
int ws_reset(usb_dev_handle *dev);
int ws_format(char *format, char *output, unsigned char urlencode, char *user, char *pass, char *error);
int ws_dump(uint16_t address,uint8_t *buffer,uint16_t size,uint8_t width);
uint16_t get_address(uint16_t base, int position);
void strcatenc(char *out,char *text,unsigned char urlencode);
void signal_handler(int signal);
long diff_time(const struct timeval *tact, const struct timeval *tlast);
char* URLencode(char *str);
char* URLdecode(char *str);
struct wrecord
{ time_t datetime;
char winddir[4];
float tempout,tempin,windspeed,windspeedms,windgust,tempchill,tempdew,pressabs,pressrel,rain,rainhour,rainday;
float illu;
short humin,humout,age;
short uv,winddeg;
char ok;
} w,w1;
struct calib
{ float tempin_factor,tempin_offset,tempout_factor,tempout_offset,humin_factor,humin_offset,humout_factor,humout_offset;
float windspeed_factor,windspeed_offset,windgust_factor,windgust_offset,pressabs_factor,pressabs_offset,rain_factor,rain_offset,winddir_offset;
float illu_factor,illu_offset,uv_factor,uv_offset;
} c = {1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,0,1,0};
typedef enum log_event
} log_event;
FILE *_log_debug=NULL,*_log_warning=NULL,*_log_error=NULL,*_log_info=NULL;
void logger(log_event event,char *function,char *msg,...);
char *errorstring="N/A"; // What to write if value is not available or out of range
char *format=DEFAULT_FORMAT, *add_url[MAX_ADD_URLS];
char *FHEM_errorstring="";
char *FHEM_file=NULL;
char *frewe_server_url=NULL, *frewe_server_key=NULL, *frewe_server_url_submit=NULL, *frewe_server_url_lasttime=NULL, *frewe_server_url_error=NULL, *frewe_server_url_alarm=NULL;
char *frewe_server_senddata=NULL, *frewe_server_resend=NULL, *error_email=NULL;
char *ws_type="WH1080"; // default to WH1080
char ws_entry_size;
int run_interval=0; // in seconds, 0 means run once and exit, change it by -r option or RunInterval cfg
int read_period; // Minutes between each stored reading (set in the WS configuration)
int altitude=0; // default altitude is sea level in meter - change it by -A option or Altitude cfg
uint16_t vendor=DEFAULT_VENDOR,product=DEFAULT_PRODUCT;
char add_url_counter=0, alm_counter=0;
usb_dev_handle *dev=NULL;
//char filebuf[256];
char *filebuf=NULL; // Will be allocated by html_fetcher
char *frewe_server_url_submit_template = "%s?serverkey=%s&action=addrecord&datetime=%%n&tempin=%%I&tempout=%%O&tempdew=%%E&tempchill=%%C&humin=%%h&humout=%%H&windgust=%%G&windspeed=%%W&winddir=%%D&pressabs=%%P&pressrel=%%L&rain=%%R&illu=%%M&uv=%%U&rainrate=%%S";
char *frewe_server_url_lasttime_template = "%s?serverkey=%s&action=getlasttime";
char *frewe_server_url_error_template = "%s?serverkey=%s&action=submiterror&email=%s";
char *frewe_server_url_alarm_template = "%s?serverkey=%s&action=alarm";
struct wservice
{ char *name;
char *userkey;
char *passkey;
char *url;
char *user;
char *pass;
char md5;
char *error;
char resend;
} ws[] =
{ { "Weather Underground", "WUnderground_StationID", "WUnderground_Password", "", NULL, NULL, 0, "", 1},
{ "PWS Weather", "PWSWeather_StationID", "PWSWeather_Password", "", NULL, NULL, 0, "", 0},
{ "Awekas", "Awekas_Username", "Awekas_Password", ";%X;%Y;%Z;%O;%H;%L;%T;%W;%d;;;;de;;%G;%m;%U;;;;%S;", NULL, NULL, 1, "", 0},
{ "MET Office", "METOffice_SiteID", "METOffice_SiteAuthKey", "", NULL, NULL, 0, "", 1},
// { "Wetternetz Sachsen", "WetternetzSachsen_UserID", "WetternetzSachsen_Password", ";%X;V2.0;%K;%Z;%Y;%O;--;--;--;--;--;%H;%E;--;--;--;--;--;--;--;%S;--;--;--;--;%T;--;%W;%d;%G;--;--;--;--;--;--;--;--;--;%C;--;--;%L;%P;--;--;--;--;--;--;--;--;--;--;--;--;--;--;%m;--;--;%U;--;%M;--;--;--;--;--;--;--;--;--;--;--;--;--;--;--;--;%R;--;--;--;--;--;--;--;--;--;--;--;--;--", NULL, NULL, 0, "", 0},
{ "Wetternetz Sachsen", "WetternetzSachsen_UserID", "WetternetzSachsen_Password", ";%X;V2.1;FreetzW;%Z;%Y;-2;%O;--;--;--;--;--;%H;--;--;%S;--;--;--;%W;%d;%G;--;--;--;--;--;%C;--;--;%L;%P;--;--;--;--;--;--;--;%m;--;--;%U;--;--;--;--;--;--;--;--;--;--;--;--;--;--;--;--;--;--;--;--;--;", NULL, NULL, 0, "", 0},
{ "", "Wetter.com_Username", "Wetter.com_Password", "", NULL, NULL, 0, "", 1},
{ " OBSOLETED", "Wetter.de_Username", "Wetter.de_Password", "", NULL, NULL, 0, "", 1}, // This is only for compatibility to pre 1.11 version config files
{ "Wedaal", "Wedaal_Username", "Wedaal_StationPass", ";%X;%O;%H;%L;%T;%W;%d;%Z;%Y;;;;;;;;;;;;;;;", NULL, NULL, 1, "", 0}
char *walarm_type[] =
{ "HighOutdoorTemp", "LowOutdoorTemp", "HighWindchillTemp", "LowWindchillTemp", "HighDewTemp", "LowDewTemp",
"HighIndoorTemp", "LowIndoorTemp", "HighOutdoorHumidity", "LowOutdoorHumidity", "HighIndoorHumidity", "LowIndoorHumidity",
"HighRelPressure", "LowRelPressure", "HighWind", "LowWind", "HighGust", "LowGust",
"HighRainHour", "LowRainHour", "HighRainDay", "LowRainDay", "HighIllumination", "LowIllumination", "HighUV", "LowUV"
struct walarm
{ char *type;
float threshold;
char set;
char *url;
char *run;
char *email;
} alm[MAX_ALARMS];
int main(int argc, char **argv)
int rv=0,c,i,l;
uint8_t help=0,dump=0, md5[16];
char *cp, md5str[33];
int position=0,startpos,endpos,curpos; // default position is 0 (=now) - altering this by -p option can lead to read some of stored values
int pos60, pos0h;
int data_count;
int last_age;
uint16_t address,address0,address60,address0h;
long pause;
time_t starttime,curtime,lasttime;
struct timeval tact, tlast;
struct tm *tmptr, tm;
char *output;
FILE *fd;
// Handle signals, don't break on SIGHUP!
signal(SIGTERM, signal_handler);
signal(SIGQUIT, signal_handler);
signal(SIGINT, signal_handler);
signal(SIGFPE, signal_handler);
signal(SIGILL, signal_handler);
signal(SIGSEGV, signal_handler);
signal(SIGBUS, signal_handler);
// Init add_url array, need it?
for (i=0;i<MAX_ADD_URLS;i++) add_url[i]=NULL;
// Parse options
while (rv==0 && (c=getopt(argc,argv,"hH?vxf:d:a:A:p:e:t:s:c:u:r:t:k:"))!=-1)
switch (c)
case 'a': // set device id
logger(LOG_DEBUG,"main","USB device set to vendor=%04X product=%04X",vendor,product);
case 'A': // set altitude
logger(LOG_DEBUG,"main","altitude set to %d",altitude);
case 'p': // set position
logger(LOG_DEBUG,"main","weather station log position set to %d",position);
case 'c': // read configuration from file
logger(LOG_DEBUG,"main","reading cfg from file %s returned %d",optarg,i);
case 'r': // set continuous run with time interval
logger(LOG_DEBUG,"main","continuos run interval set to %d seconds",run_interval);
case 'v': // verbose messages
logger(LOG_DEBUG,"main","Verbose messaging turned on");
case 'x': // XML export
optarg = "<data>\
case 'f': // Format output
logger(LOG_DEBUG,"main","Format output using '%s'",optarg);
case 's': // Server URL
logger(LOG_DEBUG,"main","Server URL set to '%s'",optarg);
case 'k': // Server Key
logger(LOG_DEBUG,"main","Server Key set to '%s'",optarg);
case 't': // Device Type
logger(LOG_DEBUG,"main","Device type set to '%s'",optarg);
case 'u': // Additional URL
logger(LOG_DEBUG,"main","Additional URL set to '%s'",optarg);
if (add_url_counter<MAX_ADD_URLS) add_url[add_url_counter++]=optarg;
case 'e': // Error string
logger(LOG_DEBUG,"main","Error string set to: '%s'",optarg);
case 'd': // Dump raw data from weather station
uint16_t a,s,w;
// sscanf(optarg,"0x%hX:0x%hX",&a,&s);
if (sscanf(optarg,"0x%hX:0x%hX",&a,&s)<2)
if (sscanf(optarg,"0x%hX:%hu",&a,&s)<2)
if (sscanf(optarg,"%hu:0x%hX",&a,&s)<2)
if (sscanf(optarg,"%hu:%hu",&a,&s)<2)
if (sscanf(optarg,":0x%hX",&s)<1)
logger(LOG_DEBUG,"main","Dump options address=%u size=%u",a,s);
if (dev==NULL) rv=ws_open(&dev,vendor,product,0);
if (dev)
uint8_t *b;
logger(LOG_DEBUG,"main","Allocating %u bytes for read buffer",s);
if (!b)
logger(LOG_ERROR,"main","Could not allocate %u bytes for read buffer",s);
logger(LOG_DEBUG,"main","Allocated %u bytes for read buffer",s);
if (dev) ws_close(&dev);
case 'H':
printf("Freetz Weather Client (frewe-client) for FRITZ!Box\n");
printf("Alexey Ozerov (c) 2014 - ver. %s\n\n", PROGRAM_VERSION);
case '?':
case 'h':
printf("Freetz Weather Client (frewe-client) for FRITZ!Box\n");
printf("Alexey Ozerov (c) 2014 - ver. %s\n\n", PROGRAM_VERSION);
printf(" -? -h Display this help\n");
printf(" -a <v>:<p> Change the vendor:product address of the usb device from the default\n");
printf(" -A <alt in m> Change altitude\n");
printf(" -c <filename> Read configuration from cfg file\n");
printf(" -p <pos> Alter position in weather station log from current position (can be +- value)\n");
printf(" -v Verbose output, enable debug and warning messages\n");
printf(" -d [addr]:[len] Dump length bytes from address\n");
printf(" -x XML output\n");
printf(" -r <sec> Run continuosly with given interval in seconds\n");
printf(" -s <url> Freetz weather server URL\n");
printf(" -k <key> Freetz weather server key\n");
printf(" -t <type> Weather Station Type: WH1080 (default) or WH3080\n");
printf(" -u <url> Additional URL to submit the data (format like -f)\n");
printf(" -e <errstr> Write this errstr if measured value is out of range (e.g. outdoor unit is disconnected)\n");
printf(" -f <string> Format output to user defined string\n");
printf(" %%a - record age\n");
printf(" %%C - outside wind chill temperature C\n");
printf(" %%c - outside wind chill temperature F\n");
printf(" %%D - wind direction - named\n");
printf(" %%d - wind direction - degrees\n");
printf(" %%E - outside dew temperature C\n");
printf(" %%e - outside dew temperature F\n");
printf(" %%G - wind gust in km/h\n");
printf(" %%g - wind gust in mph\n");
printf(" %%h - inside humidity\n");
printf(" %%H - outside humidity\n");
printf(" %%I - inside temperature C\n");
printf(" %%i - inside temperature F\n");
printf(" %%K - weather station type\n");
printf(" %%L - relative pressure in hPa\n");
printf(" %%l - relative pressure in inch\n");
printf(" %%m - illumination in W/m (WH3080 only)\n");
printf(" %%M - illumination in lux (WH3080 only)\n");
printf(" %%N - date/time string YYYY-MM-DD HH:MM:SS local time\n");
printf(" %%n - date/time string YYYY-MM-DD HH:MM:SS UTC time\n");
printf(" %%O - outside temperature C\n");
printf(" %%o - outside temperature F\n");
printf(" %%P - absolute pressure in hPa\n");
printf(" %%p - absolute pressure in inch\n");
printf(" %%R - rain total from meteostation start in mm\n");
printf(" %%r - rain total from meteostation start in inch\n");
printf(" %%S - rain last 60 min in mm\n");
printf(" %%s - rain last 60 min in inch\n");
printf(" %%T - rain from 0h local time in mm\n");
printf(" %%t - rain from 0h local time in inch\n");
printf(" %%U - UV radiation (WH3080 only)\n");
printf(" %%v - wind speed in m/s\n");
printf(" %%W - wind speed in km/h\n");
printf(" %%w - wind speed in mph\n");
printf(" %%Y - date string DD.MM.YYYY local time\n");
printf(" %%y - date/time string YYYYMMDDhhmm local time\n");
printf(" %%Z - time string HH:MM local time\n");
if (rv==0 && help==0 && dump==0)
// Set entry size according to device type and create buffers
if(strcasecmp(ws_type,"WH3080")==0 || strcasecmp(ws_type,"WH3081")==0)
uint8_t buffer[ws_entry_size],buffer60[ws_entry_size],buffer0h[ws_entry_size];
// Prepare frewe-server URLs
if (frewe_server_url!=NULL && frewe_server_key!=NULL)
if (strcasecmp(frewe_server_senddata,"On")==0)
{ frewe_server_url_submit = malloc(l+strlen(frewe_server_url_submit_template));
if (!frewe_server_url_submit)
logger(LOG_ERROR,"main","Could not allocate %u bytes for frewe-server URL",l+strlen(frewe_server_url_submit_template));
if (strcasecmp(frewe_server_resend,"On")==0)
{ frewe_server_url_lasttime = malloc(l+strlen(frewe_server_url_lasttime_template));
if (!frewe_server_url_lasttime)
logger(LOG_ERROR,"main","Could not allocate %u bytes for frewe-server URL",l+strlen(frewe_server_url_lasttime_template));
if (error_email!=NULL)
{ frewe_server_url_error = malloc(l+strlen(frewe_server_url_error_template)+strlen(error_email));
if (!frewe_server_url_error)
logger(LOG_ERROR,"main","Could not allocate %u bytes for frewe-server URL",l+strlen(frewe_server_url_error_template)+strlen(error_email));
if (1)
{ frewe_server_url_alarm = malloc(l+strlen(frewe_server_url_alarm_template));
if (!frewe_server_url_alarm)
logger(LOG_ERROR,"main","Could not allocate %u bytes for frewe-server URL",l+strlen(frewe_server_url_alarm_template));
// Make a pause for the Fritzbox to set time and connect to internet
while (starttime<10000)
logger(LOG_WARNING,"main","Time is still unset: %d, wait 10 secs...", starttime);
// Get read period from WS
if (rv==0) rv=ws_read(dev,WS_READ_PERIOD_ADDRESS,buffer,1);
if (rv==0)
{ read_period=buffer[0];
logger(LOG_DEBUG,"main","Weather station read period is %d minutes",read_period);
if (rv!=0)
{ logger(LOG_ERROR,"main","Can't get read period from weather station. Stopped!");
return rv;
// Start main loop for run_interval repetitions
rv=0; // reset errors
startpos=endpos=position; // default
// Read current time, this will be the time for record in position 0
gettimeofday(&tlast, NULL);
// Get the current data count (records actually saved on ws)
if (rv==0)
{ rv=ws_open(&dev,vendor,product,0);
if (rv==0) rv=ws_read(dev,WS_DATA_COUNT_ADDRESS,buffer,2);
if (rv==0)
{ data_count=buffer[0]+buffer[1]*256;
if (data_count<0 || data_count>WS_TOTAL_ENTRIES) data_count=WS_TOTAL_ENTRIES;
logger(LOG_DEBUG,"main","Data count is %d",data_count);
// Get last time saved on frewe-server and calculate positions
if (rv==0 && frewe_server_url_lasttime!=NULL)
logger(LOG_DEBUG,"main","Getting lasttime from server URL: %s", frewe_server_url_lasttime);
if (rv==0 && strlen(filebuf)>25) rv=1; // Got some buggy output which can cause SIGSERV in strptime
if (rv==0 && strncasecmp(filebuf,"Not found",9)==0) // If lasttime not found try to read all records from WS
logger(LOG_INFO,"main","Will now read ALL entries from %d to %d, this will take time...",startpos,endpos);
else if (rv==0) // If lasttime found read only newer records
logger(LOG_DEBUG,"main","Last record datetime is %s",filebuf);
if (!strptime(filebuf, "%Y-%m-%d %H:%M:%S", &tm)) rv=1;
if (rv!=0) logger(LOG_ERROR,"main","Failed to convert lasttime from %s", filebuf);
if (rv==0)
{ tm.tm_isdst = -1; // tells mktime() to determine whether daylight saving time is in effect
lasttime = timegm(&tm); // mktime assumes the time in tm struct is localtime, but this time it's UTC
if (lasttime == -1) rv=1;
if (rv!=0) logger(LOG_ERROR,"main","Failed to get lasttime seconds from %s", filebuf);
if (rv==0)
{ logger(LOG_DEBUG,"main","Lasttime on frewe-server is %d, time gap is %d",lasttime,curtime-lasttime);
startpos=floor((float)(curtime-lasttime-1)/read_period/60)*-1; // -1 logical seconds to avoid double submittions
if (startpos<1-data_count) startpos=1-data_count;
logger(LOG_WARNING,"main","Will now read entries from %d to %d",startpos,endpos);
rv=0; // Ignore this error and read the current position
{ logger(LOG_ERROR,"main","Failed to get lasttime from %s", frewe_server_url_lasttime);
rv=0; // Ignore this error and get the data only for the current position
// Warn if position doesn't meet a real record
if (rv==0 && (startpos>0 || startpos<1-data_count || endpos >0 || endpos<1-data_count))
logger(LOG_INFO,"main","Position is out of available data, %d records are saved on device",data_count);
// Read last record address & age
if (rv==0)
if (rv==0) rv=ws_read(dev,WS_CURRENT_POSITION_ADDRESS,buffer,2);
if (rv==0) address=buffer[0]+buffer[1]*256;
if (rv==0) rv=ws_read(dev,address,buffer,sizeof(buffer));
if (rv==0) last_age = (int) buffer[0x00];
if (rv!=0) logger(LOG_ERROR,"main","Can't read last position address or age from WS");
// Positions loop
if (rv==0)
for (curpos=startpos;curpos<=endpos;curpos++) // NB: data errors don't break this loop
rv=ws_open(&dev,vendor,product,0); // rv is reset here
// Read record for the current position
if (rv==0) rv=ws_read(dev,address0,buffer,sizeof(buffer));