From 2349fc1add0c1f5d2b38dafb8d52d3fc473b6abb Mon Sep 17 00:00:00 2001 From: Frederik Lindenaar <jfl@schutler.net> Date: Sun, 3 Mar 2019 20:14:51 +0100 Subject: [PATCH] Added support for I2C MCP9808 sensor (closes #4) based on https://github.com/ControlEverythingCommunity/MCP9808/blob/master/Python/MCP9808.py --- plugins/check_temperature | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 86 insertions(+), 20 deletions(-) diff --git a/plugins/check_temperature b/plugins/check_temperature index 34a106f..450758a 100755 --- a/plugins/check_temperature +++ b/plugins/check_temperature @@ -2,10 +2,10 @@ # # check_temperature - Nagios temperature check for DS18B20 sensor on RaspberryPi # -# Version 1.1, latest version, documentation and bugtracker available at: +# Version 1.2, latest version, documentation and bugtracker available at: # https://gitlab.lindenaar.net/scripts/nagios-plugins # -# Copyright (c) 2017 Frederik Lindenaar +# Copyright (c) 2017 - 2019 Frederik Lindenaar # # This script is free software: you can redistribute and/or modify it under the # terms of version 3 of the GNU General Public License as published by the Free @@ -27,15 +27,21 @@ from argparse import ArgumentParser as StandardArgumentParser, FileType, \ import logging # Constants (no need to change but allows for easy customization) -VERSION="1.1" +VERSION="1.2" PROG_NAME=splitext(basename(__file__))[0] PROG_VERSION=PROG_NAME + ' ' + VERSION -SENSOR_SCALE=1000 + +CPU_SENSOR_DEV = '/sys/class/thermal/thermal_zone0/temp' +I2C_MCP9808_CONFIG_ADDR=0x1 +I2C_MCP9808_CONFIG = [ 0x00, 0x00 ] # continuous conversion (power-up default) +I2C_MCP9808_PRECISION_ADDR=0x08 +I2C_MCP9808_PRECISION=3 # 0=0.5, 1=0.25, 2=0.125, 3=0.0625 degr. C +I2C_MCP9808_TEMP_ADDR=0x05 W1_SENSOR_DEV_DIR = '/sys/bus/w1/devices/' W1_SENSOR_DEV_PREFIX = '28-' W1_SENSOR_DEV_SUFFIX = '/w1_slave' W1_SENSOR_READ_RETRIES=10 -CPU_SENSOR_DEV = '/sys/class/thermal/thermal_zone0/temp' +W1_SENSOR_SCALE=1000 LOG_FORMAT='%(levelname)s - %(message)s' LOG_FORMAT_FILE='%(asctime)s - ' + LOG_FORMAT @@ -81,15 +87,15 @@ class SetLogFile(StoreAction): ############################################################################### -def convert_celcius(temp_read): +def convert_celcius(temp_read, scale = 1): """Converts raw temperature sensore value to degrees Celcius""" - return float(temp_read) / float(SENSOR_SCALE) + return float(temp_read) / float(scale) CONVERT_CELCIUS = ( convert_celcius, 'C', 'Celcius' ) -def convert_farenheit(temp_read): +def convert_farenheit(temp_read, scale = 1): """Converts raw temperature sensore value to degrees Farenheit""" - return float(temp_read * 9) / float(5 * SENSOR_SCALE) + 32.0 + return float(temp_read * 9) / float(5 * scale) + 32.0 CONVERT_FARENHEIT = ( convert_farenheit, 'F', 'Farenheit' ) @@ -98,6 +104,11 @@ def isempty(string): return string is None or len(string) == 0 +def hex_int(string): + """Use int()'s auto-detection to parse 10-base and 16-base (0x..) numbers""" + return int(string, 0); + + def read_rpi_cpu_temp(args): """Reads CPU temperature and converts it to desired unit, returns temperature""" with open(args.file, 'r') as f: @@ -111,6 +122,51 @@ def read_rpi_cpu_temp(args): return temp, 1 +def read_i2c_mcp9808_temp(args): + """Returns temperature from I2C MCP9808 sensor in desired unit""" + + try: + import smbus + except ImportError: + try: + import smbus2 as smbus + except ImportError: + logger.critical("Unable to import either smbus or smbus2 library"); + raise ImportError("missing I2C library, please install python-smbus or smbus2"); + + try: + bus = smbus.SMBus(args.i2cbus) # Get I2C bus + except OSError as e: + logger.critical(e) + raise IOError("Invalid I2C bus: %d" % args.i2cbus) + + try: + bus.write_i2c_block_data(args.address, I2C_MCP9808_CONFIG_ADDR, + I2C_MCP9808_CONFIG) + bus.write_byte_data(args.address, I2C_MCP9808_PRECISION_ADDR, + I2C_MCP9808_PRECISION) + sleep(0.5) + + # Read temperature (0x05), 2 bytes (MSB, LSB) + data = bus.read_i2c_block_data(args.address, I2C_MCP9808_TEMP_ADDR, 2) + logger.debug('Temperature sensor data from MCP9808 %#02x: 0x%02x%02x', + args.address, data[0], data[1]) + + # Convert the data to 13-bits + temp_read = ((data[0] & 0x1F) * 256) + data[1] + if temp_read > 4095 : + temp_read -= 8192 + + temp = args.converter[0](temp_read, 16) + logger.debug('Temperature sensor value %d is %.2f%s', + temp_read, temp, args.converter[1]) + return temp, 1 + except IOError as e: + logger.critical(e) + raise IOError("Error while communicating with I2C device %#02x" % + args.address) + + def get_w1_sensor_device_filename(args, dev_dir=W1_SENSOR_DEV_DIR, prefix=W1_SENSOR_DEV_PREFIX, suffix=W1_SENSOR_DEV_SUFFIX): """Auto-determine sensor datafile name (unless args.file is set)""" @@ -138,8 +194,8 @@ def get_w1_sensor_device_filename(args, dev_dir=W1_SENSOR_DEV_DIR, return filename -def read_w1_temp(args): - """Reads sensor data and converts it to desired unit, returns temperature""" +def read_w1_ds18b20_temp(args): + """Returns temperature from 1-wire ds18b20 sensor in desired unit""" device_file = get_w1_sensor_device_filename(args) lines=[ '' ] tries = 0 @@ -160,7 +216,7 @@ def read_w1_temp(args): errmsg = 'temperature sensor data format is not supported' else: temp_read = int(lines[1][equals_pos+2:]) - temp = args.converter[0](temp_read) + temp = args.converter[0](temp_read, W1_SENSOR_SCALE) logger.debug('Temperature sensor value %d is %.2f%s', temp_read, temp, args.converter[1]) return temp, tries @@ -174,11 +230,7 @@ def parse_args(): # Setup argument parser, the workhorse gluing it all together parser = ArgumentParser( - epilog='(*) by default the script will look for the first device that ' - 'matches %s* in %s, if multiple entries are found -s or -f must ' - 'be used to specify which sensor to read.' % - (W1_SENSOR_DEV_PREFIX, W1_SENSOR_DEV_DIR), - description='Nagios check plugin for 1-wire temp. sensor on RaspberryPi' + description='Nagios check plugin for temperature sensors on RaspberryPi' ) parser.add_argument('-V', '--version',action="version",version=PROG_VERSION) @@ -218,6 +270,16 @@ def parse_args(): help='read built-in Raspberry Pi CPU temperature') cmdparser.set_defaults(func=read_rpi_cpu_temp, cmdparser=cmdparser, retries=0) + i2cparser = ArgumentParser(add_help=False) + i2cparser.add_argument('-a', '--address', type=hex_int, + help='I2C Address of sensor, use 0x.. for hex (*)') + i2cparser.add_argument('-b', '--i2cbus', default=1, type=int, + help='I2C Bus to use (defaults to 1)') + cmdparser = subparser.add_parser('i2c_mcp9808', parents=[i2cparser], + help='read I2C connected MCP9808 sensor', + epilog='(*) default I2C address for an MCP9808 is 0x18') + cmdparser.set_defaults(func=read_i2c_mcp9808_temp, cmdparser=cmdparser, retries=0, address=0x18) + w1parser = ArgumentParser(add_help=False) pgroup = w1parser.add_mutually_exclusive_group(required=False) pgroup.add_argument('-s', '--serial', @@ -228,8 +290,12 @@ def parse_args(): help='number of times to retry reading sensor data when' ' unstable (defaults to %d)' % W1_SENSOR_READ_RETRIES) cmdparser = subparser.add_parser('w1_ds18b20', parents=[w1parser], - help='read 1-wire connected DS18b20 sensor') - cmdparser.set_defaults(func=read_w1_temp, cmdparser=cmdparser) + help='read 1-wire connected DS18b20 sensor', + epilog='(*) by default the script will look for the first device that ' + 'matches %s* in %s, if multiple entries are found -s or -f must ' + 'be used to specify which sensor to read.' % + (W1_SENSOR_DEV_PREFIX, W1_SENSOR_DEV_DIR)) + cmdparser.set_defaults(func=read_w1_ds18b20_temp, cmdparser=cmdparser) # parse arguments and post-process command line options args = parser.parse_args() @@ -268,7 +334,7 @@ if __name__ == '__main__': except (KeyboardInterrupt) as e: nagios_exit(NAGIOS_UNKNOWN,'temperature sensor read aborted by user') - except (IOError, ValueError) as e: + except (IOError, ValueError, ImportError) as e: nagios_exit(NAGIOS_UNKNOWN,'temperature sensor read failed: %s' % e) elapse = endtime-starttime -- libgit2 0.22.2