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 Jun 03 17:05:56 2022 +0000
Revision:
60:6b21ca38ee7c
Parent:
59:1ca41f08b6a4
cleanup

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 60:6b21ca38ee7c 41 #define RADIO_DELAY 800
jsa1969 49:bbb506b58e6e 42
jsa1969 56:6a7142c84db5 43 #define CMD_SET_BASELINE "bl "
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 53:d560c097f1b4 131 uint16_t eco2_base = 0;
jsa1969 53:d560c097f1b4 132 uint16_t tvoc_base = 0;
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 59:1ca41f08b6a4 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 53:d560c097f1b4 140 if (nvStore->getIAQBaseline(&eco2_base, &tvoc_base)) {
jsa1969 53:d560c097f1b4 141 radio -> sendToMakeCodeDevices(ManagedString(eco2_base)
jsa1969 53:d560c097f1b4 142 + " / "
jsa1969 53:d560c097f1b4 143 + ManagedString(tvoc_base));
jsa1969 59:1ca41f08b6a4 144 }*/
jsa1969 49:bbb506b58e6e 145 }
jsa1969 52:112eaa5282c6 146
jsa1969 49:bbb506b58e6e 147 return baseLineOK;
jsa1969 49:bbb506b58e6e 148 }
jsa1969 49:bbb506b58e6e 149
jsa1969 11:6844a5578b4f 150 void measureAndDisplaySgp30() {
jsa1969 11:6844a5578b4f 151 if (sgp30!=NULL) {
jsa1969 11:6844a5578b4f 152 int sgpMaxPixels = 10;
jsa1969 11:6844a5578b4f 153 int secondLineStart = 2;
jsa1969 11:6844a5578b4f 154 if (bme680!=NULL) {
jsa1969 11:6844a5578b4f 155 sgpMaxPixels = 5;
jsa1969 11:6844a5578b4f 156 secondLineStart = 1;
jsa1969 49:bbb506b58e6e 157 uBit.display.image.setPixelValue(3, 4, displayBrightness);
jsa1969 11:6844a5578b4f 158 if (sgp30->setHumidity(bme680Data->humidity, bme680Data->temperature)) {
jsa1969 44:67a19da5f269 159 uBit.display.image.setPixelValue(3, 4, 0);
jsa1969 11:6844a5578b4f 160 }
jsa1969 11:6844a5578b4f 161 uBit.sleep(10);
jsa1969 11:6844a5578b4f 162 }
jsa1969 49:bbb506b58e6e 163 uBit.display.image.setPixelValue(3, 4, displayBrightness);
jsa1969 38:0f29a0ea64ca 164 bool measureOK = sgp30->IAQmeasure();
jsa1969 38:0f29a0ea64ca 165 if (measureOK) {
jsa1969 52:112eaa5282c6 166 sgp30->IAQmeasureRaw();
jsa1969 49:bbb506b58e6e 167 uBit.display.image.setPixelValue(1, 4, displayBrightness);
jsa1969 11:6844a5578b4f 168 uBit.display.image.setPixelValue(3, 4, 0);
jsa1969 18:c4ac93e01027 169
jsa1969 59:1ca41f08b6a4 170 if (sgp30->TVOC < 60000) nvStore->updateVoc(sgp30->TVOC);
jsa1969 59:1ca41f08b6a4 171 if (sgp30->eCO2 < 60000) nvStore->updateCo(sgp30->eCO2);
jsa1969 18:c4ac93e01027 172
jsa1969 12:96eb06e837f4 173 int co2Dots = min (5, sgp30->eCO2 /1500);
jsa1969 47:881bfe77a00a 174 displayPixels(0, 5 - RangeTransform::exponentialTransform(sgp30->TVOC, 20000, sgpMaxPixels), sgpMaxPixels);
jsa1969 11:6844a5578b4f 175 displayPixels(secondLineStart, co2Dots, sgpMaxPixels);
jsa1969 59:1ca41f08b6a4 176 checkAndStoreSgp30Baseline();
jsa1969 13:996026828be7 177 } else {
jsa1969 13:996026828be7 178 uBit.display.image.setPixelValue(1, 4, 0);
jsa1969 11:6844a5578b4f 179 }
jsa1969 2:544117df8c65 180 }
jsa1969 2:544117df8c65 181 }
jsa1969 2:544117df8c65 182
jsa1969 49:bbb506b58e6e 183
jsa1969 12:96eb06e837f4 184 void bmeMeasureLoop() {
jsa1969 12:96eb06e837f4 185 if (bme680!=NULL) {
jsa1969 12:96eb06e837f4 186 ++runningLoops;
jsa1969 12:96eb06e837f4 187 while (!cancelMeasureLoops){
jsa1969 11:6844a5578b4f 188 measureAndDisplayBme680();
jsa1969 45:d6a9fd9c8200 189 uBit.sleep(500);
jsa1969 12:96eb06e837f4 190 }
jsa1969 12:96eb06e837f4 191 --runningLoops;
jsa1969 12:96eb06e837f4 192 }
jsa1969 24:07a1d2e6914e 193 release_fiber();
jsa1969 12:96eb06e837f4 194 }
jsa1969 12:96eb06e837f4 195
jsa1969 12:96eb06e837f4 196 void sgpMeasureLoop() {
jsa1969 12:96eb06e837f4 197 if (sgp30!=NULL) {
jsa1969 12:96eb06e837f4 198 ++runningLoops;
jsa1969 12:96eb06e837f4 199 while (!cancelMeasureLoops){
jsa1969 11:6844a5578b4f 200 measureAndDisplaySgp30();
jsa1969 12:96eb06e837f4 201 uBit.sleep(950);
jsa1969 2:544117df8c65 202 }
jsa1969 12:96eb06e837f4 203 --runningLoops;
jsa1969 2:544117df8c65 204 }
jsa1969 24:07a1d2e6914e 205 release_fiber();
jsa1969 12:96eb06e837f4 206 }
jsa1969 12:96eb06e837f4 207
jsa1969 34:069c4f6d5b2c 208 void startMeasureLoops() {
jsa1969 12:96eb06e837f4 209 if (runningLoops>0) {
jsa1969 12:96eb06e837f4 210 uBit.display.scroll("already running");
jsa1969 12:96eb06e837f4 211 return;
jsa1969 12:96eb06e837f4 212 }
jsa1969 14:71060505061e 213 create_fiber(sgpMeasureLoop);
jsa1969 12:96eb06e837f4 214 create_fiber(bmeMeasureLoop);
jsa1969 2:544117df8c65 215 }
jsa1969 2:544117df8c65 216
jsa1969 49:bbb506b58e6e 217
jsa1969 11:6844a5578b4f 218 void init680(){
jsa1969 9:5150afa50eb6 219 if (bme680!=NULL){
jsa1969 9:5150afa50eb6 220 delete bme680;
jsa1969 9:5150afa50eb6 221 }
jsa1969 10:8e6b71a46871 222
jsa1969 49:bbb506b58e6e 223 uint32_t gasMax = nvStore->getGasMax();
jsa1969 10:8e6b71a46871 224
jsa1969 14:71060505061e 225 bme680 = new Bme680(callbacks);
jsa1969 9:5150afa50eb6 226 int code = bme680->init();
jsa1969 9:5150afa50eb6 227 if (code == MICROBIT_OK){
jsa1969 9:5150afa50eb6 228 if (bme680Data==NULL) {
jsa1969 9:5150afa50eb6 229 bme680Data = new struct bme680_field_data;
jsa1969 9:5150afa50eb6 230 }
jsa1969 9:5150afa50eb6 231 code = bme680->measure(
jsa1969 9:5150afa50eb6 232 bme680Data,
jsa1969 9:5150afa50eb6 233 uBit.thermometer.getTemperature(),
jsa1969 9:5150afa50eb6 234 100, 100);
jsa1969 9:5150afa50eb6 235 }
jsa1969 9:5150afa50eb6 236 if (code != MICROBIT_OK){
jsa1969 9:5150afa50eb6 237 delete bme680;
jsa1969 9:5150afa50eb6 238 bme680 = NULL;
jsa1969 9:5150afa50eb6 239 delete bme680Data;
jsa1969 9:5150afa50eb6 240 bme680Data = NULL;
jsa1969 9:5150afa50eb6 241 uBit.display.scroll(code);
jsa1969 49:bbb506b58e6e 242 ManagedString send("no BME680 " + code);
jsa1969 49:bbb506b58e6e 243 radio -> sendToMakeCodeDevices(send);
jsa1969 9:5150afa50eb6 244 } else {
jsa1969 49:bbb506b58e6e 245 uBit.display.image.setPixelValue(0, 4, displayBrightness);
jsa1969 59:1ca41f08b6a4 246 //radio -> sendToMakeCodeDevices("BME680 up");
jsa1969 9:5150afa50eb6 247 }
jsa1969 11:6844a5578b4f 248 }
jsa1969 9:5150afa50eb6 249
jsa1969 11:6844a5578b4f 250 void initSgp30(){
jsa1969 9:5150afa50eb6 251 if (sgp30!=NULL){
jsa1969 9:5150afa50eb6 252 delete sgp30;
jsa1969 9:5150afa50eb6 253 }
jsa1969 49:bbb506b58e6e 254
jsa1969 43:f968ca84d4ed 255 sgp30 = new Sgp30(callbacks);
jsa1969 9:5150afa50eb6 256 if (sgp30->test() && sgp30->begin()) {
jsa1969 49:bbb506b58e6e 257 uBit.display.image.setPixelValue(1, 4, displayBrightness);
jsa1969 59:1ca41f08b6a4 258 //radio -> sendToMakeCodeDevices("sgp30 up");
jsa1969 49:bbb506b58e6e 259
jsa1969 49:bbb506b58e6e 260 uint16_t eco2_base;
jsa1969 49:bbb506b58e6e 261 uint16_t tvoc_base;
jsa1969 49:bbb506b58e6e 262 if (nvStore->getIAQBaseline(&eco2_base, &tvoc_base)) {
jsa1969 49:bbb506b58e6e 263 sgp30->setIAQBaseline(eco2_base, tvoc_base);
jsa1969 49:bbb506b58e6e 264 uBit.display.image.setPixelValue(2, 4, displayBrightness);
jsa1969 59:1ca41f08b6a4 265 /*
jsa1969 49:bbb506b58e6e 266 uBit.sleep(RADIO_DELAY);
jsa1969 51:133f2fb8a7c5 267 radio -> sendToMakeCodeDevices("sgp30 basline");
jsa1969 49:bbb506b58e6e 268 uBit.sleep(RADIO_DELAY);
jsa1969 56:6a7142c84db5 269 radio -> sendToMakeCodeDevices("eco: "
jsa1969 56:6a7142c84db5 270 + ManagedString(eco2_base));
jsa1969 49:bbb506b58e6e 271 uBit.sleep(RADIO_DELAY);
jsa1969 56:6a7142c84db5 272 radio -> sendToMakeCodeDevices("co2: "
jsa1969 56:6a7142c84db5 273 + ManagedString(tvoc_base)
jsa1969 56:6a7142c84db5 274 );
jsa1969 59:1ca41f08b6a4 275 */
jsa1969 49:bbb506b58e6e 276 } else {
jsa1969 49:bbb506b58e6e 277 uBit.sleep(RADIO_DELAY);
jsa1969 59:1ca41f08b6a4 278 //radio -> sendToMakeCodeDevices("sgp30 no baseline");
jsa1969 49:bbb506b58e6e 279 }
jsa1969 9:5150afa50eb6 280 } else {
jsa1969 49:bbb506b58e6e 281 radio -> sendToMakeCodeDevices("no sgp30");
jsa1969 9:5150afa50eb6 282 delete sgp30;
jsa1969 9:5150afa50eb6 283 sgp30 = NULL;
jsa1969 40:a67a880cb538 284 }
jsa1969 40:a67a880cb538 285 }
jsa1969 11:6844a5578b4f 286
jsa1969 49:bbb506b58e6e 287 void sgp30SetBaseline(ManagedString message){
jsa1969 49:bbb506b58e6e 288 // sgp30->setIAQBaseline(eco2_base, tvoc_base);
jsa1969 56:6a7142c84db5 289 if (! (message.substring(0, 3) == CMD_SET_BASELINE)) {
jsa1969 50:63442fd5e709 290 radio -> sendToMakeCodeDevices("sgp30 not setting baseline");
jsa1969 49:bbb506b58e6e 291 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 292 radio -> sendToMakeCodeDevices(message);
jsa1969 49:bbb506b58e6e 293 return;
jsa1969 49:bbb506b58e6e 294 }
jsa1969 49:bbb506b58e6e 295
jsa1969 49:bbb506b58e6e 296 uint16_t eco2_base = 0;
jsa1969 49:bbb506b58e6e 297 uint16_t tvoc_base = 0;
jsa1969 49:bbb506b58e6e 298
jsa1969 49:bbb506b58e6e 299 int messageLength = message.length();
jsa1969 49:bbb506b58e6e 300
jsa1969 56:6a7142c84db5 301 int i = 3;
jsa1969 49:bbb506b58e6e 302 char c = ' ';
jsa1969 49:bbb506b58e6e 303
jsa1969 49:bbb506b58e6e 304 do{
jsa1969 49:bbb506b58e6e 305 c = message.charAt(i);
jsa1969 56:6a7142c84db5 306 if (c>='0' && c<='9') {
jsa1969 49:bbb506b58e6e 307 eco2_base*=10;
jsa1969 49:bbb506b58e6e 308 eco2_base+= c-'0';
jsa1969 49:bbb506b58e6e 309 }
jsa1969 49:bbb506b58e6e 310 } while (++i < messageLength && c != ' ');
jsa1969 49:bbb506b58e6e 311
jsa1969 49:bbb506b58e6e 312 if (eco2_base!=0 && c==' ') {
jsa1969 49:bbb506b58e6e 313 do{
jsa1969 49:bbb506b58e6e 314 c = message.charAt(i);
jsa1969 49:bbb506b58e6e 315
jsa1969 56:6a7142c84db5 316 if (c>='0' && c<='9') {
jsa1969 49:bbb506b58e6e 317 tvoc_base*=10;
jsa1969 49:bbb506b58e6e 318 tvoc_base+= c-'0';
jsa1969 49:bbb506b58e6e 319 }
jsa1969 49:bbb506b58e6e 320 } while (++i < messageLength);
jsa1969 49:bbb506b58e6e 321 }
jsa1969 49:bbb506b58e6e 322
jsa1969 49:bbb506b58e6e 323 if (tvoc_base != 0){
jsa1969 50:63442fd5e709 324 sgp30->setIAQBaseline(eco2_base, tvoc_base);
jsa1969 50:63442fd5e709 325 nvStore->storeIAQBaseline(eco2_base, tvoc_base);
jsa1969 56:6a7142c84db5 326
jsa1969 50:63442fd5e709 327 radio -> sendToMakeCodeDevices("sgp30 baseline set and stored");
jsa1969 49:bbb506b58e6e 328 uBit.sleep(RADIO_DELAY);
jsa1969 56:6a7142c84db5 329 radio -> sendToMakeCodeDevices(message);
jsa1969 49:bbb506b58e6e 330 uBit.sleep(RADIO_DELAY);
jsa1969 56:6a7142c84db5 331 radio -> sendToMakeCodeDevices("eco: " + ManagedString(eco2_base));
jsa1969 51:133f2fb8a7c5 332 uBit.sleep(RADIO_DELAY);
jsa1969 56:6a7142c84db5 333 radio -> sendToMakeCodeDevices("voc: " + ManagedString(tvoc_base));
jsa1969 49:bbb506b58e6e 334 } else {
jsa1969 50:63442fd5e709 335 radio -> sendToMakeCodeDevices("sgp30 not setting baseline");
jsa1969 49:bbb506b58e6e 336 uBit.sleep(RADIO_DELAY);
jsa1969 56:6a7142c84db5 337 radio -> sendToMakeCodeDevices(message);
jsa1969 56:6a7142c84db5 338 uBit.sleep(RADIO_DELAY);
jsa1969 50:63442fd5e709 339 radio -> sendToMakeCodeDevices("eco:");
jsa1969 49:bbb506b58e6e 340 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 341 radio -> sendToMakeCodeDevices(eco2_base);
jsa1969 51:133f2fb8a7c5 342 uBit.sleep(RADIO_DELAY);
jsa1969 50:63442fd5e709 343 radio -> sendToMakeCodeDevices("voc:");
jsa1969 49:bbb506b58e6e 344 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 345 radio -> sendToMakeCodeDevices(tvoc_base);
jsa1969 49:bbb506b58e6e 346 }
jsa1969 49:bbb506b58e6e 347
jsa1969 49:bbb506b58e6e 348 }
jsa1969 49:bbb506b58e6e 349
jsa1969 11:6844a5578b4f 350 void initSensors() {
jsa1969 12:96eb06e837f4 351 waitForLooppsToFinish();
jsa1969 11:6844a5578b4f 352 init680();
jsa1969 11:6844a5578b4f 353 initSgp30();
jsa1969 9:5150afa50eb6 354 }
jsa1969 9:5150afa50eb6 355
jsa1969 49:bbb506b58e6e 356 void publishResults() {
jsa1969 12:96eb06e837f4 357 waitForLooppsToFinish();
jsa1969 49:bbb506b58e6e 358 if (sgp30 != NULL) {
jsa1969 56:6a7142c84db5 359 ManagedString send = ManagedString("SgpTvoc ")
jsa1969 52:112eaa5282c6 360 + ManagedString((int)sgp30->TVOC)
jsa1969 56:6a7142c84db5 361 + " "
jsa1969 52:112eaa5282c6 362 + ManagedString((int)nvStore->getVocMax());
jsa1969 49:bbb506b58e6e 363 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 364 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 365
jsa1969 56:6a7142c84db5 366 //send = ManagedString("SgpMaxVoc ") + ManagedString((int)nvStore->getVocMax());
jsa1969 52:112eaa5282c6 367 //radio -> sendToMakeCodeDevices(send);
jsa1969 52:112eaa5282c6 368 //uBit.sleep(RADIO_DELAY);
jsa1969 52:112eaa5282c6 369
jsa1969 57:1687d6e37410 370 send = ManagedString("SgpO2 ")
jsa1969 52:112eaa5282c6 371 + ManagedString((int)sgp30->eCO2)
jsa1969 56:6a7142c84db5 372 + " "
jsa1969 52:112eaa5282c6 373 + ManagedString((int)nvStore->getCoMax());
jsa1969 49:bbb506b58e6e 374 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 375 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 376
jsa1969 56:6a7142c84db5 377 //send = ManagedString("SgpMaxCo2 ") + ManagedString((int)nvStore->getCoMax());
jsa1969 52:112eaa5282c6 378 //radio -> sendToMakeCodeDevices(send);
jsa1969 52:112eaa5282c6 379 //uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 380
jsa1969 56:6a7142c84db5 381 send = ManagedString("SgpRawH ")
jsa1969 56:6a7142c84db5 382 + ManagedString((int)sgp30->rawH2);
jsa1969 56:6a7142c84db5 383 radio -> sendToMakeCodeDevices(send);
jsa1969 56:6a7142c84db5 384 uBit.sleep(RADIO_DELAY);
jsa1969 56:6a7142c84db5 385 send = ManagedString("SgpRawE ")
jsa1969 52:112eaa5282c6 386 + ManagedString((int)sgp30->rawEthanol);
jsa1969 49:bbb506b58e6e 387 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 388 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 389 }else {
jsa1969 49:bbb506b58e6e 390 radio -> sendToMakeCodeDevices("IAQ no sgp30");
jsa1969 49:bbb506b58e6e 391 uBit.sleep(RADIO_DELAY);
jsa1969 8:1bdb50e03d39 392 }
jsa1969 17:a2c4dd192146 393
jsa1969 49:bbb506b58e6e 394 if (bme680Data!=NULL) {
jsa1969 59:1ca41f08b6a4 395 ManagedString send = ManagedString("Bme680G ") + ManagedString((int)bme680Data->gas_resistance);
jsa1969 49:bbb506b58e6e 396 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 397 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 398
jsa1969 59:1ca41f08b6a4 399 send = ManagedString("Bme680+G ") + ManagedString((int)nvStore->getGasMax());
jsa1969 49:bbb506b58e6e 400 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 401 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 402
jsa1969 59:1ca41f08b6a4 403 send = ManagedString("Bme680-G ") + ManagedString((int)nvStore->getGasMin());
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("Bme680T ") + ManagedString((int)bme680Data->temperature);
jsa1969 49:bbb506b58e6e 408 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 409 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 410
jsa1969 56:6a7142c84db5 411 send = ManagedString("Bme680+-T ")
jsa1969 56:6a7142c84db5 412 + ManagedString((int)nvStore->getTempMax())
jsa1969 56:6a7142c84db5 413 + ManagedString(" ")
jsa1969 56:6a7142c84db5 414 + ManagedString((int)nvStore->getTempMin());
jsa1969 49:bbb506b58e6e 415 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 416 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 417
jsa1969 56:6a7142c84db5 418 /*
jsa1969 53:d560c097f1b4 419 send = ManagedString("Bme680-T ") + ManagedString((int)nvStore->getTempMin());
jsa1969 52:112eaa5282c6 420 radio -> sendToMakeCodeDevices(send);
jsa1969 52:112eaa5282c6 421 uBit.sleep(RADIO_DELAY);
jsa1969 56:6a7142c84db5 422 */
jsa1969 52:112eaa5282c6 423 send = ManagedString("Bme680Press ") + ManagedString((int)bme680Data->pressure);
jsa1969 49:bbb506b58e6e 424 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 425 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 426
jsa1969 53:d560c097f1b4 427 send = ManagedString("Bme680+P ") + ManagedString((int)nvStore->getPressMax());
jsa1969 49:bbb506b58e6e 428 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 429 uBit.sleep(RADIO_DELAY);
jsa1969 53:d560c097f1b4 430 send = ManagedString("Bme680-P ") + ManagedString((int)nvStore->getPressMin());
jsa1969 49:bbb506b58e6e 431 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 432 uBit.sleep(RADIO_DELAY);
jsa1969 49:bbb506b58e6e 433
jsa1969 52:112eaa5282c6 434 send = ManagedString("Bme680Hum ") + ManagedString((int)bme680Data->humidity);
jsa1969 49:bbb506b58e6e 435 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 436 uBit.sleep(RADIO_DELAY);
jsa1969 52:112eaa5282c6 437
jsa1969 53:d560c097f1b4 438 send = ManagedString("Bme680+H ") + ManagedString((int)nvStore->getHumMax());
jsa1969 52:112eaa5282c6 439 radio -> sendToMakeCodeDevices(send);
jsa1969 52:112eaa5282c6 440 uBit.sleep(RADIO_DELAY);
jsa1969 53:d560c097f1b4 441 send = ManagedString("Bme680-H ") + ManagedString((int)nvStore->getHumMin());
jsa1969 49:bbb506b58e6e 442 radio -> sendToMakeCodeDevices(send);
jsa1969 49:bbb506b58e6e 443 } else {
jsa1969 49:bbb506b58e6e 444 radio -> sendToMakeCodeDevices("IAQ no BME680");
jsa1969 8:1bdb50e03d39 445 }
jsa1969 49:bbb506b58e6e 446 startMeasureLoops();
jsa1969 49:bbb506b58e6e 447 release_fiber();
jsa1969 0:cef60cc92da0 448 }
jsa1969 0:cef60cc92da0 449
jsa1969 49:bbb506b58e6e 450 /*
jsa1969 12:96eb06e837f4 451 void onButtonA(MicroBitEvent evt)
jsa1969 12:96eb06e837f4 452 {
jsa1969 12:96eb06e837f4 453 if (runningLoops>0) {
jsa1969 12:96eb06e837f4 454 displayValuesTxt();
jsa1969 12:96eb06e837f4 455 } else {
jsa1969 34:069c4f6d5b2c 456 startMeasureLoops();
jsa1969 12:96eb06e837f4 457 }
jsa1969 12:96eb06e837f4 458 }
jsa1969 49:bbb506b58e6e 459 */
jsa1969 49:bbb506b58e6e 460 void switchLightLevel(MicroBitEvent evt)
jsa1969 12:96eb06e837f4 461 {
jsa1969 49:bbb506b58e6e 462 switch (displayBrightness) {
jsa1969 49:bbb506b58e6e 463 case 255:
jsa1969 50:63442fd5e709 464 displayBrightness = 1;
jsa1969 20:2f11b93f4cbf 465 break;
jsa1969 50:63442fd5e709 466 case 1:
jsa1969 49:bbb506b58e6e 467 displayBrightness =0;
jsa1969 20:2f11b93f4cbf 468 break;
jsa1969 20:2f11b93f4cbf 469 default:
jsa1969 49:bbb506b58e6e 470 displayBrightness = 255;
jsa1969 20:2f11b93f4cbf 471 }
jsa1969 12:96eb06e837f4 472 }
jsa1969 12:96eb06e837f4 473
jsa1969 49:bbb506b58e6e 474
jsa1969 49:bbb506b58e6e 475 void clearIQQBaseline(MicroBitEvent evt)
jsa1969 9:5150afa50eb6 476 {
jsa1969 49:bbb506b58e6e 477 nvStore->clearIQQBaseline();
jsa1969 49:bbb506b58e6e 478 uBit.display.scroll("base line clear");
jsa1969 49:bbb506b58e6e 479 uBit.reset();
jsa1969 9:5150afa50eb6 480 }
jsa1969 9:5150afa50eb6 481
jsa1969 49:bbb506b58e6e 482
jsa1969 34:069c4f6d5b2c 483 const char* runSofwareModuleTests() {
jsa1969 48:52cad865a84f 484 // heap we've got plenty, stack is rare
jsa1969 54:7a73502d5308 485 AppSpecificTestrunner* runner = new AppSpecificTestrunner(&uBit);
jsa1969 33:af2dfab24ca9 486 const char* result = runner->runAll();
jsa1969 33:af2dfab24ca9 487 delete runner;
jsa1969 33:af2dfab24ca9 488 return result;
jsa1969 33:af2dfab24ca9 489 }
jsa1969 33:af2dfab24ca9 490
jsa1969 49:bbb506b58e6e 491 void onData(MicroBitEvent e) {
jsa1969 49:bbb506b58e6e 492 ManagedString rcv= radio->received();
jsa1969 49:bbb506b58e6e 493 if (rcv == CMD_MEASURE_AND_PUBLISH) {
jsa1969 49:bbb506b58e6e 494 create_fiber(publishResults);
jsa1969 49:bbb506b58e6e 495 } else if (rcv == CMD_RESET_TEMP_HUM_CO2) {
jsa1969 49:bbb506b58e6e 496 nvStore->resetTmpHumCo2();
jsa1969 49:bbb506b58e6e 497 create_fiber(publishResults);
jsa1969 49:bbb506b58e6e 498 } else if (rcv == CMD_STORE_CURRENT_BASELINE) {
jsa1969 52:112eaa5282c6 499 if (!checkAndStoreSgp30Baseline()) {
jsa1969 52:112eaa5282c6 500 radio -> sendToMakeCodeDevices("sgp30 baseln not upd");
jsa1969 49:bbb506b58e6e 501 }
jsa1969 56:6a7142c84db5 502 } else if (rcv.substring(0, 3) == CMD_SET_BASELINE) {
jsa1969 49:bbb506b58e6e 503 sgp30SetBaseline(rcv);
jsa1969 49:bbb506b58e6e 504 }
jsa1969 49:bbb506b58e6e 505 }
jsa1969 49:bbb506b58e6e 506
jsa1969 0:cef60cc92da0 507 int main()
jsa1969 0:cef60cc92da0 508 {
jsa1969 0:cef60cc92da0 509 uBit.init();
jsa1969 49:bbb506b58e6e 510 uBit.display.scroll("i");
jsa1969 0:cef60cc92da0 511
jsa1969 49:bbb506b58e6e 512 //uBit.messageBus.listen(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, onButtonA);
jsa1969 49:bbb506b58e6e 513 uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, switchLightLevel);
jsa1969 49:bbb506b58e6e 514 uBit.messageBus.listen(MICROBIT_ID_BUTTON_AB, MICROBIT_BUTTON_EVT_CLICK, clearIQQBaseline);
jsa1969 49:bbb506b58e6e 515 uBit.messageBus.listen(MICROBIT_ID_RADIO, MICROBIT_RADIO_EVT_DATAGRAM, onData);
jsa1969 2:544117df8c65 516
jsa1969 46:2fed2865a0f3 517 callbacks = new I2cCallbacks(&uBit);
jsa1969 46:2fed2865a0f3 518 nvStore = new IaqNonVolatileStore(&uBit);
jsa1969 49:bbb506b58e6e 519
jsa1969 49:bbb506b58e6e 520 radio = new JavaScriptRadio(&uBit, RADIO_GROUP);
jsa1969 33:af2dfab24ca9 521
jsa1969 46:2fed2865a0f3 522 uBit.display.scroll("t");
jsa1969 34:069c4f6d5b2c 523 const char* testResults = runSofwareModuleTests();
jsa1969 33:af2dfab24ca9 524 if (! Testrunner::messageOK(testResults)) {
jsa1969 54:7a73502d5308 525 uBit.display.scroll(testResults);
jsa1969 54:7a73502d5308 526 //radio -> sendToMakeCodeDevices("IAQ tests failed " +ManagedString(testResults));
jsa1969 33:af2dfab24ca9 527 return -1;
jsa1969 54:7a73502d5308 528 }
jsa1969 46:2fed2865a0f3 529
jsa1969 54:7a73502d5308 530 uBit.display.scroll("s");
jsa1969 34:069c4f6d5b2c 531 // includes hardware tests
jsa1969 9:5150afa50eb6 532 initSensors();
jsa1969 9:5150afa50eb6 533
jsa1969 34:069c4f6d5b2c 534 startMeasureLoops();
jsa1969 2:544117df8c65 535
jsa1969 0:cef60cc92da0 536 release_fiber();
jsa1969 56:6a7142c84db5 537 }