Seeking help from the Hive Mind,
This one’s time urgent because we need to be installed in 2 weeks - so all ideas much appreciated!
Building a project with 17 distance sensors (currently HR SC04 Ultrasonic sensors) installed on a 3m high concentric floating ceiling, facing towards the floor. The floating ceiling consists of 2 concentric circles, 1x 8m diameter, and 1x 4 m diameter inside. The sensors are to be cabled from the centre outwards, think spider web of sensor cables, 12x 4m length, 4x 2m length. Photos attached.
The intention is that people who walk/dance under these sensors trigger sound samples and audio effects (arduino Mega>MIDI over serial>Ableton live 9). Arduino code below.
Some sensors are reading binary triggered/not trigger within the distance range, and sending MIDI NoteOnOff messages, which trigger banks of one-shot sound samples.
The rest are reading variable distances (person standing under the sensor, crouching down, and reaching up), and sending MIDI Control Change messages to Ableton to control audio effects, spatial panning, volume etc.
At the moment the ultrasonic sensors are triggering reliably over a distance only with certain materials (e.g. dense with a 90degree angle to return to trigger’s echo to sensor), which is expected but not ideal for triggering sensors when people walk underneath.
Looking at swapping these out for infrared sensors to increase reliability of sensor trigger. We’ve been suggested to consider the ‘Sharp GP2Y0A710K Distance Sensor (100-550cm) SEN0085’ sensor, which I’ve been told will be more reliable for detecting humans underneath. My question is, will the variable range be reliable/smooth enough to control MIDI control messages?
Or are there any other sensors we should be considering for this desired effect?
Arduino code below:
//3pin library
#include "NewPing.h"
#define TOTAL_NUMBER_OF_SENSORS 3 //Total Number of sonar sensors. YOU MUST CHANGE THIS IF YOU ADD MORE SENSORS
#define NUMBER_OF_NOTE_SENSORS 1 //Num of NOTE sensors. YOU MUST CHANGE THIS IF YOU WANT TO CHANGE THE NUMBER OF NOTE SENSORS (We use this and TOTAL_NUMBER_OF_SENSORS to know how many controllers we have)
#define NUMBER_OF_CONTROLLER_SENSORS TOTAL_NUMBER_OF_SENSORS - NUMBER_OF_NOTE_SENSORS //Calculated from the previous two
#define PIN_FOR_SENSOR_1 2
#define PIN_FOR_SENSOR_2 3
#define PIN_FOR_SENSOR_3 4
#define MAX_DISTANCE 400 // The upper limit to what we want to sense
#define FLOOR_DISTANCE 200 // Anything above this distance is considered to be the floor. YOU MUST CHANGE THIS IF THE DISTANCE BETWEEN THE SENSORS AND FLOORS CHANGE
//trigger first, echo second
NewPing sonar[TOTAL_NUMBER_OF_SENSORS] = { // Sensor object array. IF ADDED MORE 'PIN_FOR_SENSOR_X' ENTRIES YOU MUST ADD THE SAME HERE
NewPing (PIN_FOR_SENSOR_1, PIN_FOR_SENSOR_1, MAX_DISTANCE), // Each sensor's trigger pin, echo pin, and max distance to ping.
NewPing (PIN_FOR_SENSOR_2, PIN_FOR_SENSOR_2, MAX_DISTANCE),
NewPing (PIN_FOR_SENSOR_3, PIN_FOR_SENSOR_3, MAX_DISTANCE),
};
uint8_t MIDI_CC = 0b10110000; // 176 = Control Change and Midi Channel 1
uint8_t MIDI_NOTE = 0b10010001; // 145 = Note On and Midi Channel 2 (second nibble is Channel)
/*
* Note sensor things start
*/
uint8_t notes[NUMBER_OF_NOTE_SENSORS][4] = { //4 is for the number of samples in each bank
{61, 62, 63, 64}, //We need a line here for each NUMBER_OF_NOTE_SENSORS
// {71,72,73,74},
// {81,82,83,84},
};
int noteToPlayForSensor[NUMBER_OF_NOTE_SENSORS] = {0}; //num of sensors that are noteOnOffs, everything starts at 0
bool noteIsPlayingForSensor[NUMBER_OF_NOTE_SENSORS] = {false}; //A way to keep track of whether a note is playing
/*
* Note sensor things end
*/
/*
* Controller sensor things start
*/
uint8_t controller[NUMBER_OF_CONTROLLER_SENSORS] = {21, 23}; //Each sensor output is tied to a controller. YOU MUST ADD MORE NUMBERS IF YOU HAVE MORE CONTROLLERS
int previousControllerDistance[NUMBER_OF_CONTROLLER_SENSORS] = {0};
int greaterThanFloorDistanceCounter[NUMBER_OF_CONTROLLER_SENSORS] = {0};
/*
* Controller sensor things end
*/
void sendMIDI(uint8_t statusByte, uint8_t data1, uint8_t data2) {
Serial.write(statusByte);
Serial.write(data1);
Serial.write(data2);
// Serial.print(statusByte);
// Serial.print(" ");
// Serial.print(data1);
// Serial.print(" ");
// Serial.println(data2);
}
void setup() {
Serial.begin(115200);
}
void loop() {
for (uint8_t currentSensor = 0; currentSensor < TOTAL_NUMBER_OF_SENSORS; ++currentSensor) {
delay(50);
int distance = sonar[currentSensor].ping_cm();
if (currentSensor < NUMBER_OF_NOTE_SENSORS) { //if it's a note sensor
if (distance >= FLOOR_DISTANCE) { // if ping distance is greater or equal to floor
if (noteIsPlayingForSensor[currentSensor]) { //if note was playing
sendMIDI(MIDI_NOTE, notes[currentSensor][noteToPlayForSensor[currentSensor]], 0); //Send noteOff
noteToPlayForSensor[currentSensor] = (noteToPlayForSensor[currentSensor] + 1) % 4; // set the next note to play (modulo is num of samples in each bank)
noteIsPlayingForSensor[currentSensor] = false; // set noteIsPlayingForSensor to false
}
}
else if (distance <= 0) { //distance is 0 if we get no ping back
// do nothing
}
else {
if (noteIsPlayingForSensor[currentSensor]) { //we check the current sensor state here since the last ping
//do nothing because a person is still standing underneath
} else {
sendMIDI(MIDI_NOTE, notes[currentSensor][noteToPlayForSensor[currentSensor]], 127);
noteIsPlayingForSensor[currentSensor] = true;
}
}
}
else { //else it's not a note sensor so is a controller sensor
uint8_t currentControllerSensor = currentSensor - NUMBER_OF_NOTE_SENSORS; //We can't use currentSensor because there are note sensors first. So we subtract the number of note sensors
int mappedDistance = map(distance, 50, FLOOR_DISTANCE, 127, 0); //We need to map our numbers between 127 and 0
int constrainedMappedDistance = constrain(mappedDistance, 0, 127); //Mapping still allows numbers beyond what we need, so we constrain
//if distance is less than floor
//store this distance
//set greaterThanFloorDistanceCounter to 0
//send this distance over midi
//else
//increase greaterThanFloorDistanceCounter by 1
//if greaterThanFloorDistanceCounter > 4
//send floor distance over midi
//else send previous stored value
if (distance < FLOOR_DISTANCE){
previousControllerDistance[currentControllerSensor] = constrainedMappedDistance;
greaterThanFloorDistanceCounter[currentControllerSensor] = 0;
sendMIDI(MIDI_CC, controller[currentControllerSensor], constrainedMappedDistance); //send the constrainedMappedDistances
} else {
greaterThanFloorDistanceCounter[currentControllerSensor] = greaterThanFloorDistanceCounter[currentControllerSensor] + 1;
if (greaterThanFloorDistanceCounter[currentControllerSensor] > 3){
sendMIDI(MIDI_CC, controller[currentControllerSensor], 0);
} else {
sendMIDI(MIDI_CC, controller[currentControllerSensor], previousControllerDistance[currentControllerSensor]); //send the constrainedMappedDistances
}
}
}
}
}