• Puriphico

update: bluetooth connectivity in Puriphico device

Updated: Jun 15

As I make progress with the development of the prototype, I plan to keep a log here, in the blog, under the "Updates" category. Subscribe to be alerted whenever a new update has been developed! (Also, if you are looking to implement more technical improvements in your Arduino programs, these examples can serve as a framework, so I suggest reading fully and analyzing the code. That said, when you spot an improvement to be made in my codes, PLEASE let me know ASAP - I'm looking for feedback!)


Update type: BLE (Bluetooth Low Energy) incorporation in .4 prototype


Brief description: The prototype now incorporates Bluetooth functionality which, depending on the program uploaded to the microcontroller, can transmit different data to a connected iPhone. Examples of transmitted data include the following: handwashing frequencies analyzed with FFT and/or the percentage of people who have washed their hands for 20 seconds, among other data. An app has not been developed to accommodate the Bluetooth connectivity; rather, to use the Bluetooth feature, at the moment, the user must use a BLE-connectivity app, such as LightBlue or nRF Connect. In the app, the user can search for "Arduino," tap the 'connect' icon, and witness the data transmitting in real-time. In testing, I use BLE functionality to analyze the handwashing frequencies as the device collects data: first, the device sends data to my iPhone; then, the iPhone sends the collected data to respective Adafruit libraries which, depending on the data trasmitted, graph the data values. In this way, I can compare the disparate audio frequencies (collected by the device) of handwashing, running water, and background noise.


Figures:

  • In the below code, I instruct the Arduino board to send collected and analyzed audio values to my iPhone while simultaneously communicating with the attached computer (via USB cable) to confirm the values I am receiving.

  • Additionally, when I connect the board to my iPhone with Bluetooth, the board gives me a visual indication of this transition through built-in LED's.

#include <PDM.h>

#include <ArduinoBLE.h>

// #include <arduinoFFT.h> //for the Fourier transform


// BLEService audioService(uuidOfService);

// BLEUnsignedCharCharacteristic audioLevelChar("2101", BLERead | BLENotify);


const char* uuidOfService = "00001101-0000-1000-8000-00805f9b34fb";

const char* uuidOfRxChar = "00001142-0000-1000-8000-00805f9b34fb";

const char* uuidOfTxChar = "00001143-0000-1000-8000-00805f9b34fb";


BLEService audioService(uuidOfService);

const int WRITE_BUFFER_SIZE = 256;

bool WRITE_BUFFER_FIZED_LENGTH = false;


// RX / TX Characteristics as follows:

BLECharacteristic rxChar(uuidOfRxChar, BLEWriteWithoutResponse | BLEWrite, WRITE_BUFFER_SIZE, WRITE_BUFFER_FIZED_LENGTH);

BLEByteCharacteristic txChar(uuidOfTxChar, BLERead | BLENotify | BLEBroadcast);

// Variables:

short sampleBuffer[256];

volatile int samplesRead;

#define LEDR (23u) // these are the built-in LED indicators, which will later be activated in called functions within the void loop

#define LEDG (22u)

// double washhandsav - these variables are used in my primary code for Puriphico; however, given that this is just a small subset to illustrate the bluetooth functionality, they are not needed.

// double washhandstotal;


void setup() {

Serial.begin(9600);

while (!Serial);


pinMode(LED_BUILTIN, OUTPUT);

pinMode(LEDR, OUTPUT);

pinMode(LEDG, OUTPUT);


if (!BLE.begin()) {

Serial.println("starting BLE failed!");

while (1);

}

BLE.setLocalName("AudioMonitor");

BLE.setAdvertisedService(audioService);

audioService.addCharacteristic(rxChar);

audioService.addCharacteristic(txChar);

BLE.addService(audioService);


// Bluetooth LE connection handlers.

BLE.setEventHandler(BLEConnected, onBLEConnected);

BLE.setEventHandler(BLEDisconnected, onBLEDisconnected);

// Event driven reads.

rxChar.setEventHandler(BLEWritten, onRxCharValueUpdate);

// Let's tell devices about us.

BLE.advertise();

Serial.println("Bluetooth device active, waiting for connections...");



/*

Serial.println("Peripheral advertising info: ");

Serial.print("Name: ");

Serial.println(nameOfPeripheral);

Serial.print("MAC: ");

Serial.println(BLE.address());

Serial.print("Service UUID: ");

Serial.println(microphoneService.uuid());

Serial.print("rxCharacteristic UUID: ");

Serial.println(uuidOfRxChar);

Serial.print("txCharacteristics UUID: ");

Serial.println(uuidOfTxChar);

*/


PDM.onReceive(onPDMdata);

// PDM.setBufferSize(SAMPLES);

if (!PDM.begin(1, 16000)) {

Serial.println("Failed to start PDM!");

while (1);

}


}


void onPDMdata() {

int bytesAvailable = PDM.available();

PDM.read(sampleBuffer, bytesAvailable);

samplesRead = bytesAvailable / 2;

}


void loop() {

BLEDevice central = BLE.central();

if (central)

{

// Only send data if we are connected to a central device.

while (central.connected()) {

connectedLight();


// Send the microphone values to the central device.

if (samplesRead) {

// print samples to the serial monitor or plotter

for (int i = 0; i < samplesRead; i++) {

txChar.writeValue(sampleBuffer[i]);

}

// Clear the read count

samplesRead = 0;

}

}

} else {

disconnectedLight();

}

}


void onRxCharValueUpdate(BLEDevice central, BLECharacteristic characteristic) {

Serial.print("Characteristic event, read: ");

byte test[256];

int dataLength = rxChar.readValue(test, 256);


for(int i = 0; i < dataLength; i++) {

Serial.print((char)test[i]);

}

Serial.println();

Serial.print("Value length = ");

Serial.println(rxChar.valueLength());

}


void onBLEConnected(BLEDevice central) {

Serial.print("Connected event, central: ");

Serial.println(central.address());

connectedLight();

}


void onBLEDisconnected(BLEDevice central) {

Serial.print("Disconnected event, central: ");

Serial.println(central.address());

disconnectedLight();

}


void connectedLight() {

digitalWrite(LEDR, LOW);

digitalWrite(LEDG, HIGH);

}


void disconnectedLight() {

digitalWrite(LEDR, HIGH);

digitalWrite(LEDG, LOW);

}

  • The above code's functionality is illustrated in the below video:

  • Finally, here is the final code, which incorporates the Bluetooth functionality with my original Puriphico program. In this code, the Arduino sends the values of the the underCounter (# of people that washed their hands) and the counter (# of people who washed their hands for 20 seconds) variables to my iPhone (it stores this data as the Puriphico runs; then, when my iPhone connects to the board via Bluetooth, it will send over the collected data).

  • Note: this code does not incorporate the calibration system of the microphone, hence the somewhat basic audio sampling. Also, this code largely emits the use of many separate void functions for visual simplicity, so you can see the "bare bones."


#include <PDM.h>

#include <Adafruit_GFX.h>

#include <Adafruit_SSD1306.h>

#include <arduinoFFT.h> //for the Fourier transform

#include <ArduinoBLE.h>


arduinoFFT FFT = arduinoFFT();


#define SCREEN_WIDTH 128 // OLED display width, in pixels

#define SCREEN_HEIGHT 32 // OLED display height, in pixels

#define OLED_RESET 4

#define SAMPLES 256 //Must be a power of 2

#define SAMPLING_FREQUENCY 16000

#define LEDR (23u)

#define LEDG (22u)


Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);


// The sensorPin is for the PIR sensor

byte sensorPin = 10;


// The three parameters below are for the sound

short sampleBuffer[SAMPLES];

volatile int samplesRead;

double vReal[SAMPLES];

double vImag[SAMPLES];

double washhandsav;

double washhandstotal;

void onPDMdata(void);


// These are variables used to time the sound, motion or both

int timer = 0;

int timer2 = 0;

int timer3 = 0;

int setting = 0;


// Counter is the total number of successful handwash

// Undercounter is the total number of handwash started

byte counter = 0;

byte underCounter = 0;


// sensorCount is used to know whether only one or both sensors detect handwashing

byte sensorCount = 0;

byte auxiliaryCount = 0;

byte auxiliaryCount2 = 0;


const char* uuidOfService = "00001101-0000-1000-8000-00805f9b34fb";

const char* uuidOfRxChar = "00001142-0000-1000-8000-00805f9b34fb";

const char* uuidOfTxChar = "00001143-0000-1000-8000-00805f9b34fb";


BLEService handwashService(uuidOfService);


BLECharacteristic rxChar(uuidOfRxChar, BLEWriteWithoutResponse | BLEWrite, counter, underCounter);

BLEByteCharacteristic txChar(uuidOfTxChar, BLERead | BLENotify | BLEBroadcast);


void setup() {

Serial.begin(9600);

while (!Serial);


PDM.onReceive(onPDMdata);

PDM.setBufferSize(SAMPLES);

if (!PDM.begin(1, 16000)) {

Serial.println("Failed to start PDM!");

while (1);

}



// LED pins setup

pinMode(LED_BUILTIN, OUTPUT);

pinMode(5, OUTPUT);

pinMode(6, OUTPUT);

// PIR pin setup

pinMode(sensorPin,INPUT);

// Start with two LEDs off

digitalWrite(5, LOW);

digitalWrite(6, LOW);



// starting BLE

if (!BLE.begin()) {

Serial.println("starting BLE failed!");

while (1);

}


delay(2000);

display.begin(SSD1306_SWITCHCAPVCC, 0x3C);

if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64

Serial.println(F("SSD1306 allocation failed"));

for(;;);

}

display.clearDisplay();

display.setTextColor(WHITE);

display.setRotation(0);

display.setTextWrap(false);

display.dim(0);


BLE.setLocalName("HandwashMonitor");

BLE.setAdvertisedService(handwashService);

handwashService.addCharacteristic(rxChar);

handwashService.addCharacteristic(txChar);

BLE.addService(handwashService);

// Bluetooth LE connection handlers.

BLE.setEventHandler(BLEConnected, onBLEConnected);

BLE.setEventHandler(BLEDisconnected, onBLEDisconnected);

// fill in other info here

// rxChar.setEventHandler(BLEWritten, onRxCharValueUpdate);

// rxChar.setEventHandler(BLEWritten, onRxCharValueUpdate2);

BLE.advertise();

Serial.println("Bluetooth device active, waiting for connections...");


}



void onPDMdata() {

int bytesAvailable = PDM.available();

PDM.read(sampleBuffer, bytesAvailable);

samplesRead = bytesAvailable / 2;

}



void loop() {

if (samplesRead) {

for (int i = 0; i < samplesRead; i++) {

if (sampleBuffer[i] >= 75 || sampleBuffer[i]<=-75) {

auxiliaryCount2 = auxiliaryCount2 + 1;

}

}

}

byte state = digitalRead(sensorPin);

if (state == 1) {

auxiliaryCount = auxiliaryCount + 1;

// Serial.println("User is seen");

} else if (state == 0) {

auxiliaryCount = 0;

}

if (auxiliaryCount > 0) {

sensorCount = sensorCount + 1;

}

if (auxiliaryCount2 > 0) {

sensorCount = sensorCount + 1;

}

if (auxiliaryCount > 0 or auxiliaryCount2 > 0) {

if (auxiliaryCount > 0 and auxiliaryCount2 > 0) {

setting = setting + 1;

timer2 = 0;

}

if (setting > 0) {

timer = timer + 1;

digitalWrite(6, HIGH);

digitalWrite(5, LOW);

delay(500);

digitalWrite(6, LOW);

delay(500);

display.clearDisplay();

display.setTextSize(3);

display.setCursor(10,10);

display.println(timer);

display.display();

/*

if (auxiliaryCount > 0 and auxiliaryCount2 == 0) {

timer2 = timer2 + 1;

if (timer2 == 4) {

timer = 0;

timer2 = 0;

setting = 0;

}

}

*/


if (sensorCount == 1) {

timer2 = timer2 + 1;

if (timer2 == 5) {

timer = 0;

timer2 = 0;

setting = 0;

}

}

if (timer == 5) {

underCounter = underCounter + 1;

Serial.println("underCounter:");

Serial.print(underCounter);

}

if (timer == 20) {

counter = counter + 1;

digitalWrite(5, HIGH);

digitalWrite(6, LOW);

delay(500);

digitalWrite(5,LOW);

delay(500);

digitalWrite(5,HIGH);

delay(500);

digitalWrite(5,LOW);

delay(500);

digitalWrite(5,HIGH);

delay(500);

digitalWrite(5,LOW);

delay(500);

display.clearDisplay();

timer = 0;

timer2 = 0;

setting = 0;

delay(2000);

Serial.println("Counter:");

Serial.print(counter);

}

}

}

// if (auxiliaryCount == 0 and auxiliaryCount2 == 0)

else {

timer = 0;

timer2 = 0;

setting = 0;

digitalWrite(6, LOW);

digitalWrite(5, LOW);

display.clearDisplay();

display.setTextSize(1);

display.setCursor(0, 0);

display.println("20:");

display.setCursor(20,0);

display.println(counter);

display.setCursor(0, 25);

display.println("Total:");

display.setCursor(40,25);

display.println(underCounter);

display.display();

}

auxiliaryCount = 0;

auxiliaryCount2 = 0;

sensorCount = 0;

// setting = 0;

digitalWrite(sensorPin, LOW);


BLEDevice central = BLE.central();

if (central)

{

// Only send data if we are connected to a central device.

while (central.connected()) {

connectedLight();

txChar.writeValue(counter);

delay(1000);

txChar.writeValue(underCounter);

}

} else {

disconnectedLight();

}

}


void onBLEConnected(BLEDevice central) {

Serial.print("Connected event, central: ");

Serial.println(central.address());

connectedLight();

}


void onBLEDisconnected(BLEDevice central) {

Serial.print("Disconnected event, central: ");

Serial.println(central.address());

disconnectedLight();

}


void connectedLight() {

digitalWrite(LEDR, LOW);

digitalWrite(LEDG, HIGH);

}


void disconnectedLight() {

digitalWrite(LEDR, HIGH);

digitalWrite(LEDG, LOW);

}

Thanks for tuning in. As always, leave your thoughts in the comments, and if you like the comprehensiveness, 'like' this article!


#BLE #Bluetooth #BluetoothLowEnergy #Arduino #Puriphico


125 views0 comments

Recent Posts

See All