CanGrow/include/CanGrow_Sensor.h

758 lines
21 KiB
C

/*
*
* include/CanGrow_Sensor.h - sensor header file
*
*
*
* ADD A NEW SENSOR
* ****************
* If you want to add a new sensor, you have to to following things:
*
* Check what it's the last used SensorIndex ID. If it's Sensor 08, you have to
* take 09 as next.
*
* Copy Sensor/00_Example.h to Sensor/09_YourSensor.h and rename everything in it from
* "00_Example" to "09_YourSensor" and edit all the needed functions and variables to your needs.
*
* Add a new include line to CanGrow_Sensor.h (this file)
* #include "Sensor/09_YourSensor.h"
*
* Add a new Entry to the SensorIndex Array, like:
* ***** SensorIndex[] *****
* ,{
* // 9 - YourSensor
* // Sensor Name
* SENSOR_09_NAME,
* {
* // Sensor Readings
* SENSOR_READ_TYPE_TEMP,
* SENSOR_READ_TYPE_HUMIDITY,
* SENSOR_READ_TYPE_RAW
* },
* // Maximal Sensor Units (most time the Sum of available Addresses)
* sizeof(Sensor_09_YourSensor_Addr),
* }
* ************************
*
* If you are done with that, you have to add a new Switch case for the new Sensor ID
* to Sensor_Addr_Init_Update() and Sensor_getValue() like:
*
* ***** Sensor_Addr_Init_Update() *****
* // Sensor 09
* case 9:
* switch(mode) {
* case 0:
* return Sensor_09_YourSensor_Addr[AddrId];
* break;
*
* case 1:
* return Sensor_09_YourSensor_Init(AddrId);
* break;
*
* case 2:
* Sensor_09_YourSensor_Update(AddrId);
* break;
* }
* break;
* *************************************
*
* ***** Sensor_getValue() *****
* // Sensor 09
* case 9:
* return Sensor_09_YourSensor[AddrId][ReadValId];
* break;
* *****************************
*/
/* should come as dependency with all adafruit sensor libs. If not, see here:
* https://github.com/adafruit/Adafruit_Sensor */
#include <Adafruit_Sensor.h>
#include "Sensor/Sensor_Common.h"
#include "Sensor/01_ADC_builtin.h"
#include "Sensor/02_BME280.h"
#include "Sensor/03_BME680.h"
#include "Sensor/04_SHT3x.h"
#include "Sensor/05_MLX90614.h"
#include "Sensor/06_TCS34725.h"
#include "Sensor/07_ADS1115.h"
#include "Sensor/08_ADS1015.h"
#include "Sensor/09_Chirp.h"
#include "Sensor/10_CCS811.h"
/*
* Sensor Todo list:
*
* - CCS811 CO2 sensor, will have type SENSOR_TYPE_I2C_WITH_GPIO, it needs signal on pin WAK
* cheap - ~ 8€ on Aliexpress
* - HX711 for weight sensor, this sensor needs two GPIOs for communication
* - SCD30/40 CO2 sensor, expensive, >70€
*/
struct Sensor_Index {
/*
* Sensor Index
* - name
* - readings (array, up to 8 entries)
* - 0 unset
* - 1 Raw
* - 2 Temp
* - 3 Humidity
* - 4 Moisture
* - 5 Pressure
* - 6 Gas restistance
* - max units
*
*/
const char name[16];
const byte type;
const byte read[Max_Sensors_Read];
const byte max;
const byte gpioMax;
};
Sensor_Index SensorIndex[] {
/*
* Example:
*
* // 0 - Example
* { SENSOR_00_NAME,
* {
* SENSOR_READ_TYPE_TEMP,
* SENSOR_READ_TYPE_HUMIDITY,
* SENSOR_READ_TYPE_RAW,
* SENSOR_READ_TYPE_RAW
* },
* // max nr of sensor units by nr of available addresses
* sizeof(Sensor_00_Example_Addr),
* },
*
*/
/* 0 is for unset in config */
{ "unset", 255, {
{},
}},
// 1 - internal ADC
{ SENSOR_01_NAME,
SENSOR_TYPE_INTADC,
{
SENSOR_READ_TYPE_RAW
},
SENSOR_01_MAX,
},
// 2 - BME280
{ SENSOR_02_NAME,
SENSOR_TYPE_I2C,
{
SENSOR_READ_TYPE_TEMP,
SENSOR_READ_TYPE_HUMIDITY,
SENSOR_READ_TYPE_PRESSURE,
SENSOR_READ_TYPE_ALTITUDE
},
// max nr of sensor units by nr of available addresses
sizeof(Sensor_02_BME280_Addr),
},
// 3 - BME680
{ SENSOR_03_NAME,
SENSOR_TYPE_I2C,
{
SENSOR_READ_TYPE_TEMP,
SENSOR_READ_TYPE_HUMIDITY,
SENSOR_READ_TYPE_PRESSURE,
SENSOR_READ_TYPE_ALTITUDE,
SENSOR_READ_TYPE_GAS_RESISTANCE
},
sizeof(Sensor_03_BME680_Addr),
},
// 4 - SHT3x
{ SENSOR_04_NAME,
SENSOR_TYPE_I2C,
{
SENSOR_READ_TYPE_TEMP,
SENSOR_READ_TYPE_HUMIDITY
},
sizeof(Sensor_04_SHT3X_Addr),
},
// 5 - MLX90614
{ SENSOR_05_NAME,
SENSOR_TYPE_I2C,
{
/* Ambient temp */
SENSOR_READ_TYPE_TEMP,
/* Object temp */
SENSOR_READ_TYPE_TEMP
},
sizeof(Sensor_05_MLX90614_Addr),
},
// 6 - TCS34725
{ SENSOR_06_NAME,
SENSOR_TYPE_I2C,
{
SENSOR_READ_TYPE_COLOR_TEMP,
SENSOR_READ_TYPE_LUX,
SENSOR_READ_TYPE_COLOR_RED,
SENSOR_READ_TYPE_COLOR_GREEN,
SENSOR_READ_TYPE_COLOR_BLUE
},
sizeof(Sensor_06_TCS34725_Addr),
},
{
// 7 - ADS1115
SENSOR_07_NAME,
SENSOR_TYPE_I2C,
{
/* A0 */
SENSOR_READ_TYPE_RAW,
/* A1 */
SENSOR_READ_TYPE_RAW,
/* A2 */
SENSOR_READ_TYPE_RAW,
/* A3 */
SENSOR_READ_TYPE_RAW
},
sizeof(Sensor_07_ADS1115_Addr),
},
{
// 8 - ADS1015
SENSOR_08_NAME,
SENSOR_TYPE_I2C,
{
SENSOR_READ_TYPE_RAW,
SENSOR_READ_TYPE_RAW,
SENSOR_READ_TYPE_RAW,
SENSOR_READ_TYPE_RAW
},
sizeof(Sensor_08_ADS1015_Addr),
},
{
// 9 - I2C Chirp soilmoisture/temperature sensor
SENSOR_09_NAME,
SENSOR_TYPE_I2C,
{
// raw soilmoisture value
SENSOR_READ_TYPE_RAW,
// temperature
SENSOR_READ_TYPE_TEMP,
/* raw light value takes 3s to use, so we dont use it. if you need it,
* uncomment it here and in Sensor/09_Chirp Sensor_09_Chirp_Update() */
// SENSOR_READ_TYPE_RAW
},
sizeof(Sensor_09_Chirp_Addr),
},
{
/* 10 - CCS811 CO2 I2C sensor */
SENSOR_10_NAME,
SENSOR_TYPE_I2C,
{
/* CO2 as parts per million */
SENSOR_READ_TYPE_PARTS_PER_MILLION,
/* TVOC value (Total Volatile Organic Compouds)*/
SENSOR_READ_TYPE_TVOC,
},
sizeof(Sensor_10_CCS811_Addr),
}
};
/* sum up of number of sensors. Dont forget to increment if you add one :) */
const byte SensorIndex_length = 10;
byte Sensor_Addr_Init_Update(const byte SensorIndexId, const byte AddrId, const byte mode, const byte Gpio2 = 0) {
const static char LogLoc[] PROGMEM = "[Sensor:Addr_Init_Update]";
/* Multi purpose function.
*
* Modes:
* 0 - get the address as byte (i2c_addr index, gpio index)
* 1 - init the sensor, returns true (1) if succeeded
* 2 - update sensors data
*
* When using a sensor which is using bare GPIOs like int ADC, or some 1- or 2-Wire sensors
* AddrId is used for the first GPIO and gpio2 for the second.
* Maybe i come up later with a better idea, but for now...
*/
#ifdef DEBUG3
if(mode > 0)
Log.verbose(F("%s Mode: %d, SensorIndexId: %d, AddrId: %d" CR), LogLoc, mode, SensorIndexId, AddrId);
#endif
switch(SensorIndexId) {
/*
* Example:
*
* case 0:
* if(!onlyReturn)
* Sensor_00_Example_Init(AddrId);
* return Sensor_00_Example_Addr[AddrId];
* break;
*/
/* Sensor 01 */
/* Internal ADC is an exception. Its clearly not an I2C device, but as I let the ADC
* have for both, 8266 and 32, configurable GPIOs, it kinda has.
* AddrId with int ADC is GPIOindex[].type */
case 1:
switch(mode) {
case 0:
/* internal ADC does not has a address here */
return 0;
break;
case 1:
return Sensor_01_ADC_Init(AddrId);
break;
case 2:
Sensor_01_ADC_Update(AddrId);
break;
}
break;
/* Sensor 02 */
case 2:
switch(mode) {
case 0:
return Sensor_02_BME280_Addr[AddrId];
break;
case 1:
return Sensor_02_BME280_Init(AddrId);
break;
case 2:
Sensor_02_BME280_Update(AddrId);
break;
}
break;
/* Sensor 03 */
case 3:
switch(mode) {
case 0:
return Sensor_03_BME680_Addr[AddrId];
break;
case 1:
return Sensor_03_BME680_Init(AddrId);
break;
case 2:
Sensor_03_BME680_Update(AddrId);
break;
}
break;
/* Sensor 04 */
case 4:
switch(mode) {
case 0:
return Sensor_04_SHT3X_Addr[AddrId];
break;
case 1:
return Sensor_04_SHT3X_Init(AddrId);
break;
case 2:
Sensor_04_SHT3X_Update(AddrId);
break;
}
break;
/* Sensor 05 */
case 5:
switch(mode) {
case 0:
return Sensor_05_MLX90614_Addr[AddrId];
break;
case 1:
return Sensor_05_MLX90614_Init(AddrId);
break;
case 2:
Sensor_05_MLX90614_Update(AddrId);
break;
}
break;
/* Sensor 06 */
case 6:
switch(mode) {
case 0:
return Sensor_06_TCS34725_Addr[AddrId];
break;
case 1:
return Sensor_06_TCS34725_Init(AddrId);
break;
case 2:
Sensor_06_TCS34725_Update(AddrId);
break;
}
break;
/* Sensor 07 */
case 7:
switch(mode) {
case 0:
return Sensor_07_ADS1115_Addr[AddrId];
break;
case 1:
return Sensor_07_ADS1115_Init(AddrId);
break;
case 2:
Sensor_07_ADS1115_Update(AddrId);
break;
}
break;
/* Sensor 08 */
case 8:
switch(mode) {
case 0:
return Sensor_08_ADS1015_Addr[AddrId];
break;
case 1:
return Sensor_08_ADS1015_Init(AddrId);
break;
case 2:
Sensor_08_ADS1015_Update(AddrId);
break;
}
break;
/* Sensor 09 */
case 9:
switch(mode) {
case 0:
return Sensor_09_Chirp_Addr[AddrId];
break;
case 1:
return Sensor_09_Chirp_Init(AddrId);
break;
case 2:
Sensor_09_Chirp_Update(AddrId);
break;
}
break;
/* Sensor 10 */
case 10:
switch(mode) {
case 0:
return Sensor_10_CCS811_Addr[AddrId];
break;
case 1:
return Sensor_10_CCS811_Init(AddrId);
break;
case 2:
Sensor_10_CCS811_Update(AddrId);
break;
}
break;
/* unknown sensor id */
default:
Log.error(F("%s SensorIndex ID %d not found" CR), LogLoc, config.system.sensor.type[AddrId]);
break;
}
return 0;
}
float Sensor_getValue(const byte SensorIndexId, const byte AddrId, const byte ReadValId = 0) {
const static char LogLoc[] PROGMEM = "[Sensor:getValue]";
/* not the best solution, but solution for the moment
* i hope i can come up in future with a way, where i do not have to
* maintain three different places with huge switch cases
* and return everything as float, even when it could be easy an int
*
* here we read the value ReadVal from the given SensorIndexId and its AddrId
* In case of RAW readings, like from ADCs , There is an Index ReadValId as well
*/
switch(SensorIndexId) {
/* Sensor 01 */
case 1:
#ifdef ESP8266
return Sensor_01_ADC[AddrId];
#endif
#ifdef ESP32
/* */
return Sensor_01_ADC[Sensor_01_ADC_ArrId(AddrId)];
#endif
break;
/* Sensor 02 */
case 2:
return Sensor_02_BME280[AddrId][ReadValId];
break;
/* Sensor 03 */
case 3:
return Sensor_03_BME680[AddrId][ReadValId];
break;
/* Sensor 04 */
case 4:
return Sensor_04_SHT3X[AddrId][ReadValId];
break;
/* Sensor 05 */
case 5:
return Sensor_05_MLX90614[AddrId][ReadValId];
break;
/* Sensor 06 */
case 6:
return Sensor_06_TCS34725[AddrId][ReadValId];
break;
/* Sensor 07 */
case 7:
return Sensor_07_ADS1115[AddrId][ReadValId];
break;
/* Sensor 08 */
case 8:
return Sensor_08_ADS1015[AddrId][ReadValId];
break;
/* Sensor 09 */
case 9:
return Sensor_09_Chirp[AddrId][ReadValId];
break;
/* Sensor 10 */
case 10:
return Sensor_10_CCS811[AddrId][ReadValId];
break;
/* unknown sensor id */
default:
Log.error(F("%s SensorIndex ID %d not found" CR), LogLoc, SensorIndexId);
break;
}
return 0;
}
/*
* *********************************************************************************************
* From here on you do not need to touch any code (hopefully) if you want to add a new sensor!
* *********************************************************************************************
*/
float Sensor_getCalibratedValue(const byte SensorId, const byte ReadId) {
float valueRaw;
float value;
/* if SensorId is configured and there is a reading on ReadingId */
if((config.system.sensor.type[SensorId] > 0) && (SensorIndex[config.system.sensor.type[SensorId]].read[ReadId] > 0)) {
/* first, get the raw / original value */
if(SensorIndex[config.system.sensor.type[SensorId]].type == SENSOR_TYPE_INTADC) {
valueRaw = Sensor_getValue(config.system.sensor.type[SensorId], config.system.sensor.gpio[SensorId][0], ReadId);
} else if(SensorIndex[config.system.sensor.type[SensorId]].type == SENSOR_TYPE_I2C) {
valueRaw = Sensor_getValue(config.system.sensor.type[SensorId], config.system.sensor.i2c_addr[SensorId], ReadId);
}
/* if reading is RAW, check what to do with it */
if(SensorIndex[config.system.sensor.type[SensorId]].read[ReadId] == SENSOR_READ_TYPE_RAW) {
/* config.system.sensor.rawConvert
* 0 - unconfigured, return raw value
* 1 - soilmoisture, return percentage
* 2 - other TBD */
switch(config.system.sensor.rawConvert[SensorId][ReadId]) {
/* soilmoisture as percentage */
case SENSOR_CONVERT_RAW_TYPE_SOILMOISTURE:
/* dont use map when both , low and high, are 0 - this causes a crash */
if((config.system.sensor.low[SensorId][ReadId] > 0) || (config.system.sensor.high[SensorId][ReadId] > 0)) {
/* use map to calculate percentage value */
value = map(valueRaw, config.system.sensor.low[SensorId][ReadId], config.system.sensor.high[SensorId][ReadId], 0, 100);
} else {
value = 0;
}
return value;
break;
default:
return valueRaw;
break;
}
} else {
/* if not a RAW value, return the value with the offset */
return valueRaw + config.system.sensor.offset[SensorId][ReadId];
}
}
return 0;
}
void Sensor_Log_Readings(const byte SensorIndexId , const byte AddrId) {
const static char LogLoc[] PROGMEM = "[Sensor:Log_Readings]";
Log.verbose(F("%s Sensor %s (%d)" CR), LogLoc, SensorIndex[SensorIndexId].name, AddrId);
/* iterate through the SensorIndex readings */
for(byte j = 0; j < Max_Sensors_Read; j++) {
/* if SensorIndex[].read[] > 0 (means there is a value to read) */
if(SensorIndex[SensorIndexId].read[j] > 0 ) {
if(SensorIndex[SensorIndexId].type == SENSOR_TYPE_INTADC) {
Log.verbose(F("%s - %s: %F %s" CR), LogLoc, Sensor_Read_descr[SensorIndex[SensorIndexId].read[j]],
Sensor_getValue(SensorIndexId, AddrId), Sensor_Read_unit[SensorIndex[SensorIndexId].read[j]]);
} else {
Log.verbose(F("%s - %s: %F %s" CR), LogLoc, Sensor_Read_descr[SensorIndex[SensorIndexId].read[j]],
Sensor_getValue(SensorIndexId, AddrId, j), Sensor_Read_unit[SensorIndex[SensorIndexId].read[j]]);
}
}
}
}
void Sensor_Init() {
/* main function that does initialize all configured sensors at once. called from setup()*/
const static char LogLoc[] PROGMEM = "[Sensor:Init]";
/* Go through all configured sensors and initialize them */
#ifdef DEBUG
Log.verbose(F("%s == Sensor drivers ==" CR), LogLoc);
for(byte i = 1; i <= SensorIndex_length; i++) {
Log.verbose(F("%s Sensor_Index %d, Name %s, Readings" CR), LogLoc, i, SensorIndex[i].name );
for(byte j = 0; j < Max_Sensors_Read; j++) {
if(SensorIndex[i].read[j] > 0 ) {
Log.verbose(F("%s %d: %S %S (%d)" CR), LogLoc, j, Sensor_Read_descr[SensorIndex[i].read[j]], Sensor_Read_unit[SensorIndex[i].read[j]], SensorIndex[i].read[j]);
}
}
}
Log.verbose(F("%s == configured Sensors ==" CR), LogLoc);
#endif
/* iterate through configured sensors */
for(byte i = 0; i < Max_Sensors; i++) {
if(config.system.sensor.type[i] > 0) {
/* if sensor type is internal ADC */
if(SensorIndex[config.system.sensor.type[i]].type == SENSOR_TYPE_INTADC) {
#ifdef DEBUG
Log.verbose(F("%s Sensor ID %d: %s - %s (GPIO ID %d, GPIO %d), offering" CR), LogLoc, i,config.system.sensor.name[i],
SensorIndex[config.system.sensor.type[i]].name, config.system.sensor.gpio[i][0], GPIOindex[config.system.sensor.gpio[i][0]].gpio);
#endif
/* initialize */
sensorStatus[i] = Sensor_Addr_Init_Update(config.system.sensor.type[i], config.system.sensor.gpio[i][0], SENSOR_AIU_MODE_INIT);
#ifdef DEBUG
/* when init was successful, list the sensor values */
if(sensorStatus[i] == true) {
Sensor_Log_Readings(config.system.sensor.type[i], config.system.sensor.gpio[i][0]);
}
#endif
} else if(SensorIndex[config.system.sensor.type[i]].type == SENSOR_TYPE_I2C) {
/* when SensorIndex[].type is == I2C sensor*/
/* get only the I2C Address */
byte Addr = Sensor_Addr_Init_Update(config.system.sensor.type[i], config.system.sensor.i2c_addr[i], SENSOR_AIU_MODE_ADDR);
#ifdef DEBUG
Log.verbose(F("%s Sensor ID %d: %s - %s (I2C %d, 0x%x), offering" CR), LogLoc, i,config.system.sensor.name[i],
SensorIndex[config.system.sensor.type[i]].name, config.system.sensor.i2c_addr[i], Addr);
#endif
/* initialize */
sensorStatus[i] = Sensor_Addr_Init_Update(config.system.sensor.type[i], config.system.sensor.i2c_addr[i], SENSOR_AIU_MODE_INIT);
#ifdef DEBUG
/* when init was successful, list the sensor values */
if(sensorStatus[i] == true) {
Sensor_Log_Readings(config.system.sensor.type[i], config.system.sensor.i2c_addr[i]);
}
#endif
}
}
}
}
void Sensor_Update() {
/* Update all configured sensors Values */
const static char LogLoc[] PROGMEM = "[Sensor:Update]";
#ifdef DEBUG2
unsigned long mStart = millis();
unsigned long mStop;
Log.verbose(F("%s Start %u" CR), LogLoc, mStart);
#endif
/* go through all possible existing Sensor configurations */
for(byte i = 0; i < Max_Sensors; i++) {
/* every configured one */
if(config.system.sensor.type[i] > 0) {
/* if internal ADC */
if(SensorIndex[config.system.sensor.type[i]].type == SENSOR_TYPE_INTADC) {
if(sensorStatus[i] == true) {
#ifdef DEBUG2
Log.verbose(F("%s (%d) %s: %s (%d)" CR), LogLoc, i, config.system.sensor.name[i], SensorIndex[config.system.sensor.type[i]].name, config.system.sensor.gpio[i][0]);
#endif
/* perform update of sensor values */
Sensor_Addr_Init_Update(config.system.sensor.type[i], config.system.sensor.gpio[i][0], SENSOR_AIU_MODE_UPDATE);
#ifdef DEBUG2
Sensor_Log_Readings(config.system.sensor.type[i], config.system.sensor.gpio[i][0]);
#endif
}
#ifdef DEBUG2
else {
Log.verbose(F("%s Sensor %d (%s, %d) not initialized." CR), LogLoc, i, SensorIndex[config.system.sensor.type[i]].name, Sensor_Addr_Init_Update(config.system.sensor.type[i], config.system.sensor.gpio[i][0], SENSOR_AIU_MODE_ADDR));
}
#endif
/* Everything above 1 is an I2C sensor */
} else if(SensorIndex[config.system.sensor.type[i]].type == SENSOR_TYPE_I2C) {
if(sensorStatus[i] == true) {
#ifdef DEBUG2
Log.verbose(F("%s (%d) %s: %s (%d)" CR), LogLoc, i, config.system.sensor.name[i], SensorIndex[config.system.sensor.type[i]].name, config.system.sensor.i2c_addr[i]);
#endif
/* perform update of sensor values */
Sensor_Addr_Init_Update(config.system.sensor.type[i], config.system.sensor.i2c_addr[i], SENSOR_AIU_MODE_UPDATE);
#ifdef DEBUG2
Sensor_Log_Readings(config.system.sensor.type[i], config.system.sensor.i2c_addr[i]);
#endif
}
#ifdef DEBUG2
else {
Log.verbose(F("%s Sensor %d (%s, 0x%x) not initialized." CR), LogLoc, i, SensorIndex[config.system.sensor.type[i]].name, Sensor_Addr_Init_Update(config.system.sensor.type[i], config.system.sensor.i2c_addr[i], SENSOR_AIU_MODE_ADDR));
}
#endif
}
}
}
#ifdef DEBUG2
mStop = millis();
Log.verbose(F("%s Stop %u (%u)" CR), LogLoc, mStop, mStop - mStart);
#endif
}