Seite 1 von 1

modul serial nicht gefunden

Verfasst: Freitag 28. Februar 2020, 18:48
von shivahoj
Hallo,
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()

wenn ich das aufrufe, passiert das:

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
Das folgende habe ich schon alles versucht, jeweils gefolgt vom Aufruf obigen Python-Skriptes.(ich habe in einem anderen Thread hier was von venvs gelesen, weiss aber nicht, was das ist. Auch las ich, dass man Version deinstallieren soll, weil sonst "falsche"(alte? inkompatible?) module aufgerufen werden.

Code: Alles auswählen

pip uninstall pyserial
sudo easy_install pip
pip uninstall pyserial
pip install pyserial
pip uninstall serial
pip install serial
Wie gesagt, danach jedesmal(erfolglos) danach das Skript gestartet.
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?

Re: modul serial nicht gefunden

Verfasst: Freitag 28. Februar 2020, 19:40
von sparrow
Offensichtlich hast du 2 verchiedene Python-Versionen installiert. Eine Python Version 2.x und eine Python Version 3.x. Das Script ruft explizit Python 3 auf. Mit pip kann man Module installieren, die Frage ist nur: Insalliert dein Pip für Python 2 oder Python 3.
Versuch mal das explizite Intallieren für Python 3:

Code: Alles auswählen

python3 -m pip install SomePackage

Re: modul serial nicht gefunden

Verfasst: Freitag 28. Februar 2020, 21:56
von shivahoj
Danke!
Das hat geholfen, jetzt rennt das Skript:

Code: Alles auswählen

Macbook17:dl141th dirk$ python3 -m pip install pyserial
Collecting pyserial
Using cached https://files.pythonhosted.org/packages/0d/e4/2a744dd9e3be04a0c0907414e2a01a7c88bb3915cbe3c8cc06e209f59c30/pyserial-3.4-py2.py3-none-any.whl
Installing collected packages: pyserial
Successfully installed pyserial-3.4
Macbook17:dl141th dirk$ ./dl141th.py
usage: dl141th.py [-h] -d DEVICE [-v] [-r file] [-t] [-i interval unit] [-s]
dl141th.py: error: the following arguments are required: -d/--device/--port
Macbook17:dl141th dirk$ ./dl141th.py -h
usage: dl141th.py [-h] -d DEVICE [-v] [-r file] [-t] [-i interval unit] [-s]

Configure and/or read out the DL-141 TH data logger

optional arguments:
  -h, --help            show this help message and exit
  -d DEVICE, --device DEVICE, --port DEVICE
                        name of serial port device, e.g. /dev/ttyUSB0 for
                        Linux or COM3 for Windows
  -v, --verbose         synchronize time with local time of PC
  -r file, --read file  read data and store in file
  -t, --sync_time       synchronize time with local time of PC
  -i interval unit, --interval interval unit
                        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
  -s, --start           start logging immediately. interval must be specified!
Macbook17:dl141th dirk$