”MEMIDION" is MIDI instruments that you can play with DAW software or MIDI controllers.

 

Structure

MEMIDION is a slide whistle that controlled by the micro-controller. The motor with the caterpillar slides the slide whistle. The slide amount is controlled by ToF range finder sensor VL6180.  LED Tape NeoPixel emit light following the amount of slide.

 

DAW Sound Source Mode

The MEMIDION works in two modes, DAW Sound Source Mode and Instrument Mode.

When PC (DAW) is connected to Arduino UNO, it switches to DAW Sound Source Mode.


Send a MIDI signal from the DAW and move MEMIDION.

Arduino USB-MIDI device conversion

Write Moco for LUFA firmware to Arduino and let Arduino recognize it as a MIDI device. 

The detail is right below.

  https://github.com/kuwatay/mocolufa

Arduino UNO receives MIDI signals and sends them to Arduino Pro Mini with SoftwareSerial to control the motor and NeoPixels.

Setting of DAW software Reason

Anything is OK if DAW can output MIDI, I used Reason this time.

Place the external MIDI instrument and select "MocoLUFA" for the device.

 

Operation

Automatic performance as MIDI sound source with DAW is also possible. My breath has to keep blowing....

 

Instrument Mode

When MEMIDION is not connected to the PC (DAW), it operates as a single musical instrument.

It receives MIDI signal from MIDIController via USB Host Shield and controls the motor and NeoPixels with Arduino Pro Mini.

Operation


Arduino IDE Code

I used the following as a USB MIDI library

https://github.com/felis/USB_Host_Shield_2.0

I programmed with reference to the following

https://github.com/felis/USB_Host_Shield_2.0/tree/master/examples/USBH_MIDI/USBH_MIDI_dump

The library for distance sensor VL6180X is below.

https://github.com/pololu/vl6180x-arduino

The library for NeoPixel is below.

https://github.com/adafruit/Adafruit_NeoPixel

#include <usbh_midi.h>
#include <usbhub.h>
#include <SPI.h>

#include <Wire.h>
#include <VL6180X.h>

#include <Adafruit_NeoPixel.h>

#include <SoftwareSerial.h>
SoftwareSerial mySerial(7, 9); // RX, TX

USB Usb;
USBH_MIDI  Midi(&Usb);

uint8_t Note, oldNote;

VL6180X sensor;

int meas;
int aim = 50;
int diff = 6;

#define PIN A3
#define NUM_LEDS 24
#define BRIGHTNESS 30
int num;

int modeState = 0;

Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRBW + NEO_KHZ800);

void setup() {
  Serial.begin(115200);
  mySerial.begin(9600);

  pinMode(8, INPUT);

  Wire.begin();

  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  digitalWrite(2, LOW);
  digitalWrite(3, LOW);
  
  sensor.init();
  sensor.configureDefault();
  sensor.setTimeout(500);

  strip.setBrightness(BRIGHTNESS);
  strip.begin();
  strip.show();

  if (Usb.Init() == -1) {
    while (1); //halt
  }
  
  delay( 200 );
}

void loop() {
  if(modeState != digitalRead(8)){
    modeState = digitalRead(8);
    Serial.print("Mode : ");
    Serial.println(modeState);
  }

  //Instrument Mode
  Usb.Task();
  if (Usb.getUsbTaskState() == USB_STATE_RUNNING && modeState == 0) {
    char buf[20];
    uint8_t bufMidi[64];
    uint16_t  rcvd;

    if (Midi.RecvData( &rcvd,  bufMidi) == 0 && bufMidi[0] == 9) {
      Note = bufMidi[2];
      Serial.print("Instrument Mode : ");
      Serial.println(Note);
      MotorInitialMove();
    }
  }

  //Sound Source Mode
  if (mySerial.available() && modeState == 1){
    Note =  mySerial.read();
    Serial.print("Sound Source Mode : ");
    Serial.println(Note);
    MotorInitialMove();
  }

  //Slide distance setting
  if(Note == 57) aim = 16//A
  if(Note == 58) aim = 23//A#
  if(Note == 59) aim = 35//B
  if(Note == 60) aim = 49//C
  if(Note == 61) aim = 57//C#
  if(Note == 62) aim = 64//D
  if(Note == 63) aim = 75//D#
  if(Note == 64) aim = 83//E
  if(Note == 65) aim = 91; //F
  if(Note == 66) aim = 100; //F#
  if(Note == 67) aim = 106; //G
  if(Note == 68) aim = 111; //G#
  if(Note == 69) aim = 119; //A
  if(Note == 70) aim = 124; //A#
  if(Note == 71) aim = 129; //B
  if(Note == 72) aim = 132; //C
  

  //Distance sensor measurement
  meas = sensor.readRangeSingleMillimeters();
  Serial.print("Range Sensor : ");
  Serial.println(meas);

  //Motor operation
  if(meas > (aim - diff) && meas <  (aim + diff)){
    digitalWrite(2, LOW);
    digitalWrite(3, LOW);
  }else if(meas > aim){
    digitalWrite(2, LOW);
    digitalWrite(3, HIGH);
  }else if(meas < aim){
    digitalWrite(2, HIGH);
    digitalWrite(3, LOW);
  }

  //LED On
  num = map(meas, 16, 132, 3, 20);
  for(int i=num; i<strip.numPixels(); i++) {
    strip.setPixelColor(i, Wheel(255/strip.numPixels() * i));
    //delay(100);
  }
  strip.show();
  
  //LED Off
  strip.clear();

  oldNote = Note;
}

//Initial operation of the motor
void MotorInitialMove() {
  if(Note > oldNote){
    digitalWrite(2, HIGH);
    digitalWrite(3, LOW);
  }else{
    digitalWrite(2, LOW);
    digitalWrite(3, HIGH);
  }
  delay(50);
      
  digitalWrite(2, LOW);
  digitalWrite(3, LOW);
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3,0);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3,0);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0,0);
}