Project tutorial
Mask. ME

Mask. ME © MIT

Introducing a smart mask built-on Arduino, that has the potential to disrupt lives in better manner.

  • 6,733 views
  • 3 comments
  • 12 respects

Components and supplies

Necessary tools and machines

21j8631 40
Soldering Station, 110 V
Carbon Fiber Fabric 2X2 Twill Weave
Mask w Br.Valve

Apps and online services

About this project

The threat of influenza pandemic is likely to be with us for some time, as new viral-strains will continue to emerge and travel quickly through the new global economy. In the absence of vaccines or effective treatments, non-pharmacological interventions such as hygiene, social distancing, and the use of respirators and medical masks must be implemented as secondary means of preventing or slowing down the transmission. Of these, many public health practitioners regard the use of masks to be an intervention of last resort.

With adequate time and planning, stockpiling or ramping up production, or both, would ensure that there would be enough respirators or medical masks for all those who may need them, but with limited resources and time, supplies are likely to be insufficient. Thus, reality may require that disposable N95 respirators and medical masks be pushed beyond their approved uses in the hope that they can offer some level of protection beyond their intended limits of use. Moreover, individuals with no access to respirators or masks, even disposables, may feel driven to invent their own respiratory protection measures;

According to the reports from IMC, Beijing :'The new coronavirus is sensitive to heat. It can be effectively inactivated by heating for 30 minutes at 56 degrees Celsius. Therefore, the single dry heat sterilization (70 degrees Celsius heating for 30 minutes) can effectively inactivate the virus without affecting the protective function(electrostatic absorption efficiency) of the mask'.

Hardware-Details:

The Arduino Nano 33 BLE Sense is a completely new board on a well-known form factor. It comes with a series of embedded sensors such as:

1) Microphone to capture and analyse sound in real time

2) 9-axis Inertial Measurement Unit

3) Temperature, Humidity & Barometric sensor for getting highly accurate measurements of the environmental conditions

4) Gesture, Proximity, Color and Intensity sensor

The communications chipset on the Nano 33 BLE Sense can be both a BLE and Bluetooth® client and host device. Something pretty unique in the world of microcontroller platforms, with an added possibility of running Edge Computing applications (AI) on it using TinyML. You can create your machine learning models using TensorFlow™ Lite and upload them to your board using the Arduino IDE.

Salient features of Mask.ME

  • Measurement of ambient Exhaled Breath Parameters.
  • Measurement and comparison of ambient environment parameters.
  • Transmitting Temp, Humidity and WearerI.D. through iBeacon protocol for contact-tracing over BLE.
  • Deploying real-time cough & sneeze detection ML model build through edge-impulse.
  • Active sterilization of the mask through 2x2 twill weave Carbon-fiber mesh heating.

and, a haptic feed-back through embedded vibration-disc based on alerts and event-notifications within the nRF Connect smartphone-app.

Design-It.. on step-basis:

While developing on Mask.ME project, several projects and journals have been a continual inspiration, starting from the Technion University's self-cleaning mask, furthered to smart-masks developed by LG,Xiaomi,Innosparksand Dettol-SiTi

Validating the purposeof self sterilization:

The initial experiments were done utilizing carbon-fiber tape that generated temperatures above 50 degrees celsius, but was found to be inconvenient due to non-consistent thermal propagation, hence-after a 2x2 twill weave CF was used at varying voltages starting from 3V upto 9V, that resulted in temperatures of 62 degrees at 9V/2Amps and 57-58 degrees at 5.1V (RPi adapter). Note: The temperature was measured through DS18B20 sensor in direct contact with the fabric-cutout.

A small USB-A cable is connected to electrodes that are further fused with CF fabric using thermal-paste. The DS18B20 temperature sensor monitors the average temperature of the fabric and reports output in the serial-terminal. Still, more tests need to be done, to prove the efficacy of 2x2 twill weave arrangement as opposed to uni-layer arrangement (used by TU).

Monitoring Vitals On The Go:

Prolonged usage of masks induce significantly different temperature and humidity in the micro-climates of the facemasks, which have profound influences on heart rate and thermal stress and subjective perception of discomfort. In-mask temperature is closely associated with exhaled breath temperature, which is further linked to lung inflammation(as shown in some studies).

This has been addressed through close monitoring of exhaled breath parameters and ambient environment parameters and compared on a practical basis with temperature deviation of (+2) and humidity deviation of (+10) values respectively. This approach utilizes sparkfun CCS811 and BME280 sensor breakout connected to Nano 33 BLE Sense, that all-together measure CO2, TVoCs, T, H within the mask micro-climate and the values from HTS221 on board sensor, that further generates haptic alert to further notify the wearer to take preventive measures. One can also monitor his vitals, by connecting the sensor service over BLE through the nRF Connect App.

This feature has further been developed for minimal contact-tracing of individuals using Bluetooth Low Energy ibeacon protocol, wherein the Major and Minor values are updated with in-mask humidity and temperature along with hard-coded wearer I.D. and calibrated Tx power level, that alltogether can serve to track individuals witihin an enterprise equipped with iBeacon Servers or through DIY RPi iBeacon Scanner.

all he says is- 'God Bless You':

The last part of this project utilizes the machine learning capabilities of Nano 33 BLE Sense through Edge Impulse Studio, a detailed walkthrough has already been provided at :

https://www.hackster.io/edge-impulse/cough-detection-with-tinyml-on-arduino-417f37

Through the use of on-board microphone, the Nano 33 BLE Sense board is trained with 15 samples each of 3 classes(cough, sneeze and noise) data and each has been sampled over a second and trained using 2D Convolution.

Mel-frequency cepstral coefficients (MFCCs) are coefficients that collectively make up an MFC. They are derived from a type of cepstral representation of the audio clip (a nonlinear "spectrum-of-a-spectrum"). The difference between the cepstrum and the mel-frequency cepstrum is that in the MFC, the frequency bands are equally spaced on the mel scale, which approximates the human auditory system's response more closely than the linearly-spaced frequency bands used in the normal cepstrum. This frequency warping can allow for better representation of sound, for example, in audio compression.

MFCC values are not very robust in the presence of additive noise, and so it is common to normalise their values in speech recognition systems to lessen the influence of noise. Some researchers propose modifications to the basic MFCC algorithm to improve robustness, such as by raising the log-mel-amplitudes to a suitable power (around 2 or 3) before taking the DCT (Discrete Cosine Transform), which reduces the influence of low-energy components.

Code

Mask.ME iBeacon minimal CTArduino
//Trace and update on the Mask.ME parameters within iBeacon Protocol; including Contact-ID, (Insitu.)Temp. and Humidity
#include <ArduinoBLE.h>
#include <BeaconNano.h>
#include <Arduino_HTS221.h>
BeaconNano bn;

void setup() {  
  Serial.begin(9600);
  while (!Serial);
  if (!HTS.begin()) {
    while (1);
  }
  HTS.begin();
  pinMode(3, OUTPUT);
  //bn.setUuid("97268244704bb0483b0ae75002111843");
  bn.setUuid("1D003FFFFFFFFFFFFFFFFFFFFFFF1234");
  int temperature = HTS.readTemperature();
  int humidity = HTS.readHumidity();
  bn.setMajor(humidity);
  bn.setMinor(temperature);
  bn.setTx(-60);
  bn.startBeacon();
  
}

void loop() {
    //bn.stopBeacon();
      //delay(5000);
      //bn.startBeacon();
      // Pull-down reset line for timed sensor update inside iBeacon packet.
      //digitalWrite(3, LOW);     
  }
Cough and Sneeze Detection based on Nano 33 BLE SenseArduino
// Cough & Sneeze Classifier built through Edge Impulse Studio.
// If your target is limited in memory remove this macro to save 10K RAM
#define EIDSP_QUANTIZE_FILTERBANK   0
#include <PDM.h>
#include <cscan2_inference.h>

typedef struct {
    int16_t *buffer;
    uint8_t buf_ready;
    uint32_t buf_count;
    uint32_t n_samples;
} inference_t;

static inference_t inference;
static bool record_ready = false;
static signed short sampleBuffer[2048];
static bool debug_nn = false; // Set this to true to see e.g. features generated from the raw signal

/**
 * @brief      Arduino setup function
 */
void setup()
{
    // put your setup code here, to run once:
    Serial.begin(115200);
    Serial.println("SpecVital Inference...");
    // summary of inferencing settings (from model_metadata.h)
    ei_printf("Inferencing settings:\n");
    ei_printf("\tInterval: %.2f ms.\n", (float)EI_CLASSIFIER_INTERVAL_MS);
    ei_printf("\tFrame size: %d\n", EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE);
    ei_printf("\tSample length: %d ms.\n", EI_CLASSIFIER_RAW_SAMPLE_COUNT / 16);
    ei_printf("\tNo. of classes: %d\n", sizeof(ei_classifier_inferencing_categories) / sizeof(ei_classifier_inferencing_categories[0]));
    if (microphone_inference_start(EI_CLASSIFIER_RAW_SAMPLE_COUNT) == false) {
        ei_printf("ERR: Failed to setup audio sampling\r\n");
        return;
    }
}

/**
 * @brief      Arduino main function. Runs the inferencing loop.
 */
void loop()
{
    ei_printf("Starting inferencing in 2 seconds...\n");
    delay(5000);
    ei_printf("Recording...\n");
    bool m = microphone_inference_record();
    if (!m) {
        ei_printf("ERR: Failed to record audio...\n");
        return;
    }
    ei_printf("Recording done\n");

    signal_t signal;
    signal.total_length = EI_CLASSIFIER_RAW_SAMPLE_COUNT;
    signal.get_data = &microphone_audio_signal_get_data;
    ei_impulse_result_t result = { 0 };

    EI_IMPULSE_ERROR r = run_classifier(&signal, &result, debug_nn);
    if (r != EI_IMPULSE_OK) {
        ei_printf("ERR: Failed to run classifier (%d)\n", r);
        return;
    }

    // print the predictions
    ei_printf("Predictions (DSP: %d ms., Classification: %d ms., Anomaly: %d ms.): \n",
        result.timing.dsp, result.timing.classification, result.timing.anomaly);
    for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
        ei_printf("    %s: %.5f\n", result.classification[ix].label, result.classification[ix].value);
    }
#if EI_CLASSIFIER_HAS_ANOMALY == 1
    ei_printf("    anomaly score: %.3f\n", result.anomaly);
#endif
}
/**
 * @brief      Printf function uses vsnprintf and output using Arduino Serial
 *
 * @param[in]  format     Variable argument list
 */
void ei_printf(const char *format, ...) {
    static char print_buf[1024] = { 0 };
    va_list args;
    va_start(args, format);
    int r = vsnprintf(print_buf, sizeof(print_buf), format, args);
    va_end(args);
    if (r > 0) {
        Serial.write(print_buf);
    }
}
/**
 * @brief      PDM buffer full callback
 *             Get data and call audio thread callback
 */
static void pdm_data_ready_inference_callback(void)
{
    int bytesAvailable = PDM.available();
    // read into the sample buffer
    int bytesRead = PDM.read((char *)&sampleBuffer[0], bytesAvailable);
    if (record_ready == true || inference.buf_ready == 1) {
        for(int i = 0; i < bytesRead>>1; i++) {
            inference.buffer[inference.buf_count++] = sampleBuffer[i];
            if(inference.buf_count >= inference.n_samples) {
                inference.buf_count = 0;
                inference.buf_ready = 1;
            }
        }
    }
}
/**
 * @brief      Init inferencing struct and setup/start PDM
 *
 * @param[in]  n_samples  The n samples
 *
 * @return     { description_of_the_return_value }
 */
static bool microphone_inference_start(uint32_t n_samples)
{
    inference.buffer = (int16_t *)malloc(n_samples * sizeof(int16_t));
    if(inference.buffer == NULL) {
        return false;
    }
    inference.buf_count  = 0;
    inference.n_samples  = n_samples;
    inference.buf_ready  = 0;
    // configure the data receive callback
    PDM.onReceive(&pdm_data_ready_inference_callback);
    // optionally set the gain, defaults to 20
    PDM.setGain(80);
    //ei_printf("Sector size: %d nblocks: %d\r\n", ei_nano_fs_get_block_size(), n_sample_blocks);
    PDM.setBufferSize(4096);
    // initialize PDM with:
    // - one channel (mono mode)
    // - a 16 kHz sample rate
    if (!PDM.begin(1, EI_CLASSIFIER_FREQUENCY)) {
        ei_printf("Failed to start PDM!");
    }
    record_ready = true;
    return true;
}
/**
 * @brief      Wait on new data
 *
 * @return     True when finished
 */
static bool microphone_inference_record(void)
{
    inference.buf_ready = 0;
    inference.buf_count = 0;
    while(inference.buf_ready == 0) {
        delay(10);
    }
    return true;
}
/**
 * Get raw audio signal data
 */
static int microphone_audio_signal_get_data(size_t offset, size_t length, float *out_ptr)
{
    arm_q15_to_float(&inference.buffer[offset], out_ptr, length);
    return 0;
}

/**
 * @brief      Stop PDM and release buffers
 */
static void microphone_inference_end(void)
{
    PDM.end();
    free(inference.buffer);
}

#if !defined(EI_CLASSIFIER_SENSOR) || EI_CLASSIFIER_SENSOR != EI_CLASSIFIER_SENSOR_MICROPHONE
#error "Invalid model for current sensor."
#endif
Monitoring Exhale Parameters through CCS811/BME280Arduino
// For Scanning Insitu Exhale Parameters via nRFConnect or any BLE Service App
// Carbon-dioxide & Volatile OCs have been pseudo-mapped under Pressure & Rainfall respectively(ppm & ppb);)
#include <ArduinoBLE.h>
#include <Wire.h>
#include <SparkFunBME280.h>
#include <SparkFunCCS811.h>
#define CCS811_ADDR 0x5B
#define PIN_NOT_WAKE 5
CCS811 myCCS811(CCS811_ADDR);
BME280 myBME280;

BLEService sensorService("181A");  // Environmental_Sensor-Service
// 180D HeartRate
// 181B BodyComposi.
BLEShortCharacteristic sensorLevelChar1("2A6E", BLERead | BLENotify);   // Temperature-Characteristic
BLEShortCharacteristic sensorLevelChar2("2A6F", BLERead | BLENotify);   // Humidity-Characteristic
BLELongCharacteristic sensorLevelChar3("2A6D", BLERead | BLENotify);    // PCo2-Characteristic
BLELongCharacteristic sensorLevelChar4("2A78", BLERead | BLENotify);    // PTVoC-Characteristic
// 2A37 HRmeasurement
// 2A2C Magdeclination
// 2A98 weight
// 2A6D Pressure
// 2A9D Recom.HRMax.
// 2A8E Height
// 2A8D Max.HR
// 2A8C Gender
// 2A00 DeviceName
// 2A38 bodysensorlocation
int oldSensorLevel1 = 0; 
int oldSensorLevel2 = 0;
int oldSensorLevel3 = 0;
int oldSensorLevel4 = 0;
long previousMillis = 0; 

void setup() {
  Serial.begin(9600);
  while (!Serial);
  pinMode(23, OUTPUT);

  if (!BLE.begin()) {
    Serial.println("Bluetooth init. failed!");
    while (1);
  }
  Wire.begin();
  CCS811Core::status returnCode = myCCS811.begin();
  Serial.print("CCS811 begin exited with: ");
  printDriverError( returnCode );
  Serial.println();
  myBME280.settings.commInterface = I2C_MODE;
  myBME280.settings.I2CAddress = 0x77;
  myBME280.settings.commInterface = I2C_MODE;
  myBME280.settings.I2CAddress = 0x77;
  myBME280.settings.runMode = 3; //Normal mode
  myBME280.settings.tStandby = 0;
  myBME280.settings.filter = 4;
  myBME280.settings.tempOverSample = 5;
  myBME280.settings.pressOverSample = 5;
  myBME280.settings.humidOverSample = 5;
  delay(10);
  myBME280.begin();

  BLE.setLocalName("Mask.xME");
  BLE.setAdvertisedService(sensorService);
  sensorService.addCharacteristic(sensorLevelChar1);
  BLE.addService(sensorService);
  sensorLevelChar1.writeValue(oldSensorLevel1);

  sensorService.addCharacteristic(sensorLevelChar2);
  BLE.addService(sensorService);
  sensorLevelChar2.writeValue(oldSensorLevel2);

  sensorService.addCharacteristic(sensorLevelChar3);
  BLE.addService(sensorService);
  sensorLevelChar3.writeValue(oldSensorLevel3);

  sensorService.addCharacteristic(sensorLevelChar4);
  BLE.addService(sensorService);
  sensorLevelChar4.writeValue(oldSensorLevel4);

  // start advertising
  BLE.advertise();
  Serial.println("Bluetooth active, waiting to be connected.....");
}

void loop() {
  // waiting for a BLE central device.
  BLEDevice central = BLE.central();
  if (central) {
    Serial.print("Connected to central: ");
    Serial.println(central.address());
    digitalWrite(23, HIGH);  // indicate connection
    while (central.connected()) {
  if (myCCS811.dataAvailable())
  {
    myCCS811.readAlgorithmResults();
    printInfoSerial();
    float BMEtempC = myBME280.readTempC();
    float BMEhumid = myBME280.readFloatHumidity();
    myCCS811.setEnvironmentalData(BMEhumid, BMEtempC);
  }
  else if (myCCS811.checkForStatusError())
  {
    printSensorError();
  }
      long currentMillis = millis();
      if (currentMillis - previousMillis >= 200) {
        previousMillis = currentMillis;
        updateSensorLevel1();
        updateSensorLevel2();
        updateSensorLevel3();
        updateSensorLevel4();
      }
    }
    digitalWrite(23, LOW);
    Serial.print("Disconnected from central: ");
    Serial.println(central.address());
  }
}

void updateSensorLevel1() {
  float temperature = myBME280.readTempC();
  Serial.print("Temperature = ");
  Serial.print(temperature, 0);
  //float T = myBME280.readTempC();
  Serial.println(" °C");
  float sensorLevel1 = map(temperature, 0, 100, 0, 10000);
  sensorLevelChar1.writeValue(sensorLevel1);
  oldSensorLevel1 = sensorLevel1;
}

void updateSensorLevel2() {
  float humidity = myBME280.readFloatHumidity();
  Serial.print("Humidity = ");
  Serial.print(humidity, 0);
  Serial.println(" %");
  float sensorLevel2 = map(humidity, 0, 100, 0, 10000);
  sensorLevelChar2.writeValue(sensorLevel2); 
  oldSensorLevel2 = sensorLevel2; 
}

void updateSensorLevel3() {
  float CO2 = myCCS811.getCO2();  
  Serial.print("Co2 = ");
  Serial.print(CO2);
  Serial.println(" ppm");
  float sensorLevel3 = map(CO2, 0, 100, 0, 1000);
  sensorLevelChar3.writeValue(sensorLevel3); 
  oldSensorLevel3 = sensorLevel3; 
}

void updateSensorLevel4() {
  float VoC = myCCS811.getTVOC();
  Serial.print("TVoC = ");
  Serial.print(VoC);
  Serial.println(" ppb");
  float sensorLevel4 = map(VoC, 0, 100, 0, 100);
  sensorLevelChar4.writeValue(sensorLevel4); 
  oldSensorLevel4 = sensorLevel4; 
}

void printInfoSerial()
{
  Serial.println("CCS811 data:");
  Serial.print(" CO2 concentration : ");
  Serial.print(myCCS811.getCO2());
  Serial.println(" ppm");
  Serial.print(" TVOC concentration : ");
  Serial.print(myCCS811.getTVOC());
  Serial.println(" ppb");

  Serial.println("BME280 data:");
  Serial.print(" Temperature: ");
  Serial.print(myBME280.readTempC(), 2);
  Serial.println(" degrees C");
  Serial.print(" %RH: ");
  Serial.print(myBME280.readFloatHumidity(), 2);
  Serial.println(" %");
  Serial.println();
}

void printDriverError( CCS811Core::status errorCode )
{
  switch ( errorCode )
  {
    case CCS811Core::SENSOR_SUCCESS:
      Serial.print("SUCCESS");
      break;
    case CCS811Core::SENSOR_ID_ERROR:
      Serial.print("ID_ERROR");
      break;
    case CCS811Core::SENSOR_I2C_ERROR:
      Serial.print("I2C_ERROR");
      break;
    case CCS811Core::SENSOR_INTERNAL_ERROR:
      Serial.print("INTERNAL_ERROR");
      break;
    case CCS811Core::SENSOR_GENERIC_ERROR:
      Serial.print("GENERIC_ERROR");
      break;
    default:
      Serial.print("Unspecified error.");
  }
}
void printSensorError()
{
  uint8_t error = myCCS811.getErrorRegister();
  if ( error == 0xFF ) //comm error
  {
    Serial.println("Failed to get ERROR_ID register.");
  }
  else
  {
    Serial.print("Error: ");
    if (error & 1 << 5) Serial.print("HeaterSupply");
    if (error & 1 << 4) Serial.print("HeaterFault");
    if (error & 1 << 3) Serial.print("MaxResistance");
    if (error & 1 << 2) Serial.print("MeasModeInvalid");
    if (error & 1 << 1) Serial.print("ReadRegInvalid");
    if (error & 1 << 0) Serial.print("MsgInvalid");
    Serial.println();
  }
}
Serial Output for debugging sensor valuesArduino
// 
#include <Arduino_HTS221.h>
#include <Wire.h>
#include <SparkFunBME280.h>
#include <SparkFunCCS811.h>
#define CCS811_ADDR 0x5B
#define PIN_NOT_WAKE 5
CCS811 myCCS811(CCS811_ADDR);
BME280 myBME280;
#define MAXIMWIRE_EXTERNAL_PULLUP
#include <MaximWire.h>
#define PIN_BUS 2
MaximWire::Bus bus(PIN_BUS);
MaximWire::DS18B20 device;

void setup()
{
  Serial.begin(9600);
  while (!Serial);
  if (!HTS.begin()) {
  while (1);
  }
  Serial.println();
  Serial.println("Apply BME280 data to CCS811 for compensation.");
  pinMode(3, OUTPUT);
  Wire.begin();
  CCS811Core::status returnCode = myCCS811.begin();
  Serial.print("CCS811 begin exited with: ");
  printDriverError( returnCode );
  Serial.println();
  myBME280.settings.commInterface = I2C_MODE;
  myBME280.settings.I2CAddress = 0x77;
  myBME280.settings.commInterface = I2C_MODE;
  myBME280.settings.I2CAddress = 0x77;
  myBME280.settings.runMode = 3; //Normal mode
  myBME280.settings.tStandby = 0;
  myBME280.settings.filter = 4;
  myBME280.settings.tempOverSample = 5;
  myBME280.settings.pressOverSample = 5;
  myBME280.settings.humidOverSample = 5;
  delay(10);
  myBME280.begin();
}
void loop()
{
  float temperature = HTS.readTemperature();
  float humidity    = HTS.readHumidity();
  //delay(1000);

      MaximWire::Discovery discovery = bus.Discover();
    do {
        MaximWire::Address address;
        if (discovery.FindNextDevice(address)) {
            if (address.GetModelCode() == MaximWire::DS18B20::MODEL_CODE) {
                //Serial.print(" (DS18B20)");
                MaximWire::DS18B20 device(address);
                float temp = device.GetTemperature<float>(bus);
                Serial.print("Thermal Mesh Temperature: ");
                Serial.print(temp);
                Serial.print(" °C");
                Serial.println();
                Serial.print("Ambient Temperature: ");
                Serial.print(temperature);
                Serial.println(" °C");
                Serial.print("Relative Humidity: ");
                Serial.print(humidity);
                Serial.println(" %");
                Serial.println();
                device.Update(bus);
            } else {
                Serial.println();
            }
        }
    } while (discovery.HaveMore());
    //delay(1000);
  
  if (myCCS811.dataAvailable())
  {
    myCCS811.readAlgorithmResults();
    printInfoSerial();
    float BMEtempC = myBME280.readTempC();
    float BMEhumid = myBME280.readFloatHumidity();
    Serial.print("Applying new values (deg C, %): ");
    Serial.print(BMEtempC);
    Serial.print(",");
    Serial.println(BMEhumid);
    Serial.println();
    myCCS811.setEnvironmentalData(BMEhumid, BMEtempC);
     if (BMEhumid > (humidity+15))
    {
      digitalWrite(3, LOW); // reverse on Nano33
      delay(100);
      digitalWrite(3, HIGH);
      delay(1000);
    } else if (BMEhumid <= (humidity+12) || (humidity))
    {
      digitalWrite(3, HIGH); // stops vibs.     
    }
    else if (BMEtempC > (temperature+2))
    {
      digitalWrite(3, LOW); // reverse on Nano33
      delay(100);
      digitalWrite(3, HIGH);
      delay(500);
    } else if (BMEtempC <= (temperature+1) || (temperature))
    {
      digitalWrite(3, HIGH); // stops vibs.     
    }
  }
  else if (myCCS811.checkForStatusError())
  {
    printSensorError();
  }
  delay(2000); //Wait for next reading
}

void printInfoSerial()
{
  Serial.println("CCS811 data:");
  Serial.print(" CO2 concentration : ");
  Serial.print(myCCS811.getCO2());
  Serial.println(" ppm");
  Serial.print(" TVOC concentration : ");
  Serial.print(myCCS811.getTVOC());
  Serial.println(" ppb");

  Serial.println("BME280 data:");
  Serial.print(" Temperature: ");
  Serial.print(myBME280.readTempC(), 2);
  Serial.println(" degrees C");
  Serial.print(" %RH: ");
  Serial.print(myBME280.readFloatHumidity(), 2);
  Serial.println(" %");
  Serial.println();
}

void printDriverError( CCS811Core::status errorCode )
{
  switch ( errorCode )
  {
    case CCS811Core::SENSOR_SUCCESS:
      Serial.print("SUCCESS");
      break;
    case CCS811Core::SENSOR_ID_ERROR:
      Serial.print("ID_ERROR");
      break;
    case CCS811Core::SENSOR_I2C_ERROR:
      Serial.print("I2C_ERROR");
      break;
    case CCS811Core::SENSOR_INTERNAL_ERROR:
      Serial.print("INTERNAL_ERROR");
      break;
    case CCS811Core::SENSOR_GENERIC_ERROR:
      Serial.print("GENERIC_ERROR");
      break;
    default:
      Serial.print("Unspecified error.");
  }
}
void printSensorError()
{
  uint8_t error = myCCS811.getErrorRegister();

  if ( error == 0xFF ) //comm error
  {
    Serial.println("Failed to get ERROR_ID register.");
  }
  else
  {
    Serial.print("Error: ");
    if (error & 1 << 5) Serial.print("HeaterSupply");
    if (error & 1 << 4) Serial.print("HeaterFault");
    if (error & 1 << 3) Serial.print("MaxResistance");
    if (error & 1 << 2) Serial.print("MeasModeInvalid");
    if (error & 1 << 1) Serial.print("ReadRegInvalid");
    if (error & 1 << 0) Serial.print("MsgInvalid");
    Serial.println();
  }
}

Schematics

Connection Details for Mask.ME
Mask me ujtun3wrpi

Comments

Similar projects you might like

The B&ND

Project tutorial by Night R

  • 3,589 views
  • 0 comments
  • 5 respects

ECG Analyzer Powered by Edge Impulse

Project tutorial by Manivannan

  • 11,782 views
  • 7 comments
  • 64 respects

Ultimate Cough Recognition: How Dangerous is Coughing?

Project tutorial by corey yin

  • 5,455 views
  • 2 comments
  • 17 respects

Hygie - Better follow and understand your menopause

Project tutorial by Jean Perardel

  • 963 views
  • 0 comments
  • 2 respects

Safe Distance Protection Badge

Project tutorial by filipmu

  • 9,382 views
  • 2 comments
  • 36 respects

BeeMonitor

Project tutorial by Team 🐬 Delfinčki 🐬

  • 6,295 views
  • 1 comment
  • 16 respects
Add projectSign up / Login