create library and make two examples of usage

This commit is contained in:
Edward Emelianov 2025-10-14 00:17:07 +03:00
parent 7a37dc0d2f
commit ea0de3c904
20 changed files with 499 additions and 149 deletions

View File

@ -17,6 +17,6 @@
*/ */
#pragma once #pragma once
#include "sensor.h" #include "i2csensorsPTH.h"
extern sensor_t BMP180; extern sensor_t BMP180;

View File

@ -17,7 +17,7 @@
*/ */
#pragma once #pragma once
#include "sensor.h" #include "i2csensorsPTH.h"
extern sensor_t BMP280; extern sensor_t BMP280;
extern sensor_t BME280; extern sensor_t BME280;

104
I2Csensors/CMakeLists.txt Normal file
View File

@ -0,0 +1,104 @@
cmake_minimum_required(VERSION 3.9)
set(PROJ i2csensorsPTH)
set(MINOR_VERSION "1")
set(MID_VERSION "1")
set(MAJOR_VERSION "0")
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
project(${PROJ} VERSION ${VERSION} LANGUAGES C)
# default flags
set(CMAKE_C_FLAGS "${CFLAGS} -O2")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS}")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} -Wextra -Wall -Werror -W -Wno-address-of-packed-member")
set(CMAKE_COLOR_MAKEFILE ON)
option(DEBUG "Compile in debug mode" OFF)
option(EXAMPLES "Build examples" ON)
if(DEBUG)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Og -g3 -ggdb -Werror")
add_definitions(-DEBUG)
set(CMAKE_BUILD_TYPE DEBUG)
set(CMAKE_VERBOSE_MAKEFILE "ON")
else()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -march=native -fdata-sections -ffunction-sections -flto=auto")
# add_definitions(-DEBUG)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections -flto=auto")
set(CMAKE_BUILD_TYPE RELEASE)
endif()
message("Build type: ${CMAKE_BUILD_TYPE}")
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES)
find_package(PkgConfig REQUIRED)
pkg_check_modules(${PROJ} REQUIRED usefull_macros)
# library
add_library(${PROJ} SHARED ${SOURCES})
# library header files
set(LIBHEADER "i2csensorsPTH.h")
# -I
include_directories(${${PROJ}_INCLUDE_DIRS})
# -L
link_directories(${${PROJ}_LIBRARY_DIRS})
# -D
add_definitions(-DPACKAGE_VERSION=\"${VERSION}\" -DMINOR_VERSION=\"${MINOR_VERSION}\"
-DMID_VERSION=\"${MID_VERSION}\" -DMAJOR_VERSION=\"${MAJOR_VESION}\")
# -l
target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES})
set(PCFILE "${CMAKE_BINARY_DIR}/${PROJ}.pc")
configure_file("${PROJ}.pc.in" ${PCFILE} @ONLY)
set_target_properties(${PROJ} PROPERTIES VERSION ${VERSION})
set_target_properties(${PROJ} PROPERTIES PUBLIC_HEADER ${LIBHEADER})
# Installation of the program
include(GNUInstallDirs)
install(TARGETS ${PROJ} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES ${PCFILE} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig)
# EXAMPLES
if(EXAMPLES)
add_subdirectory(examples)
endif()
###### gettext ######
if(NOT DEFINED NOGETTEXT)
add_definitions(-DGETTEXT)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message("Generate locale files @ make")
find_package(Gettext REQUIRED)
find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext)
if(NOT GETTEXT_XGETTEXT_EXECUTABLE OR NOT GETTEXT_MSGFMT_EXECUTABLE)
message(FATAL_ERROR "xgettext not found")
endif()
file(MAKE_DIRECTORY ${LCPATH})
file(MAKE_DIRECTORY ${LCPATH}/LC_MESSAGES)
add_custom_command(
OUTPUT ${PO_FILE}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${GETTEXT_XGETTEXT_EXECUTABLE} --from-code=koi8-r ${SOURCES} -c -k_ -kN_ -o ${PO_FILE}
COMMAND sed -i 's/charset=.*\\\\n/charset=koi8-r\\\\n/' ${PO_FILE}
COMMAND enconv ${PO_FILE}
DEPENDS ${SOURCES}
)
# we need this to prevent ru.po & .mo from deleting by make clean
add_custom_target(
RU_FILE
COMMAND [ -f ${RU_FILE} ] && ${GETTEXT_MSGMERGE_EXECUTABLE} -Uis ${RU_FILE} ${PO_FILE} || cp ${PO_FILE} ${RU_FILE}
DEPENDS ${PO_FILE} ${SOURCES}
)
add_custom_target(
MO_FILE
COMMAND make RU_FILE && ${GETTEXT_MSGFMT_EXECUTABLE} ${RU_FILE} -o ${MO_FILE}
DEPENDS ${RU_FILE}
)
add_dependencies(${PROJ} MO_FILE)
else() # install .mo file
install(FILES ${MO_FILE} DESTINATION "${LOCALEDIR}/ru/LC_MESSAGES")
endif()
endif(NOT DEFINED NOGETTEXT)

View File

@ -1,57 +0,0 @@
# run `make DEF=...` to add extra defines
PROGRAM := sensors
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 -Wno-address-of-packed-member -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

View File

@ -3,18 +3,4 @@ Reading information from different T/P/H sensors
Supported sensors: AHT10, AHT15, AHT21b, BMP180, BMP280, BME280, SI7005 Supported sensors: AHT10, AHT15, AHT21b, BMP180, BMP280, BME280, SI7005
TODO: write at least shallow doc!
```
sensors
-a, --address=arg sensor's address (if not default)
-d, --device=arg I2C device path
-h, --help show this help
-l, --list list all supported sensors
-m, --presmm pressure in mmHg instead of hPa
-s, --sensor=arg sensor's name
```

View File

@ -81,10 +81,14 @@ static uint8_t crc8(const uint8_t *data, int len){
static sensor_status_t s_process(sensor_t *s){ static sensor_status_t s_process(sensor_t *s){
if(s->status != SENS_BUSY) return s->status; if(s->status != SENS_BUSY) return s->status;
if(sl_dtime() - *((double*)s->privdata) < MEASUREMENT_TIMEOUT) return s->status;
uint8_t data[6]; uint8_t data[6];
if(!i2c_read_raw(data, 6)) return (s->status = SENS_ERR); int ans = i2c_read_raw(data, 6);
if(data[2] != crc8(data, 2) || data[5] != crc8(data + 3, 2)) return (s->status = SENS_ERR); if(sl_dtime() - *((double*)s->privdata) < MEASUREMENT_TIMEOUT){
if(!ans) return s->status; // poll ACK
}
if(!ans) return (s->status = SENS_ERR); // timeout!
i2c_write_raw(cmd_clear_status_reg, 2); // need to write any command or sensor will widthraw into itself
if(data[2] != crc8(data, 2) || data[5] != crc8(data + 3, 2)) return (s->status = SENS_ERR); // CRC error
int32_t stemp = (int32_t)(((uint32_t)data[0] << 8) | data[1]); int32_t stemp = (int32_t)(((uint32_t)data[0] << 8) | data[1]);
stemp = ((4375 * stemp) >> 14) - 4500; stemp = ((4375 * stemp) >> 14) - 4500;
s->data.T = stemp / 100.; s->data.T = stemp / 100.;

View File

@ -17,6 +17,6 @@
#pragma once #pragma once
#include "sensor.h" #include "i2csensorsPTH.h"
extern sensor_t SHT3x; extern sensor_t SHT3x;

View File

@ -16,6 +16,6 @@
*/ */
#pragma once #pragma once
#include "sensor.h" #include "i2csensorsPTH.h"
extern sensor_t SI7005; extern sensor_t SI7005;

View File

@ -17,7 +17,7 @@
#pragma once #pragma once
#include "sensor.h" #include "i2csensorsPTH.h"
extern sensor_t AHT10; extern sensor_t AHT10;
extern sensor_t AHT15; extern sensor_t AHT15;

View File

@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.9)
include_directories(..)
link_libraries(i2csensorsPTH usefull_macros -lm)
add_executable(single single_sensor.c)
add_executable(log logmany.c)
#target_link_libraries(single i2csensorsPTH usefull_macros)

View File

@ -0,0 +1,35 @@
# Examples of library usage
### logmany.c
Creates executable `log`. The purpose of this is to show how you can use library wit lots of sensors (even with fully similar)
divided by groups (to prevent same addresses on one bus) switching by PCA9548A.
Usage:
```
-H, --hlog=arg humidity logging file
-P, --plog=arg pressure logging file
-T, --tlog=arg temperature logging file
-a, --muladdr=arg multiplexer I2C address
-d, --device=arg I2C device path
-h, --help show this help
-i, --interval=arg logging interval, seconds (default: 10)
-m, --presmm pressure in mmHg instead of hPa
```
### single_sensor.c
Creates executable `single`. Open single sensor and show its parameters.
Usage:
```
-H, --heater=arg turn on/off heater (if present)
-a, --address=arg sensor's address (if not default)
-d, --device=arg I2C device path
-h, --help show this help
-l, --list list all supported sensors
-m, --presmm pressure in mmHg instead of hPa
-s, --sensor=arg sensor's name
```

View File

@ -0,0 +1,225 @@
/*
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <math.h> // for NaN
#include <signal.h>
#include <stdio.h>
#include <strings.h>
#include <unistd.h>
#include <usefull_macros.h>
#include "i2csensorsPTH.h"
typedef struct{
char *device;
char *Tlog;
char *Hlog;
char *Plog;
double interval;
int mul_addr;
int presmm; // pressure in mm instead of hPa
int help;
} glob_pars;
static glob_pars G = {
.device = "/dev/i2c-6",
.mul_addr = 0x70,
.interval = 10.,
};
static sl_option_t 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"},
{"presmm", NO_ARGS, NULL, 'm', arg_int, APTR(&G.presmm), "pressure in mmHg instead of hPa"},
{"tlog", NEED_ARG, NULL, 'T', arg_string, APTR(&G.Tlog), "temperature logging file"},
{"hlog", NEED_ARG, NULL, 'H', arg_string, APTR(&G.Hlog), "humidity logging file"},
{"plog", NEED_ARG, NULL, 'P', arg_string, APTR(&G.Plog), "pressure logging file"},
{"interval",NEED_ARG, NULL, 'i', arg_double, APTR(&G.interval), "logging interval, seconds (default: 10)"},
{"muladdr", NEED_ARG, NULL, 'a', arg_int, APTR(&G.mul_addr), "multiplexer I2C address"},
end_option
};
static FILE *tlogf = NULL, *hlogf = NULL, *plogf = NULL;
static FILE *openlog(const char *name){
FILE *l = fopen(name, "w");
if(!l) ERR("Can't open %s", name);
return l;
}
void signals(int s){
DBG("Got sig %d", s);
sensors_close();
if(tlogf != stdout) fclose(tlogf);
if(hlogf != stdout) fclose(hlogf);
if(plogf != stdout) fclose(plogf);
exit(s);
}
typedef struct{
const char *name; // name - for header in log
const char *type; // sensor's name for `sensor_new`
uint8_t nch; // channel number
uint8_t address; // address (0 for default)
sensor_t *sensor; // pointer to sensor itself
} sd_t;
// amount of all sensors connected
#define SENSORS_AMOUNT 7
// list of sensors - must be sorted by channel number
static sd_t all_sensors[SENSORS_AMOUNT] = {
{.name = "AHT15", .type = "AHT15", .nch = 0},
{.name = "SI7005", .type = "SI7005", .nch = 0},
{.name = "AHT10", .type = "AHT10", .nch = 1},
{.name = "BMP180", .type = "BMP180", .nch = 1},
{.name = "BME280", .type = "BME280", .nch = 1},
{.name = "AHT21b", .type = "AHT21", .nch = 2},
{.name = "SHT30", .type = "SHT3x", .nch = 2},
};
/*
static int chsort(const void *v1, const void *v2){
const sd_t *s1 = (const sd_t*)v1;
const sd_t *s2 = (const sd_t*)v2;
return s1->nch - s2->nch;
}*/
static int setchan(uint8_t N){
if(N > 7){ WARNX("Wrong channel number: %d", N); return FALSE; }
N = 1<<N;
int r = sensor_writeI2C(G.mul_addr, &N, 1);
if(!r) return FALSE;
usleep(100); // wait for commutation
return TRUE;
}
static void wrnames(FILE*f, uint32_t p){
for(int i = 0; i < SENSORS_AMOUNT; ++i){
sensor_props_t sp = sensor_properties(all_sensors[i].sensor);
if((sp.flags & p) == 0) continue;
fprintf(f, "%s\t", all_sensors[i].name);
}
fprintf(f, "\n");
}
static void writeheader(){
sensor_props_t p;
fprintf(tlogf, "# Temperature, degC\n");
p.flags = 0; p.T = 1; wrnames(tlogf, p.flags);
fprintf(hlogf, "# Humidity, percent\n");
p.flags = 0; p.H = 1; wrnames(hlogf, p.flags);
fprintf(plogf, "# Pressure, %s\n", G.presmm ? "mmHg" : "hPa");
p.flags = 0; p.P = 1; wrnames(plogf, p.flags);
}
static void initsensors(){
uint8_t curch = 8;
for(int i = 0; i < SENSORS_AMOUNT; ++i){
uint8_t ch = all_sensors[i].nch;
if(ch != curch){
if(!setchan(ch)) ERRX("Error selecting channel %d", ch);
curch = ch;
}
if(!(all_sensors[i].sensor = sensor_new(all_sensors[i].type))) ERRX("Can't connect %s", all_sensors[i].name);
if(!sensor_init(all_sensors[i].sensor, all_sensors[i].address)) ERRX("Can't init %s", all_sensors[i].name);
}
}
static void writedata(uint8_t *got){
if(!got) return;
int NT = 0, NH = 0, NP = 0;
static const double nan = NAN;
for(int i = 0; i < SENSORS_AMOUNT; ++i){
sensor_props_t sp = sensor_properties(all_sensors[i].sensor);
sensor_data_t D;
if(got[i] && !sensor_getdata(all_sensors[i].sensor, &D)) got[i] = 0;
if(sp.T){ ++NT; fprintf(tlogf, "%.2f\t", got[i] ? D.T : nan); }
if(sp.H){ ++NH; fprintf(hlogf, "%.2f\t", got[i] ? D.H : nan); }
if(sp.P){ ++NP; fprintf(plogf, "%.2f\t", got[i] ? (G.presmm ? D.P * 0.750062 : D.P) : nan); }
}
DBG("Measured: %d T, %d H and %d P", NT, NH, NP);
if(NT) fprintf(tlogf, "\n");
if(NH) fprintf(hlogf, "\n");
if(NP) fprintf(plogf, "\n");
}
static void startlogs(){
double t0 = sl_dtime();
uint8_t *started = MALLOC(uint8_t, SENSORS_AMOUNT);
uint8_t *got = MALLOC(uint8_t, SENSORS_AMOUNT);
uint8_t curch = 8;
while(1){
bzero(started, SENSORS_AMOUNT);
bzero(got, SENSORS_AMOUNT);
int Ngot = 0;
double t;
do{
for(int i = 0; i < SENSORS_AMOUNT; ++i){
if(got[i]) continue;
uint8_t ch = all_sensors[i].nch;
if(ch != curch){
if(!setchan(ch)) ERRX("Error selecting channel %d", ch);
curch = ch;
}
if(!started[i]){
if(sensor_start(all_sensors[i].sensor)) started[i] = 1;
else DBG("Can't start %s", all_sensors[i].name);
}
sensor_status_t sstat = sensor_process(all_sensors[i].sensor);
if(sstat == SENS_RDY){ got[i] = 1; ++Ngot; }
}
}while(Ngot != SENSORS_AMOUNT && sl_dtime() - t0 < G.interval);
if(Ngot != SENSORS_AMOUNT){ // try to reset bad sensors
for(int i = 0; i < SENSORS_AMOUNT; ++i){
if(got[i]) continue;
DBG("TRY TO INIT bad sensor #%d (%s)", i, all_sensors[i].name);
sensor_init(all_sensors[i].sensor, all_sensors[i].address);
}
}
if(Ngot) writedata(got);
while((t = sl_dtime()) - t0 < G.interval) usleep(1000);
t0 = t;
}
}
int main(int argc, char **argv){
sl_init();
sl_parseargs(&argc, &argv, cmdlnopts);
if(G.help) sl_showhelp(-1, cmdlnopts);
if(G.interval < 1.) ERRX("Interval should be >=1s");
if(G.mul_addr < 1 || G.mul_addr > 0x7f) ERRX("Wrong multiplexer address");
if(G.Tlog) tlogf = openlog(G.Tlog);
else tlogf = stdout;
if(G.Hlog) hlogf = openlog(G.Hlog);
else hlogf = stdout;
if(G.Plog) plogf = openlog(G.Plog);
else plogf = stdout;
if(!sensors_open(G.device)) ERRX("Can't open device %s", G.device);
signal(SIGINT, signals);
signal(SIGQUIT, signals);
signal(SIGABRT, signals);
signal(SIGTERM, signals);
signal(SIGHUP, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
initsensors();
writeheader();
startlogs();
signals(0);
return 0; // never reached
}

View File

@ -19,7 +19,7 @@
#include <unistd.h> #include <unistd.h>
#include <usefull_macros.h> #include <usefull_macros.h>
#include "sensor.h" #include "i2csensorsPTH.h"
typedef struct{ typedef struct{
char *device; char *device;
@ -81,7 +81,10 @@ int main(int argc, char **argv){
sl_parseargs(&argc, &argv, cmdlnopts); sl_parseargs(&argc, &argv, cmdlnopts);
if(G.help) sl_showhelp(-1, cmdlnopts); if(G.help) sl_showhelp(-1, cmdlnopts);
if(G.list){ if(G.list){
sensors_list(); char *l = sensors_list();
green("\nSupported sensors:\n");
printf(l); printf("\n\n");
FREE(l);
return 0; return 0;
} }
if(!G.sensor) ERRX("Point sensor's name"); if(!G.sensor) ERRX("Point sensor's name");

View File

@ -28,6 +28,7 @@
static uint8_t lastaddr = 0; static uint8_t lastaddr = 0;
static int I2Cfd = -1; static int I2Cfd = -1;
// common function for raw read or write
static int i2c_rw(uint8_t *data, int len, uint16_t flags){ static int i2c_rw(uint8_t *data, int len, uint16_t flags){
struct i2c_msg m; struct i2c_msg m;
struct i2c_rdwr_ioctl_data x = {.msgs = &m, .nmsgs = 1}; struct i2c_rdwr_ioctl_data x = {.msgs = &m, .nmsgs = 1};
@ -35,8 +36,9 @@ static int i2c_rw(uint8_t *data, int len, uint16_t flags){
m.flags = flags; // 0 for w and I2C_M_RD for read m.flags = flags; // 0 for w and I2C_M_RD for read
m.len = len; m.len = len;
m.buf = data; m.buf = data;
DBG("write %d with len %d", *data, len);
if(ioctl(I2Cfd, I2C_RDWR, &x) < 0){ if(ioctl(I2Cfd, I2C_RDWR, &x) < 0){
WARN("i2c_read_reg16, ioctl()"); DBG("i2c_rw, ioctl()");
return FALSE; return FALSE;
} }
return TRUE; return TRUE;
@ -143,9 +145,6 @@ int i2c_write_reg16(uint16_t regaddr, uint16_t data){
WARN("i2c_write_reg16, ioctl()"); WARN("i2c_write_reg16, ioctl()");
return FALSE; return FALSE;
} }
/* printf("Block: ");
for(int i = 0; i < 4; ++i) printf("0x%02x ", d.block[i]);
printf("\n");*/
return TRUE; return TRUE;
} }
@ -156,6 +155,7 @@ int i2c_write_reg16(uint16_t regaddr, uint16_t data){
*/ */
int i2c_set_slave_address(uint8_t addr){ int i2c_set_slave_address(uint8_t addr){
if(I2Cfd < 1) return FALSE; if(I2Cfd < 1) return FALSE;
DBG("Try to set slave addr 0x%02X", addr);
if(ioctl(I2Cfd, I2C_SLAVE, addr) < 0){ if(ioctl(I2Cfd, I2C_SLAVE, addr) < 0){
WARN("i2c_set_slave_address, ioctl()"); WARN("i2c_set_slave_address, ioctl()");
return FALSE; return FALSE;
@ -183,40 +183,10 @@ void i2c_close(){
if(I2Cfd > 0) close(I2Cfd); 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 * @brief read_data16 - read data from 16-bit addressed register
* @param regaddr - address * @param regaddr - address
* @param N - amount of bytes * @param N - amount of BYTES!!!
* @param array - data read * @param array - data read
* @return state * @return state
*/ */
@ -248,24 +218,16 @@ int i2c_read_data16(uint16_t regaddr, uint16_t N, uint8_t *array){
*/ */
int i2c_read_data8(uint8_t regaddr, uint16_t N, uint8_t *array){ 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(I2Cfd < 1 || N < 1 || N+regaddr > 0xff || !array) return FALSE;
#if 0 struct i2c_msg m[2];
uint16_t rest = N; struct i2c_rdwr_ioctl_data x = {.msgs = m, .nmsgs = 2};
do{ m[0].addr = lastaddr; m[1].addr = lastaddr;
uint8_t l = (rest > I2C_SMBUS_BLOCK_MAX) ? I2C_SMBUS_BLOCK_MAX : (uint8_t)rest; m[0].flags = 0; // write register address
if(!read_regN8(regaddr, array, l)){ m[1].flags = I2C_M_RD; // read contents
DBG("can't read"); m[0].len = 1; m[1].len = N;
return FALSE; m[0].buf = &regaddr; m[1].buf = array;
} if(ioctl(I2Cfd, I2C_RDWR, &x) < 0){
rest -= l; WARN("i2c_read_data8, ioctl()");
regaddr += l; return FALSE;
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; return TRUE;
} }

View File

@ -30,11 +30,14 @@ typedef enum{
SENS_RDY, // data ready - can get it SENS_RDY, // data ready - can get it
} sensor_status_t; } sensor_status_t;
typedef struct{ typedef union{
uint8_t T : 1; // can temperature (degC) struct{
uint8_t H : 1; // can humidity (percent) uint8_t T : 1; // can temperature (degC)
uint8_t P : 1; // can pressure (hPa) uint8_t H : 1; // can humidity (percent)
uint8_t htr : 1; // have heater uint8_t P : 1; // can pressure (hPa)
uint8_t htr : 1; // have heater
};
uint32_t flags;
} sensor_props_t; } sensor_props_t;
typedef struct{ typedef struct{
@ -48,7 +51,7 @@ typedef struct sensor_struct sensor_t;
int sensors_open(const char *dev); int sensors_open(const char *dev);
void sensors_close(); void sensors_close();
void sensors_list(); char *sensors_list();
sensor_t* sensor_new(const char *name); sensor_t* sensor_new(const char *name);
void sensor_delete(sensor_t **s); void sensor_delete(sensor_t **s);
sensor_props_t sensor_properties(sensor_t *s); sensor_props_t sensor_properties(sensor_t *s);
@ -57,3 +60,9 @@ int sensor_heater(sensor_t *s, int on);
int sensor_start(sensor_t *s); int sensor_start(sensor_t *s);
sensor_status_t sensor_process(sensor_t *s); sensor_status_t sensor_process(sensor_t *s);
int sensor_getdata(sensor_t *s, sensor_data_t *d); int sensor_getdata(sensor_t *s, sensor_data_t *d);
// I2C functions for client usage
int sensor_writeI2C(uint8_t addr, uint8_t *data, int len);
int sensor_readI2C(uint8_t addr, uint8_t *data, int len);
int sensor_readI2Cregs(uint8_t addr, uint8_t regaddr, uint16_t N, uint8_t *data);
int sensor_writeI2Creg(uint8_t addr, uint8_t regaddr, uint8_t data);

View File

@ -0,0 +1,10 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: @PROJ@
Description: I2C library working with pressure/temperature/humidity sensors
Version: @VERSION@
Libs: -L${libdir} -l@PROJLIB@
Cflags: -I${includedir}

View File

@ -79,20 +79,36 @@ void sensor_delete(sensor_t **s){
FREE((*s)); FREE((*s));
} }
// list all supported sensors // list all supported sensors, return allocated string - should be free'd later
void sensors_list(){ char *sensors_list(){
const sensor_t **p = supported_sensors; const sensor_t **p = supported_sensors;
green("Supported sensors:\n"); int L = 0, rest = 0, idx = 0;
char *list = NULL;
#define PORTIONSZ 256
while(*p){ while(*p){
printf("%s", (*p)->name); int l = strlen((*p)->name);
if(*(++p)) printf(", "); if(rest < l+2){
int add = PORTIONSZ * ((PORTIONSZ + 2 + l - rest) / PORTIONSZ);
rest += add; L += add;
list = realloc(list, L);
}
if(idx == 0) l = sprintf(list, "%s", (*p)->name);
else l = sprintf(list+idx, ",%s", (*p)->name);
rest -= l; idx += l;
//DBG("L=%d, rest=%d, idx=%d, list='%s'", L, rest, idx, list);
++p;
} }
printf("\n"); #undef PORTIONSZ
return list;
} }
// wrapper with timeout // wrapper with timeout
int sensor_start(sensor_t *s){ int sensor_start(sensor_t *s){
if(!s) return FALSE; if(!s) return FALSE;
if(!i2c_set_slave_address(s->address)){
DBG("Can't set slave address 0x%02x", s->address);
return FALSE;
}
double t0 = sl_dtime(); double t0 = sl_dtime();
int result = FALSE; int result = FALSE;
while(sl_dtime() - t0 < I2C_TIMEOUT && !(result = s->start(s))) usleep(10000); while(sl_dtime() - t0 < I2C_TIMEOUT && !(result = s->start(s))) usleep(10000);
@ -109,6 +125,10 @@ int sensor_getdata(sensor_t *s, sensor_data_t *d){
sensor_status_t sensor_process(sensor_t *s){ sensor_status_t sensor_process(sensor_t *s){
if(!s) return FALSE; if(!s) return FALSE;
if(!i2c_set_slave_address(s->address)){
DBG("Can't set slave address 0x%02x", s->address);
return FALSE;
}
return s->process(s); return s->process(s);
} }
@ -120,5 +140,42 @@ sensor_props_t sensor_properties(sensor_t *s){
int sensor_heater(sensor_t *s, int on){ int sensor_heater(sensor_t *s, int on){
if(!s || !s->properties(s).htr || !s->heater) return FALSE; if(!s || !s->properties(s).htr || !s->heater) return FALSE;
if(!i2c_set_slave_address(s->address)){
DBG("Can't set slave address 0x%02x", s->address);
return FALSE;
}
return s->heater(s, on); return s->heater(s, on);
} }
int sensor_writeI2C(uint8_t addr, uint8_t *data, int len){
if(!data || len < 1) return FALSE;
if(!i2c_set_slave_address(addr)){
DBG("Can't set slave address 0x%02x", addr);
return FALSE;
}
return i2c_write_raw(data, len);
}
int sensor_readI2C(uint8_t addr, uint8_t *data, int len){
if(!data || len < 1) return FALSE;
if(!i2c_set_slave_address(addr)){
DBG("Can't set slave address 0x%02x", addr);
return FALSE;
}
return i2c_read_raw(data, len);
}
int sensor_readI2Cregs(uint8_t addr, uint8_t regaddr, uint16_t N, uint8_t *data){
if(!data || N < 1) return FALSE;
if(!i2c_set_slave_address(addr)){
DBG("Can't set slave address 0x%02x", addr);
return FALSE;
}
return i2c_read_data8(regaddr, N, data);
}
int sensor_writeI2Creg(uint8_t addr, uint8_t regaddr, uint8_t data){
if(!i2c_set_slave_address(addr)){
DBG("Can't set slave address 0x%02x", addr);
return FALSE;
}
return i2c_write_reg8(regaddr, data);
}

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject> <!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 17.0.2, 2025-10-13T00:44:35. --> <!-- Written by QtCreator 17.0.2, 2025-10-14T00:12:49. -->
<qtcreator> <qtcreator>
<data> <data>
<variable>EnvironmentId</variable> <variable>EnvironmentId</variable>

View File

@ -8,8 +8,12 @@ SI7005.c
SI7005.h SI7005.h
aht.c aht.c
aht.h aht.h
examples/CMakeLists.txt
examples/logmany.c
examples/single_sensor.c
i2c.c i2c.c
i2c.h i2c.h
i2csensorsPTH.h
main.c main.c
sensor.c sensor.c
sensor.h sensor.h

Before

Width:  |  Height:  |  Size: 137 B

After

Width:  |  Height:  |  Size: 221 B

View File

@ -17,7 +17,7 @@
#include <stdint.h> #include <stdint.h>
#include "sensor.h" #include "i2csensorsPTH.h"
// unfortunately, we have no "self" pointer in C, so we should add this struct calling to each function for further purposes // unfortunately, we have no "self" pointer in C, so we should add this struct calling to each function for further purposes
struct sensor_struct{ struct sensor_struct{