#include "config.h"
#include "sht11.h"
#define SHT11_CMD_TEMP 0x03
#define SHT11_CMD_HUMID 0x05
#define SHT11_CMD_WSTAT 0x06
#define SHT11_CMD_RSTAT 0x07
#define SHT11_CMD_RESET 0x1E
/////////////////////////////////////////////////////////////////////////////
// This version needs external pullups on SDA!
/////////////////////////////////////////////////////////////////////////////
static void delay(void) { _delay_us(2); }
static void scl_hi(void) { setBits(PORT(SHT11_PORT), SHT11_SCL); }
static void scl_lo(void) { clrBits(PORT(SHT11_PORT), SHT11_SCL); }
static void sda_hi(void) { clrBits(DDR(SHT11_PORT), SHT11_SDA); }
static void sda_lo(void) { setBits(DDR(SHT11_PORT), SHT11_SDA); }
static void scl_pulse(void) { scl_hi(); delay(); scl_lo(); }
static uint8_t sda_val(void) { return (PIN(SHT11_PORT) & SHT11_SDA) != 0; }
/////////////////////////////////////////////////////////////////////////////
static uint8_t crc_value;
static void
crc8(uint8_t b)
{
for (uint8_t i = 0; i < 8; ++i) {
if ((crc_value ^ b) & 0x80) {
crc_value <<= 1;
crc_value ^= 0x31;
} else
crc_value <<= 1;
b <<= 1;
}
}
/////////////////////////////////////////////////////////////////////////////
static uint8_t
send(uint16_t b)
{
crc8(b);
// data
for (uint8_t i = 0; i < 8; ++i) {
if (b & 0x80)
sda_hi();
else
sda_lo();
b <<= 1;
delay();
scl_pulse();
}
// acknowledge
sda_hi();
delay();
uint8_t ack = sda_val();
scl_pulse();
return ack;
}
static uint8_t
recv_data(void)
{
// data
uint8_t b = 0;
for (uint8_t i = 0; i < 8; ++i) {
// data is transmitted MSB first
b <<= 1;
if (sda_val())
b |= 1;
scl_pulse();
delay();
}
// lo acknowledge
sda_lo();
delay();
scl_pulse();
sda_hi();
delay();
crc8(b);
return b;
}
static uint8_t
recv_crc(void)
{
// data
uint8_t b = 0;
for (uint8_t i = 0; i < 8; ++i) {
// CRC is transmitted LSB first
b >>= 1;
if (sda_val())
b |= 0x80;
scl_pulse();
delay();
}
// hi acknowledge
sda_hi();
delay();
scl_pulse();
delay();
return b;
}
static void
start(void)
{
clrBits(PORT(SHT11_PORT), SHT11_SCL | SHT11_SDA); // SCK output low, SDA input/high
setBits(DDR(SHT11_PORT), SHT11_SCL);
clrBits(DDR(SHT11_PORT), SHT11_SDA);
delay();
// reset communication
for (uint8_t i = 0; i < 10; ++i) {
scl_pulse();
delay();
}
// "start" sequence
scl_hi(); delay();
sda_lo(); delay();
scl_lo(); delay();
scl_hi(); delay();
sda_hi(); delay();
scl_lo(); delay();
}
/////////////////////////////////////////////////////////////////////////////
// Measurement sequence.
uint8_t
sht11_start_temp(void)
{
crc_value = SHT11_LOWRES << 7; // bit-reversed
start();
return send(SHT11_CMD_TEMP) == 0;
}
uint8_t
sht11_start_humid(void)
{
crc_value = SHT11_LOWRES << 7; // bit-reversed
start();
return send(SHT11_CMD_HUMID) == 0;
}
uint8_t
sht11_ready(void)
{
return sda_val() == 0;
}
static int16_t
result(void)
{
if (!sht11_ready())
return SHT11_UNAVAIL;
int16_t v = recv_data() << 8; v |= recv_data();
uint8_t crc = recv_crc();
if (crc != crc_value)
return SHT11_CRC_FAIL;
return v;
}
int16_t
sht11_result_temp(void)
{
int16_t v = result();
if (sht11_valid(v)) {
#if SHT11_LOWRES
v = v * 4 - 4000;
#else
v -= 4000;
#endif
}
return v;
}
int16_t
sht11_result_humid(void)
{
int16_t v = result();
if (sht11_valid(v)) {
#if SHT11_LOWRES
// inspired by Werner Hoch, modified for low resolution mode
const int32_t C1 = (int32_t)(-4.0 * 100);
const int32_t C2 = (int32_t)(0.648 * 100 * (1L<<24));
const int32_t C3 = (int32_t)(-7.2e-4 * 100 * (1L<<24));
v = (int16_t)((((C3 * v + C2) >> 7) * v + (1L<<16)) >> 17) + C1;
#else
// inspired by Werner Hoch
const int32_t C1 = (int32_t)(-4.0 * 100);
const int32_t C2 = (int32_t)(0.0405 * 100 * (1L<<28));
const int32_t C3 = (int32_t)(-2.8e-6 * 100 * (1L<<30));
v = (int16_t)((((((C3 * v) >> 2) + C2) >> 11) * v + (1L<<16)) >> 17) + C1;
#endif
}
return v;
}
/////////////////////////////////////////////////////////////////////////////
// Initialize.
void
sht11_init(void)
{
start();
send(SHT11_CMD_RESET);
_delay_ms(11);
start();
send(SHT11_CMD_WSTAT);
send(SHT11_LOWRES);
}