small changes and many new sensors

This commit is contained in:
2023-10-30 14:30:02 +01:00
parent 07917fe5ba
commit aad07c1b0a
21 changed files with 1642 additions and 297 deletions

98
ext/sens/AHT2x.h Normal file
View File

@@ -0,0 +1,98 @@
#pragma once
/**
* humidity and temperature sensor
* https://asairsensors.com/wp-content/uploads/2021/09/Data-Sheet-AHT21-Humidity-and-Temperature-Sensor-ASAIR-V1.0.03.pdf
*/
template <typename I2C> class AHT2x {
private:
I2C& i2c;
static constexpr const uint8_t ADDR = 0x38;
static constexpr const char* NAME = "AHT2x";
static constexpr const uint8_t BIT_BUSY = 0x80;
static constexpr const uint8_t BIT_CALIBRATED = 0x08;
public:
struct Result {
float temp;
float humi;
};
public:
AHT2x(I2C& i2c) : i2c(i2c) {
}
void init() {
softReset();
int ok = calibrate();
printf("aht calib: %d\n", ok);
}
/** is the device present on the bus? */
bool isPresent() {
return i2c.query(ADDR);
}
void softReset() {
uint8_t cmd = 0xBA;
i2c.writeRaw(ADDR, 1, &cmd);
DELAY_MS(100);
}
bool calibrate() {
uint8_t cmdCalibrate[3] = {0xBE, 0x08, 0x00};
i2c.writeRaw(ADDR, 3, cmdCalibrate);
waitUntilDone();
return readStatus() & BIT_CALIBRATED;
}
void waitUntilDone() {
for (uint8_t i = 0; i < 200; ++i) {
printf(".");
if (!(readStatus() & BIT_BUSY)) {break;}
DELAY_MS(1);
}
printf("x\n");
}
uint8_t readStatus() {
uint8_t status = 0;
i2c.readRaw(ADDR, 1, &status);
return status;
}
Result measure() {
// trigger measurement
uint8_t cmdMeasure[3] = {0xAC, 0x33, 0x00};
i2c.writeRaw(ADDR, 3, cmdMeasure);
// fetch result, also waits until the busy-bit is cleared
uint8_t raw[6];
for(uint8_t i = 0; i < 50; ++i) {
DELAY_MS(10);
i2c.readRaw(ADDR, 6, raw);
bool busy = (raw[0] & BIT_BUSY);
if (!busy) {break;}
}
// calculate
Result res;
uint32_t _humi = ((raw[1] << 16) | (raw[2] << 8) | (raw[3] << 0)) >> 4;
res.humi = _humi * 100 / 1048576.0f;
uint32_t _temp = ((raw[3] & 0x0F) << 16) | (raw[4] << 8) | (raw[5] << 0);
res.temp = (_temp * 200 / 1048576.0f) - 50;
return res;
}
};

View File

@@ -4,15 +4,29 @@
#include "../../Platforms.h"
#include "../../Debug.h"
// https://www.mouser.com/datasheet/2/783/BST-BME280-DS002-1509607.pdf
template <typename I2C> class BME280 {
I2C& i2c;
static constexpr const char* NAME = "BME280";
static constexpr uint8_t ADDR7 = 0b1110110;
static constexpr uint8_t ADDR7_1 = 0b1110110; // 0x76
static constexpr uint8_t ADDR7_2 = 0b1110111; // 0x77
static constexpr uint8_t MODE_SLEEP = 0b00;
static constexpr uint8_t MODE_FORCED = 0b01; // manual sampling
static constexpr uint8_t MODE_NORMAL = 0b11; // periodic background sampling
uint8_t ADDR7;
static constexpr uint8_t REG_CTRL1 = 0xF2; // humidity
static constexpr uint8_t REG_CTRL2 = 0xF4; // temp, pressure, mode
static constexpr uint8_t REG_CONFIG = 0xF5;
static constexpr uint8_t REG_STATUS = 0xF3;
static constexpr uint8_t REG_PRESSURE = 0xF7;
static constexpr uint8_t REG_TEMPERATURE= 0xFA;
@@ -22,9 +36,6 @@ template <typename I2C> class BME280 {
static constexpr uint8_t REG_DIG_T2 = 0x8A;
static constexpr uint8_t REG_DIG_T3 = 0x8C;
public:
bool started = false;
/** internal sensor calibration values */
@@ -52,24 +63,141 @@ public:
int8_t dig_H6 = 0;
} cal;
public:
struct Result {
float temp;
float humi;
float pres;
};
enum class Interval : uint8_t {
INT_500_US = 0b000,
INT_62_MS = 0b001,
INT_125_MS = 0b010,
INT_250_MS = 0b011,
INT_500_MS = 0b100,
INT_1000_MS = 0b101,
INT_10_MS = 0b110,
INT_20_MS = 0b111,
};
enum class Oversample : uint8_t {
X1 = 0b001,
X2 = 0b010,
X4 = 0b011,
X8 = 0b100,
X16 = 0b101,
};
private:
struct Config {
Oversample temp = Oversample::X2;
Oversample pres = Oversample::X16;
Oversample humi = Oversample::X2;
uint8_t mode = MODE_NORMAL;
Interval interval = Interval::INT_500_MS;
uint8_t fir = 0b000; // fir filter disabled
} cfg;
public:
I2C& i2c;
BME280(I2C& i2c) : i2c(i2c) {
BME280(I2C& i2c, uint8_t addrOffset = 0) : i2c(i2c), ADDR7(ADDR7_1 + addrOffset) {
}
bool isPresent() {
return i2c.query(ADDR7);
}
void init() {
readCalib();
}
void setSampling(Oversample temp, Oversample pres, Oversample humi) {
cfg.temp = temp;
cfg.pres = pres;
cfg.humi = humi;
}
uint8_t getStatus() {
uint8_t res[1];
i2c.readReg(ADDR7, REG_STATUS, 1, res);
return 0;
}
/** start periodic background measurement (tends to sensor-self-heating??) */
void measurePeriodic(Interval ival) {
cfg.mode = MODE_NORMAL;
cfg.interval = ival;
commitConfig();
}
/** measure only once, takes some time before NEW results are present (seems more stable in terms of temperature) */
void measureOnce() {
cfg.mode = MODE_FORCED;
commitConfig();
}
/** get the most recent readings */
Result getAll() {
Result res;
res.temp = getTemperature();
res.humi = getHumidity();
res.pres = getPressure();
return res;
}
/** get most recent pressure reading (hPa) */
float getPressure() {
uint8_t res[3];
i2c.readReg(ADDR7, REG_PRESSURE, 3, res);
//os_printf("res: %d - %d - %d \n", res[0], res[1], res[2]);
const uint32_t tmp = ((res[0] << 16) | (res[1] << 8) | (res[2] << 0)) >> 4;
const uint32_t pres = BME280_compensate_P_int64(tmp);
const float presF = pres / 256.0f / 100.0f; // convert from Q24.8 to float and from Pa to hPa
//const uint32_t p0 = pres / 256;
//const uint32_t p1 = (uint32_t) presF;
//const uint32_t p2 = (presF - p1) * 100000;
//Log::addInfo((NAME, "[pres] ADC: %d -> %d Pa | %d.%d hPa", tmp, p0, p1,p2);
return presF;
}
/** get most recent temperature reading */
float getTemperature() {
uint8_t res[3];
i2c.readReg(ADDR7, REG_TEMPERATURE, 3, res);
const uint32_t tmp = ((res[0] << 16) | (res[1] << 8) | (res[2] << 0)) >> 4;
const int32_t temp = BME280_compensate_T_int32(tmp);
const float tempF = temp / 100.0f;
return tempF;
}
/** get the most recent humidity reading */
float getHumidity() {
uint8_t res[2];
i2c.readReg(ADDR7, REG_HUMIDITY, 2, res);
//os_printf("res: %d - %d \n", res[0], res[1]);
const uint32_t tmp = (res[0] << 8) | (res[1] << 0);
const int32_t humi = bme280_compensate_H_int32(tmp);
const float humiF = humi / 1024.0f;
//const uint16_t h0 = humi / 1024;
//const uint16_t h1 = (uint16_t) humiF;
//const uint16_t h2 = (humiF - humi) * 10000;
//Log::addInfo((NAME, "[humi] ADC: %d -> %d -> %d.%d %%", tmp, h0, h1,h2);
return humiF;
}
private:
void readCalib() {
debugMod(NAME, "readCalib()");
Log::addInfo(NAME, "readCalib()");
// read all 24 calibration bytes for temperature and pressure
uint8_t b1[24];
@@ -93,137 +221,33 @@ private:
i2c.readReg(ADDR7, 0xA1, 1, &cal.dig_H1);
i2c.readReg(ADDR7, 0xE1, 7, b1);
cal.dig_H2 = (b1[1] << 8) | b1[0];
cal.dig_H3 = b1[3];
cal.dig_H4 = (b1[3] << 4) | (b1[4] & 0b000001111);
cal.dig_H5 = (b1[5] << 4) | ((b1[4] & 0b111100000) >> 4);
cal.dig_H3 = b1[2];
cal.dig_H4 = (b1[3] << 4) | (b1[4] & 0b00001111);
cal.dig_H5 = (b1[5] << 4) | ((b1[4] & 0b11110000) >> 4);
cal.dig_H6 = (b1[6]);
//os_printf("calib temp: %d %d %d\n", cal.dig_T1, cal.dig_T2, cal.dig_T3);
//os_printf("calib pres: %d %d %d %d %d %d %d %d %d\n", cal.dig_P1, cal.dig_P2, cal.dig_P3, cal.dig_P4, cal.dig_P5, cal.dig_P6, cal.dig_P7, cal.dig_P8, cal.dig_P9);
//os_printf("calib humi: %d %d %d %d %d %d\n", cal.dig_H1, cal.dig_H2, cal.dig_H3, cal.dig_H4, cal.dig_H5, cal.dig_H6);
//debugMod3(NAME, "calTemp: %d %d %d", cal.dig_T1, cal.dig_T2, cal.dig_T3);
//debugMod9(NAME, "calPres: %d %d %d %d %d %d %d %d %d", cal.dig_P1, cal.dig_P2, cal.dig_P3, cal.dig_P4, cal.dig_P5, cal.dig_P6, cal.dig_P7, cal.dig_P8, cal.dig_P9);
//debugMod6(NAME, "calHumi: %d %d %d %d %d %d", cal.dig_H1, cal.dig_H2, cal.dig_H3, cal.dig_H4, cal.dig_H5, cal.dig_H6);
Log::addInfo(NAME, "calTemp: %d %d %d", cal.dig_T1, cal.dig_T2, cal.dig_T3);
Log::addInfo(NAME, "calPres: %d %d %d %d %d %d %d %d %d", cal.dig_P1, cal.dig_P2, cal.dig_P3, cal.dig_P4, cal.dig_P5, cal.dig_P6, cal.dig_P7, cal.dig_P8, cal.dig_P9);
Log::addInfo(NAME, "calHumi: %d %d %d %d %d %d", cal.dig_H1, cal.dig_H2, cal.dig_H3, cal.dig_H4, cal.dig_H5, cal.dig_H6);
}
void start() {
debugMod(NAME, "start()");
const uint8_t cfgHumi = 0b101; // 16x oversampling
const uint8_t cfgPres = 0b101; // 16x oversampling
const uint8_t cfgTemp = 0b101; // 16x oversampling
const uint8_t cfgMode = 0b11;
const uint8_t cfg1 = (cfgHumi << 1);
const uint8_t cfg2 = (cfgTemp << 5) | (cfgPres << 2) | (cfgMode << 0);
i2c.writeReg(ADDR7, REG_CTRL1, 1, &cfg1);
i2c.writeReg(ADDR7, REG_CTRL2, 1, &cfg2);
void commitConfig() {
const uint8_t ctrl1 = (uint8_t(cfg.humi) << 0);
const uint8_t ctrl2 = (uint8_t(cfg.temp) << 5) | (uint8_t(cfg.pres) << 2) | (uint8_t(cfg.mode) << 0);
const uint8_t conf = (uint8_t(cfg.interval) << 5) | (cfg.fir << 2);
i2c.writeReg8(ADDR7, REG_CTRL1, MODE_SLEEP);
i2c.writeReg8(ADDR7, REG_CTRL1, ctrl1);
i2c.writeReg8(ADDR7, REG_CTRL2, ctrl2);
i2c.writeReg8(ADDR7, REG_CONFIG, conf);
}
public:
void startOnce() {
if (started) {return;}
debugMod(NAME, "startOnce()");
readCalib();
start();
started = true;
}
uint8_t getStatus() {
uint8_t res[1];
i2c.readReg(ADDR7, REG_STATUS, 1, res);
//os_printf("Status: %d \n", res[0]);
return 0;
}
/** get current pressure in hPa */
float getPressure() {
uint8_t res[3];
i2c.readReg(ADDR7, REG_PRESSURE, 3, res);
//os_printf("res: %d - %d - %d \n", res[0], res[1], res[2]);
const uint32_t tmp = ((res[0] << 16) | (res[1] << 8) | (res[2] << 0)) >> 4;
const uint32_t pres = BME280_compensate_P_int64(tmp);
const float presF = pres / 256.0f / 100.0f; // convert from Q24.8 to float and from Pa to hPa
//const uint32_t p0 = pres / 256;
//const uint32_t p1 = (uint32_t) presF;
//const uint32_t p2 = (presF - p1) * 100000;
//debugMod4(NAME, "[pres] ADC: %d -> %d Pa | %d.%d hPa", tmp, p0, p1,p2);
return presF;
}
float getTemperature() {
uint8_t res[3];
i2c.readReg(ADDR7, REG_TEMPERATURE, 3, res);
//os_printf("res: %d - %d - %d \n", res[0], res[1], res[2]);
const uint32_t tmp = ((res[0] << 16) | (res[1] << 8) | (res[2] << 0)) >> 4;
const int32_t temp = BME280_compensate_T_int32(tmp);
const float tempF = temp / 100.0f;
//debugMod2(NAME, "[temp] ADC: %d -> %d", tmp, temp);
return tempF;
}
float getHumidity() {
uint8_t res[2];
i2c.readReg(ADDR7, REG_HUMIDITY, 2, res);
//os_printf("res: %d - %d \n", res[0], res[1]);
const uint32_t tmp = (res[0] << 8) | (res[1] << 0);
const int32_t humi = bme280_compensate_H_int32(tmp);
const float humiF = humi / 1024.0f;
//const uint16_t h0 = humi / 1024;
//const uint16_t h1 = (uint16_t) humiF;
//const uint16_t h2 = (humiF - humi) * 10000;
//debugMod4(NAME, "[humi] ADC: %d -> %d -> %d.%d %%", tmp, h0, h1,h2);
return humiF;
}
/*
bool readRegister(const uint8_t addr, uint8_t* dst, const uint8_t len) {
bool ok;
// address the slave in write mode and select the first register to read
ok = i2c.startWrite(ADDR7);
if (!ok) {printf("failed start write\n"); return false;}
ok = i2c.writeByteAndCheck(addr);
if (!ok) {printf("failed to select register %d\n", addr); return false;}
//i2c::stop();
// address the slave in read mode and read [len] registers
ok = i2c.startRead(ADDR7);
if (!ok) {printf("failed start read\n"); return 0;}
i2c.readBytes(dst, len);
// done
i2c.stop();
return true;
}
bool writeRegister(const uint8_t addr, const uint8_t* src, const uint8_t len) {
bool ok;
// address the slave in write mode and select the first register to read
ok = i2c.startWrite(ADDR7);
if (!ok) {printf("failed start write\n"); return false;}
ok = i2c.writeByteAndCheck(addr);
if (!ok) {printf("failed to select register %d\n", addr); return false;}
ok = i2c.writeBytesAndCheck(src, len);
if (!ok) {printf("failed to write register contents \n"); return false;}
// done
i2c.stop();
return true;
}
*/
private:
/** conversions from ADC values to real-world values. from Bosch BMP280 manual! */
using BME280_S32_t = int32_t;
@@ -251,7 +275,7 @@ private:
var2 = var2 + ((var1*(BME280_S64_t)cal.dig_P5)<<17);
var2 = var2 + (((BME280_S64_t)cal.dig_P4)<<35);
var1 = ((var1 * var1 * (BME280_S64_t)cal.dig_P3)>>8) + ((var1 * (BME280_S64_t)cal.dig_P2)<<12);
var1 = (((((BME280_S64_t)1)<<47)+var1))*((BME280_S64_t)cal.dig_P1)>>33;
var1 = (((((BME280_S64_t)1l)<<47)+var1))*((BME280_S64_t)cal.dig_P1)>>33;
if (var1 == 0) {return 0;} // avoid exception caused by division by zero
p = 1048576-adc_P;
p = (((p<<31)-var2)*3125)/var1;

74
ext/sens/CMPS10.h Normal file
View File

@@ -0,0 +1,74 @@
#pragma once
#include "../../Platforms.h"
#include "../../Debug.h"
/**
* CMPS10 3-axis magnetometer/accelerometer module
* https://www.robot-electronics.co.uk/htm/cmps10i2c.htm
*/
template <typename I2C> class CMPS10 {
I2C& i2c;
static constexpr const char* NAME = "CMPS10";
static constexpr uint8_t ADDR7 = 0xC0>>1;
static constexpr uint8_t REG_MAG_X = 10; //10+11;
static constexpr uint8_t REG_MAG_Y = 12; //12+13;
static constexpr uint8_t REG_MAG_Z = 14; //14+15;
static constexpr uint8_t REG_ACC_X = 16; //16+17;
static constexpr uint8_t REG_ACC_Y = 18; //18+19;
static constexpr uint8_t REG_ACC_Z = 20; //20+21;
public:
struct Magnetometer {
int16_t x;
int16_t y;
int16_t z;
};
struct Acceleromter {
int16_t x;
int16_t y;
int16_t z;
};
public:
CMPS10(I2C& i2c, uint8_t addrOffset = 0) : i2c(i2c){
}
bool isPresent() {
return i2c.query(ADDR7);
}
Magnetometer getMagnetometer() {
uint8_t res[6];
i2c.readReg(ADDR7, REG_MAG_X, 6, res);
Magnetometer mag;
mag.x = ((res[0] << 8) | (res[1] << 0));
mag.y = ((res[2] << 8) | (res[3] << 0));
mag.z = ((res[4] << 8) | (res[5] << 0));
return mag;
}
Acceleromter getAcceleromter() {
uint8_t res[6];
i2c.readReg(ADDR7, REG_ACC_X, 6, res);
Acceleromter acc;
acc.x = ((res[0] << 8) | (res[1] << 0));
acc.y = ((res[2] << 8) | (res[3] << 0));
acc.z = ((res[4] << 8) | (res[5] << 0));
return acc;
}
};

146
ext/sens/ENS160.h Normal file
View File

@@ -0,0 +1,146 @@
#pragma once
/**
* a digital multi-gas sensor for indoor air quality monitoring
* https://www.sciosense.com/wp-content/uploads/documents/SC-001224-DS-7-ENS160-Datasheet.pdf
*/
template <typename I2C> class ENS160 {
private:
I2C& i2c;
static constexpr const uint8_t ADDR1 = 0x52;
static constexpr const uint8_t ADDR2 = 0x53;
uint8_t ADDR;
static constexpr const char* NAME = "ENS160";
static constexpr const uint8_t REG_PART_ID = 0x00;
static constexpr const uint8_t REG_OPMODE = 0x10;
static constexpr const uint8_t REG_TEMP_IN = 0x13;
static constexpr const uint8_t REG_RH_IN = 0x15;
static constexpr const uint8_t REG_DEVICE_STATUS = 0x20;
static constexpr const uint8_t REG_DATA_AQI = 0x21;
static constexpr const uint8_t REG_DATA_TVOC = 0x22;
static constexpr const uint8_t REG_DATA_ECO2 = 0x24;
public:
enum class Mode : uint8_t {
DEEP_SLEEP = 0x00,
IDLE = 0x01,
STANDARD = 0x02,
RESET = 0xF0,
};
union Status {
uint8_t raw;
struct {
uint8_t newgpr : 1;
uint8_t newdat : 1;
uint8_t valid : 2;
uint8_t dummy : 2;
uint8_t stater : 1;
uint8_t statas : 1;
} __attribute__((__packed__));
bool isNormal() const {return valid == 0;}
bool isWarmUp() const {return valid == 1;}
bool isStartUp() const {return valid == 2;}
bool isInvalid() const {return valid == 3;}
} __attribute__((__packed__));
union Result {
uint8_t raw[6];
struct {
Status status;
uint8_t aqi;
uint16_t tvoc;
uint16_t eco2;
} __attribute__((__packed__));
} __attribute__((__packed__));
public:
ENS160(I2C& i2c, uint8_t addr = ADDR1) : i2c(i2c), ADDR(addr) {
}
/** always 0x01 0x60 */
uint16_t getID() {
uint16_t res = 0;
i2c.readReg(ADDR, 0x00, 2, (uint8_t*)&res);
return res;
}
/** switch the mode of operation */
void setMode(Mode m) {
i2c.writeReg8(ADDR, REG_OPMODE, (uint8_t)m);
DELAY_MS(250); // not mentioned in the data-sheet but important?
}
/** set the current ambient temperature - for compensation */
void setTemp(float celsius) {
float kelvin = celsius + 273.15;
uint16_t tmp = (uint16_t) (kelvin * 64);
i2c.writeReg(ADDR, REG_TEMP_IN, 2, (uint8_t*)&tmp);
}
/** set the current ambient humidity (in %) - for compensation */
void setHumi(uint8_t humi) {
uint16_t tmp = humi * 512;
i2c.writeReg(ADDR, REG_RH_IN, 2, (uint8_t*)&tmp);
}
/** status + aqi + tvoc + eco2 */
Result getAll() {
Result res;
memset(res.raw, 0, 6);
i2c.readReg(ADDR, REG_DEVICE_STATUS, 6, res.raw);
res.status = getStatus();
//res.aqi = getAQI();
//res.tvoc = getTVOC();
//res.eco2 = getECO2();
return res;
}
Status getStatus() {
Status res;
res.raw = i2c.readReg8(ADDR, REG_DEVICE_STATUS);
return res;
}
uint8_t getAQI() {
uint8_t tmp = 0;
i2c.readReg(ADDR, REG_DATA_AQI, 1, &tmp);
return tmp & 0b111;
}
uint16_t getTVOC() {
uint16_t tmp = 0;
i2c.readReg(ADDR, REG_DATA_TVOC, 2, (uint8_t*)&tmp);
return tmp;
}
uint16_t getECO2() {
uint16_t tmp = 0;
i2c.readReg(ADDR, REG_DATA_ECO2, 2, (uint8_t*)&tmp);
return tmp;
}
/** is the device present on the bus? */
bool isPresent() {
return i2c.query(ADDR);
}
};

101
ext/sens/HTU2x.h Normal file
View File

@@ -0,0 +1,101 @@
#pragma once
/**
* humidity and temperature sensor
* https://www.ttieurope.com/content/dam/tti-europe/manufacturers/te-connectivity/resources/ENG_DS_HPC199_6_A6.pdf
*
* this sensor seems to be a bit stubborn.. requiring active polling until it is finished
* just waiting some time does not seem to work
*
*/
template <typename I2C> class HTU2x {
private:
I2C& i2c;
static constexpr const uint8_t ADDR = 0x40;
static constexpr const char* NAME = "HTU2x";
static constexpr const uint8_t CMD_QUERY_TEMP = 0xF3;
static constexpr const uint8_t CMD_QUERY_HUMI = 0xF5;
static constexpr const uint8_t CMD_READ_USER_REG = 0xE7;
static constexpr const uint8_t CMD_SOFT_RESET = 0xFE;
public:
HTU2x(I2C& i2c) : i2c(i2c) {
}
/** is the device present on the bus? */
bool isPresent() {
return i2c.query(ADDR);
}
struct Result {
float temp;
float humi;
float humiComp;
};
/** trigger a single measurement */
Result singleMeasure() {
uint8_t tmp[3]; // 2 bytes + checksum
Result res;
sendCMD(CMD_QUERY_TEMP);
if (waitForStart()) {
i2c.readBytes(tmp, 3);
i2c.stop();
uint16_t t = ((tmp[0]<<8)|(tmp[1]<<0)) & 0xFFFC;
//printf("a: %d\n", t);
res.temp = -46.85f + 175.72 * t / float(1<<16);
}
sendCMD(CMD_QUERY_HUMI);
if (waitForStart()) {
i2c.readBytes(tmp, 3);
i2c.stop();
uint16_t h = ((tmp[0]<<8)|(tmp[1]<<0)) & 0xFFFC;
//printf("b: %d\n", h);
res.humi = -6 + 125 * h / float(1<<16);
res.humiComp = res.humi + (-0.15 * (25 - res.temp));
}
return res;
}
private:
bool waitForStart() {
for (int i = 0; i < 1024; ++i) {
if (i2c.startRead(ADDR)) {return true;}
}
return false;
}
void sendCMD(uint8_t cmd) {
i2c.writeRaw(ADDR, 1, &cmd);
}
void readUserRegister() {
i2c.writeRaw(ADDR, 1, &CMD_SOFT_RESET);
vTaskDelay(100 / portTICK_PERIOD_MS);
uint8_t val = 0xaa;
i2c.readReg(ADDR, CMD_READ_USER_REG, 1, &val);
printf("user: %d\n", val);
}
};

123
ext/sens/LIS3MDL.h Normal file
View File

@@ -0,0 +1,123 @@
#pragma once
#include "../../Platforms.h"
#include "../../Debug.h"
/**
* LIS3MDL 3-axis magnetometer module
* https://www.st.com/resource/en/datasheet/lis3mdl.pdf
*/
template <typename I2C> class LIS3MDL {
I2C& i2c;
static constexpr const char* NAME = "LIS3MDL";
static constexpr uint8_t ADDR7 = 0b0011100;
static constexpr uint8_t CTRL_REG1 = 0x20;
static constexpr uint8_t CTRL_REG2 = 0x21;
static constexpr uint8_t CTRL_REG3 = 0x22;
static constexpr uint8_t CTRL_REG4 = 0x23;
static constexpr uint8_t CTRL_REG5 = 0x24;
static constexpr uint8_t REG_X = 0x28; //0x28(L= + 0x29(H)
static constexpr uint8_t REG_Y = 0x2A; //0x2A(L) + 0x2B(H)
static constexpr uint8_t REG_Z = 0x2C; //0x2C(L) + 0x2D(H)
public:
struct Magnetometer {
int16_t x;
int16_t y;
int16_t z;
};
struct Acceleromter {
int16_t x;
int16_t y;
int16_t z;
};
enum class Resolution : uint8_t {
GAUSS_4 = 0b00,
GAUSS_8 = 0b01,
GAUSS_12 = 0b10,
GAUSS_16 = 0b11,
};
enum class AxisMode : uint8_t {
LOW_POWER = 0b00,
MEDIUM_PERFORMANCE = 0b01,
HIGH_PERFORMANCE = 0b10,
ULTRA_HIGH_PERFORMANCE = 0b11,
};
enum class SamplingRate : uint8_t {
HZ_0_625,
HZ_1_25,
HZ_2_5,
HZ_5,
HZ_10,
HZ_20,
HZ_40,
HZ_80,
};
enum class OperationMode : uint8_t {
CONTINUOUS = 0b00,
SINGLE = 0b01,
OFF = 0b11,
};
public:
LIS3MDL(I2C& i2c, uint8_t addrOffset = 0) : i2c(i2c){
}
bool isPresent() {
return i2c.query(ADDR7);
}
void setResolution(Resolution res) {
getAndSet(CTRL_REG2, 0b01100000, (uint8_t)res << 5);
}
void setAxisMode(AxisMode mode) {
getAndSet(CTRL_REG1, 0b01100000, (uint8_t)mode << 5); // x and y
getAndSet(CTRL_REG4, 0b00001100, (uint8_t)mode << 2); // z
}
void setSamplingRate(SamplingRate rate) {
getAndSet(CTRL_REG1, 0b00011100, (uint8_t)rate << 2);
}
void setOperationMode(OperationMode mode) {
getAndSet(CTRL_REG3, 0b00000011, (uint8_t)mode << 0);
}
Magnetometer getMagnetometer() {
uint8_t res[6];
i2c.readReg(ADDR7, REG_X, 6, res);
Magnetometer mag;
mag.x = ((res[0] << 0) | (res[1] << 8));
mag.y = ((res[2] << 0) | (res[3] << 8));
mag.z = ((res[4] << 0) | (res[5] << 8));
return mag;
}
void getAndSet(uint8_t reg, uint8_t setMask, uint8_t setVal) {
uint8_t tmp = i2c.readReg8(ADDR7, reg);
tmp = (tmp & ~setMask) | setVal;
i2c.writeReg8(ADDR7, reg, tmp);
}
};

109
ext/sens/SGP30.h Normal file
View File

@@ -0,0 +1,109 @@
#pragma once
/**
* air quality sensor
* https://sensirion.com/media/documents/984E0DD5/61644B8B/Sensirion_Gas_Sensors_Datasheet_SGP30.pdf
*/
template <typename I2C> class SGP30 {
private:
I2C& i2c;
static constexpr const uint8_t ADDR = 0x58;
static constexpr const char* NAME = "SGP30";
static constexpr const uint16_t CMD_IAQ_INIT = 0x2003;
static constexpr const uint16_t CMD_IAQ_MEASURE = 0x2008;
static constexpr const uint16_t CMD_GET_SERIAL = 0x3682;
public:
SGP30(I2C& i2c) : i2c(i2c) {
}
void init() {
sendCommand(CMD_IAQ_INIT);
}
/** is the device present on the bus? */
bool isPresent() {
return i2c.query(ADDR);
}
struct Result {
uint16_t co2e;
uint16_t tvoc;
};
/** should be called ~ every second */
Result measure() {
sendCommand(CMD_IAQ_MEASURE);
vTaskDelay(100 / portTICK_PERIOD_MS);
uint8_t tmp[6];
i2c.readRaw(ADDR, 6, tmp);
Result res;
res.co2e = (tmp[0]<<8) | (tmp[1]<<0);
res.tvoc = (tmp[3]<<8) | (tmp[4]<<0);
return res;
}
void getSerial() {
struct Serial {
uint8_t v1[2];
uint8_t crc1;
uint8_t v2[2];
uint8_t crc2;
uint8_t v3[2];
uint8_t crc3;
} serial;
sendCommand(CMD_GET_SERIAL);
uint8_t tmp[9];
i2c.readRaw(ADDR, 9, &serial);
/*
uint8_t a = calcCRC(tmp+0, 2);
uint8_t b = calcCRC(tmp+3, 2);
uint8_t c = calcCRC(tmp+6, 2);
printf("%d %d %d %d\n", tmp[0], tmp[1], tmp[2], a);
printf("%d %d %d %d\n", tmp[3], tmp[4], tmp[5], b);
printf("%d %d %d %d\n", tmp[6], tmp[7], tmp[8], c);
*/
}
private:
void sendCommand(uint16_t cmd) {
uint8_t tmp[2];
tmp[0] = cmd >> 8;
tmp[1] = cmd >> 0;
i2c.writeRaw(ADDR, 2, tmp);
}
static uint8_t calcCRC(const uint8_t* data, uint8_t len) {
uint8_t crc = 0xff;
for (uint8_t i = 0; i < len; i++) {
crc ^= data[i];
for (uint8_t j = 0; j < 8; j++) {
if ((crc & 0x80) != 0) {
crc = (uint8_t)((crc << 1) ^ 0x31);
} else {
crc <<= 1;
}
}
}
return crc;
}
};

93
ext/sens/SHT3x.h Normal file
View File

@@ -0,0 +1,93 @@
#pragma once
/**
* humidity and temperature sensor
* https://www.mouser.com/datasheet/2/682/Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital-971521.pdf
*
* sensor seems to be really good! and very easy to use!
*
*/
template <typename I2C> class SHT3x {
private:
I2C& i2c;
static constexpr const uint8_t ADDR = 0x44;
static constexpr const char* NAME = "SHT3x";
public:
SHT3x(I2C& i2c) : i2c(i2c) {
}
/** is the device present on the bus? */
bool isPresent() {
return i2c.query(ADDR);
}
struct Result {
float temp;
float humi;
};
/** trigger a single measurement */
Result singleMeasure() {
uint16_t cmd = 0x2400; // high quality, no clock stretching
sendCommand(cmd);
vTaskDelay(100 / portTICK_PERIOD_MS);
return readResult();
}
void startPeriodicMeasure() {
uint16_t cmd = 0x2130; // high quality, 1 measurement per second
sendCommand(cmd);
}
Result getLastResult() {
return readResult();
}
private:
Result readResult() {
uint8_t tmp[6];
i2c.readRaw(ADDR, 6, (uint8_t*)&tmp);
Result res;
res.temp = -45 + 175 * ((tmp[0]<<8) | (tmp[1]<<0)) / float((1<<16)-1);
res.humi = 100 * ((tmp[3]<<8) | (tmp[4]<<0)) / float((1<<16)-1);
return res;
}
void sendCommand(uint16_t cmd) {
uint8_t tmp[2];
tmp[0] = cmd >> 8;
tmp[1] = cmd >> 0;
i2c.writeRaw(ADDR, 2, tmp);
}
static uint8_t calcCRC(const uint8_t* data, uint8_t len) {
uint8_t crc = 0xff;
for (uint8_t i = 0; i < len; i++) {
crc ^= data[i];
for (uint8_t j = 0; j < 8; j++) {
if ((crc & 0x80) != 0) {
crc = (uint8_t)((crc << 1) ^ 0x31);
} else {
crc <<= 1;
}
}
}
return crc;
}
};