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:
Mon Jan 17 11:52:41 2022 +0000
Revision:
50:63442fd5e709
Parent:
49:bbb506b58e6e
Child:
51:133f2fb8a7c5
fixed possible buffer overflow in radio module and wrong replies in base line set

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