MIDI Controller

For our project, we decided upon a MIDI controller because it sounded (ha, get it?) cool.

ヾ(⌐■_■)ノ♪

We wanted to be able to build something that would take MIDI files,  convert them into 8 bit music, and play them out loud. We took inspiration from the music which we heard in older video game consoles and arcade cabinets. The songs we would choose would be from different games, TV shows, and movies that we all enjoy. On that note (more puns), this allowed for each member of the team to have some input in the direction we took with the project and get something personally enjoyable with the finished product.

Image result for undertale dog

Frequency Spectrum Analyzer – Overview

     
     The purpose of our project was to create a Frequency Spectrum Analyzer that receives an analog input via a 3.5mm audio jack and shows the frequencies of the signal on a board of 60 LEDs. The fourier transform converts audio output frequencies back into the original input frequencies and was implemented via the fix_fft library for Arduino.
     The first step of programming was to test the LEDs with example code from Adafruit, the company that manufactures them. This strandtest program includes functions such as color flush which lights each LED in sequence to a given color with a delay between each illumination of the LEDs. Once the LEDs were proven to be functional, the LED enclosure was built (here is the strand test with the enclosure) and analog signal testing was begun. First, the 3.5mm auxiliary jack’s right channel was connected through a 3.5mm breakout adapter to the analog 0 port, then the jack’s sleeve was connected to the ground of the board. To process the signal, the 1024 analog samples were sent as an array through the fix_fft function within the fix_fft library. 10 samples were taken from the output of the fft and sent to the terminal to verify functionality. Here an issue was discovered. If the analog reference value is not set, the output will show signal noise due to floating values. This problem was fixed by adding analogReference(DEFAULT) to the setup function.
     To display the fft output values, a setBar function was created. This function receives the value for which frequency column to modify and the data for that frequency set. If the input data value is above the threshold for a given column, the LEDs will light up vertically indicating how strong the frequency is at that position. For instance, if the threshold is 15 and the column is 1, the third LED will light up with a value of data greater than 45 (15*3). To set the color of the LEDs, the setPixelColor(n,color) function was used. The bottom row’s color is set to green with a gradient reaching red at the top row. After each iteration through all received analog data, all LEDs are cleared until new data is received. This completes the cycle for spectrum conversion and displaying the data.

Midi Controller Schematics

Here we have our circuit schematic for our project. Initially designed by Evan McManus and drawn in AutoCAD 2019 by Brian Popham.

And here is a picture of the project:

To give a brief explanation of the project, we are using two SN76489AN PSGs (Programmable Sound Generators) to output 6 square waves channels, and two noise channels, which is driven by an independent Crystal Oscillator. We are using the Arduino based Teensy 3.5 board to receive native USB MIDI events. Our code is set to handle these events when they occur, such as Note On or Note Off, and will then interpret the messages, translate them into data the PSGs can read, and then output them to the PSGs. This is then handled by the PSGs, which then sends audio data to a 3.5 mm jack with bridged audio connections.

Still with us? If so, congrats!

(☞゚∀゚)☞

You survived! Hope you enjoy our project!

 

Frequency Spectrum Analyzer – Schematics


To display the fft output values, a setBar function was created. This function receives the value for which frequency column to modify and the data for that frequency set. Six strip color variables are set corresponding to the six rows of the LED grid along with a variable threshold for modifying gain. Because the LEDs follow a zig-zag pattern, the LED position values increase for every other column and decrease for the rest as you count from the base of the enclosure to the top. This can be illustrated through a table.
54 53 . . . . . .             18 17 6 5
55 52 . . . . .               19 16 7 4
56 51 . . . . .               20 15 8 3
57 50 . . . . . .             21 14 9 2
58 49 . . . . . .           22 13 10 1
59 48 47 36 35 24 23 12 11 0

An issue arises if you set the LED n value to the column number multiplied by 6. This will invert the direction of the column lighting in every odd numbered column. To address this, the odd numbered columns have a bottom LED value of 6*column+5 which correctly sets the second column base LED n value to 11 (1*6+5). Then you count down as you increase in the column.

Frequency Spectrum Analyzer – Code

Below is the entirety of the source code, but the magic happens within this segment.

// Loop for the number of samples to take from the analog signal
for (int i = 0; i < 128; i++) {
// Store the value from the from analog pin 0
val = analogRead(A0);
// Store a modified value into the arrays
data[i] = val / 4 - 128;
im[i] = 0;
}

// Run the fixed FFT function on the arrays
fix_fft(data, im, 7, 0);

Here you can see that the loop runs 128 times and this corresponds to reading the analog input 128 times. This data is then stored inside a data array which gets passed to the fix_fft library function. Within this function, it converts each of the analog values into their corresponding frequency levels. Almost all of the rest of the code is to control the LEDs themselves

/* This section of code includes the necessary libraries to run our code properly
// Adafruit_NeoPixel.h – Gives us commands to initialize and operate the NeoPixel LEDs
// fix_fft.h – Gives us commands to run the Fast Fourier Transform (FFT)
// avr/power.h – Reduces power consumption if the board runs AVR architecture */
#include <Adafruit_NeoPixel.h>
#include “fix_fft.h”
#ifdef __AVR__
#include <avr/power.h>
#endif

// Defines a global variable to store what pin the LED information will be sent from
#define PIN 0

// Variable arrays to store data from the analog signals and run through the FFT
char im[128], data[128];

/* Creates an instance of our strip of LEDs to run operations on
// Parameter 1 = Number of pixels in strip
// Parameter 2 = Arduino pin number to send data (most are valid)
// Parameter 3 = pixel type flags, add together as needed:*/
Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, PIN, NEO_GRB + NEO_KHZ800);

// The method that is run on start up for the board
void setup(){
// Sets the reference for the analog signals to the 5V default
analogReference(DEFAULT);

// Initializes the LED strip
strip.begin();

// Display the latest colors on the LED strip
strip.show();
};

// The method that runs repeated until the board shuts off
void loop(){
// Sets the minimum and maximum ADC values
int min=1024, max=0,val=0;

// Loop for the number of samples to take from the analog signal
for (int i = 0; i < 128; i++) {
// Store the value from the from analog pin 0
val = analogRead(A0);
// Store a modified value into the arrays
data[i] = val / 4 – 128;
im[i] = 0;
// If the incoming value exceeds the min or max, replace them
if(val>max)
max=val;
if(val<min)
min=val;
}

// Run the fixed FFT function on the arrays
fix_fft(data, im, 7, 0);

// Run the clearBars function to turn all LEDs off before adjusting levels
clearBars();

// Loop for each column of the visualizer (Indexed starting at 1 to avoid FFT errors)
for (i = 1; i <= 10; i++) {
// Store a modified version of the data values to filter noise and hum
double dat = sqrt(data[i] * data[i] + im[i] * im[i]);
// Send the data to the respective bar the visualizer to set its height
setBar(i-1, dat);
}
};

// A method that sets the height of a given column to represent the given data value
void setBar(int bar, double dat){
// Stores the bottom LED ID for the column
int bottom;
// Stores the threshold that is modified to compare all levels
int threshold = 15;
// Save six preset colors for each row of the visualizer
uint32_t row6 = strip.Color(255, 0, 0);
uint32_t row5 = strip.Color(255, 30, 0);
uint32_t row4 = strip.Color(255, 63, 0);
uint32_t row3 = strip.Color(45, 255, 20);
uint32_t row2 = strip.Color(10, 255, 10);
uint32_t row1 = strip.Color(0, 255, 0);

// Checks if given bar is an even number
if(bar % 2 == 0){
// Set bottom to the address of the first LED in the column
bottom = 6*bar;

/* Checks if the given data value is above certain thresholds, and if its true // // enable the corresponding LED with the rows color. */
if((dat*10) >= threshold*5){
strip.setPixelColor(bottom+5, col6);
}
if((dat*10) >= threshold*4){
strip.setPixelColor(bottom+4, col5);
}
if((dat*10) >= threshold*3){
strip.setPixelColor(bottom+3, col4);
}
if((dat*10) >= threshold*2){
strip.setPixelColor(bottom+2, col3);
}
if((dat*10) >= threshold){
strip.setPixelColor(bottom+1, col2);
}
strip.setPixelColor(bottom, col1);

// Run the show function of the LED strip to show the intensity change
strip.show();
}
else{
// Set bottom to the address of the first LED in the column
bottom = 6*bar+5;

/* Checks if the given data value is above certain thresholds, and if its true // // enable the corresponding LED with the rows color. */
if((dat*10) >= threshold*5){
strip.setPixelColor(bottom-5,col6);
}
if((dat*10) >= threshold*4){
strip.setPixelColor(bottom-4, col5);
}
if((dat*10) >= threshold*3){
strip.setPixelColor(bottom-3, col4);
}
if((dat*10) >= threshold*2){
strip.setPixelColor(bottom-2, col3);
}
if((dat*10) >= threshold){
strip.setPixelColor(bottom-1, col2);
}
strip.setPixelColor(bottom, col1);

// Run the show function of the LED strip to show the intensity change
strip.show();
}
};

// A method to clear the colors of all LEDs
void clearBars(){
// Loop for every LED in the LED strip
for(int i = 0; i < 60; i++){
// Set the LEDs color to zeros to turn them off
strip.setPixelColor(i, 0, 0, 0);
}
};

Dragon Bank: Music-related code snippet

For the speaker, we first modified a copy of the Game of Thrones theme to sound nice when repeated. Then we looked up every note frequency and wrote it out. We determined that the music sounded best at a speed of 200 bpm and found that the time an eighth note takes at this speed would be 150 ms.

Team Dracarys – Music composed for the speaker with the frequencies written out

When coding it, we set up 2 arrays. One array held the frequency of the note and the other held how long that note was to be played relative to the speed of an eighth note. So the first entry in the frequency array would correspond to the first note and would be 392.00. Then, because the note has a duration of 6 eighth notes, then a 6 went into the delay array. So the final arrays looked like this:

double frequencies[] = {392.00, 261.63, 311.13, 349.23, 392.00, 261.63, 311.13, 349.23, 293.66, 196.00, 0, 233.08, 261.63, 293.66, 196.00, 0, 233.08, 261.63, 293.66, 196.00, 0, 233.08, 261.63, 293.66, 0, 196.00, 0, 233.08, 0, 349.23, 233.08, 311.13, 293.66, 349.23, 233.08, 311.13, 293.66, 261.63, 174.61, 0, 207.65, 233.08, 261.63, 0, 174.61, 0, 207.65, 233.08, 261.63, 0, 174.61, 0, 207.65, 233.08, 261.63, 0, 174.61, 0, 207.65, 0};

int delays[] = {6, 6, 1, 1, 4, 4, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, 6, 1, 1, 4, 4, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};

Then, one loop of the music was:

for(int i = 0; i < 60; i++){
 
   if(frequencies[i] == 0){
     OCR1A = 1;
   } else{
     OCR1A = F_CPU / (frequencies[i] * TIMER1_PRESCALER * (2^octave)) - 1;
   }
   for(int j = 0; j < delays[i]; j++){
     _delay_ms(150);
   }
}//for

Then, a variable called octave went back and forth between 0 and 1 to cause it to alternate octaves with each play through. This was then all put into a while-loop what would play the song only when told to by the master board. Then all of that was put into a loop that would run forever.

Two Lock Safe – Overview

The goal of this project was to construct a simple two lock mechanism using the ATmega328P microcontroller, a servo, a solenoid, and a 12 key keypad. The project wasn’t a complete success. The solenoid does retract into an unlocked position upon correct key presses and the servo does rotate into a lock state and unlock state, but the both systems cannot happen at the same time. The problem is caused by both systems using the same timer, and could be fixed by using both timer 0 and timer 1.

Temperature-Controlled Fan

To create our circuit for the temperature controlled DC fan, we built off the existing schematics used in labs 3 and 4. Our hardware includes an LCD, AtMega 328p board, temperature controlled sensor, fan, battery pack, transistor, two diodes, and multiple resistors at varying resistances. Challenges encountered wile wiring the circuit included changing our original plan to run the whole circuit off of the 6V battery pack. We found that this set up was not achievable due to the fan needing to pull more voltage from the circuit than we intended. To combat this issue, we decided to run just the fan off of the 6V battery pack and run the remainder of the circuit off of the USB. After much trial and error working with our circuit and varying resistors, we were able to find the optimal set up for the transistor used in the circuit while also finding that we needed to replace our original fan with a smaller spare fan that was in the lab room.

Pong Video Game Hardware

Each controller consisted of one XplainedMini board and a sliding potentiometer mounted in a wooden casing. The sliding potentiometer was a PS45L-R02BR10K model which varied resistance from 0-10kΩ, with a sliding range of 45mm. It was chosen because the range of resistance was broad enough to produce many values, allowing the virtual paddle to move in “real time”. Another reason this specific potentiometer was used was its visual appeal and compatibility with a breadboard. The ability to easily insert and remove the device streamlined the design process, where any shortcomings could be easily corrected. A 10kΩ resistor was connected, in series, to the potentiometer’s voltage pin. This extra resistor was used to vary the voltage; without it, the voltage would have been a constant +5V and the virtual paddles would not move.