Mithun Das
Published

TraceX-Contact Tracing & Social Distancing Ecosystem

A wearable band and iOS app to trace contacts, enable social distancing, report active cases and inform potential exposure by instant alert.

IntermediateFull instructions provided24 hours7,560

Things used in this project

Hardware components

Nano 33 BLE Sense
Arduino Nano 33 BLE Sense
This MCU is the heart of the smart band. I am using on board sensors for temperature, humidity and air pressure readings, fall detection using on board IMU sensor.
×1
MAX30102 High-Sensitivity Pulse Oximeter and Heart-Rate Sensor for Wearable Health
Maxim Integrated MAX30102 High-Sensitivity Pulse Oximeter and Heart-Rate Sensor for Wearable Health
I am using this low cost sensor to measure blood pressure or BPM
×1
0.96" OLED 64x128 Display Module
ElectroPeak 0.96" OLED 64x128 Display Module
OLED display to show readings on smart band
×1
HM-10
Bluetooth 4.0 Module. Separate from Nano 33 BLE. This module is used solely for transmitting iBeacon advertisement.
×1
LiPo 3.7v battery
×1

Software apps and online services

IOTA Tangle
IOTA Tangle
AWS Lambda
Amazon Web Services AWS Lambda
AWS DynamoDB
Amazon Web Services AWS DynamoDB
AWS Pinpoint
Swift Programming Language

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
10 Pc. Jumper Wire Kit, 5 cm Long
10 Pc. Jumper Wire Kit, 5 cm Long

Story

Read more

Code

Arduino Nano 33 BLE code for wearable

Arduino
#include <ArduinoBLE.h>
#include <Arduino_HTS221.h>
#include <Arduino_LPS22HB.h>

#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include "MAX30105.h"
#include "spo2_algorithm.h"
#include "heartRate.h"

MAX30105 particleSensor;

#define MAX_BRIGHTNESS 255

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

//#define OLED_RESET LED_BUILTIN
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(OLED_RESET);
static const unsigned char PROGMEM logo2_bmp[] =
{ 0x03, 0xC0, 0xF0, 0x06, 0x71, 0x8C, 0x0C, 0x1B, 0x06, 0x18, 0x0E, 0x02, 0x10, 0x0C, 0x03, 0x10,              //Logo2 and Logo3 are two bmp pictures that display on the OLED if called
  0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x40, 0x01, 0x10, 0x40, 0x01, 0x10, 0xC0, 0x03, 0x08, 0x88,
  0x02, 0x08, 0xB8, 0x04, 0xFF, 0x37, 0x08, 0x01, 0x30, 0x18, 0x01, 0x90, 0x30, 0x00, 0xC0, 0x60,
  0x00, 0x60, 0xC0, 0x00, 0x31, 0x80, 0x00, 0x1B, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x04, 0x00,
};


BLEService sensorService("FFE0");  // Environmental_Sensor-Service
BLEStringCharacteristic bleCharTemp("2A6E", BLERead | BLENotify, 2);  // Temperature-Characteristic
BLEStringCharacteristic bleCharBpm("2A37", BLERead | BLENotify, 3);  // BPM-Characteristic
BLEStringCharacteristic bleCharOximetry("2A62", BLERead | BLENotify, 3);  // Oximetry-Characteristic
BLEShortCharacteristic switchChar("2A63", BLERead | BLEWrite | BLENotify);
BLEStringCharacteristic bleCharHumidity("2A64", BLERead | BLENotify, 3);  // Humidity-Characteristic
BLEStringCharacteristic bleCharPressure("2A65", BLERead | BLENotify, 4);  // Baromerter-Characteristic (hPa)



long previousMillis = 0;
int oldTemperature = 0;



const byte RATE_SIZE = 4; //Increase this for more averaging. 4 is good.
byte rates[RATE_SIZE];
byte rateSpot = 0;
long lastBeat = 0; //Time at which the last beat occurred
float beatsPerMinute;
int beatAvg;


//vibration
#define VIBRATE_PIN 2

void switchCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic) {
  // central wrote new value to characteristic, update LED
  Serial.print("Characteristic event, written: ");

  if (switchChar.value()) {
    Serial.println("LED on");
    digitalWrite(VIBRATE_PIN, HIGH);
    delay(1000);
    digitalWrite(VIBRATE_PIN, LOW);
  }

}


void setup() {
  Serial.begin(9600);
  //while (!Serial);

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  //display.drawBitmap(0, 0, logo3_bmp, 32, 32, WHITE);    //Draw the second picture (bigger heart)
  display.drawBitmap(5, 5, logo2_bmp, 24, 21, WHITE);
  display.setTextSize(2);                                //And still displays the average BPM
  display.setTextColor(WHITE);
  display.setCursor(50, 0);
  display.print("BPM");
  

  display.display();
  delay(3000);

  
  
  pinMode(22, OUTPUT);
  pinMode(24, OUTPUT);
  pinMode(VIBRATE_PIN, OUTPUT);
  digitalWrite(VIBRATE_PIN, LOW);

  if (!BLE.begin()) {
    Serial.println("Bluetooth init. failed!");
    while (1);
  }

  if (!HTS.begin()) {
    Serial.println("Failed to initialize humidity temperature sensor!");
    while (1);
  }

  if (!BARO.begin()) {
    Serial.println("Failed to initialize BARO sensor!");
    while (1);
  }


  BLE.setDeviceName("TRACEX");
  BLE.setLocalName("TRACEX");


  BLE.setAdvertisedService(sensorService);

  switchChar.setEventHandler(BLEWritten, switchCharacteristicWritten);

  sensorService.addCharacteristic(bleCharTemp);
  sensorService.addCharacteristic(bleCharBpm);
  sensorService.addCharacteristic(bleCharOximetry);
  sensorService.addCharacteristic(switchChar);
  sensorService.addCharacteristic(bleCharHumidity);
  sensorService.addCharacteristic(bleCharPressure);
  
  BLE.addService(sensorService);

  bleCharTemp.writeValue(String(0));
  bleCharBpm.writeValue(String(0));
  bleCharOximetry.writeValue(String(0));
  bleCharHumidity.writeValue(String(0));
  bleCharPressure.writeValue(String(0));

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

  //Initialize sensor
  if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //Use default I2C port, 400kHz speed
  {
    Serial.println("MAX30105 was not found. Please check wiring/power.");
    while (1);
  }
  particleSensor.setup(); //Configure sensor with default settings
  particleSensor.setPulseAmplitudeRed(0x0A); //Turn Red LED to low to indicate sensor is running
  particleSensor.setPulseAmplitudeGreen(0);



}

void updateTemperature() {
  int temperature = HTS.readTemperature(FAHRENHEIT);
//  Serial.print("Temperature = ");
//  Serial.print(temperature);
//  Serial.println(" °F");
  int sensorValue = map(temperature, 0, 100, 0, 10000);
  bleCharTemp.writeValue(String(sensorValue));
  oldTemperature = sensorValue;

  
  int humidity = HTS.readHumidity();
  bleCharHumidity.writeValue(String(humidity));

  int pressure = BARO.readPressure(PSI);
//  Serial.print("Pressure = ");
//  Serial.print(pressure);
//  Serial.println(" kPa");
  bleCharPressure.writeValue(String(pressure));
}


void updateBMP(){
 
  bleCharBpm.writeValue(String(beatAvg));
  display.clearDisplay();
  display.drawBitmap(5, 5, logo2_bmp, 24, 21, WHITE);
  display.setTextSize(2);                                //And still displays the average BPM
  display.setTextColor(WHITE);
  display.setCursor(60, 10);
 // display.print("BPM");
  
  //display.setCursor(90, 0);
 display.print(beatAvg, DEC);
 
  display.display();
  
}


void loop() {

  // poll for BLE events
  BLE.poll();

  // waiting for a BLE central device.
  BLEDevice central = BLE.central();

  if (central) {
    Serial.print("Connected to central: ");

    Serial.println(central.address());
    digitalWrite(22, HIGH);  // indicate connection
    digitalWrite(24, HIGH);

    while (central.connected()) {




      long currentMillis = millis();
      if (currentMillis - previousMillis >= 5000) {
        previousMillis = currentMillis;
        updateTemperature();
        updateBMP();
        //switchChar.writeValue(1);
        //vibrate();
        //updateHeartBeat(loggedheartRate, loggedspo2);
        //loggedheartRate = 0;
        //loggedspo2 = 0;
      }
      long irValue = particleSensor.getIR();
      if (checkForBeat(irValue) == true)
      {
        //We sensed a beat!
        long delta = millis() - lastBeat;
        lastBeat = millis();
        beatsPerMinute = 60 / (delta / 1000.0);
        if (beatsPerMinute < 255 && beatsPerMinute > 20)
        {
          rates[rateSpot++] = (byte)beatsPerMinute; //Store this reading in the array
          rateSpot %= RATE_SIZE;                    //Wrap variable
          beatAvg = 0;
          for (byte x = 0; x < RATE_SIZE; x++)
            beatAvg += rates[x];
          beatAvg /= RATE_SIZE;
        }
      }
      Serial.print("IR=");
      Serial.print(irValue);
      Serial.print(", BPM=");
      Serial.print(beatsPerMinute);
      Serial.print(", Avg BPM=");
      Serial.print(beatAvg);
      if (irValue < 50000)
        Serial.print("Bracelet removed!");
      
  



    }//end of while connected


    digitalWrite(22, LOW);
    digitalWrite(24, LOW);
    Serial.print("Disconnected from central: ");
    Serial.println(central.address());

  }


}

void vibrate() {
  Serial.println("Vibrate");
  digitalWrite(VIBRATE_PIN, HIGH);

  delay(200);
  digitalWrite(VIBRATE_PIN, LOW);
  Serial.println("End Vibrate");
}

lambda-backend.zip

JavaScript
NodeJS backend API code
No preview (download only).

Credits

Mithun Das

Mithun Das

34 projects • 180 followers
Hacker and Maker driven by passion. Ambassador at Edge Impulse and Balena. Follow me on Twitter @_mithundas

Comments