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:
Thu Jan 20 12:58:13 2022 +0000
Revision:
52:112eaa5282c6
Parent:
51:133f2fb8a7c5
Child:
53:d560c097f1b4
transmit more information

Who changed what in which revision?

UserRevisionLine numberNew contents of line
jsa1969 0:cef60cc92da0 1 /*
jsa1969 0:cef60cc92da0 2 The MIT License (MIT)
jsa1969 0:cef60cc92da0 3
jsa1969 0:cef60cc92da0 4 Copyright (c) 2016 British Broadcasting Corporation.
jsa1969 0:cef60cc92da0 5 This software is provided by Lancaster University by arrangement with the BBC.
jsa1969 0:cef60cc92da0 6
jsa1969 0:cef60cc92da0 7 Permission is hereby granted, free of charge, to any person obtaining a
jsa1969 0:cef60cc92da0 8 copy of this software and associated documentation files (the "Software"),
jsa1969 0:cef60cc92da0 9 to deal in the Software without restriction, including without limitation
jsa1969 0:cef60cc92da0 10 the rights to use, copy, modify, merge, publish, distribute, sublicense,
jsa1969 0:cef60cc92da0 11 and/or sell copies of the Software, and to permit persons to whom the
jsa1969 0:cef60cc92da0 12 Software is furnished to do so, subject to the following conditions:
jsa1969 0:cef60cc92da0 13
jsa1969 0:cef60cc92da0 14 The above copyright notice and this permission notice shall be included in
jsa1969 0:cef60cc92da0 15 all copies or substantial portions of the Software.
jsa1969 0:cef60cc92da0 16
jsa1969 0:cef60cc92da0 17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
jsa1969 0:cef60cc92da0 18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
jsa1969 0:cef60cc92da0 19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
jsa1969 0:cef60cc92da0 20 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
jsa1969 0:cef60cc92da0 21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
jsa1969 0:cef60cc92da0 22 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
jsa1969 0:cef60cc92da0 23 DEALINGS IN THE SOFTWARE.
jsa1969 0:cef60cc92da0 24 */
jsa1969 0:cef60cc92da0 25
jsa1969 0:cef60cc92da0 26 #include "MicroBit.h"
jsa1969 0:cef60cc92da0 27
jsa1969 0:cef60cc92da0 28 #include "bme680.h"
jsa1969 2:544117df8c65 29 #include "sgp30.h"
jsa1969 46:2fed2865a0f3 30 #include "IaqNonVolatileStore.h"
jsa1969 49:bbb506b58e6e 31 #include "JavaScriptRadio.h"
jsa1969 2:544117df8c65 32
jsa1969 1:f9245fb53737 33 #include "physics.h"
jsa1969 0:cef60cc92da0 34
jsa1969 46:2fed2865a0f3 35 #include "MovingAverage.h"
jsa1969 47:881bfe77a00a 36 #include "RangeTransform.h"
jsa1969 46:2fed2865a0f3 37
jsa1969 33:af2dfab24ca9 38 #include "AppSpecificTestrunner.h"
jsa1969 33:af2dfab24ca9 39
jsa1969 49:bbb506b58e6e 40 #define RADIO_GROUP 14
jsa1969 49:bbb506b58e6e 41 #define RADIO_DELAY 800
jsa1969 49:bbb506b58e6e 42
jsa1969 49:bbb506b58e6e 43 #define CMD_SET_BASELINE "baseline "
jsa1969 49:bbb506b58e6e 44 #define CMD_MEASURE_AND_PUBLISH "IAQ"
jsa1969 49:bbb506b58e6e 45 #define CMD_RESET_TEMP_HUM_CO2 "resetTmpHum"
jsa1969 49:bbb506b58e6e 46 #define CMD_STORE_CURRENT_BASELINE "updateBaseline"
jsa1969 49:bbb506b58e6e 47
jsa1969 0:cef60cc92da0 48 MicroBit uBit;
jsa1969 2:544117df8c65 49 Bme680* bme680 = NULL;
jsa1969 2:544117df8c65 50 struct bme680_field_data* bme680Data = NULL;
jsa1969 2:544117df8c65 51 Sgp30* sgp30 = NULL;
jsa1969 12:96eb06e837f4 52 I2cCallbacks* callbacks = NULL;
jsa1969 46:2fed2865a0f3 53 IaqNonVolatileStore* nvStore = NULL;
jsa1969 49:bbb506b58e6e 54 JavaScriptRadio* radio = NULL;
jsa1969 12:96eb06e837f4 55
jsa1969 12:96eb06e837f4 56 bool cancelMeasureLoops = false;
jsa1969 12:96eb06e837f4 57 int runningLoops = 0;
jsa1969 49:bbb506b58e6e 58 int displayBrightness = 255;
jsa1969 0:cef60cc92da0 59
jsa1969 12:96eb06e837f4 60 void waitForLooppsToFinish() {
jsa1969 12:96eb06e837f4 61 cancelMeasureLoops=true;
jsa1969 12:96eb06e837f4 62 while (runningLoops>0) {
jsa1969 12:96eb06e837f4 63 uBit.sleep(10);
jsa1969 12:96eb06e837f4 64 }
jsa1969 12:96eb06e837f4 65 cancelMeasureLoops = false;
jsa1969 12:96eb06e837f4 66 }
jsa1969 0:cef60cc92da0 67
jsa1969 49:bbb506b58e6e 68
jsa1969 11:6844a5578b4f 69 void displayPixels(int ystart, int pixels, int maxpixels) {
jsa1969 11:6844a5578b4f 70 int y = ystart;
jsa1969 11:6844a5578b4f 71 for (int i=0; i+((y-ystart)*5)<maxpixels; ++i) {
jsa1969 11:6844a5578b4f 72 if (i==5) {
jsa1969 11:6844a5578b4f 73 ++y;
jsa1969 11:6844a5578b4f 74 i=0;
jsa1969 11:6844a5578b4f 75 }
jsa1969 11:6844a5578b4f 76 int pixelsused = i+((y-ystart)*5);
jsa1969 49:bbb506b58e6e 77 uBit.display.image.setPixelValue(i, y, pixelsused<pixels? displayBrightness : 0);
jsa1969 10:8e6b71a46871 78 }
jsa1969 10:8e6b71a46871 79 }
jsa1969 48:52cad865a84f 80
jsa1969 1:f9245fb53737 81 int measureBme680(){
jsa1969 38:0f29a0ea64ca 82 int result = BME680_E_DEV_NOT_FOUND;
jsa1969 1:f9245fb53737 83 if (bme680!=NULL && bme680Data!=NULL) {
jsa1969 38:0f29a0ea64ca 84 result = bme680->measure(
jsa1969 1:f9245fb53737 85 bme680Data,
jsa1969 2:544117df8c65 86 bme680Data->temperature==0 ?
jsa1969 2:544117df8c65 87 uBit.thermometer.getTemperature()
jsa1969 45:d6a9fd9c8200 88 : bme680Data->temperature,
jsa1969 38:0f29a0ea64ca 89 2000, 350);
jsa1969 1:f9245fb53737 90 }
jsa1969 38:0f29a0ea64ca 91 return result;
jsa1969 0:cef60cc92da0 92 }
jsa1969 0:cef60cc92da0 93
jsa1969 11:6844a5578b4f 94 void measureAndDisplayBme680() {
jsa1969 11:6844a5578b4f 95 if (bme680!=NULL) {
jsa1969 46:2fed2865a0f3 96 // measuring
jsa1969 49:bbb506b58e6e 97 uBit.display.image.setPixelValue(4, 4, displayBrightness);
jsa1969 11:6844a5578b4f 98 if (measureBme680()==MICROBIT_OK) {
jsa1969 49:bbb506b58e6e 99 nvStore->updateGas(bme680Data->gas_resistance);
jsa1969 17:a2c4dd192146 100 nvStore->updateTemp(bme680Data->temperature);
jsa1969 17:a2c4dd192146 101 nvStore->updatePress(bme680Data->pressure);
jsa1969 17:a2c4dd192146 102 nvStore->updateHumidity(bme680Data->humidity);
jsa1969 17:a2c4dd192146 103
jsa1969 49:bbb506b58e6e 104 const uint32_t gasMax = nvStore->getGasMax();
jsa1969 46:2fed2865a0f3 105
jsa1969 46:2fed2865a0f3 106 // status (last line: 0 sensor found, 2, stray signals, 4, measuring)
jsa1969 49:bbb506b58e6e 107 uBit.display.image.setPixelValue(0, 4, displayBrightness);
jsa1969 49:bbb506b58e6e 108 uBit.display.image.setPixelValue(2, 4, nvStore->strayData() || gasMax < bme680Data->gas_resistance ? displayBrightness : 0);
jsa1969 46:2fed2865a0f3 109 // will be set below uBit.display.image.setPixelValue(4, 4, 0);
jsa1969 44:67a19da5f269 110
jsa1969 46:2fed2865a0f3 111 // if sgp 30 exists, we have less room fpr bme680 resukts
jsa1969 47:881bfe77a00a 112 const int bmeY = sgp30 != NULL ? 2 : 0;
jsa1969 47:881bfe77a00a 113 const int bmeMaxPixels = sgp30 != NULL ? 5 : 15;
jsa1969 46:2fed2865a0f3 114
jsa1969 46:2fed2865a0f3 115 // bme 680 gas state
jsa1969 47:881bfe77a00a 116 const int bmeGasPixels = (gasMax - bme680Data->gas_resistance) * bmeMaxPixels / gasMax;
jsa1969 47:881bfe77a00a 117 displayPixels(bmeY, bmeGasPixels, bmeMaxPixels);
jsa1969 46:2fed2865a0f3 118
jsa1969 46:2fed2865a0f3 119 // humidity index
jsa1969 11:6844a5578b4f 120 displayPixels(3, 5*bme680Data->humidity/100000, 5);
jsa1969 14:71060505061e 121 } else {
jsa1969 46:2fed2865a0f3 122 // indicate sensor not working
jsa1969 14:71060505061e 123 uBit.display.image.setPixelValue(0, 4, 0);
jsa1969 2:544117df8c65 124 }
jsa1969 46:2fed2865a0f3 125 // not measuring
jsa1969 12:96eb06e837f4 126 uBit.display.image.setPixelValue(4, 4, 0);
jsa1969 11:6844a5578b4f 127 }
jsa1969 11:6844a5578b4f 128 }
jsa1969 11:6844a5578b4f 129
jsa1969 49:bbb506b58e6e 130 bool checkAndStoreSgp30Baseline() {
jsa1969 49:bbb506b58e6e 131 uint16_t eco2_base;
jsa1969 49:bbb506b58e6e 132 uint16_t tvoc_base;
jsa1969 49:bbb506b58e6e 133 bool baseLineOK = sgp30->getIAQBaseline(&eco2_base, &tvoc_base);
jsa1969 49:bbb506b58e6e 134 if (baseLineOK) {
jsa1969 49:bbb506b58e6e 135 nvStore->storeIAQBaseline(eco2_base, tvoc_base);
jsa1969 52:112eaa5282c6 136 radio -> sendToMakeCodeDevices("sgp30 baseln upd");
jsa1969 52:112eaa5282c6 137 radio -> sendToMakeCodeDevices(ManagedString(eco2_base)
jsa1969 52:112eaa5282c6 138 + " / "
jsa1969 52:112eaa5282c6 139 + ManagedString(tvoc_base));
jsa1969 49:bbb506b58e6e 140 }
jsa1969 52:112eaa5282c6 141
jsa1969 49:bbb506b58e6e 142 return baseLineOK;
jsa1969 49:bbb506b58e6e 143 }
jsa1969 49:bbb506b58e6e 144
jsa1969 11:6844a5578b4f 145 void measureAndDisplaySgp30() {
jsa1969 11:6844a5578b4f 146 if (sgp30!=NULL) {
jsa1969 11:6844a5578b4f 147 int sgpMaxPixels = 10;
jsa1969 11:6844a5578b4f 148 int secondLineStart = 2;
jsa1969 11:6844a5578b4f 149 if (bme680!=NULL) {
jsa1969 11:6844a5578b4f 150 sgpMaxPixels = 5;
jsa1969 11:6844a5578b4f 151 secondLineStart = 1;
jsa1969 49:bbb506b58e6e 152 uBit.display.image.setPixelValue(3, 4, displayBrightness);
jsa1969 11:6844a5578b4f 153 if (sgp30->setHumidity(bme680Data->humidity, bme680Data->temperature)) {
jsa1969 44:67a19da5f269 154 uBit.display.image.setPixelValue(3, 4, 0);
jsa1969 11:6844a5578b4f 155 }
jsa1969 11:6844a5578b4f 156 uBit.sleep(10);
jsa1969 11:6844a5578b4f 157 }
jsa1969 49:bbb506b58e6e 158 uBit.display.image.setPixelValue(3, 4, displayBrightness);
jsa1969 38:0f29a0ea64ca 159 bool measureOK = sgp30->IAQmeasure();
jsa1969 38:0f29a0ea64ca 160 if (measureOK) {
jsa1969 52:112eaa5282c6 161 sgp30->IAQmeasureRaw();
jsa1969 49:bbb506b58e6e 162 uBit.display.image.setPixelValue(1, 4, displayBrightness);
jsa1969 11:6844a5578b4f 163 uBit.display.image.setPixelValue(3, 4, 0);
jsa1969 18:c4ac93e01027 164
jsa1969 18:c4ac93e01027 165 nvStore->updateVoc(sgp30->TVOC);
jsa1969 18:c4ac93e01027 166 nvStore->updateCo(sgp30->eCO2);
jsa1969 18:c4ac93e01027 167
jsa1969 12:96eb06e837f4 168 int co2Dots = min (5, sgp30->eCO2 /1500);
jsa1969 47:881bfe77a00a 169 displayPixels(0, 5 - RangeTransform::exponentialTransform(sgp30->TVOC, 20000, sgpMaxPixels), sgpMaxPixels);
jsa1969 11:6844a5578b4f 170 displayPixels(secondLineStart, co2Dots, sgpMaxPixels);
jsa1969 13:996026828be7 171 } else {
jsa1969 13:996026828be7 172 uBit.display.image.setPixelValue(1, 4, 0);
jsa1969 11:6844a5578b4f 173 }
jsa1969 2:544117df8c65 174 }
jsa1969 2:544117df8c65 175 }
jsa1969 2:544117df8c65 176
jsa1969 49:bbb506b58e6e 177
jsa1969 12:96eb06e837f4 178 void bmeMeasureLoop() {
jsa1969 12:96eb06e837f4 179 if (bme680!=NULL) {
jsa1969 12:96eb06e837f4 180 ++runningLoops;
jsa1969 12:96eb06e837f4 181 while (!cancelMeasureLoops){
jsa1969 11:6844a5578b4f 182 measureAndDisplayBme680();
jsa1969 45:d6a9fd9c8200 183 uBit.sleep(500);
jsa1969 12:96eb06e837f4 184 }
jsa1969 12:96eb06e837f4 185 --runningLoops;
jsa1969 12:96eb06e837f4 186 }
jsa1969 24:07a1d2e6914e 187 release_fiber();
jsa1969 12:96eb06e837f4 188 }
jsa1969 12:96eb06e837f4 189
jsa1969 12:96eb06e837f4 190 void sgpMeasureLoop() {
jsa1969 12:96eb06e837f4 191 if (sgp30!=NULL) {
jsa1969 12:96eb06e837f4 192 ++runningLoops;
jsa1969 12:96eb06e837f4 193 while (!cancelMeasureLoops){
jsa1969 11:6844a5578b4f 194 measureAndDisplaySgp30();
jsa1969 12:96eb06e837f4 195 uBit.sleep(950);
jsa1969 2:544117df8c65 196 }
jsa1969 12:96eb06e837f4 197 --runningLoops;
jsa1969 2:544117df8c65 198 }
jsa1969 24:07a1d2e6914e 199 release_fiber();
jsa1969 12:96eb06e837f4 200 }
jsa1969 12:96eb06e837f4 201
jsa1969 34:069c4f6d5b2c 202 void startMeasureLoops() {
jsa1969 12:96eb06e837f4 203 if (runningLoops>0) {
jsa1969 12:96eb06e837f4 204 uBit.display.scroll("already running");
jsa1969 12:96eb06e837f4 205 return;
jsa1969 12:96eb06e837f4 206 }
jsa1969 14:71060505061e 207 create_fiber(sgpMeasureLoop);
jsa1969 12:96eb06e837f4 208 create_fiber(bmeMeasureLoop);
jsa1969 2:544117df8c65 209 }
jsa1969 2:544117df8c65 210
jsa1969 49:bbb506b58e6e 211
jsa1969 11:6844a5578b4f 212 void init680(){
jsa1969 9:5150afa50eb6 213 if (bme680!=NULL){
jsa1969 9:5150afa50eb6 214 delete bme680;
jsa1969 9:5150afa50eb6 215 }
jsa1969 10:8e6b71a46871 216
jsa1969 49:bbb506b58e6e 217 uint32_t gasMax = nvStore->getGasMax();
jsa1969 10:8e6b71a46871 218
jsa1969 14:71060505061e 219 bme680 = new Bme680(callbacks);
jsa1969 9:5150afa50eb6 220 int code = bme680->init();
jsa1969 9:5150afa50eb6 221 if (code == MICROBIT_OK){
jsa1969 9:5150afa50eb6 222 if (bme680Data==NULL) {
jsa1969 9:5150afa50eb6 223 bme680Data = new struct bme680_field_data;
jsa1969 9:5150afa50eb6 224 }
jsa1969 9:5150afa50eb6 225 code = bme680->measure(
jsa1969 9:5150afa50eb6 226 bme680Data,
jsa1969 9:5150afa50eb6 227 uBit.thermometer.getTemperature(),
jsa1969 9:5150afa50eb6 228 100, 100);
jsa1969 9:5150afa50eb6 229 }
jsa1969 9:5150afa50eb6 230 if (code != MICROBIT_OK){
jsa1969 9:5150afa50eb6 231 delete bme680;
jsa1969 9:5150afa50eb6 232 bme680 = NULL;
jsa1969 9:5150afa50eb6 233 delete bme680Data;
jsa1969 9:5150afa50eb6 234 bme680Data = NULL;
jsa1969 9:5150afa50eb6 235 uBit.display.scroll(code);
jsa1969 49:bbb506b58e6e 236 ManagedString send("no BME680 " + code);
jsa1969 49:bbb506b58e6e 237 radio -> sendToMakeCodeDevices(send);
jsa1969 9:5150afa50eb6 238 } else {
jsa1969 49:bbb506b58e6e 239 uBit.display.image.setPixelValue(0, 4, displayBrightness);
jsa1969 49:bbb506b58e6e 240 radio -> sendToMakeCodeDevices("BME680 up");
jsa1969 9:5150afa50eb6 241 }
jsa1969 11:6844a5578b4f 242 }
jsa1969 9:5150afa50eb6 243
jsa1969 11:6844a5578b4f 244 void initSgp30(){
jsa1969 9:5150afa50eb6 245 if (sgp30!=NULL){
jsa1969 9:5150afa50eb6 246 delete sgp30;
jsa1969 9:5150afa50eb6 247 }
jsa1969 49:bbb506b58e6e 248
jsa1969 43:f968ca84d4ed 249 sgp30 = new Sgp30(callbacks);
jsa1969 9:5150afa50eb6 250 if (sgp30->test() && sgp30->begin()) {
jsa1969 49:bbb506b58e6e 251 uBit.display.image.setPixelValue(1, 4, displayBrightness);
jsa1969 49:bbb506b58e6e 252 radio -> sendToMakeCodeDevices("sgp30 up");
jsa1969 49:bbb506b58e6e 253
jsa1969 49:bbb506b58e6e 254 uint16_t eco2_base;
jsa1969 49:bbb506b58e6e 255 uint16_t tvoc_base;
jsa1969 49:bbb506b58e6e 256 if (nvStore->getIAQBaseline(&eco2_base, &tvoc_base)) {
jsa1969 49:bbb506b58e6e 257 sgp30->setIAQBaseline(eco2_base, tvoc_base);
jsa1969 49:bbb506b58e6e 258 uBit.display.image.setPixelValue(2, 4, displayBrightness);
jsa1969 49:bbb506b58e6e 259
jsa1969 49:bbb506b58e6e 260 uBit.sleep(RADIO_DELAY);
jsa1969 51:133f2fb8a7c5 261 radio -> sendToMakeCodeDevices("sgp30 basline");
jsa1969 50:63442fd5e709 262 radio -> sendToMakeCodeDevices("eco:");
jsa1969 49:bbb506b58e6e 263 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 264 radio -> sendToMakeCodeDevices(eco2_base);
jsa1969 49:bbb506b58e6e 265 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 266 radio -> sendToMakeCodeDevices(" co2:");
jsa1969 49:bbb506b58e6e 267 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 268 radio -> sendToMakeCodeDevices(tvoc_base);
jsa1969 49:bbb506b58e6e 269 } else {
jsa1969 49:bbb506b58e6e 270 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 271 radio -> sendToMakeCodeDevices("sgp30 no baseline");
jsa1969 49:bbb506b58e6e 272 }
jsa1969 9:5150afa50eb6 273 } else {
jsa1969 49:bbb506b58e6e 274 radio -> sendToMakeCodeDevices("no sgp30");
jsa1969 9:5150afa50eb6 275 delete sgp30;
jsa1969 9:5150afa50eb6 276 sgp30 = NULL;
jsa1969 40:a67a880cb538 277 }
jsa1969 40:a67a880cb538 278 }
jsa1969 11:6844a5578b4f 279
jsa1969 49:bbb506b58e6e 280 void sgp30SetBaseline(ManagedString message){
jsa1969 49:bbb506b58e6e 281 // sgp30->setIAQBaseline(eco2_base, tvoc_base);
jsa1969 49:bbb506b58e6e 282 if (! (message.substring(0, 9) == CMD_SET_BASELINE)) {
jsa1969 50:63442fd5e709 283 radio -> sendToMakeCodeDevices("sgp30 not setting baseline");
jsa1969 49:bbb506b58e6e 284 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 285 radio -> sendToMakeCodeDevices(message);
jsa1969 49:bbb506b58e6e 286 return;
jsa1969 49:bbb506b58e6e 287 }
jsa1969 49:bbb506b58e6e 288
jsa1969 49:bbb506b58e6e 289 uint16_t eco2_base = 0;
jsa1969 49:bbb506b58e6e 290 uint16_t tvoc_base = 0;
jsa1969 49:bbb506b58e6e 291
jsa1969 49:bbb506b58e6e 292 int messageLength = message.length();
jsa1969 49:bbb506b58e6e 293
jsa1969 49:bbb506b58e6e 294 int i = 13;
jsa1969 49:bbb506b58e6e 295 char c = ' ';
jsa1969 49:bbb506b58e6e 296
jsa1969 49:bbb506b58e6e 297 do{
jsa1969 49:bbb506b58e6e 298 c = message.charAt(i);
jsa1969 49:bbb506b58e6e 299
jsa1969 49:bbb506b58e6e 300 if (c<'0' || c>'9') {
jsa1969 49:bbb506b58e6e 301 eco2_base*=10;
jsa1969 49:bbb506b58e6e 302 eco2_base+= c-'0';
jsa1969 49:bbb506b58e6e 303 }
jsa1969 49:bbb506b58e6e 304 } while (++i < messageLength && c != ' ');
jsa1969 49:bbb506b58e6e 305
jsa1969 49:bbb506b58e6e 306 if (eco2_base!=0 && c==' ') {
jsa1969 49:bbb506b58e6e 307 do{
jsa1969 49:bbb506b58e6e 308 c = message.charAt(i);
jsa1969 49:bbb506b58e6e 309
jsa1969 49:bbb506b58e6e 310 if (c<'0' || c>'9') {
jsa1969 49:bbb506b58e6e 311 tvoc_base*=10;
jsa1969 49:bbb506b58e6e 312 tvoc_base+= c-'0';
jsa1969 49:bbb506b58e6e 313 }
jsa1969 49:bbb506b58e6e 314 } while (++i < messageLength);
jsa1969 49:bbb506b58e6e 315 }
jsa1969 49:bbb506b58e6e 316
jsa1969 49:bbb506b58e6e 317 if (tvoc_base != 0){
jsa1969 50:63442fd5e709 318 sgp30->setIAQBaseline(eco2_base, tvoc_base);
jsa1969 50:63442fd5e709 319 nvStore->storeIAQBaseline(eco2_base, tvoc_base);
jsa1969 49:bbb506b58e6e 320
jsa1969 50:63442fd5e709 321 radio -> sendToMakeCodeDevices("sgp30 baseline set and stored");
jsa1969 49:bbb506b58e6e 322 uBit.sleep(RADIO_DELAY);
jsa1969 50:63442fd5e709 323 radio -> sendToMakeCodeDevices("eco:");
jsa1969 49:bbb506b58e6e 324 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 325 radio -> sendToMakeCodeDevices(eco2_base);
jsa1969 49:bbb506b58e6e 326 uBit.sleep(RADIO_DELAY);
jsa1969 50:63442fd5e709 327 radio -> sendToMakeCodeDevices("voc:");
jsa1969 51:133f2fb8a7c5 328 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 329 radio -> sendToMakeCodeDevices(tvoc_base);
jsa1969 49:bbb506b58e6e 330 } else {
jsa1969 50:63442fd5e709 331 radio -> sendToMakeCodeDevices("sgp30 not setting baseline");
jsa1969 49:bbb506b58e6e 332 uBit.sleep(RADIO_DELAY);
jsa1969 50:63442fd5e709 333 radio -> sendToMakeCodeDevices("eco:");
jsa1969 49:bbb506b58e6e 334 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 335 radio -> sendToMakeCodeDevices(eco2_base);
jsa1969 51:133f2fb8a7c5 336 uBit.sleep(RADIO_DELAY);
jsa1969 50:63442fd5e709 337 radio -> sendToMakeCodeDevices("voc:");
jsa1969 49:bbb506b58e6e 338 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 339 radio -> sendToMakeCodeDevices(tvoc_base);
jsa1969 49:bbb506b58e6e 340 }
jsa1969 49:bbb506b58e6e 341
jsa1969 49:bbb506b58e6e 342 }
jsa1969 49:bbb506b58e6e 343
jsa1969 11:6844a5578b4f 344 void initSensors() {
jsa1969 12:96eb06e837f4 345 waitForLooppsToFinish();
jsa1969 11:6844a5578b4f 346 init680();
jsa1969 11:6844a5578b4f 347 initSgp30();
jsa1969 9:5150afa50eb6 348 }
jsa1969 9:5150afa50eb6 349
jsa1969 49:bbb506b58e6e 350 void publishResults() {
jsa1969 12:96eb06e837f4 351 waitForLooppsToFinish();
jsa1969 49:bbb506b58e6e 352 if (sgp30 != NULL) {
jsa1969 52:112eaa5282c6 353 ManagedString send = ManagedString("Sgp30Tvoc ")
jsa1969 52:112eaa5282c6 354 + ManagedString((int)sgp30->TVOC)
jsa1969 52:112eaa5282c6 355 + " / "
jsa1969 52:112eaa5282c6 356 + ManagedString((int)nvStore->getVocMax());
jsa1969 49:bbb506b58e6e 357 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 358 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 359
jsa1969 52:112eaa5282c6 360 //send = ManagedString("Sgp30MaxVoc ") + ManagedString((int)nvStore->getVocMax());
jsa1969 52:112eaa5282c6 361 //radio -> sendToMakeCodeDevices(send);
jsa1969 52:112eaa5282c6 362 //uBit.sleep(RADIO_DELAY);
jsa1969 52:112eaa5282c6 363
jsa1969 52:112eaa5282c6 364 send = ManagedString("Sgp30o2 ")
jsa1969 52:112eaa5282c6 365 + ManagedString((int)sgp30->eCO2)
jsa1969 52:112eaa5282c6 366 + " / "
jsa1969 52:112eaa5282c6 367 + ManagedString((int)nvStore->getCoMax());
jsa1969 49:bbb506b58e6e 368 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 369 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 370
jsa1969 52:112eaa5282c6 371 //send = ManagedString("Sgp30MaxCo2 ") + ManagedString((int)nvStore->getCoMax());
jsa1969 52:112eaa5282c6 372 //radio -> sendToMakeCodeDevices(send);
jsa1969 52:112eaa5282c6 373 //uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 374
jsa1969 52:112eaa5282c6 375 send = ManagedString("Sgp30Raw ")
jsa1969 52:112eaa5282c6 376 + ManagedString((int)sgp30->rawH2)
jsa1969 52:112eaa5282c6 377 + " / "
jsa1969 52:112eaa5282c6 378 + ManagedString((int)sgp30->rawEthanol);
jsa1969 49:bbb506b58e6e 379 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 380 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 381 }else {
jsa1969 49:bbb506b58e6e 382 radio -> sendToMakeCodeDevices("IAQ no sgp30");
jsa1969 49:bbb506b58e6e 383 uBit.sleep(RADIO_DELAY);
jsa1969 8:1bdb50e03d39 384 }
jsa1969 17:a2c4dd192146 385
jsa1969 49:bbb506b58e6e 386 if (bme680Data!=NULL) {
jsa1969 52:112eaa5282c6 387 ManagedString send = ManagedString("Bme680Gas ") + ManagedString((int)bme680Data->gas_resistance);
jsa1969 49:bbb506b58e6e 388 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 389 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 390
jsa1969 52:112eaa5282c6 391 send = ManagedString("Bme680MaxGas ") + ManagedString((int)nvStore->getGasMax());
jsa1969 49:bbb506b58e6e 392 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 393 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 394
jsa1969 52:112eaa5282c6 395 send = ManagedString("Bme680MinGas ") + ManagedString((int)nvStore->getGasMin());
jsa1969 49:bbb506b58e6e 396 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 397 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 398
jsa1969 52:112eaa5282c6 399 send = ManagedString("Bme680T ") + ManagedString((int)bme680Data->temperature);
jsa1969 49:bbb506b58e6e 400 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 401 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 402
jsa1969 52:112eaa5282c6 403 send = ManagedString("Bme680MaxT ") + ManagedString((int)nvStore->getTempMax());
jsa1969 49:bbb506b58e6e 404 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 405 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 406
jsa1969 52:112eaa5282c6 407 send = ManagedString("Bme680MinT ") + ManagedString((int)nvStore->getTempMin());
jsa1969 52:112eaa5282c6 408 radio -> sendToMakeCodeDevices(send);
jsa1969 52:112eaa5282c6 409 uBit.sleep(RADIO_DELAY);
jsa1969 52:112eaa5282c6 410
jsa1969 52:112eaa5282c6 411 send = ManagedString("Bme680Press ") + ManagedString((int)bme680Data->pressure);
jsa1969 49:bbb506b58e6e 412 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 413 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 414
jsa1969 52:112eaa5282c6 415 send = ManagedString("Bme680MaxP ") + ManagedString((int)nvStore->getPressMax());
jsa1969 49:bbb506b58e6e 416 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 417 uBit.sleep(RADIO_DELAY);
jsa1969 52:112eaa5282c6 418 send = ManagedString("Bme680MinP ") + ManagedString((int)nvStore->getPressMin());
jsa1969 49:bbb506b58e6e 419 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 420 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 421
jsa1969 52:112eaa5282c6 422 send = ManagedString("Bme680Hum ") + ManagedString((int)bme680Data->humidity);
jsa1969 49:bbb506b58e6e 423 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 424 uBit.sleep(RADIO_DELAY);
jsa1969 52:112eaa5282c6 425
jsa1969 52:112eaa5282c6 426 send = ManagedString("Bme680MaxH ") + ManagedString((int)nvStore->getHumMax());
jsa1969 52:112eaa5282c6 427 radio -> sendToMakeCodeDevices(send);
jsa1969 52:112eaa5282c6 428 uBit.sleep(RADIO_DELAY);
jsa1969 52:112eaa5282c6 429 send = ManagedString("Bme680MinH ") + ManagedString((int)nvStore->getHumMin());
jsa1969 49:bbb506b58e6e 430 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 431 } else {
jsa1969 49:bbb506b58e6e 432 radio -> sendToMakeCodeDevices("IAQ no BME680");
jsa1969 8:1bdb50e03d39 433 }
jsa1969 49:bbb506b58e6e 434 startMeasureLoops();
jsa1969 49:bbb506b58e6e 435 release_fiber();
jsa1969 0:cef60cc92da0 436 }
jsa1969 0:cef60cc92da0 437
jsa1969 49:bbb506b58e6e 438 /*
jsa1969 12:96eb06e837f4 439 void onButtonA(MicroBitEvent evt)
jsa1969 12:96eb06e837f4 440 {
jsa1969 12:96eb06e837f4 441 if (runningLoops>0) {
jsa1969 12:96eb06e837f4 442 displayValuesTxt();
jsa1969 12:96eb06e837f4 443 } else {
jsa1969 34:069c4f6d5b2c 444 startMeasureLoops();
jsa1969 12:96eb06e837f4 445 }
jsa1969 12:96eb06e837f4 446 }
jsa1969 49:bbb506b58e6e 447 */
jsa1969 49:bbb506b58e6e 448 void switchLightLevel(MicroBitEvent evt)
jsa1969 12:96eb06e837f4 449 {
jsa1969 49:bbb506b58e6e 450 switch (displayBrightness) {
jsa1969 49:bbb506b58e6e 451 case 255:
jsa1969 50:63442fd5e709 452 displayBrightness = 1;
jsa1969 20:2f11b93f4cbf 453 break;
jsa1969 50:63442fd5e709 454 case 1:
jsa1969 49:bbb506b58e6e 455 displayBrightness =0;
jsa1969 20:2f11b93f4cbf 456 break;
jsa1969 20:2f11b93f4cbf 457 default:
jsa1969 49:bbb506b58e6e 458 displayBrightness = 255;
jsa1969 20:2f11b93f4cbf 459 }
jsa1969 12:96eb06e837f4 460 }
jsa1969 12:96eb06e837f4 461
jsa1969 49:bbb506b58e6e 462
jsa1969 49:bbb506b58e6e 463 void clearIQQBaseline(MicroBitEvent evt)
jsa1969 9:5150afa50eb6 464 {
jsa1969 49:bbb506b58e6e 465 nvStore->clearIQQBaseline();
jsa1969 49:bbb506b58e6e 466 uBit.display.scroll("base line clear");
jsa1969 49:bbb506b58e6e 467 uBit.reset();
jsa1969 9:5150afa50eb6 468 }
jsa1969 9:5150afa50eb6 469
jsa1969 49:bbb506b58e6e 470
jsa1969 34:069c4f6d5b2c 471 const char* runSofwareModuleTests() {
jsa1969 48:52cad865a84f 472 // heap we've got plenty, stack is rare
jsa1969 33:af2dfab24ca9 473 AppSpecificTestrunner* runner = new AppSpecificTestrunner();
jsa1969 33:af2dfab24ca9 474 const char* result = runner->runAll();
jsa1969 33:af2dfab24ca9 475 delete runner;
jsa1969 33:af2dfab24ca9 476 return result;
jsa1969 33:af2dfab24ca9 477 }
jsa1969 33:af2dfab24ca9 478
jsa1969 49:bbb506b58e6e 479 void onData(MicroBitEvent e) {
jsa1969 49:bbb506b58e6e 480 ManagedString rcv= radio->received();
jsa1969 49:bbb506b58e6e 481 if (rcv == CMD_MEASURE_AND_PUBLISH) {
jsa1969 49:bbb506b58e6e 482 create_fiber(publishResults);
jsa1969 49:bbb506b58e6e 483 } else if (rcv == CMD_RESET_TEMP_HUM_CO2) {
jsa1969 49:bbb506b58e6e 484 nvStore->resetTmpHumCo2();
jsa1969 49:bbb506b58e6e 485 create_fiber(publishResults);
jsa1969 49:bbb506b58e6e 486 } else if (rcv == CMD_STORE_CURRENT_BASELINE) {
jsa1969 52:112eaa5282c6 487 if (!checkAndStoreSgp30Baseline()) {
jsa1969 52:112eaa5282c6 488 radio -> sendToMakeCodeDevices("sgp30 baseln not upd");
jsa1969 49:bbb506b58e6e 489 }
jsa1969 49:bbb506b58e6e 490 } else if (rcv == CMD_SET_BASELINE) {
jsa1969 49:bbb506b58e6e 491 sgp30SetBaseline(rcv);
jsa1969 49:bbb506b58e6e 492 }
jsa1969 49:bbb506b58e6e 493 }
jsa1969 49:bbb506b58e6e 494
jsa1969 0:cef60cc92da0 495 int main()
jsa1969 0:cef60cc92da0 496 {
jsa1969 0:cef60cc92da0 497 uBit.init();
jsa1969 49:bbb506b58e6e 498 uBit.display.scroll("i");
jsa1969 0:cef60cc92da0 499
jsa1969 49:bbb506b58e6e 500 //uBit.messageBus.listen(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, onButtonA);
jsa1969 49:bbb506b58e6e 501 uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, switchLightLevel);
jsa1969 49:bbb506b58e6e 502 uBit.messageBus.listen(MICROBIT_ID_BUTTON_AB, MICROBIT_BUTTON_EVT_CLICK, clearIQQBaseline);
jsa1969 49:bbb506b58e6e 503 uBit.messageBus.listen(MICROBIT_ID_RADIO, MICROBIT_RADIO_EVT_DATAGRAM, onData);
jsa1969 2:544117df8c65 504
jsa1969 46:2fed2865a0f3 505 callbacks = new I2cCallbacks(&uBit);
jsa1969 46:2fed2865a0f3 506 nvStore = new IaqNonVolatileStore(&uBit);
jsa1969 49:bbb506b58e6e 507
jsa1969 49:bbb506b58e6e 508 radio = new JavaScriptRadio(&uBit, RADIO_GROUP);
jsa1969 33:af2dfab24ca9 509
jsa1969 46:2fed2865a0f3 510 uBit.display.scroll("t");
jsa1969 34:069c4f6d5b2c 511 const char* testResults = runSofwareModuleTests();
jsa1969 33:af2dfab24ca9 512 if (! Testrunner::messageOK(testResults)) {
jsa1969 49:bbb506b58e6e 513 //uBit.display.scroll(testResults);
jsa1969 49:bbb506b58e6e 514 radio -> sendToMakeCodeDevices("IAQ tests failed " +ManagedString(testResults));
jsa1969 33:af2dfab24ca9 515 return -1;
jsa1969 33:af2dfab24ca9 516 }
jsa1969 46:2fed2865a0f3 517
jsa1969 34:069c4f6d5b2c 518 // includes hardware tests
jsa1969 9:5150afa50eb6 519 initSensors();
jsa1969 9:5150afa50eb6 520
jsa1969 34:069c4f6d5b2c 521 startMeasureLoops();
jsa1969 2:544117df8c65 522
jsa1969 0:cef60cc92da0 523 release_fiber();
jsa1969 0:cef60cc92da0 524 }
jsa1969 0:cef60cc92da0 525