From 5c424fc61712bf631d680d7367c63cbd6034d56e Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Fri, 10 Oct 2025 23:40:09 +0300 Subject: [PATCH] start from BMP180 --- I2Csensors/BMP180.c | 273 +++++++++++++++++++++++++++++++++ I2Csensors/BMP180.h | 22 +++ I2Csensors/Makefile | 57 +++++++ I2Csensors/Readme.md | 21 +++ I2Csensors/i2c.c | 248 ++++++++++++++++++++++++++++++ I2Csensors/i2c.h | 38 +++++ I2Csensors/main.c | 104 +++++++++++++ I2Csensors/sensor.c | 84 ++++++++++ I2Csensors/sensor.cflags | 1 + I2Csensors/sensor.config | 2 + I2Csensors/sensor.creator | 1 + I2Csensors/sensor.creator.user | 218 ++++++++++++++++++++++++++ I2Csensors/sensor.cxxflags | 1 + I2Csensors/sensor.files | 7 + I2Csensors/sensor.h | 60 ++++++++ I2Csensors/sensor.includes | 1 + 16 files changed, 1138 insertions(+) create mode 100644 I2Csensors/BMP180.c create mode 100644 I2Csensors/BMP180.h create mode 100644 I2Csensors/Makefile create mode 100644 I2Csensors/Readme.md create mode 100644 I2Csensors/i2c.c create mode 100644 I2Csensors/i2c.h create mode 100644 I2Csensors/main.c create mode 100644 I2Csensors/sensor.c create mode 100644 I2Csensors/sensor.cflags create mode 100644 I2Csensors/sensor.config create mode 100644 I2Csensors/sensor.creator create mode 100644 I2Csensors/sensor.creator.user create mode 100644 I2Csensors/sensor.cxxflags create mode 100644 I2Csensors/sensor.files create mode 100644 I2Csensors/sensor.h create mode 100644 I2Csensors/sensor.includes diff --git a/I2Csensors/BMP180.c b/I2Csensors/BMP180.c new file mode 100644 index 0000000..70731b1 --- /dev/null +++ b/I2Csensors/BMP180.c @@ -0,0 +1,273 @@ +/* + * This file is part of the bmp180 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 "i2c.h" +#include "BMP180.h" + +static uint8_t addr = 0x77; + +typedef enum{ + BMP180_OVERS_1 = 0, // oversampling is off + BMP180_OVERS_2 = 1, + BMP180_OVERS_4 = 2, + BMP180_OVERS_8 = 3, + BMP180_OVERSMAX = 4 +} BMP180_oversampling; + +#define BMP180_CHIP_ID 0x55 + +/** + * BMP180 registers + */ +#define BMP180_REG_OXLSB (0xF8) +#define BMP180_REG_OLSB (0xF7) +#define BMP180_REG_OMSB (0xF6) +#define BMP180_REG_OUT (BMP180_REG_OMSB) +#define BMP180_REG_CTRLMEAS (0xF4) +#define BMP180_REG_SOFTRESET (0xE0) +#define BMP180_REG_ID (0xD0) +#define BMP180_REG_CALIB (0xAA) + +// shift for oversampling +#define BMP180_CTRLM_OSS_SHIFT (6) +// start measurement +#define BMP180_CTRLM_SCO (1<<5) +// write it to BMP180_REG_SOFTRESET for soft reset +#define BMP180_SOFTRESET_VAL (0xB6) +// start measurement of T/P +#define BMP180_READ_T (0x0E) +#define BMP180_READ_P (0x14) + +typedef enum{ + WAIT_T, + WAIT_P, + WAIT_NONE +} waitmsr_t; + +static waitmsr_t wait4 = WAIT_NONE; + +static BMP180_oversampling bmp180_os = BMP180_OVERSMAX; + +static struct { + int16_t AC1; + int16_t AC2; + int16_t AC3; + uint16_t AC4; + uint16_t AC5; + uint16_t AC6; + int16_t B1; + int16_t B2; + int16_t MB; + int16_t MC; + int16_t MD; + int32_t MCfix; + int32_t AC1_fix; +} __attribute__ ((packed)) CaliData = {0}; + +static sensor_status_t bmpstatus = SENS_NOTINIT; +static uint8_t calidata_rdy = 0; +//static uint32_t milliseconds_start = 0; // time of measurement start +//static uint32_t p_delay = 8; // delay for P measurement +static uint8_t uncomp_data[3]; // raw uncompensated data +static int32_t Tval; // uncompensated T value +// compensated values: +static uint32_t Pmeasured; // Pa +static float Tmeasured; // degC +static uint8_t devID = 0; + +/* +static void BMP180_setOS(BMP180_oversampling os){ + bmp180_os = os & 0x03; +}*/ + +// get compensation data, return 1 if OK +static int readcompdata(){ + FNAME(); + if(!i2c_read_data8(BMP180_REG_CALIB, sizeof(CaliData), (uint8_t*)&CaliData)) return FALSE; + // convert big-endian into little-endian + uint8_t *arr = (uint8_t*)&CaliData; + for(int i = 0; i < (int)sizeof(CaliData); i+=2){ + register uint8_t val = arr[i]; + arr[i] = arr[i+1]; + arr[i+1] = val; + } + // prepare for further calculations + CaliData.MCfix = CaliData.MC << 11; + CaliData.AC1_fix = CaliData.AC1 << 2; + calidata_rdy = 1; + DBG("Calibration rdy"); + return TRUE; +} + +// do a soft-reset procedure +static int BMP180_reset(){ + if(!i2c_write_reg8(BMP180_REG_SOFTRESET, BMP180_SOFTRESET_VAL)){ + DBG("Can't reset\n"); + return FALSE; + } + return TRUE; +} + +// read compensation data & write registers +static int BMP180_init(){ + bmpstatus = SENS_NOTINIT ; + if(!BMP180_reset()) return FALSE; + if(!i2c_read_reg8(BMP180_REG_ID, &devID)){ + DBG("Can't read BMP180_REG_ID"); + return FALSE; + } + DBG("Got device ID: 0x%02x", devID); + if(devID != BMP180_CHIP_ID){ + DBG("Not BMP180\n"); + return FALSE; + } + if(!readcompdata()){ + DBG("Can't read calibration data\n"); + return FALSE; + }else{ + DBG("AC1=%d, AC2=%d, AC3=%d, AC4=%u, AC5=%u, AC6=%u", CaliData.AC1, CaliData.AC2, CaliData.AC3, CaliData.AC4, CaliData.AC5, CaliData.AC6); + DBG("B1=%d, B2=%d", CaliData.B1, CaliData.B2); + DBG("MB=%d, MC=%d, MD=%d", CaliData.MB, CaliData.MC, CaliData.MD); + } + return TRUE; +} + +// start measurement, @return 1 if all OK +static int BMP180_start(){ + if(!calidata_rdy || bmpstatus == SENS_BUSY) return FALSE; + uint8_t reg = BMP180_READ_T | BMP180_CTRLM_SCO; + if(!i2c_write_reg8(BMP180_REG_CTRLMEAS, reg)){ + bmpstatus = SENS_ERR; + DBG("Can't write CTRL reg\n"); + return FALSE; + } + bmpstatus = SENS_BUSY; + wait4 = WAIT_T; + return TRUE; +} + + +// calculate T degC and P in Pa +static inline void compens(uint32_t Pval){ + // T: + int32_t X1 = ((Tval - CaliData.AC6)*CaliData.AC5) >> 15; + int32_t X2 = CaliData.MCfix / (X1 + CaliData.MD); + int32_t B5 = X1 + X2; + Tmeasured = (B5 + 8.) / 160.; + // P: + int32_t B6 = B5 - 4000; + X1 = (CaliData.B2 * ((B6*B6) >> 12)) >> 11; + X2 = (CaliData.AC2 * B6) >> 11; + int32_t X3 = X1 + X2; + int32_t B3 = (((CaliData.AC1_fix + X3) << bmp180_os) + 2) >> 2; + X1 = (CaliData.AC3 * B6) >> 13; + X2 = (CaliData.B1 * ((B6 * B6) >> 12)) >> 16; + X3 = ((X1 + X2) + 2) >> 2; + uint32_t B4 = (CaliData.AC4 * (uint32_t) (X3 + 32768)) >> 15; + uint32_t B7 = (uint32_t)((int32_t)Pval - B3) * (50000 >> bmp180_os); + int32_t p = 0; + if(B7 < 0x80000000){ + p = (B7 << 1) / B4; + }else{ + p = (B7 / B4) << 1; + } + X1 = p >> 8; + X1 *= X1; + X1 = (X1 * 3038) >> 16; + X2 = (-7357 * p) / 65536; + Pmeasured = p + ((X1 + X2 + 3791) / 16); +} + +static int still_measuring(){ + uint8_t reg; + if(!i2c_read_reg8(BMP180_REG_CTRLMEAS, ®)) return TRUE; + if(reg & BMP180_CTRLM_SCO){ + return TRUE; + } + return FALSE; +} + +static sensor_status_t BMP180_process(){ + uint8_t reg; + if(bmpstatus != SENS_BUSY) goto ret; + if(wait4 == WAIT_T){ // wait for temperature + if(still_measuring()) goto ret; + // get uncompensated data + DBG("Read uncompensated T\n"); + if(!i2c_read_data8(BMP180_REG_OUT, 2, uncomp_data)){ + bmpstatus = SENS_ERR; + goto ret; + } + Tval = uncomp_data[0] << 8 | uncomp_data[1]; + DBG("Start P measuring\n"); + reg = BMP180_READ_P | BMP180_CTRLM_SCO | (bmp180_os << BMP180_CTRLM_OSS_SHIFT); + if(!i2c_write_reg8(BMP180_REG_CTRLMEAS, reg)){ + bmpstatus = SENS_ERR; + goto ret; + } + wait4 = WAIT_P; + }else{ // wait for pressure + if(still_measuring()) goto ret; + DBG("Read uncompensated P\n"); + if(!i2c_read_data8(BMP180_REG_OUT, 3, uncomp_data)){ + bmpstatus = SENS_ERR; + goto ret; + } + uint32_t Pval = uncomp_data[0] << 16 | uncomp_data[1] << 8 | uncomp_data[2]; + Pval >>= (8 - bmp180_os); + // calculate compensated values + compens(Pval); + DBG("All data ready\n"); + bmpstatus = SENS_RDY; // data ready + wait4 = WAIT_NONE; + } +ret: + return bmpstatus; +} + +// read data & convert it +static int BMP180_getdata(sensor_data_t *d){ + if(!d) return FALSE; + d->T = Tmeasured; + d->P = Pmeasured / 100.; // convert Pa to hPa + bmpstatus = SENS_RELAX; + return TRUE; +} + +static sensor_props_t BMP180_props(){ + sensor_props_t p = {.T = 1, .H = 0, .P = 1}; + return p; +} + +static uint8_t address(uint8_t new){ + if(new) addr = new; + return addr; +} + +sensor_t BMP180 = { + .name = "BMP180", + .address = address, + .init = BMP180_init, + .start = BMP180_start, + .process = BMP180_process, + .properties = BMP180_props, + .get_data = BMP180_getdata +}; diff --git a/I2Csensors/BMP180.h b/I2Csensors/BMP180.h new file mode 100644 index 0000000..5926f18 --- /dev/null +++ b/I2Csensors/BMP180.h @@ -0,0 +1,22 @@ +/* + * This file is part of the BMP180 project. + * Copyright 2021 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 "sensor.h" + +extern sensor_t BMP180; diff --git a/I2Csensors/Makefile b/I2Csensors/Makefile new file mode 100644 index 0000000..7c5bfb6 --- /dev/null +++ b/I2Csensors/Makefile @@ -0,0 +1,57 @@ +# 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 -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/I2Csensors/Readme.md b/I2Csensors/Readme.md new file mode 100644 index 0000000..a965c0f --- /dev/null +++ b/I2Csensors/Readme.md @@ -0,0 +1,21 @@ +Reading information from different T/P/H sensors +================================================ + +Supported sensors: + +- BMP180 - T/P sensor + + +``` + +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 + +``` + diff --git a/I2Csensors/i2c.c b/I2Csensors/i2c.c new file mode 100644 index 0000000..dff049b --- /dev/null +++ b/I2Csensors/i2c.c @@ -0,0 +1,248 @@ +/* + * Copyright 2025 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; + +/** + * @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; + } + 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/I2Csensors/i2c.h b/I2Csensors/i2c.h new file mode 100644 index 0000000..dc13a54 --- /dev/null +++ b/I2Csensors/i2c.h @@ -0,0 +1,38 @@ +/* + * Copyright 2025 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_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/I2Csensors/main.c b/I2Csensors/main.c new file mode 100644 index 0000000..eb242ab --- /dev/null +++ b/I2Csensors/main.c @@ -0,0 +1,104 @@ +/* + * Copyright 2025 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 "sensor.h" + +typedef struct{ + char *device; + char *sensor; + int slaveaddr; + int list; + int presmm; // pressure in mm instead of hPa + int help; +} glob_pars; + +static glob_pars G = { + .device = "/dev/i2c-6", +}; + +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"}, + {"address", NEED_ARG, NULL, 'a', arg_int, APTR(&G.slaveaddr), "sensor's address (if not default)"}, + {"sensor", NEED_ARG, NULL, 's', arg_string, APTR(&G.sensor), "sensor's name"}, + {"list", NO_ARGS, NULL, 'l', arg_int, APTR(&G.list), "list all supported sensors"}, + {"presmm", NO_ARGS, NULL, 'm', arg_int, APTR(&G.presmm), "pressure in mmHg instead of hPa"}, + end_option +}; + +static int start(const sensor_t *s, uint8_t addr){ + if(!sensor_init(s, addr)){ + WARNX("Can't init sensor"); + return FALSE; + } + if(!sensor_start(s)){ + WARNX("Can't start measurements"); + return FALSE; + } + return TRUE; +} + +static int printdata(const sensor_t *s){ + sensor_data_t D; + if(!s->get_data(&D)){ + WARNX("Can't read data, try again"); + if(!sensor_start(s)) WARNX("Oops: can't start"); + return FALSE; + } + sensor_props_t props = s->properties(); + if(props.T) printf("T=%.2f\n", D.T); + if(props.H) printf("H=%.2f\n", D.H); + if(props.P){ + if(G.presmm) D.P *= 0.750062; + printf("P=%.1f\n", D.P); + } + return TRUE; +} + +int main(int argc, char **argv){ + sl_init(); + sl_parseargs(&argc, &argv, cmdlnopts); + if(G.help) sl_showhelp(-1, cmdlnopts); + if(G.list){ + sensors_list(); + return 0; + } + if(!G.sensor) ERRX("Point sensor's name"); + if(G.slaveaddr && (G.slaveaddr < 8 || G.slaveaddr > 0x77)) ERRX("I2C address should be 7-bit and not forbidden"); + if(!sensors_open(G.device)) ERR("Can't open %s", G.device); + const sensor_t* s = sensor_find(G.sensor); + if(!s){ WARNX("Can't find sensor `%s` in supported list", G.sensor); goto clo; } + if(!start(s, G.slaveaddr)) goto clo; + while(1){ + sensor_status_t status = s->process(); + if(status == SENS_RDY){ // data ready - get it + if(!printdata(s)) continue; + break; + }else if(status == SENS_ERR){ + WARNX("Error in measurement, try again"); + if(!start(s, G.slaveaddr)) break; + } + } + +clo: + sensors_close(); + return 0; +} diff --git a/I2Csensors/sensor.c b/I2Csensors/sensor.c new file mode 100644 index 0000000..38d374c --- /dev/null +++ b/I2Csensors/sensor.c @@ -0,0 +1,84 @@ +/* + * Copyright 2025 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 "BMP180.h" +#include "i2c.h" +#include "sensor.h" + +// NULL-terminated list of all supported sensors +static const sensor_t* supported_sensors[] = {&BMP180, NULL}; + +// just two stupid wrappers +int sensors_open(const char *dev){ + return i2c_open(dev); +} +void sensors_close(){ + i2c_close(); +} + +// init sensor with optional new address +int sensor_init(const sensor_t *s, uint8_t address){ + if(!s) return FALSE; + address = s->address(address); + if(!i2c_set_slave_address(address)){ + DBG("Can't set slave address 0x%02x", address); + return FALSE; + } + if(!i2c_read_reg8(0, NULL)){ + DBG("Can't connect!"); + return FALSE; + } + double t0 = sl_dtime(); + int result = FALSE; + while(sl_dtime() - t0 < SENS_TIMEOUT && !(result = s->init())) usleep(10000); + return result; +} + +// find supported sensor by name +const sensor_t* sensor_find(const char *name){ + if(!name || !*name) return NULL; + const sensor_t **p = supported_sensors; + while(*p){ + if(0 == strcmp((*p)->name, name)) return *p; + ++p; + } + return NULL; +} + +// list all supported sensors +void sensors_list(){ + const sensor_t **p = supported_sensors; + green("Supported sensors:\n"); + while(*p){ + printf("%s", (*p)->name); + if(*(++p)) printf(", "); + } + printf("\n"); +} + +// wrapper with timeout +int sensor_start(const sensor_t *s){ + if(!s) return FALSE; + double t0 = sl_dtime(); + int result = FALSE; + while(sl_dtime() - t0 < SENS_TIMEOUT && !(result = s->start())) usleep(10000); + return result; +} diff --git a/I2Csensors/sensor.cflags b/I2Csensors/sensor.cflags new file mode 100644 index 0000000..68d5165 --- /dev/null +++ b/I2Csensors/sensor.cflags @@ -0,0 +1 @@ +-std=c17 \ No newline at end of file diff --git a/I2Csensors/sensor.config b/I2Csensors/sensor.config new file mode 100644 index 0000000..5047553 --- /dev/null +++ b/I2Csensors/sensor.config @@ -0,0 +1,2 @@ +#define _XOPEN_SOURCE 9999 +#define _POSIX_C_SOURCE 333333L diff --git a/I2Csensors/sensor.creator b/I2Csensors/sensor.creator new file mode 100644 index 0000000..e94cbbd --- /dev/null +++ b/I2Csensors/sensor.creator @@ -0,0 +1 @@ +[General] diff --git a/I2Csensors/sensor.creator.user b/I2Csensors/sensor.creator.user new file mode 100644 index 0000000..3b8d6e5 --- /dev/null +++ b/I2Csensors/sensor.creator.user @@ -0,0 +1,218 @@ + + + + + + EnvironmentId + {7bd84e39-ca37-46d3-be9d-99ebea85bc0d} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + true + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + KOI8-R + false + 4 + false + 0 + 80 + true + true + 1 + 0 + false + true + false + 0 + true + true + 0 + 8 + true + false + 1 + true + false + true + *.md, *.MD, Makefile + false + true + true + + + + ProjectExplorer.Project.PluginSettings + + + true + false + true + true + true + true + + false + + + 0 + true + + true + Builtin.BuildSystem + + true + true + Builtin.DefaultTidyAndClazy + 2 + false + + + + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + true + Desktop + Desktop + {65a14f9e-e008-4c1b-89df-4eaa4774b6e3} + 0 + 0 + 0 + + /tmp/1/home/eddy/BMP180 + + + + all + + true + GenericProjectManager.GenericMakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + clean + + true + GenericProjectManager.GenericMakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + По умолчанию + GenericProjectManager.GenericBuildConfiguration + 0 + 0 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + + ProjectExplorer.CustomExecutableRunConfiguration + + false + true + true + + 1 + + 1 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + + ProjectExplorer.CustomExecutableRunConfiguration + + false + true + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/I2Csensors/sensor.cxxflags b/I2Csensors/sensor.cxxflags new file mode 100644 index 0000000..6435dfc --- /dev/null +++ b/I2Csensors/sensor.cxxflags @@ -0,0 +1 @@ +-std=c++17 \ No newline at end of file diff --git a/I2Csensors/sensor.files b/I2Csensors/sensor.files new file mode 100644 index 0000000..e8f2a7c --- /dev/null +++ b/I2Csensors/sensor.files @@ -0,0 +1,7 @@ +BMP180.c +BMP180.h +i2c.c +i2c.h +main.c +sensor.c +sensor.h diff --git a/I2Csensors/sensor.h b/I2Csensors/sensor.h new file mode 100644 index 0000000..8eab781 --- /dev/null +++ b/I2Csensors/sensor.h @@ -0,0 +1,60 @@ +/* + * Copyright 2025 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 + +// timeout of i2c waiting +#define SENS_TIMEOUT (2.) + +typedef enum{ + SENS_NOTINIT, // wasn't inited + SENS_BUSY, // measurement in progress + SENS_ERR, // error occured + SENS_RELAX, // do nothing + SENS_RDY, // data ready - can get it +} sensor_status_t; + +typedef struct{ + uint8_t T : 1; // can temperature (degC) + uint8_t H : 1; // can humidity (percent) + uint8_t P : 1; // can pressure (hPa) +} sensor_props_t; + +typedef struct{ + double T; + double H; + double P; +} sensor_data_t; + +typedef struct{ + const char *name; // name + uint8_t (*address)(uint8_t new);// set/get sensor's address (get - if `new`==0) + int (*init)(); // init device - only @ start after POR + int (*start)(); // start measuring + sensor_status_t (*process)(); // main polling process + sensor_props_t (*properties)(); // get properties + int (*get_data)(sensor_data_t*);// read data +} sensor_t; + +int sensors_open(const char *dev); +void sensors_close(); +int sensor_init(const sensor_t *s, uint8_t address); +void sensors_list(); +const sensor_t* sensor_find(const char *name); +int sensor_start(const sensor_t *s); diff --git a/I2Csensors/sensor.includes b/I2Csensors/sensor.includes new file mode 100644 index 0000000..9c558e3 --- /dev/null +++ b/I2Csensors/sensor.includes @@ -0,0 +1 @@ +.