Context

This installation has been created as part of the ‘Ce que çe peut être’ project, which constitutes our diploma project at the ENSA Paris Malaquais, in 2018:

Body sensors

List of hardware

The ‘Ce que ça peut être’ project was firstly composed of a set of sensors that we made several people wear. This suit was comprised of the following sensors:

  • 4x flex sensors, to track elbow and knee flexion
  • 1x flexible stretch sensor, to track the breathing rhythm
  • 1x pulse sensor, to track the heart rate
  • 1x photosensitive resistance, to track the ambient luminosity
  • 1x DHT12 sensor, to track temperature and humidity on the body’s skin
  • 1x ADXL335 sensor (accelerometer), to track the body’s XYZ position
  • 1x microSD card reader/writer, to store the captured data onboard
  • 1x Arduino MEGA 2560, to compute all

Assembly

Arduino code

/* 
 *  MLAV.LAND
 *  http://mlav.land
 *  20 05 2018
 *  MAUD LÉVY & ANTOINE VERCOUTÈRE
 *  
 *  ////////////////////////////////////////////////////////////////////////
 *  
 *  INPUTS PINS :
 *    PIN A0 : FLEX 1 (coude) :: orange
 *    PIN A1 : FLEX 2 (coude) :: jaune
 *    PIN A2 : FLEX 3 (genou) :: blanc
 *    PIN A3 : FLEX 4 (genou) :: gris
 *    
 *    PIN A5 : RESPIRATION :: marron
 *    
 *    PIN SDA (20) : SDA DHT12 :: vert
 *    PIN SCL (21) : SCL DHT12 :: bleu
 *    
 *    PIN A7 : COEUR BPM :: violet
 *    PIN A8 : PHOTORESISTANCE :: orange
 *    
 *    PIN A10 : AXE X ACCELEROMETRE :: violet
 *    PIN A11 : AXE Y ACCELEROMETRE :: vert
 *    PIN A12 : AXE Z ACCELEROMETRE :: bleu
 *  
 *  ////////////////////////////////////////////////////////////////////////
 *    
 *  OUTPUT : 
 *    TEMPS(ms)
 *    BPM, (VARIABLE PULSE SENSOR - non utilisé), (VARIABLE PULSE SENSOR - non utilisé)
 *    FLEX 1 ; FLEX 2 ; FLEX 3 ; FLEX 4 ; HUMIDITE ; TEMPERATURE ; RESPIRATION ; LUMINOSITE ; AXE X ACCELEROMETRE ; AXE Y ACCELEROMETRE ; AXE Z ACCELEROMETRE
 *    
 *  UNITÉS :
 *    BPM :: BATTEMENT PAR MINUTE
 *    FLEX SENSORS :: DEGRÉS
 *    HUMIDITÉ :: POURCENTAGE
 *    TEMPÉRATURE :: DEGRÉS CELSIUS
 *    RESPIRATION :: SANS UNITÉ, MOUVEMENT
 *    LUMINOSITÉ :: SANS UNITÉ (à rechercher)
 *    AXES X, Y, Z ACCELEROMÈTRE :: G
 *    
 */
 
// TEMPS
 
unsigned long time;
 
// INPUT ADXL335 / 5V (accéléromètre)
 
const int xPin = A10;
const int yPin = A11;
const int zPin = A12;
 
// CALIBRAGE ADXL335
 
int xRawMin = 267;
int xRawMax = 403;
 
int yRawMin = 265;
int yRawMax = 402;
 
int zRawMin = 279;
int zRawMax = 419;
 
// PINS FLEX
 
int FlexPin1 = A0; //coude
int FlexPin2 = A1; //coude
int FlexPin3 = A2; //genou
int FlexPin4 = A3; //genou
 
// CALIBRAGE FLEXS
 
int rawMinFlex1 = 670;
int rawMaxFlex1 = 800;
 
int rawMinFlex2 = 598;
int rawMaxFlex2 = 780;
 
int rawMinFlex3 = 666;
int rawMaxFlex3 = 750;
 
int rawMinFlex4 = 565;
int rawMaxFlex4 = 690;
 
// PIN RESISTANCE PHOTOSENSIBLE
 
int PhotoPin = A8;
 
// FLEXIBLE STRETCH (respiration)
 
int flexResp = A5;    // Sensor connected to analog pin A0
 
// CALIBRAGE RESPIRATION
// distance du capteur respiration pour calibrage d'origine : 11,5cm
 
int rawMinResp = 700;
int rawMaxResp = 840;
 
// INIT DHT12
 
#include <DHT12.h> 
// Set dht12 i2c comunication on default Wire pin
DHT12 dht12;
 
// INIT PULSESENSOR
 
const int PIN_INPUT = A7;
int UpperThreshold = 600;
int LowerThreshold = 500;
int reading = 0;
float BPM = 0.0;
bool IgnoreReading = false;
bool FirstPulseDetected = false;
unsigned long FirstPulseTime = 0;
unsigned long SecondPulseTime = 0;
unsigned long PulseInterval = 0;
const unsigned long delayTime = 10;
const unsigned long delayTime2 = 200;
const unsigned long baudRate = 115200;
unsigned long previousMillis = 0;
unsigned long previousMillis2 = 0;
 
// ECRIRE SD INIT
 
#include <SPI.h>
#include <SD.h>
 
File data;
 
void setup(){
  Serial.begin(baudRate);
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
 
  SD.begin(53); //INIT SD PRINT
  
 
  pinMode(FlexPin1, INPUT);
  pinMode(FlexPin2, INPUT);
  pinMode(FlexPin3, INPUT);
  pinMode(FlexPin4, INPUT);
  
  dht12.begin();
 
}
 
void loop(){
	// Get current time
	unsigned long currentMillis = millis();
 
	// First event
	if(myTimer1(delayTime, currentMillis) == 1){
		reading = analogRead(PIN_INPUT); 
		// Heart beat leading edge detected.
		if(reading > UpperThreshold && IgnoreReading == false){
			if(FirstPulseDetected == false){
				FirstPulseTime = millis();
				FirstPulseDetected = true;
			  }
			else{
				SecondPulseTime = millis();
				PulseInterval = SecondPulseTime - FirstPulseTime;
				FirstPulseTime = SecondPulseTime;
			}
			IgnoreReading = true;
			digitalWrite(LED_BUILTIN, HIGH);
		}
		// Heart beat trailing edge detected.
		if(reading < LowerThreshold && IgnoreReading == true){
			IgnoreReading = false;
			digitalWrite(LED_BUILTIN, LOW);
		}  
	    // Calculate Beats Per Minute.
	    BPM = (1.0/PulseInterval) * 60.0 * 1000;
	}
 
	// Second event
	if(myTimer2(delayTime2, currentMillis) == 1){
		int flexADCa = analogRead(FlexPin1);
	    float angleA = map(flexADCa,rawMaxFlex1,rawMinFlex1,0,90);
	
	    int flexADCb = analogRead(FlexPin2);
	    float angleB = map(flexADCb,rawMaxFlex2,rawMinFlex2,0,90);
	
	    int flexADCc = analogRead(FlexPin3);
	    float angleC = map(flexADCc,rawMaxFlex3,rawMinFlex3,0,90);
	
	    int flexADCd = analogRead(FlexPin4);
	    float angleD = map(flexADCd,rawMaxFlex4,rawMinFlex4,0,90);
	   
	    //RECUPERATION DATA DHT12
	    float h12 = dht12.readHumidity();
	    float t12 = dht12.readTemperature();
	
	    //RECUPERATION Respiration
		int rawResp = analogRead(flexResp);
		int calcResp = map(rawResp, rawMinResp, rawMaxResp, 500, 0);
		float resp = calcResp/100.0;
	
		//RECUPERATION Résistance photosensible
	    int lum = analogRead(PhotoPin);
	
	    //RECUPERATION ACCELEROMETRE ADXL335 et calculs
	    // ADXL335 :: lire valeurs pins
		int xRaw = analogRead(xPin);
		int yRaw = analogRead(yPin);
		int zRaw = analogRead(zPin);
	    
		// ADXL335 :: convertir valeurs brutes en 'millième de G"
		long xScaled = map(xRaw, xRawMin, xRawMax, 3000, -3000);
		long yScaled = map(yRaw, yRawMin, yRawMax, 3000, -3000);
		long zScaled = map(zRaw, zRawMin, zRawMax, -3000, 3000);
	  
		// ADXL335 :: remise à l'échelle en G
		float xAccel = xScaled / 1000.0;
		float yAccel = yScaled / 1000.0;
		float zAccel = zScaled / 1000.0;
	
		// TEMPS
		time = millis();
		Serial.print(time); //temps (millisecondes)
		Serial.print(" ; ");
		
		Serial.print(BPM); // print BPM
		Serial.print(" ; ");
	
		Serial.print(String(angleA) + " ; "); //angle coude (degrés)
		Serial.print(String(angleB) + " ; "); //angle coude (degrés)
		Serial.print(String(angleC) + " ; "); //angle genou (degrés)
		Serial.print(String(angleD) + " ; "); //angle genou (degrés)
		
		Serial.print(String(h12) + " ; "); // humidité (pourcentage)
		Serial.print(String(t12) + " ; "); //température (degrés celsius)
		
		Serial.print(float(resp)); //respiration (mouvement)
		Serial.print(" ; ");
		
		Serial.print(lum,DEC); //luminosité (sans échelle)
		Serial.print(" ; ");
	        
	    Serial.print(xAccel); //accéléromètre axe X (G)
		Serial.print(" ; ");
		Serial.print(yAccel); //accéléromètre axe Y (G)
		Serial.print(" ; ");
		Serial.println(zAccel); //accéléromètre axe Z (G)
	
	    data = SD.open("data.txt", FILE_WRITE);
	
		data.print(time);
		data.print(" ; ");
		data.print(BPM);
		data.print(" ; ");
		data.print(String(angleA) + " ; ");
		data.print(String(angleB) + " ; ");
		data.print(String(angleC) + " ; ");
		data.print(String(angleD) + " ; ");
		data.print(String(h12) + " ; ");
		data.print(String(t12) + " ; ");
		data.print(resp,DEC);
		data.print(" ; ");
		data.print(lum,DEC);
		data.print(" ; ");
		data.print(xAccel);
		data.print(" ; ");
		data.print(yAccel);
		data.print(" ; ");
		data.println(zAccel);
	
		data.close();
	}
}
 
// First event timer
int myTimer1(long delayTime, long currentMillis){
	if(currentMillis - previousMillis >= delayTime){previousMillis = currentMillis;return 1;}
	else{return 0;}
}
 
// Second event timer
int myTimer2(long delayTime2, long currentMillis){
	if(currentMillis - previousMillis2 >= delayTime2){previousMillis2 = currentMillis;return 1;}
	else{return 0;}
}

Raw data

Here is an example of the data then stored on the microSD card in the DATA.TXT file:

1001	 inf 	33	41	-21	-30	38,6	29,2	0,930000019	724	-10,9	-0,85	1,24
1251	 inf 	32	40	-21	-31	38,6	29,2	0,860000038	725	-11,73	-0,9	1,16
1501	 inf 	30	40	-22	-31	38,6	29,2	0,830000019	724	-12,13	-0,94	1,2
1751	 inf 	31	44	-26	-27	38,6	29,2	0,75	723	-2,43	-0,85	1,41
2001	 inf 	45	56	-24	-15	38,6	29,2	0,720000029	682	-2,47	-0,9	1,37
2253	 inf 	51	62	-25	-15	38,6	29	0,680000019	682	-2,51	-0,9	1,37
2501	 inf 	66	72	-24	-15	38,6	29	0,65	703	-2,51	-0,9	1,24
2751	 inf 	49	53	-24	-15	38,6	29	0,610000038	724	-2,51	-0,85	1,28
3001	 inf 	32	41	-25	-15	38,6	29	0,579999971	726	-2,51	-0,81	1,2
3251	 inf 	29	39	-24	-16	38,6	29	0,5	725	-2,51	-0,85	1,24
3501	 inf 	29	39	-24	-16	38,6	29	0,430000019	726	-2,56	-0,9	1,28
3751	17,48	29	39	-24	-16	38,6	29	0,4	727	-10,76	-0,85	1,28
4001	17,48	29	38	-25	-16	38,6	29	0,360000014	726	-11,43	-0,85	1,28
4253	17,48	28	38	-25	-16	38,5	29	0,360000014	728	-11,82	-0,85	1,33
4501	98,2	28	38	-25	-16	38,5	29	0,360000014	726	-12,22	-0,85	1,33

EEG data

Concurrently, we ran an EEG on people’s head to capture their brain waves. For this, we used the OpenBCI Cyton (8-channels).

The raw data we captured was in this format:

Time:250Hz,Epoch,C3,Cz,C4,P3,Pz,P4,O1,O2,Channel 9,Channel 10,Channel 11,Event Id,Event Date,Event Duration
0.0000000000,0,18030.5937500000,-84601.4843750000,42789.2617187500,21799.2988281250,12438.0302734375,10542.4677734375,34692.2304687500,45794.6562500000,-0.0700000003,0.9360000491,0.1880000085,,,
0.0040000000,0,18036.9414062500,-84472.8515625000,42803.7695312500,21794.0234375000,12437.0693359375,10544.9042968750,34684.7890625000,45784.1484375000,-0.0700000003,0.9360000491,0.1880000085,,,
0.0079999999,0,18026.9726562500,-84446.4296875000,42794.0234375000,21789.5742187500,12434.9453125000,10533.9970703125,34673.5664062500,45784.2148437500,-0.0700000003,0.9360000491,0.1880000085,,,