A VL53L0X time-of-flight sensor measures hand distance from 30 mm to about 1.2 m. We map that reading to both pitch and filter sweep, driving a piezo buzzer (for quick tests) or a 3.5 mm jack feeding a powered speaker. Optional MIDI-over-USB lets you pipe the data into a DAW.
| Connection | Arduino | Notes |
|---|---|---|
| VL53L0X VIN | 5V | Module regulates down to 2.8 V internally. |
| VL53L0X GND | GND | Keep wiring short; ToF sensors hate noise. |
| VL53L0X SDA | SDA (D2) | On Micro/Leonardo, SDA = D2. |
| VL53L0X SCL | SCL (D3) | SCL = D3 on the Micro. |
| Buzzer + | D9 | Use a 100 Ω resistor in series for speakers. |
| Buzzer − | GND | Share ground with audio jack sleeve. |
| Pot wiper | A0 | Outer legs to 5V/GND. Adjusts musical scale. |
| 3.5 mm jack tip | D9 via 100 Ω + 10 µF | AC-couple for line-level output. |
#include <Wire.h>
#include <Adafruit_VL53L0X.h>
#ifdef USBCON
#include <MIDIUSB.h>
#endif
constexpr bool MIDI_ENABLE = false;
constexpr uint8_t AUDIO_PIN = 9;
constexpr uint8_t SCALE_POT = A0;
constexpr uint16_t MIN_MM = 40;
constexpr uint16_t MAX_MM = 600;
Adafruit_VL53L0X lox = Adafruit_VL53L0X();
uint16_t currentNote = 0;
const uint8_t MAJOR_SCALE[] = {0, 2, 4, 5, 7, 9, 11};
const uint8_t MINOR_PENTA[] = {0, 3, 5, 7, 10};
const uint8_t CHROMATIC[] = {0,1,2,3,4,5,6,7,8,9,10,11};
void setup() {
pinMode(AUDIO_PIN, OUTPUT);
analogWrite(AUDIO_PIN, 0);
Wire.begin();
if (!lox.begin()) {
while (true) {
tone(AUDIO_PIN, 220, 200);
delay(400);
}
}
}
void loop() {
VL53L0X_RangingMeasurementData_t measure;
lox.rangingTest(&measure, false);
if (measure.RangeStatus != 4) {
uint16_t dist = constrain(measure.RangeMilliMeter, MIN_MM, MAX_MM);
float norm = 1.0 - ((dist - MIN_MM) / float(MAX_MM - MIN_MM));
uint16_t freq = mapFrequency(norm);
if (!MIDI_ENABLE) {
tone(AUDIO_PIN, freq, 10);
} else {
#ifdef USBCON
if (freq != currentNote) {
if (currentNote) midiNoteOff(currentNote);
midiNoteOn(freq);
currentNote = freq;
}
#endif
}
}
delay(5);
}
uint16_t mapFrequency(float ratio) {
ratio = constrain(ratio, 0.0, 1.0);
int scaleChoice = map(analogRead(SCALE_POT), 0, 1023, 0, 2);
const uint8_t* scale;
size_t scaleLen;
switch (scaleChoice) {
case 0: scale = MINOR_PENTA; scaleLen = sizeof(MINOR_PENTA); break;
case 1: scale = MAJOR_SCALE; scaleLen = sizeof(MAJOR_SCALE); break;
default: scale = CHROMATIC; scaleLen = sizeof(CHROMATIC); break;
}
float noteSpan = ratio * (scaleLen * 5); // 5 octaves of fun
int index = int(noteSpan) % scaleLen;
int octave = int(noteSpan) / int(scaleLen);
int midiNote = 48 + scale[index] + octave * 12; // base C3
return midiToFreq(midiNote);
}
uint16_t midiToFreq(int note) {
return uint16_t(440.0 * powf(2.0, (note - 69) / 12.0));
}
#ifdef USBCON
void midiNoteOn(int note) {
midiEventPacket_t noteOn = {0x09, 0x90, (uint8_t)note, 0x64};
MidiUSB.sendMIDI(noteOn);
MidiUSB.flush();
}
void midiNoteOff(int note) {
midiEventPacket_t noteOff = {0x08, 0x80, (uint8_t)note, 0x00};
MidiUSB.sendMIDI(noteOff);
MidiUSB.flush();
}
#endif
Use the potentiometer to slide between scales mid-performance. For example, dial fully counter-clockwise for moody minor pentatonic drones, center for a bright major scale, and clockwise for chromatic sweeps.