/** * Copyright (c) 1992-2009 Stefan Bethke. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Schlepperbande: src/temper/temper.c,v 1.3 2010/01/01 20:27:13 stb Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #define TEMPER_LM75_ADDRESS 0x9e #define LM75_POINTER_TEMPERATURE 0 #define LM75_POINTER_CONTROL 1 #define LM75_POINTER_T_HYST 2 #define LM75_POINTER_T_OS 3 #define LM75_CONTROL_POLARITY 0x04 #define LM75_CONTROL_RESOLUTION_09BIT 0x00 #define LM75_CONTROL_RESOLUTION_10BIT 0x20 #define LM75_CONTROL_RESOLUTION_11BIT 0x40 #define LM75_CONTROL_RESOLUTION_12BIT 0x60 /* * Basic I2C bit-banging functions. iic_start(), iic_read_bit(), and iic_write_bit() * always end just before the falling edge of SCL is due. */ struct iic_port { int fd; int sleep; }; #if defined(IIC_DEBUG) char scl_debug[1024]; char sdo_debug[1024]; char sdi_debug[1024]; static void debug_init() { scl_debug[0] = '\0'; sdo_debug[0] = '\0'; sdi_debug[0] = '\0'; } static void debug_add(int scl, int sdo, int sdi) { strcat(scl_debug, scl ? "\xe2\x80\xbe" : "_"); strcat(sdo_debug, sdo ? "\xe2\x80\xbe" : "_"); strcat(sdi_debug, sdi ? "\xe2\x80\xbe" : "_"); } static void debug_marker() { strcat(scl_debug, "\xc2\xb7"); strcat(sdo_debug, "\xc2\xb7"); strcat(sdi_debug, "\xc2\xb7"); } static void debug_print() { printf("\nscl %s\nsdo %s\nsdi %s\n", scl_debug, sdo_debug, sdi_debug); debug_init(); } #endif static int iic_scl_sda(struct iic_port *iic, int scl, int sda) { int s; if (ioctl(iic->fd, TIOCMGET, &s) < 0) err(EX_OSERR, "ioctl(TIOCMGET)"); if (scl) s |= TIOCM_DTR; else s &= ~TIOCM_DTR; if (sda) s |= TIOCM_RTS; else s &= ~TIOCM_RTS; if (ioctl(iic->fd, TIOCMSET, &s) < 0) err(EX_OSERR, "ioctl(TIOCMSET)"); s = (s & TIOCM_CTS) ? 1 : 0; #if defined(IIC_DEBUG) debug_add(scl, sda, s); #endif return s; } static void iic_start(struct iic_port *iic) { #if defined(IIC_DEBUG) debug_marker(); #endif iic_scl_sda(iic, 1, 1); usleep(iic->sleep); iic_scl_sda(iic, 1, 0); usleep(iic->sleep); } static void iic_stop(struct iic_port *iic) { #if defined(IIC_DEBUG) debug_marker(); #endif iic_scl_sda(iic, 0, 0); usleep(iic->sleep); iic_scl_sda(iic, 1, 0); usleep(iic->sleep); iic_scl_sda(iic, 1, 1); usleep(iic->sleep); } static int iic_read_bit(struct iic_port *iic) { int v; iic_scl_sda(iic, 0, 1); usleep(iic->sleep); v = iic_scl_sda(iic, 1, 1); usleep(iic->sleep); return v; } static void iic_write_bit(struct iic_port *iic, int v) { iic_scl_sda(iic, 0, v); usleep(iic->sleep); iic_scl_sda(iic, 1, v); usleep(iic->sleep); } static uint8_t iic_read_byte(struct iic_port *iic, int last) { int i; uint8_t v; #if defined(IIC_DEBUG) debug_marker(); #endif for (i = 0, v = 0; i < 8; i++) { v = (v << 1) | iic_read_bit(iic); } #if defined(IIC_DEBUG) debug_marker(); #endif iic_write_bit(iic, last); return v; } static int iic_write_byte(struct iic_port *iic, uint8_t v) { int i; #if defined(IIC_DEBUG) debug_marker(); #endif for (i = 0; i < 8; i++) { iic_write_bit(iic, v & 0x80); v <<= 1; } #if defined(IIC_DEBUG) debug_marker(); #endif return iic_read_bit(iic); } static int iic_read(struct iic_port *iic, uint8_t a, int l, uint8_t *b) { int r; iic_start(iic); if ((r = iic_write_byte(iic, a | 0x01) ? -1 : 0)) goto end; for (; l > 0; l--, b++, r++) { *b = iic_read_byte(iic, l==0); } end: iic_stop(iic); #if defined(IIC_DEBUG) debug_print(); #endif return r; } static int iic_write(struct iic_port *iic, uint8_t a, int l, const uint8_t *b) { int r; iic_start(iic); if ((r = iic_write_byte(iic, a & 0xfe) ? -1 : 0)) goto end; for (; l > 0; l--, b++, r++) { if (iic_write_byte(iic, *b)) goto end; } end: iic_stop(iic); #if defined(IIC_DEBUG) debug_print(); #endif return r; } /* * Talk to the LM75. */ static int lm75_set_control(struct iic_port *iic, uint8_t a, uint8_t c) { uint8_t b[2]; b[0] = 1; b[1] = c; return iic_write(iic, a, 2, b) != 2; } static int lm75_set_pointer(struct iic_port *iic, uint8_t a, uint8_t p) { return iic_write(iic, a, 1, &p) != 1; } static uint16_t lm75_read_word(struct iic_port *iic, uint8_t a) { uint8_t v[2]; int r; r = iic_read(iic, a, 2, v); if (r != 2) { return 0xffff; } return v[0] << 8 | v[1]; } static int lm75_set(struct iic_port *iic, uint8_t a, uint8_t p, uint16_t v) { uint8_t b[3]; b[0] = p; b[1] = v >> 8; b[2] = v & 0xff; return iic_write(iic, a, 3, b) != 3; } void temper_init(struct iic_port *iic, double high, double low) { int16_t h, l; uint8_t c; #if defined(IIC_DEBUG) debug_init(); #endif iic->sleep = 5000; iic_stop(iic); usleep(10000); iic_read(iic, 0x00, 0, (uint8_t *)""); lm75_set_pointer(iic, TEMPER_LM75_ADDRESS, 0); usleep(10000); c = LM75_CONTROL_RESOLUTION_12BIT; if (high > low) { h = high * 256; l = low * 256; } else { h = low * 256; l = high * 256; c |= LM75_CONTROL_POLARITY; } if ( lm75_set_control(iic, TEMPER_LM75_ADDRESS, c) || lm75_set(iic, TEMPER_LM75_ADDRESS, LM75_POINTER_T_OS, h) || lm75_set(iic, TEMPER_LM75_ADDRESS, LM75_POINTER_T_HYST, l) || lm75_set_pointer(iic, TEMPER_LM75_ADDRESS, 0)) { fprintf(stderr, "Can't initialize TEMPer.\n"); exit(1); } } double temper_read_temp(struct iic_port *iic) { int16_t v; double t; v = lm75_read_word(iic, TEMPER_LM75_ADDRESS); if (v & 0x8000) { v |= 0x000f; /* sign-extend lower bits */ } t = v / 256.0; return t; } static void usage() { fprintf(stderr, "usage: temper [-d device] [-h high] [-l low] [-n count]\n" " -d device USB serial port of the TEMPer\n" " -h high thermostat on temperature\n" " -l low thermostat off temperature\n" " -n count number of readings to take, <0 is infinite\n" "If the high temperature is below the low temperature, operation of the\n" "thermostat is reversed, and the output is turned on when the measured\n" "temperature is below the threshold.\n" ""); exit(EX_USAGE); } int main(int argc, char **argv) { int c; int count = -1; double low = 35; double high = 45; struct iic_port iic; char *dev = "/dev/cuaU0"; while ((c = getopt(argc, argv, "d:h:l:n:?")) != -1) { switch (c) { case 'd': dev = optarg; break; case 'h': high = strtod(optarg, NULL); break; case 'l': low = strtod(optarg, NULL); break; case 'n': count = atoi(optarg); break; case '?': default: usage(); } } argc -= optind; argv += optind; if (argc != 0) usage(); iic.fd = open(dev, 0); if (iic.fd < 0) err(EX_OSERR, "can't open %s", dev); temper_init(&iic, high, low); if (count < 0) { while (1) { printf("%4.1lf\n", temper_read_temp(&iic)); sleep(1); } } else { while (count > 0) { printf("%03.2lf\n", temper_read_temp(&iic)); count--; sleep(1); } } return 0; }