Web Synth with Particle Photon Part 2: Firmware

Continuing the series on hooking up your synthesizer to the Internet, this post focuses on the firmware that runs on the Particle Photon itself. If you are used to writing Arduino code, this will look very familiar. There are differences though, so keep an open mind when you see something new.

Setup

It’s not worth walking through the Particle IDE setup since it is different for each platform and pretty straightforward.

If you haven’t done it already, the Photon device setup is also very easy using the smartphone app.

The Code

Once everything is installed and you’re all logged in, this code is all you need to make the Photon relay notes from Web API to MIDI port:


#include "application.h"

void setup() {

  Serial1.begin(31250);

  bool success = Particle.function("playnote", funcPlayNote);

}

void loop() {


}

void noteOn(byte note, byte velocity, byte channel) {

  Serial1.write(0x90); // | channel);
  //delay(1);
  Serial1.write(note);
  //delay(1);
  Serial1.write(velocity);

}


// Take a note name like "D0" or "C#2" and play it as a brief MIDI note
// Note:  this function will return an error at any point if a
//        formatting error is detected.
int funcPlayNote(String note) {

  char key = 0;
  String octave = "";
  bool isSharp = false;

  note.toUpperCase();

  if (note.length() == 2) {

      key = note.charAt(0);
      octave = note.charAt(1);

  } else if (note.length() == 3) {

    key = note.charAt(0);

    if (note.charAt(1) == '#') {
      isSharp = true;
    } else return -1;   // three character input but not sharp!

    octave = note.charAt(2);

  } else return -1;   // format error, too short or too long


  // find the white key value
  switch(note.charAt(0))
  {
    case 'A':
      key = 9;
      break;
    case 'B':
      key = 11;
      break;
    case 'C':
      key = 0;
      break;
    case 'D':
      key = 2;
      break;
    case 'E':
      key = 4;
      break;
    case 'F':
      key = 5;
      break;
    case 'G':
      key = 7;
      break;
    default:
      return -1;    // not a note letter, bad format
  }

  // bump by 1/2 step if sharp
  if (isSharp == true)
    key++;

  key += octave.toInt() * 12;   // multiply in the octave
  // Note: if the octave is non-numeric it will default to the 0th octave!

  noteOn(key, 100, 0);

  delay(200);

  noteOn(key, 0, 0);  // note off

  return 0;
}

Breaking it down


#include "application.h"

void setup() {

  Serial1.begin(31250);

  bool success = Particle.function("playnote", funcPlayNote);

}

void loop() {


}

First is the standard include. Instead of #include "Arduino.h", Particle uses a more generic sounding "application.h" If it makes you feel better, "Arduino.h" is aliased to "application.h" anyway, so you can use that instead if you want. But it’s best not to fool yourself into thinking this is an Arduino because it’s not. More on that later.

(Note: the original code used #include <application.h>, but it’s difficult to get brackets like that to play nice with WordPress and code blocks. The brackets are more traditional since this is a built-in library, but either way works.)

As usual, void setup() is used to run initialization code. In this case the hardware serial port (Serial1) is set up to 31.250kbps, the MIDI baud rate.

The second command is unique to the Particle system: this line registers a function called “funcPlayNote” to the Particle Web API and exposes it as a function called “playnote”. In the firmware, this is the key magic of Particle.

That function is defined just like any other C function:

int funcPlayNote(String note)

The main limitation being that according to the Particle documentation, it must take a String argument and return int.

The final part is the loop() function. It’s empty! This may seem unusual for two reasons: Not only does this sketch seem to do nothing, it doesn’t even call an ‘update’ function for the web interface stuff. This is all taken care of by the Particle firmware behind the scenes. We’ve already done everything necessary to get this sketch to respond to Web API requests by calling Particle.function() to connect things up, so the loop() function can just idle.

Receive a note from the Web

Now that funcPlayNote() is connected to the Particle system, when the device is online, this function will be called each time a Web client makes a request to the “playnote” method. This function is a simple brute-force string processor that parses 2 or 3 character inputs, where the first one or two characters is the note name with an optional ‘sharp’ modifier (#), and the last character is the octave number.

The function could have just taken a MIDI note number which would simplify the firmware dramatically, but I preferred a human-readable format to save time later. This function was written with the “get it done to spec quickly” coding standard in mind, so it processes good inputs, bails on bad inputs, and doesn’t respect much that could be mistaken for a ‘coding standard’. There is a time for that, and this is not it 🙂

Sending a MIDI note

The last piece sends a MIDI note to the hardware MIDI port. My first thought was to bring in the wonderful open source Arduino MIDI library and use that — but that got very dicey very quickly. This is where it becomes obvious that this is not an Arduino. That library, tuned very tightly for different Arduino platforms, threw a fit when I tried to compile it in the Particle system.

Not having time to mess with it, I wrote the simple noteOn() function instead:


void noteOn(byte note, byte velocity, byte channel) {
Serial1.write(0x90); // | channel);
//delay(1);
Serial1.write(note);
//delay(1);
Serial1.write(velocity);

}

This function sends serial data over the hardware serial interface (Serial1, not to be confused with Serial, the USB console!) according to the MIDI Note On command format.

The first four bits, (0x9) represents the Note On command. The second four bits (0x0) represents the channel number 1. The second byte is the 7-bit note number, and the third byte is the 7-bit velocity value.

Since MIDI indicates command IDs with the 7th (highest) bit, each of the parameters must be in the range of 0-127. Though it is not validated in this function, trying to send a command with a note or velocity of 128-255 will do something that is not a Note On command.

Looking at it, you should be able to force this by sending a large octave number over the Web API. Limited, but interesting hacking opportunities here!

Now, it just needs a website!

Leave a Reply