atmospi icon indicating copy to clipboard operation
atmospi copied to clipboard

W1ThermSensor module

Open timofurrer opened this issue 9 years ago • 4 comments

Hi,

What about using my W1Thermsensor python module to support more w1therm temperature sensors? https://github.com/timofurrer/w1thermsensor

Cheers :beers:

timofurrer avatar May 13 '15 21:05 timofurrer

Thanks! That might be worth considering! I don't think I'll have time to look into it soon, but if you're inclined to put something together, I would be happy to review pull requests!

mstenta avatar May 26 '15 22:05 mstenta

I started using W1Thermsensor when my temperature logger developed a hardware problem and I needed test and error handling features not present in measure-ds18b20.py. I replaced measure-ds18b20.py with my own code. Unfortunately this program is no longer compatible with Atmospi as I've had to make a number of fundamental changes in order to meet some of my requirements. I now declare all sensors in a separate file, including HighCharts presentation parameters and no longer use the device table.

Although for reasons stated, I can't turn this into a pull request, I can provide some of the code [or all of it if there's interest].

The code of logger.py, the measure-ds18b20.py replacement:

#!/usr/bin/python
# -*- coding: utf-8 -*-

version = "v1.01"

import sys
import time
import datetime
import logging
import logging.handlers
import argparse
import sqlite3 as lite

from w1thermsensor import W1ThermSensor, W1ThermSensorError, NoSensorFoundError, SensorNotReadyError
from settings import settings
sensors = settings['sensor_definitions']    # pointer to sensor definitions in 'settings'
from sensors import ds18b20


def store_temperatures(sensors, sensor_family, temperatures):

    try:
        db = lite.connect(settings['db'])
        cursor = db.cursor()

        # read epoch time as an integer.
        timestamp = int(time.time())

        # Iterate through the devices.
        for i in range(0, len(sensors)):
            if sensors[i].connected:

                # store the temperatures in the database
                id = sensor_family + sensors[i].id  # prepend the device family name
                t = temperatures[id]
                logging.debug("--> %3s %s %6.3f", sensors[i].n, timestamp, t['C'])
                cursor.execute("INSERT INTO Temperature (DeviceID, Timestamp, C) VALUES(" + str(sensors[i].n) + ", " + str(timestamp) + ", " + str(t['C']) + ")")

        # commit the changes to the database
        db.commit()

    except lite.Error, e:
        if db:
            db.rollback()
        logging.error("DB ---> %s" % e.args[0])
        sys.exit(1)

    finally:
        if db:
            db.close()


def main():
    # setup argument parsing
    argparser = argparse.ArgumentParser(description='Test program for DS18B20 temperature sensors')
    argparser.add_argument('--mode', '-m', default='MONITOR',
                           help='Mode: {TEST, MONITOR, CRON, LIST}')
    argparser.add_argument('--logfile', '-lf', help='Log file:')
    argparser.add_argument('--loglevel', '-ll', default='WARNING',
                           help='Log level: {DEBUG, INFO, WARNING, ERROR, CRITICAL}')

    # get command line arguments
    cmd_args = argparser.parse_args()

    # configure the logger
    logger = logging.getLogger()
    loglevel = getattr(logging, cmd_args.loglevel.upper(), None)

    # get the program mode
    mode = cmd_args.mode.upper()

    if not isinstance(loglevel, int):
        sys.exit('ABORT: unknown log level: %s' % cmd_args.loglevel)
    else:
        logger.setLevel(loglevel)

    if cmd_args.logfile:
        handler = logging.handlers.RotatingFileHandler(cmd_args.logfile,
                                                       mode='a',
                                                       maxBytes=1048576,
                                                       backupCount=10,
                                                       delay=True)
    else:
        handler = logging.StreamHandler()

    logging_format = '%(asctime)15s %(levelname)5s-%(message)s'
    time_stamp_format = '%Y-%m-%d %H:%M:%S'
    formatter = logging.Formatter(fmt=logging_format, datefmt=time_stamp_format)
    handler.setFormatter(formatter)
    logger.addHandler(handler)

    logging.debug('ARGUMENTS: %s', cmd_args)

    # execute main code
    now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    degree = u'\N{DEGREE SIGN}'
    degreeCelsius = u'{0}'.format(degree).encode('ISO-8859-1') + "C"
    sensor_family = '28-'  # prepend '28-' for Atmospi compatibility
    sensors = ds18b20['ds18b20']  # get the list of all known DS18B20's from sensors

    if mode == "LIST":
        print '{:2s} {:13s} {:26s} {:12s} {:15s}'.format(" #", "ID", "NAME", "CALIBRATION", "CONNECTED")
        print "------------------------------------------------------------------"
        for i in range(0, len(sensors)):
            print '{:2} {:13s} {:20s} {:11.3f} {:16}'.format\
                (sensors[i].n, sensors[i].id, sensors[i].name, sensors[i].calibration, sensors[i].connected)
        print "------------------------------------------------------------------"
        sys.exit(0)

    loop = True

    while loop:
        test_set = ""  # create an empty set of temperature measurement result indicators
        temperatures = {}
        # "." = measurement successful
        # "x" = sensor not found error
        # "!" = sensor not ready error
        # sample test_set line: '2015-05-06 12:45:20 ..x....'
        # indicating that the 3rd connected sensor was not found; in this case sensor 000005fab05d
        for i in range(0, len(sensors)):
            if sensors[i].connected:
                now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                try:
                    sensor = W1ThermSensor(W1ThermSensor.THERM_SENSOR_DS18B20, sensors[i].id)  # select a specific sensor
                    temperature = sensor.get_temperature() + sensors[i].calibration  # read the temperature for this sensor
                    if mode == 'CRON':
                        temperatures[sensor_family + sensors[i].id] = {'C': temperature}
                    elif mode == 'TEST':
                        test_set += "."
                    elif mode == 'MONITOR':
                        print '{:20s} {:17s} {:6.3f} {:6.3f}'.format(now, sensors[i].name, temperature, sensors[i].calibration)

                except NoSensorFoundError as e:
                    logging.error("NOTFOUND ---> %s, %s " % (sensors[i].id, sensors[i].name))
                    test_set += "x"
                except SensorNotReadyError as e:
                    logging.error("NOTREADY ---> %s, %s " % (sensors[i].id, sensors[i].name))
                    test_set += "!"
        if mode == 'CRON':
            #print temperatures  # placeholder for database insertion
            store_temperatures(sensors, sensor_family, temperatures)
        elif mode == 'TEST':
            print now, test_set  # print the measurement set to STDOUT
        elif mode == 'MONITOR':
            print '----------------------------------------------------'

        # check if looping is required
        if mode == 'TEST' or mode == 'MONITOR':  # infinite loop unless mode == 'CRON'
            loop = True
        elif mode == 'CRON':  # terminate if mode == 'CRON'
            loop = False
        else:
            logging.error("INVALIDMODE ---> '%s'", cmd_args.mode)
            sys.exit(1)


if __name__ == '__main__':
    main()

}

The settings file:

#!/usr/bin/python
# -*- coding: utf-8 -*-

version = "v1.06"

settings = {
    # general global settings
    'db': '/home/pi/loggerdev/logger.db',   # absolute path to the SQLite database file
    'sensor_definitions': 'sensors',        # sensor definitions to be used
    'range_seconds': 7 * 24 * 3600,         # how far into the past should data be loaded (in seconds)?
    'precision': 3,                         # number of digits after the decimal point to be used for database loading
    't_unit': 'C',                          # default temperature unit

    # HighCharts presentation parameters
    'loading': 'loading...',                # text to show when HighCharts is building the graph
    'height': 800,                          # plot height in pixels
    'ceiling': 80,                          # maximum value on Y axis
    'backgroundColor': '#FFFFF1',           # color of the plot background
    'lineType': 'spline',                   # default line type
    'fillOpacity': 0.10,                    # default opacity of area splines
    'zoomType': 'xy',                       # zoom on both axes supported
    'legendLayout': 'horizontal',           # horizontal legend below X axis

    # HighCharts plotbands
    'freezing': [-20, 0, 'rgba(110, 165, 221, 0.8)'],
    'non_condensing': [55, 80, '#FFF0D1'],
}

The sensors file:

#!/usr/bin/python
# -*- coding: utf-8 -*-

version = "v1.01"

class TemperatureSensor:
    def __init__(self,
                 n=0,
                 id="",
                 name="",
                 calibration=0.0,
                 connected=1,
                 legendIndex=0,
                 seriesType="spline",
                 lineColor="rgba(215, 44, 44, 1)"):
        self.n = n
        self.id = id
        self.name = name
        self.calibration = calibration
        self.connected = connected
        self.legendIndex = legendIndex
        self.seriesType = seriesType
        self.lineColor = lineColor

# declare all known DS18B20 temperature sensors
ds18b20_list = [TemperatureSensor(1, "000005e4d76b", "radiator aanvoer", -0.375, 1, 5, "spline", "rgba(0, 204, 0, 0.3)"),
                TemperatureSensor(2, "000005e4f2fd", "radiator retour", -0.125, 1, 6, "areaspline", "rgba(0, 204, 0, 0.3)"),
                TemperatureSensor(3, "000005e5f606", "CV retour", -1.375, 1, 2, "spline", "rgba(0, 140, 255, 0.3)"),
                TemperatureSensor(4, "000005e65e0c", "---NOT CONNECTED---", 0.0, 0),
                TemperatureSensor(5, "000005e77695", "tapwater", -1.375, 1, 7, "spline", "rgba(255, 200, 200, 0.4)"),
                TemperatureSensor(6, "000005fab05d", "CV ruimte", 0.063, 1, 9, "spline", "rgba(73, 46, 194, 0.3)"),
                TemperatureSensor(7, "000005fab2e0", "vloer aanvoer", 0.188, 1, 3, "spline", "#FF9900"),
                TemperatureSensor(8, "000005fab89c", "vloer retour", -0.125, 1, 4, "areaspline", "rgba(250, 187, 0, 0.3)"),
                TemperatureSensor(9, "000005fb03d6", "CV aanvoer", -0.750, 1, 1, "spline", "rgba(255, 0, 0, 0.5)"),
                TemperatureSensor(10, "000005fb1b38", "---NOT CONNECTED---", 0.0, 0),
                TemperatureSensor(11, "0000061be3e2", "buitentemperatuur", 0.078, 1, 11, "spline", "rgba(179, 179, 179, 0.5)")]

ds18b20 = {

    # This is the list of declared DS18B20 temperature sensors
    'ds18b20': ds18b20_list,

}

This code has allowed me to find the problem in my logger [which turned out to be a faulty Pi] and has been collecting data for a few weeks without any problems.

ultima-originem avatar May 27 '15 22:05 ultima-originem

@timofurrer Any particular reason for closing this? I'm still open to it - just haven't worked on Atmospi in a bit.

mstenta avatar Feb 17 '16 21:02 mstenta

It was more an accident than intend.. I'll reopen

timofurrer avatar Feb 17 '16 21:02 timofurrer