Skip to content

Commit

Permalink
v2.5
Browse files Browse the repository at this point in the history
- Add support for BMx820 sensor (temperature only).
- Modify former "light sensor" keypad menu to not only show measured lux level from a light sensor, but also current ambient temperature as measured by a connected temperature sensor. Rename menu to "Sensors" accordingly.
- Add temperature offset value. User can program an offset that will be added to the measured temperature for compensating sensor inaccuracies or suboptimal sensor placement. In order to calibrate the offset, use the keypad menu "SENSORS" since the temperature shown there is not rounded (unlike what is shown on a speedo display if it has less than three digits).
  • Loading branch information
CircuitSetup committed Dec 7, 2022
1 parent 5634fa0 commit 6630fb4
Show file tree
Hide file tree
Showing 13 changed files with 257 additions and 82 deletions.
131 changes: 115 additions & 16 deletions Software/src/sensors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* Sensor Class: Temperature and Light Sensor handling
*
* This is designed for
* - MCP9808-based temperature sensors,
* - MCP9808, BMx820 temperature sensors,
* - BH1750, TSL2561 and VEML7700/VEML6030 light sensors.
* -------------------------------------------------------------------
* License: MIT
Expand Down Expand Up @@ -45,15 +45,20 @@ static void defaultDelay(unsigned int mydelay)
delay(mydelay);
}

void tcSensor::prepareRead(uint16_t regno)
{
Wire.beginTransmission(_address);
Wire.write((uint8_t)(regno));
Wire.endTransmission(false);
}

uint16_t tcSensor::read16(uint16_t regno, bool LSBfirst)
{
uint16_t value = 0;
size_t i2clen = 0;

if(regno <= 0xff) {
Wire.beginTransmission(_address);
Wire.write((uint8_t)(regno));
Wire.endTransmission(false);
prepareRead(regno);
}

i2clen = Wire.requestFrom(_address, (uint8_t)2);
Expand All @@ -70,13 +75,29 @@ uint16_t tcSensor::read16(uint16_t regno, bool LSBfirst)
return value;
}

uint32_t tcSensor::read24(uint16_t regno)
{
uint32_t value = 0;
size_t i2clen = 0;

prepareRead(regno);

i2clen = Wire.requestFrom(_address, (uint8_t)3);

if(i2clen > 0) {
value = Wire.read() << 16;
value |= (Wire.read() << 8);
value |= Wire.read();
}

return value;
}

uint8_t tcSensor::read8(uint16_t regno)
{
uint16_t value = 0;

Wire.beginTransmission(_address);
Wire.write((uint8_t)(regno));
Wire.endTransmission(false);
prepareRead(regno);

Wire.requestFrom(_address, (uint8_t)1);

Expand Down Expand Up @@ -112,7 +133,7 @@ void tcSensor::write8(uint16_t regno, uint8_t value)
/*****************************************************************
* tempSensor Class
*
* Supports MCP9808-based temperature sensors
* Supports MCP9808 and BMx820 temperature sensors
*
****************************************************************/

Expand Down Expand Up @@ -145,6 +166,17 @@ void tcSensor::write8(uint16_t regno, uint8_t value)
#define TC_TEMP_RES_MCP9808 2
static const uint16_t wakeDelayMCP9808[4] = { 30, 65, 130, 250 };

#define BMx280_REG_DIG_T1 0x88
#define BMx280_REG_DIG_T2 0x8a
#define BMx280_REG_DIG_T3 0x8c
#define BMx280_REG_ID 0xd0
#define BME280_REG_RESET 0xe0
#define BMx820_REG_CTRLH 0xf2
#define BMx280_REG_STATUS 0xf3
#define BMx820_REG_CTRLM 0xf4
#define BMx820_REG_CONF 0xf5
#define BMx820_REG_TEMP 0xfa


// Store i2c address
tempSensor::tempSensor(int numTypes, uint8_t addrArr[])
Expand All @@ -160,6 +192,7 @@ tempSensor::tempSensor(int numTypes, uint8_t addrArr[])
bool tempSensor::begin()
{
bool foundSt = false;
uint8_t temp, timeOut = 20;

_customDelayFunc = defaultDelay;

Expand All @@ -177,7 +210,15 @@ bool tempSensor::begin()
#endif
}
break;
/*...*/
case BMx820:
temp = read8(BMx280_REG_ID);
if(temp == 0x60 || temp == 0x58) {
foundSt = true;
#ifdef TC_DBG
Serial.println("tempSensor: Detected BMx820 temperature sensor");
#endif
}
break;
}

if(foundSt) {
Expand All @@ -187,13 +228,45 @@ bool tempSensor::begin()
}

switch(_st) {

case MCP9808:
// Write config register
write16(MCP9808_REG_CONFIG, 0x0);

// Set resolution
write8(MCP9808_REG_RESOLUTION, TC_TEMP_RES_MCP9808 & 0x03);
break;

case BMx820:
// Reset
write8(BME280_REG_RESET, 0xb6);
(*_customDelayFunc)(10);

// Wait for sensor to copy its calib data
do {
temp = read8(BMx280_REG_STATUS);
(*_customDelayFunc)(10);
} while((temp & 1) && --timeOut);

// read relevant calib data
_BMx280_CD_T1 = read16(BMx280_REG_DIG_T1, true);
_BMx280_CD_T2 = (int32_t)((int16_t)read16(BMx280_REG_DIG_T2, true));
_BMx280_CD_T3 = (int32_t)((int16_t)read16(BMx280_REG_DIG_T3, true));
#ifdef TC_DBG
Serial.println("BMx820 calib values");
Serial.println(_BMx280_CD_T1);
Serial.println(_BMx280_CD_T2);
Serial.println(_BMx280_CD_T3);
#endif

// setup sensor parameters
write8(BMx820_REG_CTRLM, 0x20); // Temp OSx1; Pres skipped; "sleep mode"
write8(BMx820_REG_CTRLH, 0x00); // Hum skipped
write8(BMx820_REG_CONF, 0xa0); // t_sb 1000ms; filter off, SPI3w off

write8(BMx820_REG_CTRLM, 0x23); // Temp OSx1; Pres skipped; "normal mode"
break;

default:
return false;
}
Expand Down Expand Up @@ -224,27 +297,39 @@ void tempSensor::off()
double tempSensor::readTemp(bool celsius)
{
double temp = NAN;
uint16_t t;

switch(_st) {
case MCP9808:
uint16_t t = read16(MCP9808_REG_AMBIENT_TEMP);
t = read16(MCP9808_REG_AMBIENT_TEMP);

if(t != 0xffff) {
temp = ((double)(t & 0x0fff)) / 16.0;
if(t & 0x1000) temp = 256.0 - temp;
if(!celsius) temp = temp * 9.0 / 5.0 + 32.0;
}
break;
case BMx820:
temp = BMx280_CalcTemp(read24(BMx820_REG_TEMP));
break;
}

if(!celsius && !isnan(temp)) temp = temp * 9.0 / 5.0 + 32.0;

temp += _userOffset;

#ifdef TC_DBG
Serial.print("Read temp: ");
Serial.println(temp);
#endif
#endif

return temp;
}

void tempSensor::setOffset(double myOffs)
{
_userOffset = myOffs;
}

// Private functions ###########################################################

void tempSensor::onoff(bool shutDown)
Expand Down Expand Up @@ -277,19 +362,33 @@ void tempSensor::onoff(bool shutDown)
}
}

double tempSensor::BMx280_CalcTemp(uint32_t ival)
{
int32_t var1, var2;

if(ival == 0x800000) return NAN;

ival >>= 4;

var1 = ((((ival >> 3) - (_BMx280_CD_T1 << 1))) * _BMx280_CD_T2) / 2048;
var2 = (ival >> 4) - _BMx280_CD_T1;
var2 = (((var2 * var2) / 4096) * _BMx280_CD_T3) / 16384;

return (double)(((var1 + var2) * 5 + 128) / 256) / 100.0;
}

#endif // TC_HAVETEMP


/*****************************************************************
* lightSensor Class
*
* Supports TSL2561, BH1750, VEML7700 light sensors
* Supports TSL2561, BH1750, VEML7700/6030 light sensors
*
* Sensors are set for indoor conditions and might overflow (in
* which case -1 is returned) or report bad lux values outdoors.
* For TSL2561 and BH1750, their settings can be changed by
* #defines below. VEML7700 auto-adjusts.
* #defines below. VEML7700/6030 auto-adjust.
*
****************************************************************/

Expand Down Expand Up @@ -677,7 +776,7 @@ uint32_t lightSensor::TSL2561CalcLux(uint8_t iGain, uint8_t tInt, uint32_t ch0,

ratio = (ratio1 + 1) >> 1;

if((ratio >= 0) && (ratio <= TSL2561_K1T))
if(ratio <= TSL2561_K1T)
{ b = TSL2561_B1T; m = TSL2561_M1T; }
else if (ratio <= TSL2561_K2T)
{ b = TSL2561_B2T; m = TSL2561_M2T; }
Expand All @@ -691,7 +790,7 @@ uint32_t lightSensor::TSL2561CalcLux(uint8_t iGain, uint8_t tInt, uint32_t ch0,
{ b = TSL2561_B6T; m = TSL2561_M6T; }
else if(ratio <= TSL2561_K7T)
{ b = TSL2561_B7T; m = TSL2561_M7T; }
else if(ratio > TSL2561_K8T)
else //if(ratio > TSL2561_K8T)
{ b = TSL2561_B8T; m = TSL2561_M8T; }

temp = ((channel0 * b) - (channel1 * m));
Expand Down
17 changes: 15 additions & 2 deletions Software/src/sensors.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@
class tcSensor {

protected:


void prepareRead(uint16_t regno);
uint16_t read16(uint16_t regno, bool LSBfirst = false);
uint8_t read8(uint16_t regno);
uint32_t read24(uint16_t regno);
void write16(uint16_t regno, uint16_t value, bool LSBfirst = false);
void write8(uint16_t regno, uint8_t value);

Expand All @@ -54,7 +56,8 @@ class tcSensor {
#ifdef TC_HAVETEMP // -----------------------------------------

enum {
MCP9808 = 0
MCP9808 = 0,
BMx820
};

class tempSensor : tcSensor {
Expand All @@ -71,14 +74,24 @@ class tempSensor : tcSensor {
void off();
double readTemp(bool celsius = true);

void setOffset(double myOffs);

private:

int _numTypes = 0;
uint8_t _addrArr[4*2]; // up to 4 sensor types fit here
int8_t _st = -1;

double _userOffset = 0.0;

uint32_t _BMx280_CD_T1;
int32_t _BMx280_CD_T2;
int32_t _BMx280_CD_T3;

void onoff(bool shutDown);

double BMx280_CalcTemp(uint32_t ival);

// Ptr to custom delay function
void (*_customDelayFunc)(unsigned int) = NULL;
};
Expand Down
4 changes: 2 additions & 2 deletions Software/src/speeddisplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,8 +304,8 @@ static struct dispConf displays[SP_NUM_TYPES] = {
{ false, 3, 4, 0, 0, 4, 0, 5, 0, 0x2080, 8, 4, 0, { 1, 2, 3, 4 }, font144segGrove }, // SP_GROVE_4DIG14 (right)
{ false, 1, 2, 0, 0, 2, 0, 5, 0, 0x2080, 8, 4, 0, { 1, 2, 3, 4 }, font144segGrove }, // SP_GROVE_4DIG14 (left)
#ifdef TWPRIVATE
{ false, 0, 1, 0, 0, 1, 0, 255, 0, 0, 8, 2, 0, { 0, 1 }, font14segGeneric }, // TW Custom (wallclock)
{ true, 0, 1, 0, 0, 1, 0, 2, 0, 0x0002, 8, 2, 0, { 0, 1 }, font7segGeneric }, // TW Custom (speedo replica)
{ false, 0, 1, 0, 0, 1, 0, 255, 0, 0, 8, 2, 0, { 0, 1 }, font14segGeneric }, // TW Custom (wall clock)
{ true, 0, 1, 0, 0, 1, 0, 255, 0, 0, 8, 2, 0, { 0, 1 }, font7segGeneric }, // TW Custom (speedo replica)
#endif
// .... for testing only:
//{ true, 7, 7, 0, 8, 7, 8, 255, 0, 0, 8, 2, 1, { 7 }, font7segGeneric }, // SP_TCD_TEST7
Expand Down
6 changes: 3 additions & 3 deletions Software/src/speeddisplay.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@
//
// The display's i2c slave address is 0x70 (defined in tc_time.h).
//
enum dispTypes : int {
SP_CIRCSETUP, // Original CircuitSetup.us speedo [yet to be designed]
enum dispTypes : uint8_t {
SP_CIRCSETUP = 0, // Original CircuitSetup.us speedo [yet to be designed]
SP_ADAF_7x4, // Adafruit 0.56" 4 digits, 7-segment (7x4) (ADA-878)
SP_ADAF_7x4L, // " " " (left-aligned)
SP_ADAF_B7x4, // Adafruit 1.2" 4 digits, 7-segment (7x4) (ADA-1270)
Expand Down Expand Up @@ -125,7 +125,7 @@ class speedDisplay {
bool _nightmode = false;
int _oldnm = -1;

int _dispType;
uint8_t _dispType;
bool _is7seg; // 7- or 14-segment-display?
uint8_t _speed_pos10; // Speed's 10s position in 16bit buffer
uint8_t _speed_pos01; // Speed's 1s position in 16bit buffer
Expand Down
2 changes: 1 addition & 1 deletion Software/src/tc_font.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ static const uint16_t alphaChars[128] = {
0b0000000011101110, // Y
0b0000110000001001, // Z
0b0000000000111001, // [
0b0010000100000000, //
0b0010000100000000, // \
0b0000000000001111, // ]
0b0000110000000011, // ^
0b0000000000001000, // _
Expand Down
19 changes: 10 additions & 9 deletions Software/src/tc_global.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@
// These must not contain any characters other than
// '0'-'9', 'A'-'Z', '(', ')', '.', '_', '-' or space
#ifndef IS_ACAR_DISPLAY
#define TC_VERSION "V2.4.0" // 13 chars max
#define TC_VERSION_EXTRA "NOV232022" // 13 chars max
#define TC_VERSION "V2.5.0" // 13 chars max
#define TC_VERSION_EXTRA "DEC022022" // 13 chars max
#else // A-Car
#define TC_VERSION "V2.4.0_A-CAR" // 12 chars max
#define TC_VERSION_EXTRA "11232022" // 12 chars max
#define TC_VERSION "V2.5.0_A-CAR" // 12 chars max
#define TC_VERSION_EXTRA "12022022" // 12 chars max
#endif

//#define TC_DBG // debug output on Serial
Expand All @@ -53,15 +53,16 @@
#define SP_NUM_TYPES 10 // Number of speedo display types supported
#define SP_MIN_TYPE 1 // Change to 0 when CircuitSetup speedo prop exists

// Uncomment for support of a temperature sensor (MCP9808-based) connected
// via i2c (0x18). Will be used to display ambient temperature on speedometer
// Uncomment for support of a temperature sensor (MCP9808, BMx820) connected
// via i2c. Will be used to display ambient temperature on speedometer
// display when idle. GPS speed has higher priority, ie if GPS speed is
// enabled in Config Portal, temperature will not be shown.
#define TC_HAVETEMP

// Uncomment for support of s light sensor (TLS2561, BH1750, or VEML7700)
// connected via i2c. Used for night-mode-switching. VEML7700 and GPS cannot
// be present at the same time since they share the same i2c slave address.
// Uncomment for support of a light sensor (TLS2561, BH1750, VEML7700 or
// VEML6030) connected via i2c. Used for night-mode-switching. VEML7700
// and GPS cannot be present at the same time since they share the same
// i2c slave address. VEML6030 needs to be set to 0x48 if GPS is present.
//#define TC_HAVELIGHT

// Fake Power Switch:
Expand Down
Loading

0 comments on commit 6630fb4

Please sign in to comment.