Adafruit Music Maker VS1053 feather wing MIDI working

After a bit of fiddling, I got MIDI working on the Adafruit Music Maker VS1053 feather wing.

The hardware Serial1 port needs to be used, and set to 31250. This doesn’t work out of the box, so the way around it is to assign a baud rate and then change that to 31250. I figured this out by looking at another example that showed how to use custom baud rates.

// Solder closed jumper on bottom!

// See http://www.vlsi.fi/fileadmin/datasheets/vs1053.pdf Pg 31
#define VS1053_BANK_DEFAULT 0x00
#define VS1053_BANK_DRUMS1 0x78
#define VS1053_BANK_DRUMS2 0x7F
#define VS1053_BANK_MELODY 0x79

// See http://www.vlsi.fi/fileadmin/datasheets/vs1053.pdf Pg 32 for more!
#define VS1053_GM1_SOUND 27

#define MIDI_NOTE_ON  0x90
#define MIDI_NOTE_OFF 0x80
#define MIDI_CHAN_MSG 0xB0
#define MIDI_CHAN_BANK 0x00
#define MIDI_CHAN_VOLUME 0x07
#define MIDI_CHAN_PAN 0x0A
#define MIDI_CHAN_PITCH 0xE0
#define MIDI_EFFECT_CNTRL 0x0C
#define MIDI_EFFECT_LEVEL 0x5B
#define MIDI_CHAN_PROGRAM 0xC0

#define VS1053_MIDI Serial1
#define UARTE0_BASE_ADDR          0x40002000  // As per nRF52840 Product spec - UARTE
#define UART_BAUDRATE_REG_OFFSET  0x524 // As per nRF52840 Product spec - UARTE 
#define UART0_BAUDRATE_REGISTER   (*(( unsigned int *)(UARTE0_BASE_ADDR + UART_BAUDRATE_REG_OFFSET)))

/* from nRF52840 Product spec - a baud rate of 31250 uses a register value of 0x00800000 */
#define BAUDRATE_31250            0x00800000
boolean bPlayOnce = true;

void setup() {

  // NOTE: UART0 is attached to Serial1.  Serial is attached to USB
  VS1053_MIDI.begin(19200);
 
  // original values - used to verify that we are looking at the correct registers
  Serial.print("Original UART0 Baud rate: 0x"); 
  Serial.println(UART0_BAUDRATE_REGISTER,HEX);
 
  // change the baud rate
  UART0_BAUDRATE_REGISTER = BAUDRATE_31250;
   
  // Verify the change
  Serial.print("Updated UART0 Baud rate: 0x"); 
  Serial.println(UART0_BAUDRATE_REGISTER,HEX);
  Serial.println("VS1053 MIDI test");
  
  midiSetChannelBank(0, VS1053_BANK_DRUMS1);
  midiSetChannelVolume(0, 40);
  midiSetInstrument(0, VS1053_GM1_SOUND);
  midiSetPan(0,127);
  midiSetReverbType(0,127);
  midiSetReverbLevel(0,64);
}

void loop() {  
  if(bPlayOnce)
  for (uint8_t i=0; i < 5; i++) {
    if(i%2==0)
      midiSetPan(0,0);
    else
      midiSetPan(0,127);
      delay(5);
    midiNoteOn(0, 56, 127);
    delay(200);
    midiNoteOff(0, 56, 127);
    delay(500);
  }
  bPlayOnce = false;
}

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);  
  delay(10);
  VS1053_MIDI.write(inst);
  delay(10);
}


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);
}

void midiNoteOn(uint8_t chan, uint8_t n, uint8_t vel) {
  if (chan > 15) return;
  if (n > 127) return;
  if (vel > 127) return;
  
  VS1053_MIDI.write(MIDI_NOTE_ON | chan);
  VS1053_MIDI.write(n);
  VS1053_MIDI.write(vel);
}

void midiNoteOff(uint8_t chan, uint8_t n, uint8_t vel) {
  if (chan > 15) return;
  if (n > 127) return;
  if (vel > 127) return;
  
  VS1053_MIDI.write(MIDI_NOTE_OFF | chan);
  VS1053_MIDI.write(n);
  VS1053_MIDI.write(vel);
}

void midiSetPan(uint8_t chan, uint8_t pan) {
  VS1053_MIDI.write(MIDI_CHAN_MSG | chan);
  VS1053_MIDI.write(MIDI_CHAN_PAN);
  VS1053_MIDI.write(pan);
}

void midiSetReverbLevel(uint8_t chan, uint8_t amount) {
  VS1053_MIDI.write(MIDI_CHAN_MSG | chan);
  VS1053_MIDI.write(MIDI_EFFECT_LEVEL);
  VS1053_MIDI.write(amount);
}

void midiSetReverbType(uint8_t chan, uint8_t amount) {
  VS1053_MIDI.write(MIDI_CHAN_MSG | chan);
  VS1053_MIDI.write(MIDI_EFFECT_CNTRL);
  VS1053_MIDI.write(amount); // 0 = auto, 1..127 = increasing reverb room size (I think ?)
}

Maybe this is of interest to the gen3 VS1053 library examples @ScruffR @peekay123 ?

3 Likes

Nice finding and example implementation :+1:

I guess this will only work for Gen3 devices as the way how STM32 sets custom baudrates will obviously differ.

@avtolstoy, would there be any chance to add 31250 (MIDI standard) as selectable baudrate or even implement an API to request a custom baudrate that would return the closes value the controller allows?

1 Like

31250 as a selectable baud rate would be great!

1 Like

Yea, my bad :blush:
I had 31000 in my head for some reason - I don’t know where. I removed that question from my post above.

1 Like

It should be possible to set the baudrate 31250 out of the box for Gen 2 devices. I don’t have the numbers at hand, but the peripheral should be able to run at that baudrate with some error.

As for Gen 3, this is also a supported baudrate, but for some reason we’ve forgotten to include it in the list in our HAL: https://github.com/particle-iot/device-os/blob/develop/hal/src/nRF52840/usart_hal.cpp#L573

I also like to idea of being able to set a generic baudrate across all the platforms, however I’d probably limit the error to some number and if it’s greater fail.

2 Likes

Good to hear that :+1:

Should I file a GitHub "proposition issue" or would you add it to your internal feature-request list?

Also the Gen2 reference docs don't list 31250 as valid option
https://docs.particle.io/reference/device-os/firmware/photon/#begin-


(14400, 28800 and others are missing too :flushed:)
Is there also an enum of allowed baudrates for Gen2 - I don't seem to be able to find it :pensive:

We shouldn't expect people to dig into the sources to find out :wink:

Genius!