uses BBC micro:bit to measure and display indoor air quality using Bosch BME680 and/or Sensirion SGP30

Dependencies:   microbit

uses Bosch BME680 and/or Sensirion SGP30 sensors to measure indor air quality

sensors should be connected to BBC micro:bit using i2c

commands are received and data is being sent using uBit / nordic radio protocol

display ---

last line always indicates: - first dot: bme680 detected - second dot: sgp30 detected - third dot: sgp 30 setting humidity/temperature - fourth dor: sgp30 measuring - fith dot: bme680 measuring

the detect dots should be in a stable state (not blinking) the measuring dots should be blinking (constant light means: measurement failed)

if only one bme680 is present: - first 3 lines indicate gas resistence (air quality / more dots == worse quality) - fourth line indicates humidity level

if only sgp30 is present: - first two lines indicate SGP30 VOC level - third and fourth line indicate sgp30 CO2 level

if both sensors are present: - first line indicates SGP30 VOC level - second line line indicates sgp30 CO2 level - third line indicates bme680 gas resistence (air quality) - fourth line indicates bme 680 humidity level

buttons - B display state, switches betweeen - full bright - low light - display off

AB reset sgp30 baseline in non volatile storage

data logging -- during measurements the minimum and mximum values for each measured value (temperature, air pressure, humidity,gas resistance, VOC, CO2) are being stored in non volatile storage those (and the last measurement results) are being shown when btn A has been pressed

Committer:
jsa1969
Date:
Fri Feb 15 10:55:02 2019 +0000
Revision:
46:2fed2865a0f3
Child:
47:881bfe77a00a
finally up and running again, next steps: split nvstore into general and app specific functions, split ui from main, more tests

Who changed what in which revision?

UserRevisionLine numberNew contents of line
jsa1969 46:2fed2865a0f3 1 #include "IaqNonVolatileStore.h"
jsa1969 46:2fed2865a0f3 2
jsa1969 46:2fed2865a0f3 3 #include "MovingAverage.h"
jsa1969 46:2fed2865a0f3 4 #include "StringTool.h"
jsa1969 46:2fed2865a0f3 5
jsa1969 46:2fed2865a0f3 6
jsa1969 46:2fed2865a0f3 7 /* from microbit.storage:
jsa1969 46:2fed2865a0f3 8 Key Value pairs have a fixed length key of 16 bytes, and a fixed length value of 32 bytes.
jsa1969 46:2fed2865a0f3 9 This class only populates a single block (1024 bytes) in its current state,
jsa1969 46:2fed2865a0f3 10 which means that 21 Key Value pairs can be stored.
jsa1969 46:2fed2865a0f3 11 */
jsa1969 46:2fed2865a0f3 12
jsa1969 46:2fed2865a0f3 13 #define GAS_MAX_STORE_KEY "bme_max_res" // 10 indexed values
jsa1969 46:2fed2865a0f3 14 #define GAS_MIN_STORE_KEY "bme_min_res" // 11
jsa1969 46:2fed2865a0f3 15 #define TEMP_MAX_STORE_KEY "temp_max" // 12
jsa1969 46:2fed2865a0f3 16 #define TEMP_MIN_STORE_KEY "temp_min" // 13
jsa1969 46:2fed2865a0f3 17 #define PRESS_MAX_STORE_KEY "press_max" // 14
jsa1969 46:2fed2865a0f3 18 #define PRESS_MIN_STORE_KEY "press_min" // 15
jsa1969 46:2fed2865a0f3 19 #define HUM_MAX_STORE_KEY "hum_max" // 16
jsa1969 46:2fed2865a0f3 20 #define HUM_MIN_STORE_KEY "hum_min" // 17
jsa1969 46:2fed2865a0f3 21 #define VOC_MAX_STORE_KEY "voc_max" // 18
jsa1969 46:2fed2865a0f3 22 #define CO_MAX_STORE_KEY "co_max" // 19
jsa1969 46:2fed2865a0f3 23
jsa1969 46:2fed2865a0f3 24 const int IaqNonVolatileStore::AVERAGE_BUFFER_SIZE = 10;
jsa1969 46:2fed2865a0f3 25
jsa1969 46:2fed2865a0f3 26 IaqNonVolatileStore::IaqNonVolatileStore(MicroBit* uBit) {
jsa1969 46:2fed2865a0f3 27 _uBit = uBit;
jsa1969 46:2fed2865a0f3 28
jsa1969 46:2fed2865a0f3 29 for (int i=0 ; i<10 ; ++i){
jsa1969 46:2fed2865a0f3 30 const char* key = StringTool::createIndexedKey0_9(i, GAS_MAX_STORE_KEY);
jsa1969 46:2fed2865a0f3 31 _maxGas[i] = getStored(key, 0);
jsa1969 46:2fed2865a0f3 32 delete [] key;
jsa1969 46:2fed2865a0f3 33 }
jsa1969 46:2fed2865a0f3 34 _minGas = getStored(GAS_MIN_STORE_KEY, 0xffffffff);
jsa1969 46:2fed2865a0f3 35
jsa1969 46:2fed2865a0f3 36 _tempMax = getStored16(TEMP_MAX_STORE_KEY, 0xffff);
jsa1969 46:2fed2865a0f3 37 _tempMin = getStored16(TEMP_MIN_STORE_KEY, 0x7fff);
jsa1969 46:2fed2865a0f3 38
jsa1969 46:2fed2865a0f3 39 _pressMax = getStored(PRESS_MAX_STORE_KEY, 0);
jsa1969 46:2fed2865a0f3 40 _pressMin = getStored(PRESS_MIN_STORE_KEY, 0xffffffff);
jsa1969 46:2fed2865a0f3 41
jsa1969 46:2fed2865a0f3 42 _humMax = getStored(HUM_MAX_STORE_KEY, 0);
jsa1969 46:2fed2865a0f3 43 _humMin = getStored(HUM_MIN_STORE_KEY, 0xffffffff);
jsa1969 46:2fed2865a0f3 44
jsa1969 46:2fed2865a0f3 45 _vocMax = getStored(VOC_MAX_STORE_KEY, 0);
jsa1969 46:2fed2865a0f3 46
jsa1969 46:2fed2865a0f3 47 _coMax = getStored(CO_MAX_STORE_KEY, 0);
jsa1969 46:2fed2865a0f3 48
jsa1969 46:2fed2865a0f3 49 _movingAverage = new MovingAverage(AVERAGE_BUFFER_SIZE);
jsa1969 46:2fed2865a0f3 50 }
jsa1969 46:2fed2865a0f3 51
jsa1969 46:2fed2865a0f3 52 IaqNonVolatileStore::~IaqNonVolatileStore() {
jsa1969 46:2fed2865a0f3 53 if (_movingAverage!=NULL) delete _movingAverage;
jsa1969 46:2fed2865a0f3 54 }
jsa1969 46:2fed2865a0f3 55
jsa1969 46:2fed2865a0f3 56 void IaqNonVolatileStore::clear() {
jsa1969 46:2fed2865a0f3 57 for (int i=0 ; i<10 ; ++i){
jsa1969 46:2fed2865a0f3 58 const char* key = StringTool::createIndexedKey0_9(i, GAS_MAX_STORE_KEY);
jsa1969 46:2fed2865a0f3 59 _uBit->storage.remove(key);
jsa1969 46:2fed2865a0f3 60 delete [] key;
jsa1969 46:2fed2865a0f3 61 }
jsa1969 46:2fed2865a0f3 62
jsa1969 46:2fed2865a0f3 63 _uBit->storage.remove(GAS_MIN_STORE_KEY);
jsa1969 46:2fed2865a0f3 64 _uBit->storage.remove(TEMP_MAX_STORE_KEY);
jsa1969 46:2fed2865a0f3 65 _uBit->storage.remove(TEMP_MIN_STORE_KEY);
jsa1969 46:2fed2865a0f3 66 _uBit->storage.remove(PRESS_MAX_STORE_KEY);
jsa1969 46:2fed2865a0f3 67 _uBit->storage.remove(PRESS_MIN_STORE_KEY);
jsa1969 46:2fed2865a0f3 68 _uBit->storage.remove(HUM_MAX_STORE_KEY);
jsa1969 46:2fed2865a0f3 69 _uBit->storage.remove(HUM_MIN_STORE_KEY);
jsa1969 46:2fed2865a0f3 70 _uBit->storage.remove(VOC_MAX_STORE_KEY);
jsa1969 46:2fed2865a0f3 71 _uBit->storage.remove(CO_MAX_STORE_KEY);
jsa1969 46:2fed2865a0f3 72
jsa1969 46:2fed2865a0f3 73 }
jsa1969 46:2fed2865a0f3 74
jsa1969 46:2fed2865a0f3 75 uint32_t IaqNonVolatileStore::getGasMax(const uint32_t humidity) {
jsa1969 46:2fed2865a0f3 76 return _maxGas[getIndex(humidity)];
jsa1969 46:2fed2865a0f3 77 }
jsa1969 46:2fed2865a0f3 78
jsa1969 46:2fed2865a0f3 79 uint32_t IaqNonVolatileStore::getGasMin(){
jsa1969 46:2fed2865a0f3 80 return _minGas;
jsa1969 46:2fed2865a0f3 81 }
jsa1969 46:2fed2865a0f3 82
jsa1969 46:2fed2865a0f3 83 void IaqNonVolatileStore::updateGas(uint32_t gas, const uint32_t humidity) {
jsa1969 46:2fed2865a0f3 84 const char index = getIndex(humidity);
jsa1969 46:2fed2865a0f3 85 const char* key = StringTool::createIndexedKey0_9(index, GAS_MAX_STORE_KEY);
jsa1969 46:2fed2865a0f3 86 if (!gasWithinReasonableRangeFromAverage(gas) && gas > _maxGas[index]) {
jsa1969 46:2fed2865a0f3 87 gas = _maxGas[index];
jsa1969 46:2fed2865a0f3 88 }
jsa1969 46:2fed2865a0f3 89 update(gas, &(_maxGas[index]), &_minGas, key, GAS_MIN_STORE_KEY);
jsa1969 46:2fed2865a0f3 90 delete [] key;
jsa1969 46:2fed2865a0f3 91 }
jsa1969 46:2fed2865a0f3 92
jsa1969 46:2fed2865a0f3 93 char IaqNonVolatileStore::getIndex(const uint32_t humidity) {
jsa1969 46:2fed2865a0f3 94 char index = humidity/10000;
jsa1969 46:2fed2865a0f3 95 if (index>9) index = 9;
jsa1969 46:2fed2865a0f3 96 return index;
jsa1969 46:2fed2865a0f3 97 }
jsa1969 46:2fed2865a0f3 98
jsa1969 46:2fed2865a0f3 99 int16_t IaqNonVolatileStore::getTempMax(){
jsa1969 46:2fed2865a0f3 100 return _tempMax;
jsa1969 46:2fed2865a0f3 101 }
jsa1969 46:2fed2865a0f3 102
jsa1969 46:2fed2865a0f3 103 int16_t IaqNonVolatileStore::getTempMin(){
jsa1969 46:2fed2865a0f3 104 return _tempMin;
jsa1969 46:2fed2865a0f3 105 }
jsa1969 46:2fed2865a0f3 106
jsa1969 46:2fed2865a0f3 107 void IaqNonVolatileStore::updateTemp(const int16_t temp) {
jsa1969 46:2fed2865a0f3 108 if (_tempMax < temp){
jsa1969 46:2fed2865a0f3 109 _tempMax = temp;
jsa1969 46:2fed2865a0f3 110 _uBit->storage.put(TEMP_MAX_STORE_KEY, (uint8_t *)_tempMax, sizeof(int16_t));
jsa1969 46:2fed2865a0f3 111 }
jsa1969 46:2fed2865a0f3 112 if (_tempMin > temp) {
jsa1969 46:2fed2865a0f3 113 _tempMin = temp;
jsa1969 46:2fed2865a0f3 114 _uBit->storage.put(TEMP_MIN_STORE_KEY, (uint8_t *)_tempMin, sizeof(int16_t));
jsa1969 46:2fed2865a0f3 115 }
jsa1969 46:2fed2865a0f3 116 }
jsa1969 46:2fed2865a0f3 117
jsa1969 46:2fed2865a0f3 118 uint32_t IaqNonVolatileStore::getPressMax(){
jsa1969 46:2fed2865a0f3 119 return _pressMax;
jsa1969 46:2fed2865a0f3 120 }
jsa1969 46:2fed2865a0f3 121
jsa1969 46:2fed2865a0f3 122 uint32_t IaqNonVolatileStore::getPressMin(){
jsa1969 46:2fed2865a0f3 123 return _pressMin;
jsa1969 46:2fed2865a0f3 124 }
jsa1969 46:2fed2865a0f3 125
jsa1969 46:2fed2865a0f3 126 void IaqNonVolatileStore::updatePress(const uint32_t pressure) {
jsa1969 46:2fed2865a0f3 127 update(pressure, &_pressMax, &_pressMin, PRESS_MAX_STORE_KEY, PRESS_MIN_STORE_KEY);
jsa1969 46:2fed2865a0f3 128 }
jsa1969 46:2fed2865a0f3 129
jsa1969 46:2fed2865a0f3 130 uint32_t IaqNonVolatileStore::getHumMax(){
jsa1969 46:2fed2865a0f3 131 return _humMax;
jsa1969 46:2fed2865a0f3 132 }
jsa1969 46:2fed2865a0f3 133
jsa1969 46:2fed2865a0f3 134 uint32_t IaqNonVolatileStore::getHumMin(){
jsa1969 46:2fed2865a0f3 135 return _humMin;
jsa1969 46:2fed2865a0f3 136 }
jsa1969 46:2fed2865a0f3 137
jsa1969 46:2fed2865a0f3 138 void IaqNonVolatileStore::updateHumidity(const uint32_t humidity) {
jsa1969 46:2fed2865a0f3 139 update(humidity, &_humMax, &_humMin, HUM_MAX_STORE_KEY, HUM_MIN_STORE_KEY);
jsa1969 46:2fed2865a0f3 140 }
jsa1969 46:2fed2865a0f3 141
jsa1969 46:2fed2865a0f3 142 uint32_t IaqNonVolatileStore::getVocMax(){
jsa1969 46:2fed2865a0f3 143 return _vocMax;
jsa1969 46:2fed2865a0f3 144 }
jsa1969 46:2fed2865a0f3 145
jsa1969 46:2fed2865a0f3 146 void IaqNonVolatileStore::updateVoc(const uint32_t voc) {
jsa1969 46:2fed2865a0f3 147 update(voc, &_vocMax, NULL, VOC_MAX_STORE_KEY, NULL);
jsa1969 46:2fed2865a0f3 148 }
jsa1969 46:2fed2865a0f3 149
jsa1969 46:2fed2865a0f3 150 uint32_t IaqNonVolatileStore::getCoMax(){
jsa1969 46:2fed2865a0f3 151 return _coMax;
jsa1969 46:2fed2865a0f3 152 }
jsa1969 46:2fed2865a0f3 153
jsa1969 46:2fed2865a0f3 154 void IaqNonVolatileStore::updateCo(const uint32_t co) {
jsa1969 46:2fed2865a0f3 155 update(co, &_coMax, NULL, CO_MAX_STORE_KEY, NULL);
jsa1969 46:2fed2865a0f3 156 }
jsa1969 46:2fed2865a0f3 157
jsa1969 46:2fed2865a0f3 158 void IaqNonVolatileStore::update(const uint32_t value, uint32_t* maxVal, uint32_t* minVal, const char* maxKey, const char* minKey) {
jsa1969 46:2fed2865a0f3 159 if (*maxVal < value) {
jsa1969 46:2fed2865a0f3 160 *maxVal = value;
jsa1969 46:2fed2865a0f3 161 _uBit->storage.put(maxKey, (uint8_t *)maxVal, sizeof(uint32_t));
jsa1969 46:2fed2865a0f3 162 }
jsa1969 46:2fed2865a0f3 163 if (minVal!= NULL && *minVal>value) {
jsa1969 46:2fed2865a0f3 164 *minVal = value;
jsa1969 46:2fed2865a0f3 165 _uBit->storage.put(minKey, (uint8_t *)minVal, sizeof(uint32_t));
jsa1969 46:2fed2865a0f3 166 }
jsa1969 46:2fed2865a0f3 167 }
jsa1969 46:2fed2865a0f3 168
jsa1969 46:2fed2865a0f3 169 bool IaqNonVolatileStore::gasWithinReasonableRangeFromAverage(const uint32_t gas) {
jsa1969 46:2fed2865a0f3 170 if (gas==0) return false;
jsa1969 46:2fed2865a0f3 171 _movingAverage->add(gas);
jsa1969 46:2fed2865a0f3 172 const uint32_t average = _movingAverage->average();
jsa1969 46:2fed2865a0f3 173 return (average!=MovingAverage::INVALID) && ((average<<1) > gas);
jsa1969 46:2fed2865a0f3 174 }
jsa1969 46:2fed2865a0f3 175
jsa1969 46:2fed2865a0f3 176 const uint32_t* IaqNonVolatileStore::debugInfo() {
jsa1969 46:2fed2865a0f3 177 return _movingAverage->debugInfo();
jsa1969 46:2fed2865a0f3 178 }
jsa1969 46:2fed2865a0f3 179
jsa1969 46:2fed2865a0f3 180 bool IaqNonVolatileStore::strayData() {
jsa1969 46:2fed2865a0f3 181 const uint32_t* values = _movingAverage->debugInfo();
jsa1969 46:2fed2865a0f3 182 uint32_t max = values[0];
jsa1969 46:2fed2865a0f3 183 for (int i=0 ; i<AVERAGE_BUFFER_SIZE; ++i) {
jsa1969 46:2fed2865a0f3 184 uint32_t val = values[i];
jsa1969 46:2fed2865a0f3 185 if (val > max) {
jsa1969 46:2fed2865a0f3 186 max = val;
jsa1969 46:2fed2865a0f3 187 }
jsa1969 46:2fed2865a0f3 188 }
jsa1969 46:2fed2865a0f3 189
jsa1969 46:2fed2865a0f3 190 bool stray = false;
jsa1969 46:2fed2865a0f3 191 for (int i=0 ; stray==false && i<AVERAGE_BUFFER_SIZE; ++i) {
jsa1969 46:2fed2865a0f3 192 stray = (max - values[i]) > 100000;
jsa1969 46:2fed2865a0f3 193 }
jsa1969 46:2fed2865a0f3 194 return stray;
jsa1969 46:2fed2865a0f3 195 }
jsa1969 46:2fed2865a0f3 196
jsa1969 46:2fed2865a0f3 197 uint32_t IaqNonVolatileStore::getStored(const char * key, const uint32_t defaultVal){
jsa1969 46:2fed2865a0f3 198 uint32_t value = defaultVal;
jsa1969 46:2fed2865a0f3 199 KeyValuePair* storedVal = _uBit->storage.get(key);
jsa1969 46:2fed2865a0f3 200 if (storedVal!=NULL) {
jsa1969 46:2fed2865a0f3 201 memcpy(&value, storedVal->value, sizeof(uint32_t));
jsa1969 46:2fed2865a0f3 202 delete storedVal;
jsa1969 46:2fed2865a0f3 203 }
jsa1969 46:2fed2865a0f3 204 return value;
jsa1969 46:2fed2865a0f3 205 }
jsa1969 46:2fed2865a0f3 206
jsa1969 46:2fed2865a0f3 207 int16_t IaqNonVolatileStore::getStored16(const char * key, const int16_t defaultVal){
jsa1969 46:2fed2865a0f3 208 int16_t value = defaultVal;
jsa1969 46:2fed2865a0f3 209 KeyValuePair* storedVal = _uBit->storage.get(key);
jsa1969 46:2fed2865a0f3 210 if (storedVal!=NULL) {
jsa1969 46:2fed2865a0f3 211 memcpy(&value, storedVal->value, sizeof(int16_t));
jsa1969 46:2fed2865a0f3 212 delete storedVal;
jsa1969 46:2fed2865a0f3 213 }
jsa1969 46:2fed2865a0f3 214 return value;
jsa1969 46:2fed2865a0f3 215 }