/* Soil Sensor Firmware * Ambient Temperature, Humidity, and Soil Temperature * Sensiplicity Systems * * Copyright 2015 * * Department of Botany and Plant Pathology * Center for Genome Research and Biocomputing * Oregon State University * Corvallis, OR 97331 * * This program is not free software; you can not redistribute it and/or * modify it at all. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #include #include "humidity.h" #include "time.h" #define TMP108_ADDR 0x92 /* TMP108 address with W/R bit set to 0. */ #define PCT2075_ADDR 0x90 /* PCT2075 base address with W/R bit set to 0. */ #define SI7006_ADDR 0x80 /* SI7006 address with W/R bit set to 0. */ /* Shut down power to the I2C peripheral. */ static void shutdown_i2c(void){ PRR |= _BV(PRTWI); } /* Turn on power to the I2C peripheral. */ static void startup_i2c(void){ PRR &= ~_BV(PRTWI); } /* Initialize the I2C bus. */ void init_i2c(void){ TWBR = 2; /* 100kHz clock. */ shutdown_i2c(); } /* Read a 16-bit value (most significant byte first) from the given address. * Returns 0 on success, -1 on error. */ static int8_t read16_i2c(uint8_t addr, uint16_t *data){ TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); /* Send start condition. */ wait_condition(&TWCR, _BV(TWINT), 1, 10); if(0x10 != (TWSR & ~0x03) && 0x08 != (TWSR & ~0x03)){ /* Error */ TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO); /* Send stop condition. */ wait_condition(&TWCR, _BV(TWSTO), 1, 10); TWCR = 0; return -1; } TWDR = addr | 1 ; TWCR = _BV(TWINT) | _BV(TWEN); /* Send address. */ wait_condition(&TWCR, _BV(TWINT), 1, 10); if(0x40 != (TWSR & ~0x03)){ /* Error */ TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO); /* Send stop condition. */ wait_condition(&TWCR, _BV(TWSTO), 1, 10); TWCR = 0; return -1; } TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWEA); /* Continue, and acknowledge data. */ wait_condition(&TWCR, _BV(TWINT), 1, 10); *data = ((uint16_t)TWDR << 8); TWCR = _BV(TWINT) | _BV(TWEN); /* Continue without acknowledge. */ wait_condition(&TWCR, _BV(TWINT), 1, 10); *data |= TWDR; TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO); /* Send stop condition. */ wait_condition(&TWCR, _BV(TWSTO), 1, 10); return 0; } /* Write an 8-bit value to an i2c device, ending with a stop or not. * Returns 0 on success, -1 on error. */ static int8_t write8_i2c(uint8_t addr, uint8_t data, uint8_t stop){ TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); /* Send start condition. */ wait_condition(&TWCR, _BV(TWINT), 1, 10); if(0x08 != (TWSR & ~0x03)){ /* Error */ TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO); /* Send stop condition. */ wait_condition(&TWCR, _BV(TWSTO), 1, 10); TWCR = 0; return -1; } TWDR = addr | 0; TWCR = _BV(TWINT) | _BV(TWEN); /* Send address. */ wait_condition(&TWCR, _BV(TWINT), 1, 10); if(0x18 != (TWSR & ~0x03)){ /* Error */ TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO); /* Send stop condition. */ wait_condition(&TWCR, _BV(TWSTO), 1, 10); TWCR = 0; return -1; } TWDR = data; /* Measure humidity, no hold mode. */ TWCR = _BV(TWINT) | _BV(TWEN); /* Continue. */ wait_condition(&TWCR, _BV(TWINT), 1, 10); if(0x28 != (TWSR & ~0x03)){ /* Error */ TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO); /* Send stop condition. */ wait_condition(&TWCR, _BV(TWSTO), 1, 10); TWCR = 0; return -1; } if(stop){ TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO); /* Send stop condition. */ wait_condition(&TWCR, _BV(TWSTO), 1, 10); } return 0; } /* Read the soil temperature using the old sensor present on * PCB revisions 1 and 2. * The return value is the soil temperature, in 1/16 degrees C. * If an error occurs, -273.125C (-4370) will be returned. */ int16_t get_soil_temp_old(void){ int16_t temp; startup_i2c(); if(read16_i2c(TMP108_ADDR, (uint16_t *) &temp)){ shutdown_i2c(); return -4370; } temp = temp >> 4; /* Move sign bit. */ if(temp & 0x800){ temp &= ~0x0800; temp = -temp; } shutdown_i2c(); return temp; } /* Read the soil temperature in a zone. * The return value is the soil temperature, in 1/16 degrees C. * If an error occurs, -273.125C (-4370) will be returned. */ int16_t get_soil_temp(uint8_t zone){ int16_t temp; switch(zone){ case 0: zone = 0; break; case 1: zone = 1; break; case 2: zone = 3; break; case 3: zone = 7; break; } startup_i2c(); if(read16_i2c(PCT2075_ADDR | (zone << 1), (uint16_t *) &temp)){ shutdown_i2c(); return -4370; } temp = temp >> 4 & ~1; /* The PCT2075 only reads to 1/8C */ /* Move sign bit. */ if(temp & 0x800){ temp &= ~0x0800; temp = -temp; } shutdown_i2c(); return temp; } /* Tell the Si7006 to measure the ambient humidity and temperature. * This process takes about 12ms. * Returns 0 on success, -1 on error. */ int8_t measure_humidity(void){ startup_i2c(); int8_t r = write8_i2c(SI7006_ADDR, 0xF5, 1); shutdown_i2c(); return r; } /* Read the ambient temperature and humidity. measure_humidity() must be called * before this function. * humidity is the ambient relative humidity. 0=0%RH, 4095=100%RH. * temperature is the ambient temperature, in 1/16 degrees C. * If 0 is returned, the data was read successfully. Otherwise, -1 is returned. * This may occur if not enough time has elapsed since calling measure_humidity(). */ int8_t read_humidity(uint16_t *humidity, int16_t *temperature){ uint32_t intermediate_h; int32_t intermediate_t; startup_i2c(); /* Get humidity. */ if(read16_i2c(SI7006_ADDR, humidity)){ shutdown_i2c(); *humidity = 0; *temperature = -4370; return -1; } /* Remove offset. */ if(*humidity < 3145){ *humidity = 0; } else { *humidity -= 3145; } /* Adjust scale. */ intermediate_h = *humidity; intermediate_h = (intermediate_h * 10) / 128; /* intermediate_h /= 12.8; */ *humidity = intermediate_h; /* Get temperature from last measurement. */ if(write8_i2c(SI7006_ADDR, 0xE0, 0)){ shutdown_i2c(); *humidity = 0; *temperature = -4370; return -1; } if(read16_i2c(SI7006_ADDR, (uint16_t *) temperature)){ shutdown_i2c(); *humidity = 0; *temperature = -4370; return -1; } /* Adjust scale. */ intermediate_t = *temperature; intermediate_t = (intermediate_t * 10) / 233; /* intermediate_t /= 23.3 */ /* Remove Offset. */ *temperature = intermediate_t - 750; shutdown_i2c(); return 0; }