I'm building an air harp - a musical instrument with individual notes triggered by waving a hand (or finger) over proximity sensors. I have a prototype working with 6 VL53L0x sensors connected to a Wemos D1 mini through a TCA9548A mux, using Adafruit VL53L0x library, with sound generation from an Adafruit music maker shield. It's working fairly well, but ... the lag time between triggering a sensor and note generation is quite noticeable. I've timed the calls to "rangingTest()" - 25-30 msec is used for each sensor. This is too much for a real-time instrument, particularly since I want to expand to 12 or more sensors. I've researched the sensor, and this is a typical response time; apparently it's possible to get it down to about 20 msecs, but that's still too slow for my application. Can anyone suggest alternatives? I'm open to any type of sensor that will give speedy on/off responses, with sensors placed about 3 inches apart. Thanks for any advice!
Post your code
#include "Adafruit_VL53L0X.h"
#include "Note.h"
#include <SoftwareSerial.h>
//#define TIMING 1 // define this to perform and print sensor timing
#define NUM_SENSORS 6
// objects for the vl53l0x
Adafruit_VL53L0X lox[NUM_SENSORS];
// Select I2C BUS
void TCA9548A(uint8_t bus) {
Wire.beginTransmission(0x70); // TCA9548A address
Wire.write(1 << bus); // send byte to select bus
Wire.endTransmission();
//Serial.print(bus);
}
// MIDI Setup
#define VS1053_RESET 9 // This is the pin that connects to the RESET pin on VS1053
// If you have the Music Maker shield, you don't need to connect the RESET pin!
#define VS1053_BANK_MELODY 0x79
#define VS1053_GM1_HARP 47
#define MIDI_NOTE_ON 0x90
#define MIDI_NOTE_OFF 0x80
SoftwareSerial VS1053_MIDI(D0, D3); // TX only, do not use the 'rx' side
#define NOTE_TRIGGER_DISTANCE 200
Note* notes[NUM_SENSORS];
VL53L0X_RangingMeasurementData_t measure[NUM_SENSORS]; // this holds the measurement
void read_all_sensors() {
#ifdef TIMING
uint32_t startTime = millis();
#endif
for (uint8_t sensNum = 0; sensNum < NUM_SENSORS; ++sensNum)
{
uint16_t sensorDist;
TCA9548A(sensNum);
lox[sensNum].rangingTest(&measure[sensNum], false); // pass in 'true' to get debug data printout!
if (measure[sensNum].RangeStatus != 4) { // if not out of range
sensorDist = measure[sensNum].RangeMilliMeter;
if (sensorDist < NOTE_TRIGGER_DISTANCE)
{
notes[sensNum]->turnNoteOn(0, 127);
}
else
{
notes[sensNum]->turnNoteOff(0, 127);
}
} else {
notes[sensNum]->turnNoteOff(0, 127);
}
}
#ifdef TIMING
uint16_t diffTime = millis() - startTime;
uint16_t timePerSensor = diffTime / NUM_SENSORS;
Serial.println(timePerSensor);
#endif
}
void setup() {
Serial.begin(9600);
// wait until serial port opens for native USB devices
while (! Serial) {
delay(1);
}
delay(1000);
// Start I2C communication with the Multiplexer
Wire.begin();
// Sensors
for (uint8_t sensNum = 0; sensNum < NUM_SENSORS; ++sensNum)
{
TCA9548A(sensNum);
if (!lox[sensNum].begin()) {
Serial.print(F("Failed to boot VL53L0X "));
Serial.println(sensNum);
while (1);
}
// lox[sensNum].setMeasurementTimingBudgetMicroSeconds(18000);
Serial.print("Start sensor ");
Serial.println(sensNum);
}
// MIDI
Serial.println(F("Starting MIDI..."));
VS1053_MIDI.begin(31250);
pinMode(VS1053_RESET, OUTPUT);
digitalWrite(VS1053_RESET, LOW);
delay(10);
digitalWrite(VS1053_RESET, HIGH);
delay(10);
midiSetChannelBank(0, VS1053_BANK_MELODY);
midiSetInstrument(0, VS1053_GM1_HARP);
midiSetChannelVolume(0, 127);
// initialize Pentatonic notes
notes[0] = new Note(0, 61);
notes[1] = new Note(1, 63);
notes[2] = new Note(2, 66);
notes[3] = new Note(3, 68);
notes[4] = new Note(3, 70);
notes[5] = new Note(3, 73);
}
void loop() {
read_all_sensors();
}
// MIDI routines
/***************************************************
This is an example for the Adafruit VS1053 Codec Breakout
Designed specifically to work with the Adafruit VS1053 Codec Breakout
----> https://www.adafruit.com/products/1381
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, all text above must be included in any redistribution
****************************************************/
void midiSetInstrument(uint8_t chan, uint8_t inst) {
if (chan > 15) return;
inst --; // page 32 has instruments starting with 1 not 0 :(
if (inst > 127) return;
VS1053_MIDI.write(MIDI_CHAN_PROGRAM | chan);
VS1053_MIDI.write(inst);
}
void midiSetChannelVolume(uint8_t chan, uint8_t vol) {
if (chan > 15) return;
if (vol > 127) return;
VS1053_MIDI.write(MIDI_CHAN_MSG | chan);
VS1053_MIDI.write(MIDI_CHAN_VOLUME);
VS1053_MIDI.write(vol);
}
void midiSetChannelBank(uint8_t chan, uint8_t bank) {
if (chan > 15) return;
if (bank > 127) return;
VS1053_MIDI.write(MIDI_CHAN_MSG | chan);
VS1053_MIDI.write((uint8_t)MIDI_CHAN_BANK);
VS1053_MIDI.write(bank);
}
// Note.h
#ifndef NOTE_H
#define NOTE_H
class Note {
uint8_t num;
bool noteOn;
uint8_t pitch;
public:
Note(uint8_t numIn, uint8_t pitchIn);
void turnNoteOn(uint8_t chan, uint8_t vel);
void turnNoteOff(uint8_t chan, uint8_t vel);
};
#endif
// Note
#include "Note.h"
Note::Note(uint8_t numIn, uint8_t pitchIn)
{
num = numIn;
pitch = pitchIn;
}
void Note::turnNoteOn(uint8_t chan, uint8_t vel)
{
if (!noteOn)
{
if (chan > 15) return;
if (pitch > 127) return;
if (vel > 127) return;
VS1053_MIDI.write(MIDI_NOTE_ON | chan);
VS1053_MIDI.write(pitch);
VS1053_MIDI.write(vel);
noteOn = true;
}
}
void Note::turnNoteOff(uint8_t chan, uint8_t vel)
{
if (noteOn)
{
if (chan > 15) return;
if (pitch > 127) return;
if (vel > 127) return;
VS1053_MIDI.write(MIDI_NOTE_OFF | chan);
VS1053_MIDI.write(pitch);
VS1053_MIDI.write(vel);
noteOn = false;
}
}
Please note, I did post my code as requested, and I'll be happy to get comments on it. But every bit as happy to get suggestions for alternative devices WITHOUT digging into what I'm currently doing!!
Describe what sort of sensor you are thinking about, and what type of response you need.
If a "make or break" response will do, then an LED (or laser diode)/photodiode light gate can provide a digital output in nanoseconds. Sharp digital distance sensors respond in < 3 msec.
If you want a distance measurement or gesture recognition, that will be a lot slower, and a significant amount of time will be spent simply communicating with the sensor.
I made one using IR emitters and receivers back in about 2001. Some time later I was showing it at an exhibition and recorded this video.
https://vimeo.com/manage/videos/35657529
I also used some blue LEDs which was rare back then to indicate a beam had been broken. The emitters and LEDs were in the bottom arm and the receivers were in the top arm. I multiplexed these so that only one emitter was on at one time and only one receiver was being looked at any one time. This stopped any spill over that otherwise would have happened due to the wide divergence of the beams.
It was built using a PIC processor and the software was written in assembler. There was no noticeable latency. You could specify a scale or a chromatic series of notes and sent out by MIDI to a MIDI samples player.
Seriously, that wasn't you playing it, was it?
Well I was the first person playing you never got to see my face. The second person was one of the exhibitors, and the third was the director of the building we were in. The thing is that he recorded the beat box noises so he was in effect playing himself.
I edited this down from an "official" video of the event, which was a creative arts event. This had performances of song, dance and gymnastics. I was part of the Manchester MadLabs team putting on an exhibition around this event.
I suspected that even in 2001 you would have looked - well, somewhat more mature.
I thought this thread might have been talking about the "laser harp" but apparently not. I can't see anything blatantly obvious in the code, but have not played with these sensors as yet so can't really offer anything. I am inclined to think the laser harp is much more "sexy" and possibly even easier to implement!
Why? because it would be simpler than using multiple sensors, one for each note.
I don't see that function in your code? EDIT: oh, it's in the Adafruit library.
Try this in setup():
// Start I2C communication with the Multiplexer
Wire.begin();
Wire.setClock(400000);
If that clock speed makes a hopeful improvement, that means the i²c bus must be contributing to the latency problem. I seem to remember there is more that could be done to reduce this, because you are currently using an i²c multiplexer, and obviously this adds to the latency. I if I remember right, it is possible, but tricky, to connect multiple VL53L0x sensors without using a multiplexer. Also, are you using continuous ranging mode and high speed mode?
Maybe some useful ideas in this topic from the Pololu forum:
Thanks very much for all the useful information, guys! I love the LED harp, Mike, especially the cool indicator LEDs! And the suggestion of using an IR or Laser emitter/detector pair is something I will be thinking about for the future.
PaulRB, the pointer to the Pololu forum post was a lifesaver! I replaced the Adafruit VL53L0x library with the Pololu one, and used the "startContinuous()" function as suggested in the post. I'm now getting a total time of 20-30 msecs for all six sensors, which is giving my harp a nice, responsive feel. I had previously tried with and without the mux, and it turns out the delay there is negligible, which allows me to keep the number of digital pins required down to the small number available on the NodeMCU. I think I'm getting close!
I did run into one other potential "gotcha" as I was continuing to research. I stumbled on an article that talks about problems with running i2c over cables: Taking The Leap Off Board: An Introduction To I2C Over Long Wires | Hackaday which has me a bit nervous. I thought I'd ask for advice on this before I start cutting and soldering to extend my 6-sensor 8-inch long prototype to the 12-sensor 5-foot long real thing. Here's a picture of the (soon to be wall-mounted) ceramic sculpture which will house the harp:
It's a total of 7 1/2 feet long. The harp will be contained in the central portion, with one sensor centered between each pair of diamond-shaped modules. The user will wave their hand approximately 4-8 inches above the body to trigger notes. My original plan was to house the NodeMCU and muxes (I'll need 2 of them) in the head, requiring a cable run of a bit over 5 feet. I can probably fit these into the middle segments of the body, cutting the distance in half. Is this going to get me in trouble? If so, I can try the things mentioned in the article, but thought I'd ask for some "best practices" advice from the experts here! Thanks very much!
Probably not. But officially you should keep I2C down to about two feet.
This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.