GraviTrack Enhancements - Lights and Sound

Updated 04 February 2019

Introduction

Searching for solar panels and related items on the Core Electronics website; I came across the Solarbotics GraviTrack Marble Machine Kit. I thought the kit would be a good educational tool for the grandkids. The kit was a hit, but the power switch was hard to access at the back of the machine and it only turned on and off.

I had been experimenting with programmable RGB LEDs, Piezo speakers and a Light dependent resistor. Decided to incorporate them into to Gravitrack, make it a little more interesting. The Video shows the end results. I would have liked a smaller control box but had to use what was available from the local electronics shop. (anyone wants to buy me a nice 3D printer, haha)

Circuit Design

My design process consists of :

  1. Writing down information about the project, finding datasheets and specifications, etc.
  2. Drawing a schematic using TinyCad.  (excellent CAD program)
  3. Breadboarding a proof of concept circuit.
  4. Pencil sketch of the parts layout on graph paper. (2 times vero board size)
  5. Building the circuit.

I use veroboard to build most of my circuits, it easy to work with and cheap.

The ATMega328P 28 pin DIP, is easy to program and install on veroboard.

Schematic

gravitrack-enhanced-schematic

Breadboard

gravitrack-enhanced-on-the-breadboard

Parts Layout

gravitrack-enhanced-parts-layout

Build Circuit

Gravitrack-enhanced-build-circuit

This is not a how-to build guide, this simply shows what I did.

Refer to the schematic with respect to the following discussion.

Power Supply

The original power for the Gravitrack used 2 x AAA cells. I measured 94mA to 74mA drain as the motor operated. I decided to use 4 x AA NiMH cells, which would provide plenty of power.

A Pololu Adjustable Step-Up Voltage Regulator (POLOLU-2560) is used to provide a stable 5V as the cell voltage varies. The benefit of this device is that it will operate in linear down-regulation mode when the cell voltage is slightly higher than 5V. Measurement of fully charged and fully discharged NiMH cells showed 5.6V to 4.4V. The reverse polarity protection diode drops the voltage to 5.0V to 3.8V.

LDR

This device changes its resistance dependant on how much light is present. The software values have been set so normal room light will not activate the device. Shining a torch on the sensor will then activate the device for 15 seconds. The kids like this feature.

Knock Sensor

This is a Small Enclosed Piezo w/Wires (ADA1740) that produces a voltage if it is tapped. The 1megohm resistor in parallel is necessary for the device to operate correctly. I was going to use this device as a speaker, but the sound output is very low. It works better as a vibration sensor.

Tap this and the GraviTrack starts and runs for 15 seconds, tap it while it is running and it stops.

Switches

Simple push-button switches that cause the software to do something if detected.

Melody – randomly plays 1 of 4 short tunes stored in the micro program memory.

Activate – starts and stops the GraviTrack. The knock sensor is ignored when this is active.

Lights – displays a random light sequence on the 4 RGB LEDs.

Piezo

The digital output for this is a PWM waveform at whatever frequency the software chooses. The drive to the Piezo is through a low power transistor. The tones produced represent the actual frequencies of piano keys. The tunes were taken from sheet music that mainly has one note at a time. Producing chords is a little more difficult with the ATMEGA328P.

Motor

The digital output simple turns the GraviTrack motor on and off via a transistor. The diode across the motor reduces back EMF when the motor turns off. The series resistor is to reduce the 5V drive to around 3V, that the motor is expecting.

RGB LEDs

The excellent Adafruit Library was used to program the LEDs. Placement of the LEDs is through 4 holes already in the GraviTrack. Other locations were considered, but the 4 at the front seemed best.

When powered on the LEDs would start as a random bright color. Whatever I tried, did not stop this condition from happening. I decided to switch power to the LEDs only when the micro was ready to deal with it. There is about a second or so before the micro begins to run the program code after powerup. The micro applies power to the LEDs then fades them to off, looking like it is supposed to happen. This is the best solution I have come up with. All the LEDs I have in my bit box, perform the same way, so unsure if this is a design feature or just applies to the one I bought.

Happy to hear what others have experienced.

Programming Header

In all my circuits with the ATMEga328P; I include a programming header. This allows connection to an Arduino UNO (without processor) with 5 jumper wires to change the code if necessary. I have found this to be very useful in a number of projects so far. I installed a ZIF socket in the UNO to allow easy removal of the micro for just this purpose.

Arduino Code

There are 3 include files in the main code as well as the Adafruit RGB library. The complete code is included at the end of this document.

Include Files

RGB_Colours.h = Array with RGB settings for random colour each time motor starts.

Tunes.h = the tunes to play.

pitch.h = the frequencies related to each piano key.

(cannot go below 31Hz, PWM programming limitation)

 

Sketch uses 8790 bytes (27%) of program storage space. Maximum is 32256 bytes.

Global variables use 184 bytes (8%) of dynamic memory, leaving 1864 bytes for local variables. Maximum is 2048 bytes.

Plenty of room for more tunes and other routines.

Conclusion

The controller does exactly what I intended, amuse the grandkids. Pressing buttons, shining a light and seeing something happen, they love. It makes the GraviTrack much more interesting to them.

Enjoyed building this and watching the kids play with it.

Future

I was going to incorporate more lights, more tunes and a couple of hall effect sensors to trigger something as the balls roll past. But I am happy with how it is now.

Notes: The piece of wood that falls off in the video is to keep the lower tracks together. Sometimes the balls fly off the tracks. The wood will be glued on, eventually. Sometimes the balls don't get picked up, or roll far enough to get picked up.

This is free open source for non-profit hobbyists like myself.

Arduino Main Code
//===============================================================================================
// GraviTrack Marble Machine Enhancement Version 1.4
// UNO
//===============================================================================================
/*
* Add features to the GraviTrack Marble Machine by Solarbotics
*
* Switch Motor power On Off.
* 1. LDR
* 2. Push button switch
* 3. Knock sensor
*
* Fash Lights
*
* Make Noise
*
* Created by James Grigg 07 December 2018
* Copyright.
*/
//===============================================================================================

#include "pitch.h"
#include "Tunes.h"
#include "RGB_Colours.h"
#include <Adafruit_NeoPixel.h> // Strip lights

// digital pin definitions
#define MOTOR 2 // Switch motor on and off
#define RGBLEDS 3 // Port pin for RGB LEDs, normal LEDs. Low = ON, High = OFF.
#define PWR_RGB 5 // Power switch to RGB leds. LEDs come on bright blue immediately power applied.
// providing switch allows control over when they turn on.
#define PUSH_BUTTON1 6 // Push button to Activate
#define PUSH_BUTTON2 7 // Push button to Flash Lights
#define PUSH_BUTTON3 8 // Push button to Melody
#define PIEZO_OUT 9 // PIEZO Output

#define LDR A0 // Light Dependant Resistor Input
#define PIEZO_IN A1 // PIEZO knock sensor

#define L_ON 150 // Light level bright enough to run Motor.
#define L_OFF 350 // Light level to low, turn Motor Off.
#define KNOCK_LVL 50 // PIEZO detect knock, turn Motor On.

#define NUM_LEDS 4 // Number of RGB LEDs.
#define BRIGHTNESS 50 // Lowest 10, max 50.
#define BRIGHTLEVELS 15 //

#define Num_of_Melodies 4 // number of melodies
#define Num_of_RGB 3 // number of RGB sequences

// actual definitions to use on working model.
#define MIN_RUN 2000 // minimum time to run motor. Keeps motor running if light changes rapidly.
#define MAX_RUN 15000 // maximum time to run motor.
#define OFF_TIME 5000 // time to wait till run again from LDR.

// temp definitions, used to speed up motor on / off state for testing only.
//#define MIN_RUN 1000 // minimum time to run motor. Keeps motor running if light changes rapidly.
//#define MAX_RUN 3000 // maximum time to run motor.
//#define OFF_TIME 2000 // time to wait in dark till run again from LDR.

int LedBrightness[BRIGHTLEVELS] = {0,1,2,3,4,5,10,15,20,25,30,35,40,45,50};
int Brightness = BRIGHTNESS;
int countloops = 0;

int RGB_Num = 0;

unsigned long on_millis = 0L;
unsigned long off_millis = 0L;

bool Motor_On = false;
bool Light = false;
bool Knock = false;
bool RunOnceLight = false;
bool ButtonActivate = false;

Adafruit_NeoPixel RGB = Adafruit_NeoPixel(NUM_LEDS, RGBLEDS, NEO_GRB + NEO_KHZ800);
//===============================================================================================
void setup() {
pinMode(MOTOR, OUTPUT); // setup port pins.
pinMode(PWR_RGB, OUTPUT);
pinMode(PIEZO_OUT, OUTPUT);

digitalWrite(PWR_RGB, HIGH);
digitalWrite(PIEZO_OUT, LOW);
pinMode(PUSH_BUTTON1, INPUT_PULLUP);
pinMode(PUSH_BUTTON2, INPUT_PULLUP);
pinMode(PUSH_BUTTON3, INPUT_PULLUP);

randomSeed((int)(analogRead(0))); // set random seed based on light level

RGB.setBrightness(0); // LEDs start up as bright blue, fade out
RGB.begin();
digitalWrite(PWR_RGB, LOW);
RGBFadeAll(0,100,random(256),random(256),random(256));
millisDelay(100);
ClearRGBLeds();

if (analogRead(LDR) < L_ON) { Play_Tune(Notes0, Beats0, Num_Notes0); }
if (analogRead(LDR) > L_OFF) { Play_Tune(Notes1, Beats1, Num_Notes1); }

millisDelay(500);
}
//===============================================================================================
void loop() {

// check for light level to activate device
int lightLvl = analogRead(LDR);
if (lightLvl > L_OFF) { Light = false; } // Light level low
if (lightLvl < L_ON) { Light = true; } // Light level enough to run

// check for knock to activate or deactivate the motor
int knocklvl = analogRead(PIEZO_IN);
if (knocklvl > KNOCK_LVL) { Knock_Detect(); }

// check from button pressed to activate or deactivate the motor
if (digitalRead(PUSH_BUTTON3) == LOW) { Play_Melody(); }

// check from button pressed to activate or deactivate the motor
if (digitalRead(PUSH_BUTTON2) == LOW) { Flash_Lights(); }

// check from button pressed to activate or deactivate the motor
if (digitalRead(PUSH_BUTTON1) == LOW) { Button_Pressed(); }
// check light state and activate motor if light on
Check_Light_Condition();

// check if motor has been running long enough, only works light or knock activation
if (Motor_On && !ButtonActivate) {
if ((unsigned long)(millis() - on_millis) > MAX_RUN) { MotorState(false); } // maths here avoid millis rollover after 50 days
// unsigned long will never be negative for compare
// but battery wont last 50 days anyway, for future code
// on different device
}

// clear Run Once state after enough time has expired and after motor has stopped
if (!Motor_On && RunOnceLight && !ButtonActivate) {
if ((unsigned long)(millis() - off_millis) > OFF_TIME) { RunOnceLight = false; }
}
millisDelay(50);
}
//===============================================================================================
void Play_Melody(void) {
int n = random(4);
switch (n) {
case 0:
Play_Tune(Notes10, Beats10, Num_Notes10);
break;
case 1:
Play_Tune(Notes20, Beats20, Num_Notes20);
break;
case 2:
Play_Tune(Notes30, Beats30, Num_Notes30);
break;
case 3:
Play_Tune(Notes40, Beats40, Num_Notes40);
break;
default:
break;
}
return;
}
//===============================================================================================
void Flash_Lights(void) {
int n = random(6);
switch (n) {
case 0:
RGBFadeAlt(1,100);
delay(100);
RGBFadeAlt(0,100);
delay(100);
ClearRGBLeds();
break;
case 1:
RGBFadeAll(1,100,250,150,50);
delay(100);
RGBFadeAll(0,100,250,150,50);
delay(100);
ClearRGBLeds();
break;
case 2:
RGBFadeAll(1,100,50,250,0);
delay(100);
RGBFadeAll(0,100,50,250,0);
delay(100);
ClearRGBLeds();
break;
case 3:
RGBFadeAll(1,100,0,50,250);
millisDelay(100);
RGBFadeAll(0,100,0,50,250);
millisDelay(100);
ClearRGBLeds();
break;
case 4:
RGBChase(0,50,250,0,0);
RGBChase(1,50,250,0,0);
RGBChase(0,50,0,250,0);
RGBChase(1,50,0,250,0);
RGBChase(0,50,0,250,250);
RGBChase(1,50,0,250,250);
RGBChase(0,50,250,250,250);
RGBChase(1,50,250,250,250);
millisDelay(100);
break;
case 5:
RGBChase(0,50,250,0,0);
RGBChase(0,50,250,0,0);
RGBChase(0,50,250,0,0);
RGBChase(0,50,250,0,0);
RGBChase(0,50,250,0,0);
millisDelay(100);
break;
default:
break;
}
return;
}
//===============================================================================================
//===============================================================================================
// Control Routines, Push button switch, Knock sensor, Light levels
//========================================================================================
void Check_Light_Condition(void) {
if (Light) {
if (!ButtonActivate) { // check for button mode
if (!Motor_On && !RunOnceLight) { // motor off and run once off, start motor
RunOnceLight = true;
MotorState(true);
}
} else {
if (Motor_On && !ButtonActivate) { // run motor for at least some seconds
if ((unsigned long)(millis() - on_millis) > MIN_RUN) { MotorState(false); }
// covers state where light could change rapidly
} // leading to rapid motor on / off
}
}
return;
}
//===============================================================================================
void Knock_Detect(void) {
if (!ButtonActivate) {
Knock = true;
if (!Motor_On) { MotorState(true); } else { MotorState(false); }
Play_Tune(Notes2, Beats2, Num_Notes2);
}
millisDelay(250); // debounce
Knock = false;
return;
}
//===============================================================================================
void Button_Pressed(void) {
// processed a button pressed state. If motor on, turns it off, if motor off turns it on.
if (Motor_On) {
ButtonActivate = false;
MotorState(false);
} else {
ButtonActivate = true;
MotorState(true);
}
while (digitalRead(PUSH_BUTTON3) == LOW) { } // debounce
millisDelay(250); // debounce, seems to work better with a small delay after button pressed
return;
}
//===============================================================================================void MotorState(bool r) {
// motor driver, starts and stops the motor
if (r) {
digitalWrite(MOTOR, HIGH);
Motor_On = true;
RGB_Num = random(RGB_Num_Colours);
SetRGBLedAll(RBG_Colour[RGB_Num][0],RBG_Colour[RGB_Num][1],RBG_Colour[RGB_Num][2]);
if (!Knock) { Play_Tune(Notes3, Beats3, Num_Notes3); }
on_millis = millis();
} else {
digitalWrite(MOTOR, LOW);
Motor_On = false;
ClearRGBLeds();
if (!Knock) { Play_Tune(Notes4, Beats4, Num_Notes4); }
off_millis = millis();
}
return;
}
//===============================================================================================
//===============================================================================================
// RGB LED Routines
//========================================================================================
void RGBFadeAll(bool up, int wait, int G, int R, int B){
// Varies Led brightness up and down. All Leds same colour and brightness.
// up = true increase brightness, up = false decrease brightness.
// wait = time Led is on.
// G, R, B specify colour to use.
//
int b = 0;
for (int i = 0; i < BRIGHTLEVELS; i++) {
if (up) { b = LedBrightness[i]; } else { b = LedBrightness[(BRIGHTLEVELS-1)-i]; }
RGB.setBrightness(b);
for (int j = 0; j < NUM_LEDS; j++) { SetRGBLed(j, G,R,B); }
millisDelay(wait);
}
return;
}
//========================================================================================
void RGBFadeAlt(bool up, int wait){
// Varies Led brightness. Alternate colours Green Red Blue White Green Red Blue White etc ......
// up = true increase brightness, up = false decrease brightness.
// wait = time Led is on.
//
int b = 0;
int c = 250;
for (int i = 0; i < BRIGHTLEVELS; i++) {
if (up) { b = LedBrightness[i]; } else { b = LedBrightness[(BRIGHTLEVELS-1)-i]; }
RGB.setBrightness(b);
int j = 0;
while(j < NUM_LEDS) {
SetRGBLed(j, c,0,0);
SetRGBLed(j+1, 0,c,0);
SetRGBLed(j+2, 0,0,c);
SetRGBLed(j+3, c,c,c);
j += 4;
}
millisDelay(wait);
}
return;
}
//========================================================================================
void RGBChase(bool dirR, int wait, int G, int R, int B) {
// RGB Leds chase each other, one on at a time.
// dirR: true = right, false = left.
// wait = mSec Led is On.
//
RGB.setBrightness(BRIGHTNESS); // use highest level
for (int i = 0; i < NUM_LEDS; i++) {
if (dirR) { SetRGBLed(i, G,R,B); } else { SetRGBLed((NUM_LEDS-1)-i, G,R,B); }
millisDelay(wait);
ClearRGBLeds();
}
return;
}
//===============================================================================================
//===============================================================================================
// Utility Routines
//===============================================================================================
void millisDelay (int d) {
unsigned long _millisCount = millis() + d;
while (millis() < _millisCount) {}
return;
}
//========================================================================================
void Play_Tune(const int * n, const int * b, const int s) {
for (int i = 0; i < s; i++) {
int dur = 1000 / pgm_read_word(&b[i]); // converts beats into millisecond duration
tone(PIEZO_OUT, pgm_read_word(&n[i]), dur); // pgm_read_word necessary due to location of data
millisDelay(dur * 1.3); // allow notes duration plus 30%
noTone(PIEZO_OUT);
}
return;
}
//========================================================================================
void SetRGBLedAll(int G, int R, int B) {
RGB.setBrightness(BRIGHTLEVELS / 2);
for (int i = 0; i < NUM_LEDS; i++) {
RGB.setPixelColor(i, RGB.Color(G,R,B));
RGB.show();
}
return;
}
//========================================================================================
void SetRGBLed(int L, int G, int R, int B) {
if (L < NUM_LEDS) {
RGB.setPixelColor(L, RGB.Color(G,R,B));
RGB.show();
}
return;
}
//===============================================================================================
void ClearRGBLeds() {
for (int i = 0; i < NUM_LEDS; i++) { RGB.setPixelColor(i, RGB.Color(0,0,0)); }
RGB.show();
return;
}
//===============================================================================================
//===============================================================================================
//===============================================================================================
//===============================================================================================
//===============================================================================================
//===============================================================================================
//===============================================================================================
//===============================================================================================
//===============================================================================================
//===============================================================================================

Include Files

RGB_Colours.h

//================================================================================
// These RGB settings are used for a random display each time the motor starts
//================================================================================

const int RBG_Colour[][3] = { 0,0,250, 0,250,0, 250,0,0, 0,250,250, 250,250,0, 250,0,250, 250,250,250, 100,250,0,
250,100,0, 0,250,100, 0,100,250, 250,0,100, 100,0,250 };
const int RGB_Num_Colours = (sizeof(RBG_Colour)/3)/2;

//=================================================================================
//=================================================================================
//=================================================================================
//=================================================================================

Tunes.h

//===================================================================================
/*
* Tunes to play on PIEZO speaker
*
*/
//===================================================================================
// 4 beats to a bar. 8 = 1/2 beat.
// 16 = 1/4 beat, 2 = 2 beats.
// divide into 1000 to get duration in millisecs
//===================================================================================
//Power on - plays on start up of device if light high enough
const int Notes0[] PROGMEM = {_C4,_C5,_A3,_A4,_AS3,_AS4, 0,0,_C4,_C5,_A3,_A4,_AS3,_AS4, 0,0};
const int Beats0[] PROGMEM = { 12, 12, 12, 12, 12, 12, 6,3, 12, 12, 12, 12, 12, 12, 6,3};
const int Num_Notes0 PROGMEM = sizeof(Notes0) / 2;

//===================================================================================
//Power on - plays on start up of device if light low enough
const int Notes1[] PROGMEM = {_D4,_FS4,_G4,0,_D4,0,_FS4,_A3,_G4,_D4,_FS4,_G4,_FS4,_FS4,_D4,_D4,_D4,_FS4,_G4,0};
const int Beats1[] PROGMEM = { 4, 4, 4,8, 8,8, 4, 8, 2, 4, 4, 4, 8, 8, 4, 2, 4, 4, 4,8};
const int Num_Notes1 PROGMEM = sizeof(Notes1) / 2;

//===================================================================================
//Knock detected - plays when knock sensor activated
const int Notes2[] PROGMEM = {_A1, 0,_A2, 0,_A1};
const int Beats2[] PROGMEM = { 8, 16, 8, 16, 8};
const int Num_Notes2 PROGMEM = sizeof(Notes2) / 2;

//===================================================================================
//Play when Light and Push button activate motor
const int Notes3[] PROGMEM = {_D2,_D3,_D4,_D5,_D6,_D7,_D8};
const int Beats3[] PROGMEM = { 8, 8, 8, 8, 8, 8, 8};
const int Num_Notes3 PROGMEM = sizeof(Notes3) / 2;

//===================================================================================
//Play when motor stoppped
const int Notes4[] PROGMEM = {_D8,_D7,_D6,_D5,_D4,_D3,_D2};
const int Beats4[] PROGMEM = { 8, 8, 8, 8, 8, 8, 8};
const int Num_Notes4 PROGMEM = sizeof(Notes4) / 2;

//===================================================================================
// Tunes to play randomly when switch pressed
//===================================================================================
// Axel F - Beverly Hills Cop
const int Notes10[] PROGMEM = {_D4,0,_F4,_D4,0,_D4,_G4,_D4,_C4,_D4,0,_A4,_D4,0,_D4,_AS4,_A4,_F4,_D4,_A4,_D5,_D4,_C4,0,_C4,_A3,_E4,_D4,0};
const int Beats10[] PROGMEM = {8,8,6,16,16,16,8,8,8,8,8,6,16,16,16,8,8,8,8,8,8,16,16,16,16,8,8,2,2};
const int Num_Notes10 PROGMEM = sizeof(Notes10) / 2;

//===================================================================================
// smoke on water
const int Notes20[] PROGMEM = {_D4,_FS4,_G4,0,_D4,0,_FS4,_A3,_G4,_D4,_FS4,_G4,_FS4,_FS4,_D4,_D4,_D4,_FS4,_G4,0,_D4,0,_FS4,_A3,_G4,_D4,_FS4,_G4,_FS4,_FS4,_D4,_D4};
const int Beats20[] PROGMEM = {4,4,4,8,8,8,4,8,2,4,4,4,8,8,4,2,4,4,4,8,8,8,4,8,2,4,4,4,8,8,4,2};
const int Num_Notes20 PROGMEM = sizeof(Notes20) / 2;

//===================================================================================
//Mario main theme melody
const int Notes30[] PROGMEM = {_E7,_E7,0,_E7,0,_C7,_E7,0,_G7,0,0,0,_G6,0,0,0,_C7,0,0,_G6,0,0,_E6,0,0,_A6,0,_B6,0,_AS6,_A6,0,_G6,_E7,_G7,_A7,0,_F7,_G7,0,_E7,0,_C7,
_D7,_B6,0,0,_C7,0,0,_G6,0,0,_E6,0,0,_A6,0,_B6,0,_AS6,_A6,0,_G6,_E7,_G7,_A7,0,_F7,_G7,0,_E7,0,_C7,_D7,_B6,0,0};
const int Beats30[] PROGMEM = {12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,9,9,9,12,12,12,12,12,12,12,12,12,12,
12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,9,9,9,12,12,12,12,12,12,12,12,12,12,12,12,};
const int Num_Notes30 PROGMEM = sizeof(Notes30) / 2;

//===================================================================================
//Underworld melody
const int Notes40[] PROGMEM = {_C4,_C5,_A3,_A4,_AS3,_AS4, 0,0,_C4,_C5,_A3,_A4,_AS3,_AS4, 0,0,_F3,_F4,_D3,_D4,_DS3,_DS4, 0,0,_F3,_F4,_D3,_D4,_DS3,_DS4, 0,0,_DS4,_CS4,
_D4,_CS4,_DS4,_DS4,_GS3,_G3,_CS4,_C4,_FS4,_F4,_E3,_AS4,_A4,_GS4,_DS4,_B3,_AS3,_A3,_GS3,0, 0, 0};
const int Beats40[] PROGMEM = {12,12,12,12,12,12,6,3,12,12,12,12,12,12,6,3,12,12,12,12,12,12,6,3,12,12,12,12,12,12,6,6,18,18,18,6,6,6,6,6,6,18,18,18,18,18,18,10,10,
10,10,10,10,3,3,3};
const int Num_Notes40 PROGMEM = sizeof(Notes40) / 2;

//===================================================================================
//===================================================================================
//===================================================================================

pitch.h

/*************************************************
* Public Constants
*************************************************/

#define _B0 31
#define _C1 33
#define _CS1 35
#define _D1 37
#define _DS1 39
#define _E1 41
#define _F1 44
#define _FS1 46
#define _G1 49
#define _GS1 52
#define _A1 55
#define _AS1 58
#define _B1 62
#define _C2 65
#define _CS2 69
#define _D2 73
#define _DS2 78
#define _E2 82
#define _F2 87
#define _FS2 93
#define _G2 98
#define _GS2 104
#define _A2 110
#define _AS2 117
#define _B2 123
#define _C3 131
#define _CS3 139
#define _D3 147
#define _DS3 156
#define _E3 165
#define _F3 175
#define _FS3 185
#define _G3 196
#define _GS3 208
#define _A3 220
#define _AS3 233
#define _B3 247
#define _C4 262
#define _CS4 277
#define _D4 294
#define _DS4 311
#define _E4 330
#define _F4 349
#define _FS4 370
#define _G4 392
#define _GS4 415
#define _A4 440
#define _AS4 466
#define _B4 494
#define _C5 523
#define _CS5 554
#define _D5 587
#define _DS5 622
#define _E5 659
#define _F5 698
#define _FS5 740
#define _G5 784
#define _GS5 831
#define _A5 880
#define _AS5 932
#define _B5 988
#define _C6 1047
#define _CS6 1109
#define _D6 1175
#define _DS6 1245
#define _E6 1319
#define _F6 1397
#define _FS6 1480
#define _G6 1568
#define _GS6 1661
#define _A6 1760
#define _AS6 1865
#define _B6 1976
#define _C7 2093
#define _CS7 2217
#define _D7 2349
#define _DS7 2489
#define _E7 2637
#define _F7 2794
#define _FS7 2960
#define _G7 3136
#define _GS7 3322
#define _A7 3520
#define _AS7 3729
#define _B7 3951
#define _C8 4186
#define _CS8 4435
#define _D8 4699
#define _DS8 4978

Have a question? Ask the Author of this guide today!

Please enter minimum 20 characters

Your comment will be posted (automatically) on our Support Forum which is publicly accessible. Don't enter private information, such as your phone number.

Expect a quick reply during business hours, many of us check-in over the weekend as well.

Comments


Loading...
Feedback

Please continue if you would like to leave feedback for any of these topics:

  • Website features/issues
  • Content errors/improvements
  • Missing products/categories
  • Product assignments to categories
  • Search results relevance

For all other inquiries (orders status, stock levels, etc), please contact our support team for quick assistance.

Note: click continue and a draft email will be opened to edit. If you don't have an email client on your device, then send a message via the chat icon on the bottom left of our website.

Makers love reviews as much as you do, please follow this link to review the products you have purchased.