diff --git a/SI7005_HTU21D/Makefile b/SI7005_HTU21D/Makefile new file mode 100644 index 0000000..530d862 --- /dev/null +++ b/SI7005_HTU21D/Makefile @@ -0,0 +1,57 @@ +# run `make DEF=...` to add extra defines +PROGRAM := sihtu +LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all +LDFLAGS += -lusefull_macros -lm +SRCS := $(wildcard *.c) +DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111 +OBJDIR := mk +CFLAGS += -O2 -Wall -Wextra -Wno-trampolines -std=gnu99 +OBJS := $(addprefix $(OBJDIR)/, $(SRCS:%.c=%.o)) +DEPS := $(OBJS:.o=.d) +TARGFILE := $(OBJDIR)/TARGET +CC = gcc +#TARGET := RELEASE + +ifeq ($(shell test -e $(TARGFILE) && echo -n yes),yes) + TARGET := $(file < $(TARGFILE)) +else + TARGET := RELEASE +endif + +ifeq ($(TARGET), DEBUG) + .DEFAULT_GOAL := debug +endif + +release: $(PROGRAM) + +debug: CFLAGS += -DEBUG -Werror +debug: TARGET := DEBUG +debug: $(PROGRAM) + +$(TARGFILE): $(OBJDIR) + @echo -e "\t\tTARGET: $(TARGET)" + @echo "$(TARGET)" > $(TARGFILE) + +$(PROGRAM) : $(TARGFILE) $(OBJS) + @echo -e "\t\tLD $(PROGRAM)" + $(CC) $(OBJS) $(LDFLAGS) -o $(PROGRAM) + +$(OBJDIR): + @mkdir $(OBJDIR) + +ifneq ($(MAKECMDGOALS),clean) +-include $(DEPS) +endif + +$(OBJDIR)/%.o: %.c + @echo -e "\t\tCC $<" + $(CC) $< -MD -c $(LDFLAGS) $(CFLAGS) $(DEFINES) -o $@ + +clean: + @echo -e "\t\tCLEAN" + @rm -rf $(OBJDIR) 2>/dev/null || true + +xclean: clean + @rm -f $(PROGRAM) + +.PHONY: clean xclean diff --git a/SI7005_HTU21D/Readme b/SI7005_HTU21D/Readme new file mode 100644 index 0000000..0354091 --- /dev/null +++ b/SI7005_HTU21D/Readme @@ -0,0 +1,2 @@ +Code to work with I2C T/RH sensors SI7005 or HTU21D (can't work together due to identical +addresses) diff --git a/SI7005_HTU21D/htu21d.c b/SI7005_HTU21D/htu21d.c new file mode 100644 index 0000000..e594571 --- /dev/null +++ b/SI7005_HTU21D/htu21d.c @@ -0,0 +1,166 @@ +/* + * This file is part of the sihtu project. + * Copyright 2022 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "htu21d.h" +#include "i2c.h" + +// read with no hold master! +// REGISTERS/COMANDS: +#define HTU21_READ_TEMP (0xF3) +#define HTU21_READ_HUMID (0xF5) +#define HTU21_READ_USERREG (0xE7) +#define HTU21_WRITE_USERREG (0xE6) +#define HTU21_SOFT_RESET (0xFE) +// status mask & val +#define HTU21_STATUS_MASK (0x03) +#define HTU21_HUMID_FLAG (0x02) +// user reg fields +#define HTU21_REG_VBAT (0x40) +#define HTU21_REG_D1 (0x80) +#define HTU21_REG_D0 (0x01) +#define HTU21_REG_HTR (0x04) +#define HTU21_REG_ODIS (0x02) +#define HTU21_REG_VBAT (0x40) +#define HTU21_REG_DEFVAL (0x02) + +static HTU21D_status htustatus = HTU21D_RELAX; +static float Tmeasured, Hmeasured; +static double lastw = 0.; // last time of measurements start + +HTU21D_status HTU21D_get_status(){ + return htustatus; +} + +static int writecmd(uint8_t cmd){ + if(1 != write(i2c_getfd(), &cmd, 1)) return FALSE; + return TRUE; +} + +/** + * @brief HTU21D_read_ID - read user register and compare with default + * @return TRUE if all OK + */ +int HTU21D_read_ID(){ + if(htustatus != HTU21D_RELAX) return FALSE; + uint8_t ID; + if(!i2c_read_reg8(HTU21_READ_USERREG, &ID)){ + DBG("Can't read HTU_REG_ID"); + return FALSE; + } + DBG("HTU, reg: 0x%02x", ID); + if(ID != HTU21_REG_DEFVAL){ + DBG("Not HTU21D or need reloading\n"); + writecmd(HTU21_SOFT_RESET); + return FALSE; + } + return TRUE; +} + +int HTU21D_startmeasure(){ + htustatus = HTU21D_BUSY; + if(!writecmd(HTU21_READ_TEMP)){ + htustatus = HTU21D_ERR; + return FALSE; + } + DBG("Wait for T\n"); + lastw = dtime(); + return TRUE; +} + +static void HTU21D_cmdH(){ + htustatus = HTU21D_BUSY; + if(!writecmd(HTU21_READ_HUMID)){ + htustatus = HTU21D_ERR; + return; + } + DBG("Wait for H, dt=%g", dtime() - lastw); + lastw = dtime(); +} + +int HTU21D_getTH(float *T, float *H){ + if(htustatus != HTU21D_RDY) return FALSE; + if(T) *T = Tmeasured; + if(H) *H = Hmeasured; + htustatus = HTU21D_RELAX; + return TRUE; +} + +#define SHIFTED_DIVISOR 0x988000 //This is the 0x0131 polynomial shifted to farthest left of three bytes +// check CRC, return 0 if all OK +static uint32_t htu_check_crc(uint8_t *crc){ + DBG("HTU check CRC\n"); + uint32_t remainder = (crc[0] << 16) | (crc[1] << 8) | crc[2]; + uint32_t divsor = (uint32_t)SHIFTED_DIVISOR; + int i; + for(i = 0; i < 16; i++) { + if (remainder & (uint32_t)1 << (23 - i)) + remainder ^= divsor; + divsor >>= 1; + } + return remainder; +} + +void HTU21D_process(){ + uint8_t d[3]; + if(htustatus != HTU21D_BUSY) return; + if(3 != read(i2c_getfd(), d, 3)){ // NACK'ed - not ready + if(dtime() - lastw > HTU21D_CONVTIMEOUT){ + DBG("Wait too long -> err"); + htustatus = HTU21D_ERR; + return; + } + return; + } + DBG("Got: 0x%02x, 0x%02x, 0x%02x", d[0], d[1], d[2]); + if(htu_check_crc(d)){ + htustatus = HTU21D_ERR; + DBG("CRC failed\n"); + return; + } + uint16_t TH = (uint16_t)((d[0]<<8) | d[1]);; + if(!(TH & HTU21_HUMID_FLAG)){ // temperature measured + Tmeasured = -46.85 + 175.72*(TH & 0xFFFC)/65536.; + DBG("T=%.1f", Tmeasured); + HTU21D_cmdH(); + }else{ // humidity measured + Hmeasured = -6.f + 125.f*(TH & 0xFFFC)/65536.; + DBG("H=%.1f", Hmeasured); + htustatus = HTU21D_RDY; + } +} + +int HTU21D_heater(int ON){ + if(htustatus != HTU21D_RELAX) return FALSE; + uint8_t val; + if(!i2c_read_reg8(HTU21_READ_USERREG, &val)){ + DBG("Can't read userreg"); + return FALSE; + } + DBG("REG: 0x%02x", val); + if(ON) val |= HTU21_REG_HTR; + else val &= ~HTU21_REG_HTR; + DBG("REG -> 0x%02x", val); + if(!i2c_write_reg8(HTU21_WRITE_USERREG, val)){ + DBG("Can't write write userreg"); + return FALSE; + } + return TRUE; +} diff --git a/SI7005_HTU21D/htu21d.h b/SI7005_HTU21D/htu21d.h new file mode 100644 index 0000000..59404f0 --- /dev/null +++ b/SI7005_HTU21D/htu21d.h @@ -0,0 +1,38 @@ +/* + * This file is part of the sihtu project. + * Copyright 2022 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +#define HTU21D_CONVTIMEOUT (2.0) + +typedef enum{ + HTU21D_BUSY, // measurement in progress + HTU21D_ERR, // error in I2C + HTU21D_RELAX, // relaxed state + HTU21D_RDY, // data ready - can get it +} HTU21D_status; + +HTU21D_status HTU21D_get_status(); + +int HTU21D_read_ID(); +void HTU21D_process(); +int HTU21D_startmeasure(); +int HTU21D_getTH(float *T, float *H); +int HTU21D_heater(int ON); + diff --git a/SI7005_HTU21D/i2c.c b/SI7005_HTU21D/i2c.c new file mode 100644 index 0000000..3f4bd64 --- /dev/null +++ b/SI7005_HTU21D/i2c.c @@ -0,0 +1,257 @@ +/* + * This file is part of the sihtu project. + * Copyright 2022 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static uint8_t lastaddr = 0; +static int I2Cfd = -1; + +int i2c_getfd(){return I2Cfd;} + +/** + * @brief i2c_read_reg8 - read 8-bit addressed register (8 bit) + * @param regaddr - register address + * @param data - data read + * @return state + */ +int i2c_read_reg8(uint8_t regaddr, uint8_t *data){ + if(I2Cfd < 1) return FALSE; + struct i2c_smbus_ioctl_data args; + union i2c_smbus_data sd; + args.read_write = I2C_SMBUS_READ; + args.command = regaddr; + args.size = I2C_SMBUS_BYTE_DATA; + args.data = &sd; + if(ioctl(I2Cfd, I2C_SMBUS, &args) < 0){ + WARN("i2c_read_reg8, ioctl()"); + return FALSE; + } + if(data) *data = sd.byte; + return TRUE; +} + +/** + * @brief i2c_write_reg8 - write to 8-bit addressed register + * @param regaddr - address + * @param data - data + * @return state + */ +int i2c_write_reg8(uint8_t regaddr, uint8_t data){ + if(I2Cfd < 1) return FALSE; + struct i2c_smbus_ioctl_data args; + union i2c_smbus_data sd; + sd.byte = data; + args.read_write = I2C_SMBUS_WRITE; + args.command = regaddr; + args.size = I2C_SMBUS_BYTE_DATA; + args.data = &sd; + if(ioctl(I2Cfd, I2C_SMBUS, &args) < 0){ + WARN("i2c_write_reg8, ioctl()"); + return FALSE; + } + uint8_t t; + if(!i2c_read_reg8(regaddr, &t)) return FALSE; + if(t != data){ + WARNX("Read not same thing that write: 0x%02x instead of 0x%02x", t, data); + return FALSE; + } + return TRUE; +} + +/** + * @brief i2c_read_reg16 - read 16-bit addressed register (to 16-bit data) + * @param regaddr - address + * @param data - data + * @return state + */ +int i2c_read_reg16(uint16_t regaddr, uint16_t *data){ + if(I2Cfd < 1) return FALSE; + struct i2c_msg m[2]; + struct i2c_rdwr_ioctl_data x = {.msgs = m, .nmsgs = 2}; + m[0].addr = lastaddr; m[1].addr = lastaddr; + m[0].flags = 0; + m[1].flags = I2C_M_RD; + m[0].len = 2; m[1].len = 2; + uint8_t a[2], d[2] = {0}; + a[0] = regaddr >> 8; + a[1] = regaddr & 0xff; + m[0].buf = a; m[1].buf = d; + if(ioctl(I2Cfd, I2C_RDWR, &x) < 0){ + WARN("i2c_read_reg16, ioctl()"); + return FALSE; + } + if(data) *data = (uint16_t)((d[0] << 8) | (d[1])); + return TRUE; +} + +/** + * @brief i2c_write_reg16 - write 16-bit data value to 16-bit addressed register + * @param regaddr - address + * @param data - data to write + * @return state + */ +int i2c_write_reg16(uint16_t regaddr, uint16_t data){ + if(I2Cfd < 1) return FALSE; + union i2c_smbus_data d; + d.block[0] = 3; + d.block[1] = regaddr & 0xff; + d.block[2] = data >> 8; + d.block[3] = data & 0xff; + struct i2c_smbus_ioctl_data args; + args.read_write = I2C_SMBUS_WRITE; + args.command = regaddr >> 8; + args.size = I2C_SMBUS_I2C_BLOCK_DATA; + args.data = &d; + if(ioctl(I2Cfd, I2C_SMBUS, &args) < 0){ + WARN("i2c_write_reg16, ioctl()"); + return FALSE; + } +/* printf("Block: "); + for(int i = 0; i < 4; ++i) printf("0x%02x ", d.block[i]); + printf("\n");*/ + return TRUE; +} + +/** + * @brief i2c_set_slave_address - set current slave address + * @param addr - address + * @return state + */ +int i2c_set_slave_address(uint8_t addr){ + if(I2Cfd < 1) return FALSE; + if(ioctl(I2Cfd, I2C_SLAVE, addr) < 0){ + WARN("i2c_set_slave_address, ioctl()"); + return FALSE; + } + lastaddr = addr; + return TRUE; +} + +/** + * @brief i2c_open - open I2C device + * @param path - full path to device + * @return state + */ +int i2c_open(const char *path){ + if(I2Cfd > 0) close(I2Cfd); + I2Cfd = open(path, O_RDWR); + if(I2Cfd < 1){ + WARN("i2c_open, open()"); + return FALSE; + } + return TRUE; +} + +void i2c_close(){ + if(I2Cfd > 0) close(I2Cfd); +} + +#if 0 +// Don't work :( +/** + * @brief read_regN8 - read up to I2C_SMBUS_BLOCK_MAX bytes from 8-bit addressed register + * @param regaddr - address + * @param data - data to read + * @param N - amount of bytes + * @return state + */ +static int read_regN8(uint8_t regaddr, uint8_t *data, uint16_t N){ + if(I2Cfd < 1 || N > I2C_SMBUS_BLOCK_MAX || N == 0 || !data) return FALSE; + struct i2c_smbus_ioctl_data args = {0}; + union i2c_smbus_data sd = {0}; + sd.block[0] = N; + DBG("block: %d, %d, %d", sd.block[0], sd.block[1], sd.block[2]); + DBG("Try to get %d bytes from 0x%02x", N, regaddr); + args.read_write = I2C_SMBUS_READ; + args.command = regaddr; + args.size = I2C_SMBUS_BLOCK_DATA; + args.data = &sd; + if(ioctl(I2Cfd, I2C_SMBUS, &args) < 0){ + WARN("read_regN8, ioctl()"); + return FALSE; + } + DBG("block: %d, %d, %d", sd.block[0], sd.block[1], sd.block[2]); + memcpy(data, sd.block+1, N); + return TRUE; +} +#endif + +/** + * @brief read_data16 - read data from 16-bit addressed register + * @param regaddr - address + * @param N - amount of bytes + * @param array - data read + * @return state + */ +int i2c_read_data16(uint16_t regaddr, uint16_t N, uint8_t *array){ + if(I2Cfd < 1 || N == 0 || !array) return FALSE; + struct i2c_msg m[2]; + struct i2c_rdwr_ioctl_data x = {.msgs = m, .nmsgs = 2}; + m[0].addr = lastaddr; m[1].addr = lastaddr; + m[0].flags = 0; + m[1].flags = I2C_M_RD; + m[0].len = 2; m[1].len = N; + uint8_t a[2]; + a[0] = regaddr >> 8; + a[1] = regaddr & 0xff; + m[0].buf = a; m[1].buf = array; + if(ioctl(I2Cfd, I2C_RDWR, &x) < 0){ + WARN("i2c_read_data16, ioctl()"); + return FALSE; + } + return TRUE; +} + +/** + * @brief read_data8 - read data from 8-bit addressed register + * @param regaddr - address + * @param N - amount of bytes + * @param array - data read + * @return state + */ +int i2c_read_data8(uint8_t regaddr, uint16_t N, uint8_t *array){ + if(I2Cfd < 1 || N < 1 || N+regaddr > 0xff || !array) return FALSE; +#if 0 + uint16_t rest = N; + do{ + uint8_t l = (rest > I2C_SMBUS_BLOCK_MAX) ? I2C_SMBUS_BLOCK_MAX : (uint8_t)rest; + if(!read_regN8(regaddr, array, l)){ + DBG("can't read"); + return FALSE; + } + rest -= l; + regaddr += l; + array += l; + }while(rest); +#endif + for(uint16_t i = 0; i < N; ++i){ + if(!i2c_read_reg8((uint8_t)(regaddr+i), array++)){ + DBG("can't read @%dth byte", i); + return FALSE; + } + } + return TRUE; +} diff --git a/SI7005_HTU21D/i2c.h b/SI7005_HTU21D/i2c.h new file mode 100644 index 0000000..8dd1c41 --- /dev/null +++ b/SI7005_HTU21D/i2c.h @@ -0,0 +1,40 @@ +/* + * This file is part of the sihtu project. + * Copyright 2022 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +int i2c_getfd(); +int i2c_open(const char *path); +void i2c_close(); +int i2c_set_slave_address(uint8_t addr); +int i2c_read_reg8(uint8_t regaddr, uint8_t *data); +int i2c_write_reg8(uint8_t regaddr, uint8_t data); +int i2c_read_data8(uint8_t regaddr, uint16_t N, uint8_t *array); +int i2c_read_reg16(uint16_t regaddr, uint16_t *data); +int i2c_write_reg16(uint16_t regaddr, uint16_t data); +int i2c_read_data16(uint16_t regaddr, uint16_t N, uint8_t *array); + diff --git a/SI7005_HTU21D/main.c b/SI7005_HTU21D/main.c new file mode 100644 index 0000000..2622eb8 --- /dev/null +++ b/SI7005_HTU21D/main.c @@ -0,0 +1,129 @@ +/* + * This file is part of the sihtu project. + * Copyright 2022 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include "htu21d.h" +#include "si7005.h" +#include "i2c.h" + +#define DEVADDR (0x40) + +typedef struct{ + char *device; + int help; + int heater; +} glob_pars; + +static glob_pars G = {.device = "/dev/i2c-3", .heater = -1}; + +static myoption cmdlnopts[] = { + {"help", NO_ARGS, NULL, 'h', arg_int, APTR(&G.help), _("show this help")}, + {"device", NEED_ARG, NULL, 'd', arg_string, APTR(&G.device), _("I2C device path")}, + {"heater", NEED_ARG, NULL, 'H', arg_int, APTR(&G.heater), _("turn on (>0) or off (0) heater")}, + end_option +}; + +static void showd(float T, float H){ +// Sonntag1990 +#define dB 17.62f +#define dC 243.12f + if(T < -273.15f || H < 0.f) return; + float gamma = logf(H/100.f) + dB*T/(dC + T); + float Tdp = dC * gamma / (dB - gamma); + printf("T=%.1fC, H=%.1f%%, Tdp=%.1fC\n", T, H, Tdp); + static int i = 0; + if(++i > 3) signals(0); +} + +static void processSI(){ + static double t0 = 0.; + si7005_process(); + si7005_status s = si7005_get_status(); + if(s == SI7005_RELAX){ + if(dtime() - t0 > 1.){ + DBG("need to start measure"); + if(si7005_startmeasure()) t0 = dtime(); + } + }else if(s == SI7005_RDY){ // humidity can be shown + DBG("Got data"); + float T, H; + if(!si7005_getTH(&T, &H)) return; + showd(T, H); + t0 = dtime(); + }else if(s == SI7005_ERR){ + DBG("got error"); + if(si7005_startmeasure()) t0 = dtime(); + } +} +static void processHTU(){ + static double t0 = 0.; + HTU21D_process(); + HTU21D_status s = HTU21D_get_status(); + if(s == HTU21D_RELAX){ + if(dtime() - t0 > 1.){ + DBG("need to start measure"); + if(HTU21D_startmeasure()) t0 = dtime(); + } + }else if(s == HTU21D_RDY){ // humidity can be shown + DBG("Got data"); + float T, H; + if(!HTU21D_getTH(&T, &H)) return; + showd(T, H); + t0 = dtime(); + }else if(s == HTU21D_ERR){ + DBG("got error"); + if(HTU21D_startmeasure()) t0 = dtime(); + } +} + +int main(int argc, char **argv){ + initial_setup(); + parseargs(&argc, &argv, cmdlnopts); + if(G.help) showhelp(-1, cmdlnopts); + if(!i2c_open(G.device)) ERR("Can't open %s", G.device); + if(!i2c_set_slave_address((uint8_t)DEVADDR)){ + WARN("Can't set slave address 0x%02x", DEVADDR); + goto clo; + } + //if(!i2c_read_reg8(0, NULL)) ERR("Can't connect!"); + int si = 1; // == 0 if htu + if(!si7005_read_ID()){ + DBG("Don't see SI7005"); + si = 0; + if(!HTU21D_read_ID()){ + ERRX("Neither SI7005 nor HTU21D not found"); + }else while(!HTU21D_startmeasure()) usleep(1000); + }else while(!si7005_startmeasure()) usleep(1000); + if(G.heater > -1){ + int ans = FALSE; + if(si) ans = si7005_heater(G.heater); + else ans = HTU21D_heater(G.heater); + if(!ans) WARNX("Can't turn on heater"); + } + while(1){ + if(si) processSI(); + else processHTU(); + } +clo: + i2c_close(); + return 0; +} diff --git a/SI7005_HTU21D/si7005.c b/SI7005_HTU21D/si7005.c new file mode 100644 index 0000000..e9e7b3e --- /dev/null +++ b/SI7005_HTU21D/si7005.c @@ -0,0 +1,168 @@ +/* + * This file is part of the sihtu project. + * Copyright 2022 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "i2c.h" +#include "si7005.h" + +#define SI7005_REGSTATUS 0 +#define SI7005_STATUSNRDY 1 +#define SI7005_REGDATA 1 +#define SI7005_REGCONFIG 3 +#define SI7005_CONFFAST (1<<5) +#define SI7005_CONFTEMP (1<<4) +#define SI7005_CONFHEAT (1<<1) +#define SI7005_CONFSTART (1<<0) +#define SI7005_REGID 0x11 + +#define SI7005_ID 0x50 + +static si7005_status sistatus = SI7005_RELAX; + +static float Tmeasured, Hmeasured; +static double lastw = 0.; // last time of measurements start + +si7005_status si7005_get_status(){ + return sistatus; +} + +/** + * @brief si7005_read_ID - read device ID + * @return TRUE if all OK + */ +int si7005_read_ID(){ + if(sistatus != SI7005_RELAX) return FALSE; + //i2c_write_reg8(SI7005_REGCONFIG, 0); + uint8_t ID; + if(!i2c_read_reg8(SI7005_REGID, &ID)){ + DBG("Can't read SI_REG_ID"); + return FALSE; + } + DBG("SI, device ID: 0x%02x", ID); + if(ID != SI7005_ID){ + DBG("Not SI7005\n"); + return FALSE; + } + return TRUE; +} + +/* + * start themperature reading @return TRUE if all OK + */ +int si7005_startmeasure(){ + sistatus = SI7005_BUSY; + if(!i2c_write_reg8(SI7005_REGCONFIG, SI7005_CONFTEMP | SI7005_CONFSTART)){ + DBG("Can't write start Tmeas"); + sistatus = SI7005_ERR; + return FALSE; + } + DBG("Wait for T\n"); + lastw = dtime(); + return TRUE; +} + +/* + * start humidity reading @return TRUE if all OK + */ +static void si7005_cmdH(){ + sistatus = SI7005_BUSY; + i2c_write_reg8(SI7005_REGCONFIG, SI7005_CONFSTART); + if(!i2c_write_reg8(SI7005_REGCONFIG, SI7005_CONFSTART)){ + DBG("Can't write start Hmeas"); + sistatus = SI7005_ERR; + return; + } + DBG("Wait for H, dt=%g", dtime() - lastw); + lastw = dtime(); +} + +int si7005_getTH(float *T, float *H){ + if(sistatus != SI7005_RDY) return FALSE; + DBG("dt=%g", dtime() - lastw); + DBG("Measured T=%.1f, H=%.1f", Tmeasured, Hmeasured); + // correct T/H +#define A0 (-4.7844f) +#define A1 (0.4008f) +#define A2 (-0.00393f) + if(H){ + *H = Hmeasured - (A2*Hmeasured*Hmeasured + A1*Hmeasured + A0); + } + if(T) *T = Tmeasured; + sistatus = SI7005_RELAX; + return TRUE; +} + +/* + * process state machine + */ +void si7005_process(){ + uint8_t c, d[3]; + if(sistatus != SI7005_BUSY) return; + if(3 != read(i2c_getfd(), d, 3)){ + DBG("Can't read status"); + sistatus = SI7005_ERR; + return; + } + DBG("Status: 0x%02x, H: 0x%02x, L: 0x%02x", d[0], d[1], d[2]); + if(!i2c_read_reg8(SI7005_REGCONFIG, &c)){ + DBG("Can't read config"); + sistatus = SI7005_ERR; + return; + } + DBG("Config: 0x%02x", c); + if(d[0] & SI7005_STATUSNRDY){ // not ready yet + //if(c & SI7005_CONFSTART){ + if(dtime() - lastw > SI7005_CONVTIMEOUT){ + DBG("Wait too long -> err"); + sistatus = SI7005_ERR; + return; + } + usleep(20000); + return; + } + uint16_t TH = (uint16_t)((d[1]<<8) | d[2]); + if(c & SI7005_CONFTEMP){ // temperature measured + TH >>= 2; + Tmeasured = TH/32.f - 50.f; + DBG("T=%.1f", Tmeasured); + si7005_cmdH(); + }else{ // humidity measured + TH >>= 4; + Hmeasured = TH/16.f - 24.f; + DBG("H=%.1f", Hmeasured); + sistatus = SI7005_RDY; + } +} + +/** + * @brief si7005_heater - turn on/off heater + * @param ON == 1 to turn on + * @return FALSE if failed + */ +int si7005_heater(int ON){ + if(sistatus != SI7005_RELAX) return FALSE; + uint8_t reg = (ON) ? SI7005_CONFHEAT : 0; + if(!i2c_write_reg8(SI7005_REGCONFIG, reg)){ + DBG("Can't write write regconfig"); + return FALSE; + } + return TRUE; +} diff --git a/SI7005_HTU21D/si7005.h b/SI7005_HTU21D/si7005.h new file mode 100644 index 0000000..b9084a9 --- /dev/null +++ b/SI7005_HTU21D/si7005.h @@ -0,0 +1,41 @@ +/* + * This file is part of the sihtu project. + * Copyright 2022 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +// conversion timeout (s) +#define SI7005_CONVTIMEOUT 2.0 + +typedef enum{ + SI7005_BUSY, // measurement in progress + SI7005_ERR, // error in I2C + SI7005_RELAX, // relaxed state + SI7005_RDY, // data ready - can get it +} si7005_status; + +si7005_status si7005_get_status(); + +int si7005_read_ID(); +void si7005_process(); +int si7005_startmeasure(); +int si7005_getTH(float *T, float *H); +int si7005_heater(int ON); + + diff --git a/SI7005_HTU21D/sihtu.cflags b/SI7005_HTU21D/sihtu.cflags new file mode 100644 index 0000000..68d5165 --- /dev/null +++ b/SI7005_HTU21D/sihtu.cflags @@ -0,0 +1 @@ +-std=c17 \ No newline at end of file diff --git a/SI7005_HTU21D/sihtu.config b/SI7005_HTU21D/sihtu.config new file mode 100644 index 0000000..e0284f4 --- /dev/null +++ b/SI7005_HTU21D/sihtu.config @@ -0,0 +1,2 @@ +// Add predefined macros for your project here. For example: +// #define THE_ANSWER 42 diff --git a/SI7005_HTU21D/sihtu.creator b/SI7005_HTU21D/sihtu.creator new file mode 100644 index 0000000..e94cbbd --- /dev/null +++ b/SI7005_HTU21D/sihtu.creator @@ -0,0 +1 @@ +[General] diff --git a/SI7005_HTU21D/sihtu.creator.user b/SI7005_HTU21D/sihtu.creator.user new file mode 100644 index 0000000..6603b8e --- /dev/null +++ b/SI7005_HTU21D/sihtu.creator.user @@ -0,0 +1,174 @@ + + + + + + EnvironmentId + {7bd84e39-ca37-46d3-be9d-99ebea85bc0d} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + KOI8-R + false + 4 + false + 80 + true + true + 1 + false + true + false + 0 + true + true + 0 + 8 + true + false + 1 + true + false + true + *.md, *.MD, Makefile + false + true + + + + ProjectExplorer.Project.PluginSettings + + + true + false + true + true + true + true + + + 0 + true + + true + Builtin.BuildSystem + + true + true + Builtin.DefaultTidyAndClazy + 2 + + + + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop + Desktop + {65a14f9e-e008-4c1b-89df-4eaa4774b6e3} + 0 + 0 + 0 + + /tmp/1/home/eddy/SI7005 + + + + all + + true + GenericProjectManager.GenericMakeStep + + 1 + Сборка + Сборка + ProjectExplorer.BuildSteps.Build + + + + + clean + + true + GenericProjectManager.GenericMakeStep + + 1 + Очистка + Очистка + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + По умолчанию + GenericProjectManager.GenericBuildConfiguration + + 1 + + + 0 + Развёртывание + Развёртывание + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + 2 + + ProjectExplorer.CustomExecutableRunConfiguration + + false + true + false + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/SI7005_HTU21D/sihtu.cxxflags b/SI7005_HTU21D/sihtu.cxxflags new file mode 100644 index 0000000..6435dfc --- /dev/null +++ b/SI7005_HTU21D/sihtu.cxxflags @@ -0,0 +1 @@ +-std=c++17 \ No newline at end of file diff --git a/SI7005_HTU21D/sihtu.files b/SI7005_HTU21D/sihtu.files new file mode 100644 index 0000000..0772f21 --- /dev/null +++ b/SI7005_HTU21D/sihtu.files @@ -0,0 +1,7 @@ +htu21d.c +htu21d.h +i2c.c +i2c.h +main.c +si7005.c +si7005.h diff --git a/SI7005_HTU21D/sihtu.includes b/SI7005_HTU21D/sihtu.includes new file mode 100644 index 0000000..9c558e3 --- /dev/null +++ b/SI7005_HTU21D/sihtu.includes @@ -0,0 +1 @@ +.