Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
Night R
Published © MIT

Mask. ME

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

IntermediateFull instructions providedOver 5 days9,792

Things used in this project

Story

Read more

Schematics

Connection Details for Mask.ME

Code

Mask.ME iBeacon minimal CT

Arduino
//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 Sense

Arduino
// 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/BME280

Arduino
// 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 values

Arduino
// 
#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();
  }
}

Github Repo for Mask.ME

Credits

Night R

Night R

16 projects • 50 followers
R&D Engineer @ IoT Solutions Provider, Robotics Engineer @ SIS Corp., Passionate for Hardware hacking, 12+ years experience in programming..

Comments