本文へスキップ

レーザーハープ

2025.06
レーザーハープの電子工作

平沢進のレーザーハープ的な電子工作。

音はスピーカーではなくLogicからMIDIで。pythonとarduinoはシリアル通信して、pythonからMIDIをLogicに送っている。

構成

Arduino(光センサ) → シリアル → Python → MIDI → IAC Driver → Logic

Arduino Uno、Nano、ESP32の3種で試した。ビルドと書き込みはarduino-cliをMakefileから叩いている。

BOARD_ESP32 := esp32:esp32:esp32
PORT_ESP32  := /dev/cu.usbserial-21240

upload-esp32: compile-esp32
	$(ARDUINO_CLI) upload -p $(PORT_ESP32) --fqbn $(BOARD_ESP32) $(INPUT)

マイコン側

やっているのは、トグルスイッチでレーザーのON/OFFを切り替えることと、光センサの値をシリアルに流すことだけ。

void loop() {
    bool currentState = digitalRead(switchPin);
    int val1 = analogRead(32);

    if (lastState == HIGH && currentState == LOW) {
        isRunning = !isRunning;
        Serial.println(isRunning ? "START" : "STOP");
        digitalWrite(laserPin, isRunning ? HIGH : LOW);
        delay(200);
    }
    lastState = currentState;

    if (isRunning) {
        Serial.println(val1);
        delay(10);
    }
}

スイッチは INPUT_PULLUP で受けて、押した瞬間(HIGHからLOWへ落ちた瞬間)だけを見ている。押しっぱなしで何度も反転しないようにするのと、接点のばたつきを避けるために200ms待つ。

センサ値は10ms間隔、つまり毎秒100回ほど送っている。

Python側

pyserialで読んで、midoでIAC Driverに流す。

ser = serial.Serial('/dev/cu.usbmodem212101', 9600)
outport = mido.open_output('IAC Driver Bus 1')

絶対値ではなく差分で判定する

光センサの値をしきい値と直接比べる方式にはしていない。部屋の明るさで基準値が動いてしまい、場所を変えるたびに調整が要るため。

代わりに前回の値との差を見て、変化したときだけ音を鳴らす。

THRESHOLD_DIFF = 100

diff1 = val1 - prev_val1

if diff1 > THRESHOLD_DIFF and not note1_on_sent:
    outport.send(Message('note_on', note=60, velocity=100))
    note1_on_sent = True
elif diff1 < -THRESHOLD_DIFF and note1_on_sent:
    outport.send(Message('note_off', note=60))
    note1_on_sent = False

手がビームを横切った瞬間に大きな差が出るので、その立ち上がりでnote_on、抜けた立ち下がりでnote_offになる。環境光はゆっくりしか変化しないので、この方式なら引っかからない。

note1_on_sent のフラグは、ビームを遮っている間ずっとnote_onが送られ続けるのを防ぐためのもの。MIDIは鳴らしっぱなしとレガートの区別がないので、片方を送ったらもう片方が来るまで送らない、という状態を持たせている。

複数弦のバージョンでは、センサの値をカンマ区切りで送り、それぞれに別のノート番号を割り当てている。

参考