Ich will diesen Datenlogger https://www.conrad.de/de/p/multi-datenl ... 05055.html unter Mac OS X Mojave abfragen. Das hat in früheren Betriebssystem-Versionen immer problemlos funktioniert.
Dazu benutze ich dieses Python Skript:https://sourceforge.net/projects/dl141th/
Code: Alles auswählen
#! /usr/bin/env python3
"""Configure and/or read out the Voltcraft DL-141TH Temperature-/Humidity data logger.
This program can be used to control a DL-141TH data logger attached to the USB port.
On Windows machines, the CP2102 USB to UART driver must be installed.
That driver can be downloaded from http://www.silabs.com/products/mcu/pages/usbtouartbridgevcpdrivers.aspx.
This program requires a python3 installation including the pyserial module.
The available command line options are displayed when the program is executed with the -h argument.
The name of the serial port must be always specified by the -d option (alternative names: --device --port)
FUNCTIONALITY
The following functionality is provided (note that each call stops logging)
1.) Read out log data and store it to a file [option -r]
2.) Synchronize the clock of the DL-141TH with the local time of the PC [option -t]
3.) Setup logging in a given interval [option -i]
The interval must bei either 1..59 seconds, 1..59 minutes, 1..23 hours
For the unit of the interval, each abbreviation of seconds, minuts, hours is allowed in upper or lower case.
Examples:
Logging interval 3 seconds: -i 3 sec
Logging interval 1 minute: -i 1 Minute
Logging interval 2 hours: -i 2 h
4.) Start logging immediately [option -s]
Of this option is not set, then logging starts when you press one of the buttons of the data logger
The following functionality of the data logger is not supported
5.) Blinking of the LEDs
This would be technically possible but drains the battery
6.) Setting/supervision of alarm intervals
This makes not much sense as alarm LED lights up only once per minute.
7.) Circular Logging
I did not get this running. See dl141th.protocolanalysis.txt
USAGE EXAMPLES
It is assumed that the device is attached to the serial port COM3 on a Windows machine
- Synchronize time and start logging each 5 sec after button press
python dl141th.py -d COM3 -t -i 5 sec
- Synchronize time and start logging once per minute imemdiately
python dl141th.py -d COM3 -t -i 1 min -s
- read out logged data and store to file dl141th.log
python dl141th.py -d COM3 -r dl141th.log
LOG FILE FORMAT
The log file consists of
- one header line describing the colums of the data lines
- one data line per measurement interval
The date is specified as year-month-day
The time is specified as hours:minutes:seconds
The temperature is given in degrees Celcius with 1 decimal digit
The relative humidity is given in % with 1 decimal digit
LOG FILE FORMAT EXAMPLE
Date Time Temperature Humidity
2014-05-30 16:49:36 22.5 53.3
2014-05-30 16:49:41 22.5 53.3
2014-05-30 16:49:46 22.5 53.3
2014-05-30 16:49:51 22.5 53.3
LOG FILE VISUALIZATION
Assuming that the name of the log file is dl141th.log, the following gnuplot script plots the data
file = 'dl141th.log'
set title 'Temperature and relative humidity over time'
set xlabel "Time"
set ylabel "Temperature in degrees Celsius"
set y2label "Relative Humidity in %"
set ytics nomirror
set y2tics
set xdata time
set format x "%H:%M:%S"
set xtics rotate by -45
set timefmt "%Y-%m-%d %H:%M:%S"
plot file using 1:3 title "Temperature", '' using 1:4 axes x1y2 title "Relative Humidity"
LICENSE
This program is provided under a MIT style license.
See the file dl141th.license.txt.
In essence this means that you can use it for whatever purpose but there is no warranty.
TECHNICAL BACKGROUND
The file dl141th.protocolanalysis.txt contains an analysis of the protocol to control the DL-141TH
"""
#The program starts here
import sys, serial, time, datetime, argparse
#global variables
verbose = 0 #debug level
def openSerial(device):
#For reading 654 bytes at 9600 baud, less than 1 second should be sufficient
#But on Windows 7, I observed data loss, so I had to increase the timeout to 2 seconds.
#Maybe this is a USB-serial issue.
ser = serial.Serial(device, 9600, timeout=2)
return ser
def clearInput(ser):
time.sleep(0.1)
while ser.inWaiting():
if verbose>=2:
print("{0} bytes inWaiting".format(ser.inWaiting()))
ser.flushInput()
time.sleep(0.1)
def sendRequest(ser, request):
n = ser.write(request)
if verbose>=2:
print("{0} bytes written".format(n))
return n
def readReply(ser, n_expected_bytes):
result=ser.read(n_expected_bytes)
if verbose>=2:
print("{0} bytes read".format(len(result)))
if len(result):
print("{0:2X}".format(result[0]))
if len(result) < n_expected_bytes:
print("{0} bytes received, but {1} expected!!!\n".format(len(result), n_expected_bytes));
return result
def readChunk(ser, index, n_expected_bytes):
clearInput(ser)
cmd = [0x68, 0x68, 0x68, 0x68, 0x68, 0x68]
cmd.append(index)
req = bytes (cmd)
sendRequest(ser, req)
reply = readReply(ser, n_expected_bytes)
return reply
def readCircNum(ser):
clearInput(ser)
req = bytes ([0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x64])
sendRequest(ser, req)
reply = readReply(ser, 654)
if verbose>=1:
print("64: {0} bytes read".format(len(reply)))
if len(reply):
print("{0:2X}".format(reply[0]))
return reply
def readLog(ser, fo):
clearInput(ser)
req = bytes ([0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68])
sendRequest(ser, req)
reply = readReply(ser, 21)
result = len(reply) >=21 and (reply[0] == 0xDD) and (reply[7] == 0xCE) and (reply[20] == 0xAA)
if result:
log_size = reply[1]*256+reply[2]
circ_cnt = reply[3]*256+reply[4]
interval = reply[6]
if reply[5] == 0x20:
interval_sec = interval*3600
elif reply[5] == 0x30:
interval_sec = interval*60
elif reply[5] == 0x60:
interval_sec = interval
else:
raise Exception('invalid interval unit')
if verbose >= 2:
print("interval_sec = {0}".format(interval_sec))
year = 2000 + (10*((reply[8]&0xF0)>>4)) + (reply[8]&0x0F)
month = 10*((reply[9]&0xF0)>>4) + (reply[9]&0x0F)
day = 10*((reply[10]&0xF0)>>4) + (reply[10]&0x0F)
hour = 10*((reply[11]&0xF0)>>4) + (reply[11]&0x0F)
minute = 10*((reply[12]&0xF0)>>4) + (reply[12]&0x0F)
second = 10*((reply[13]&0xF0)>>4) + (reply[13]&0x0F)
date = datetime.datetime(year, month, day, hour, minute, second)
#TODO correct setting of start date/time for circular_logging with roll-over
if verbose >= 2:
print("date: {0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}".format(year, month, day, hour, minute, second))
if verbose:
print("Log size: {0}bytes".format(log_size))
print("Circ cnt: {0}bytes".format(circ_cnt))
if (log_size // 4):
log = []
i_chunk = 0 #chunk
while (i_chunk*654)<log_size:
sys.stdout.write("\rreading chunk: {0}/{1}".format(i_chunk,(log_size-1)//654))
sys.stdout.flush()
n_expected_bytes = min (654, log_size-i_chunk*654)
reply = readChunk(ser, i_chunk, n_expected_bytes)
log.extend(list(reply))
i_chunk+=1
print("\nTotal bytes read: {0} (expected: {1})".format(len(log), log_size))
#readCircNum(ser)
offset = 0
while offset<log_size:
t = 0.1*int.from_bytes([log[offset+0], log[offset+1]], byteorder='big', signed=True)
h = 0.1*int.from_bytes([log[offset+2], log[offset+3]], byteorder='big', signed=True)
#fo.write("{0:05d} {1:s} t={2:.1f} h={3:.1f}\n".format(i_reading,date.strftime("%Y-%m-%d %H:%M:%S"),t,h))
fo.write("{0:s} {1:.1f} {2:.1f}\n".format(date.strftime("%Y-%m-%d %H:%M:%S"), t, h))
date = date + datetime.timedelta(seconds=interval_sec)
offset+=4
result = (len(log) == log_size)
else:
if verbose:
if len(reply) >=21:
print("Cannot read log header: {0} {1:02X} {2:02X} {3:02X}".format(len(reply), reply[0], reply[7], reply[20]))
else:
print("Cannot read log header: {0}".format(len(reply)))
return result
def setCurrentTime(ser):
clearInput(ser)
now = datetime.datetime.now()
year_bcd = int("{0:02d}".format(now.year % 100),16)
month_bcd = int("{0:02d}".format(now.month),16)
day_bcd = int("{0:02d}".format(now.day),16)
hour_bcd = int("{0:02d}".format(now.hour),16)
minute_bcd = int("{0:02d}".format(now.minute),16)
second_bcd = int("{0:02d}".format(now.second),16)
if verbose:
print("Set date/time to 20{0:02X}-{1:02X}-{2:02X} {3:02X}:{4:02X}:{5:02X} ".format(year_bcd, month_bcd, day_bcd, hour_bcd, minute_bcd, second_bcd))
req = bytes ([0x58, year_bcd, month_bcd, day_bcd, hour_bcd, minute_bcd, second_bcd])
sendRequest(ser, req)
reply = readReply(ser, 1)
result = len(reply) > 0 and (reply[0] == 0xEE)
return result
def setupLogging(ser, interval, circular_logging = False, start_immediately = False):
"""send a request to setup logging
Alarm thresholds are not supported for simplicity
Blinking of the REC LED is always OFF
Positional arguments:
ser -- the serial port handle
interval -- the logging interval as string list ['value', 'unit']. Supported units: 'seconds', 'minutes', 'hours' or abbreviations of those
Keyword arguments:
circular_logging -- if True, circular logging is activated
start_immediately -- if True, logging starts immediately, if False logging starts after button press
"""
clearInput(ser)
i_value, i_unit = interval[0], interval[1].lower()
i_value_int = int(i_value)
if i_value_int <= 0:
raise Exception('Invalid interval value', i_value)
byte2 = 0x20 #LED alarm off
if "hours".startswith(i_unit):
byte2 = byte2 + 0x03
if i_value_int > 24:
raise Exception('Interval more than 24 hours', i_value_int)
if verbose:
print("Log interval {0} hours".format(i_value_int))
elif "minutes".startswith(i_unit):
byte2 = byte2 + 0x02
if i_value_int > 59:
raise Exception('Interval more than 59 minutes - please use hours', i_value_int)
if verbose:
print("Log interval {0} minutes".format(i_value_int))
elif "seconds".startswith(i_unit):
byte2 = byte2 + 0x01
if i_value_int > 59:
raise Exception('Interval more than 59 seconds - please use minutes', i_value_int)
if verbose:
print("Log interval {0} seconds".format(i_value_int))
else:
raise Exception('Invalid interval unit', i_unit)
if start_immediately:
if verbose:
print("Start immediately")
else:
byte2 = byte2 + 0x80
if circular_logging:
if verbose:
print("Circular Logging")
else:
byte2 = byte2 + 0x40
if verbose >=3:
print("Byte2: {0:02X}".format(byte2))
req = bytes([0x48, byte2, i_value_int, 0, 0, 0, 0])
sendRequest(ser, req)
reply = readReply(ser, 1)
result = len(reply) > 0 and (reply[0] == 0x1E)
return result
##main()
parser = argparse.ArgumentParser(description='Configure and/or read out the DL-141 TH data logger')
parser.add_argument('-d', '--device', '--port', required=True, help='name of serial port device, e.g. /dev/ttyUSB0 for Linux or COM3 for Windows')
parser.add_argument('-v', '--verbose', action='count', help='synchronize time with local time of PC')
parser.add_argument('-r', '--read', metavar=('file'), help='read data and store in file')
parser.add_argument('-t', '--sync_time', action='store_true', help='synchronize time with local time of PC')
parser.add_argument('-i', '--interval', nargs=2, metavar=('interval', 'unit'), help='setup logging with given interval and unit. Supported units: "seconds", "minutes", "hours" or abbreviations of those. Examples: -i 3 sec, -i 1 Minute, -i 2 h')
#circular logging deactivated as I did not manage to read out the data
#parser.add_argument('-c', '--circular', action='store_true', help='enable circular logging. interval must be specified!')
parser.add_argument('-s', '--start', action='store_true', help='start logging immediately. interval must be specified!')
args = parser.parse_args()
if args.verbose:
verbose = args.verbose
if args.device:
ser = openSerial(args.device)
clearInput(ser)
if args.read:
fo = open(args.read, mode='w')
if not fo:
raise Exception('cannot open file', args.read)
fo.write("Date Time Temperature Humidity\n")
if not readLog(ser, fo):
raise Exception('readLog failure')
fo.close()
time.sleep(1)
if args.sync_time:
if not setCurrentTime(ser):
raise Exception('setCurrentTime failure')
time.sleep(2)
if args.interval:
if not setupLogging(ser, args.interval, circular_logging=False, start_immediately=args.start):
raise Exception('setupLogging failure')
time.sleep(1)
ser.close()
Code: Alles auswählen
Macbook17:dl141th dirk$ ./dl141th.py
Traceback (most recent call last):
File "./dl141th.py", line 88, in <module>
import sys, serial, time, datetime, argparse
ModuleNotFoundError: No module named 'serial'
Macbook17:dl141th dirk$ python --version
Python 2.7.10
Macbook17:dl141th dirk$ python3 --version
Python 3.7.6
Code: Alles auswählen
pip uninstall pyserial
sudo easy_install pip
pip uninstall pyserial
pip install pyserial
pip uninstall serial
pip install serial
Früher hat das "einfach so" geklappt, Datenlogger per USB angeschlossen, Skript gestartet, fertig. Der Datenlogger wird als Serial UART am Mac erkannt.
Leider habe ich sonst weiter nicht soo viel Ahnung, wo ich suchen sollte...
Was mache ich falsch?