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:
- Videos related to this installation can be found here
- Texts related to this project can be found here
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,33EEG 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,,,