Using Arduino Mega to ring organ chimes

I have 2 1/2 octaves of chimes removed from a church organ. The chimes are approx. 24" to 72" in length. They are activated with an electro magnet that runs on 9v. I want to be able to play preset tunes on the chimes.

Using the Mega and an rtc (DS3231) I have written a program that chimes one note on the hour and the half hour, silenced at night and corrected from a 24 hour clock (ie chimes twice at 14:00).

I want to be able to have the chimes play songs on certain days of the year and certain times of the day. For example play Happy Birthday on my birthday, Stars and Stripes Forever on July 4th. Eventually maybe a total of 30 - 50 songs.

I believe that I can use MIDI files but I only need to extract the Noteon and Noteoff and pitch. However I have not been able to find example of folks using MIDI to play mechanical notes (as opposed to playing notes on a speaker).

So I would like suggestions on how to design this next part of the project. I think I can learn the coding once I have a clearer understanding of a reasonably elegant way to build the structure ( my last coding experience was with COBAL on punch cards in the very early 80's). I am loving the Verify button.

Thanks Dan

Sounds like a fun project. I'd start with installing the MIDI library. That will give you the ability to read incoming data (NoteOn and NoteOff). I'm not really sure what you mean when you say playing mechanical notes vs. playing notes on a speaker. If the midi file calls for a given note, you would have to activate the corresponding chime?

The electro magnets need 9v so I have a transistor switch that takes the signal from the arduino and sounds the chime. I don't understand much about how MIDI works with a speaker but I believe that for the chimes I will need 1 pin per chime so will need to send the correct on/off to the correct pin. With a speaker I imagine it would be simpler to just let the bits and bytes into the speaker and let the speaker figure it all out.

Yes, you will need 1 pin/transistor per chime. For a speaker, you would just call the tone() function wich does pulse width modulation (PWM) to change the frequency. I don't know how many chimes are contained in 2 1/2 octaves but that may be a lot of pins. Start here to read all about MIDI

I've never used MIDI but I know something about it.

Actually, it's easier than using a speaker (especially if you want realistic sounds).

MIDI is "messages". I call it "sheet music for the computer"... Notes & timing... It tells the instrument (or computer) what note to play, when, and how loud. (And there is some other information such as which instrument should be played.)

When you play a MIDI file on a computer the MIDI messages get converted to sound using virtual instruments (software).

If you have an actual instrument (like in your case) your software simply has to decode the message and fire the solenoid (or generate and play the sound in the case of a MIDI keyboard).

You can either read a MIDI file from memory or send the MIDI from a computer via USB (or you can add a regular-old MIDI port). Or, you can forget about MIDI and just hard-code the notes, since you'll presumably have a limited number of songs.

Thanks, I'll get reading. I have 23 chimes so the Mega should have enough pins.

You may consider the TPIC6B595 shift registers if your solenoids don’t draw more than about 100 mA each. That way you don’t need to worry about sufficient pins, or wiring up all those transistors.

I would code the songs in my own format, that is more suitable for your application. Chimes & solenoids, that implies a single hit - no duration for a tone. Or are those solenoids operating air valves? Then duration becomes a point. Anyway, create yourself a simple file format. Tone, start time, maybe duration. That saves a lot of work in the Arduino trying to decode a tune. You probably need an external SD card to store them, the built-in EEPROM is likely not enough.

wvmarle: You may consider the TPIC6B595 shift registers

Cool part. Stashing that info away...

The chimes are hit when an electro magnet is energized, and damped when it is released. Tone duration is a bit of an issue because the damping is not complete. In any case notes can be held but there are lot of over tones and the sound can get a bit mixed. I only need 21 or so pins so the Mega should be good on that front.

Currently my code is only using 1% of program space. My thought on using MIDI was that I would need to have limit the size of my sketch and that the songs would take up too much room, maybe thats not a valid concern.

wvmarle can you point me to an example of what a 'simple file format' might look like, I like that idea as I will be playing a 30 second chorus not all of American Pie.

Thanks

1 byte for the note (byte). 4 bytes for the start time in ms from start of song (unsigned long - 2 bytes is enough if it's never more than 65 seconds). 2 bytes for the duration (up to 65 seconds). That's 7 bytes per note played. Read from start of file. No headers or anything so you have to keep track of where it starts and so.

I used to program G-code in EXCEL.
each row was one complete part
that way, I could use the columns to change numbers.

if you have any samples of code, I could try to set one up in excel for you.

=============

I looked at happy birthday for the arduino

LINK to Page

int speakerPin = 9;

int length = 28; // the number of notes

char notes[] = "GGAGcB GGAGdc GGxecBA yyecdc";

int beats[] = { 2, 2, 8, 8, 8, 16, 1, 2, 2, 8, 8,8, 16, 1, 2,2,8,8,8,8,16, 1,2,2,8,8,8,16 };

int tempo = 150;

void playTone(int tone, int duration) {

for (long i = 0; i < duration * 1000L; i += tone * 2) {

   digitalWrite(speakerPin, HIGH);

   delayMicroseconds(tone);

   digitalWrite(speakerPin, LOW);

   delayMicroseconds(tone);

}

}

void playNote(char note, int duration) {

char names[] = {'C', 'D', 'E', 'F', 'G', 'A', 'B',           

                 'c', 'd', 'e', 'f', 'g', 'a', 'b',

                 'x', 'y' };

int tones[] = { 1915, 1700, 1519, 1432, 1275, 1136, 1014,

                 956,  834,  765,  593,  468,  346,  224,

                 655 , 715 };

int SPEE = 5;

// play the tone corresponding to the note name

for (int i = 0; i < 17; i++) {

   if (names[i] == note) {
    int newduration = duration/SPEE;
     playTone(tones[i], newduration);

   }

}

}

void setup() {

pinMode(speakerPin, OUTPUT);

}

void loop() {

for (int i = 0; i < length; i++) {

   if (notes[i] == ' ') {

     delay(beats[i] * tempo); // rest

   } else {

     playNote(notes[i], beats[i] * tempo);

   }

   // pause between notes

   delay(tempo);

}

}

That works if you want to play only one tone at a time; that organ I suppose will play multiple tones at a time.

Bluebeeman Can you post some code of something that is working

Here is what I have working so far. After a couple of days reading about arrays I think that will be the simplest way to go forward. I am planning on using two arrays for each tune one for notes and the second for timing.

//This program is designed to ring a chime every hour. The chime is silenced between 22:00 and 7:00.
// Chime rings once on the half hour. Chime dongs now.hour - 1 on upload.

// Date and time functions using a DS3231 RTC connected via I2C and Wire lib
#include "RTClib.h"

RTC_DS3231 rtc;


int newhour = 0; //used to hold now.hour from the rtc
int oldhour = 0; //used to compare with now.hour
int half = 0; //used to chime on the half hour holds now.minute from rtc
int chime = 5; // the pin the chime is connected to
int HHChime = 7; //the pin the half hour chime is connected to not yet
int led = 13; // the pin the LED is connected to
int bongs = 0; // used to convert 24 hour time to 12 hour chimes
int x = 1; // used to count number of chimes for the hour

void setup () {
  pinMode (chime, OUTPUT); // declare chime as output
  pinMode (led, OUTPUT); // declare led as output


  Serial.begin(9600);

  delay(3000); // wait for console opening

  // I don't know what this code is doing but when I take it out the time is messed up.
  if (! rtc.begin())
  {
    Serial.println("Couldn't find RTC");
    while (1);
  }
}
// Function HalfHour will be called from if statement on the half hour and ring once.
int HalfHour()
{
  digitalWrite(chime, HIGH); //sound half hour chime
  delay (2000);                // allow chime to ring
  digitalWrite(chime, LOW); //damp chime
}

// Function SoundChime will be called from if statement when oldhour and newhour match
// and time is between 7 am and 10 pm.

int SoundChime()
{
  if (newhour <= 12)
  {
    (bongs = newhour); // for times between midnight and noon. Night time shut off is in main code below.
  }
  else
  { (bongs = newhour - 12); //for all P.M. times to reduce the bongs to 12 hour time.
  }
  while (x < bongs)
  {
    digitalWrite(chime, HIGH); //sound chime
    delay (1500);              // allow chime to ring
    digitalWrite (chime, LOW); // damp chime
    delay (300);              // wait to sound again
    ++x ;                     // counts the hours
  }
  x = 0;
}

void loop ()
{
  DateTime now = rtc.now(); // calls time from RTC
  if (oldhour == 0)
  {
    oldhour = now.hour(); // initializes old hour
  }


  // This is the important block. Calls SoundChime during the day only.
  // Calls HalfHour on the 30 minute mark only during the day. 

  newhour = now.hour();
  if (newhour > 6 && newhour < 23 && newhour == oldhour) // if its not the middle of the night and newhour and oldhour match sound chime
    SoundChime();
  oldhour = newhour + 1; // after sounding chime add one to oldhour and wait for time to catch up.

  half = now.minute();
  if (newhour > 6 && newhour < 23 && half == 30) // checks for 30 minutes abd not the middle of the night.
  {
    HalfHour();
    delay(60000) ; // one minute delay lets now.minute move ahead to 31.
  }



  // This block is just here so I can watch whats going on.

  Serial.print(now.hour(), DEC);
  Serial.print(':');
  Serial.print(now.minute(), DEC);
  Serial.print(':');
  Serial.print(now.second(), DEC);
  Serial.println();
  delay (500);
  Serial.print("old hour ");// check value of old hour
  Serial.println(oldhour);
  delay (500);
  Serial.print("half "); // check value of half
  Serial.println(half);
  delay (500);
  Serial.print("new hour "); // check value of new hour
  Serial.println(newhour);

  delay(5000);
}
/code]
  // I don't know what this code is doing but when I take it out the time is messed up.
  if (! rtc.begin())
  {
    Serial.println("Couldn't find RTC");
    while (1);
  }

The important part here is rtc.begin(). That is a mandatory call, it initialises the library. The rest can go indeed.

int HalfHour()

You give the function a return type (int) but you don't actually return a value. The return type should be void in this case:

void HalfHour()

Two more things to add to your coding skillset: - do read up on the different variable types C++ has to offer. You declare everything as int (a 2-byte type that hold values from -32,768 to 32,767); while many of those values should never be negative or even greater than 255, so they would fit in a single byte. - there's a lot of delay() in that code. There should be none of that. Actually I'd say you're not allowed to use delay() until you understand why this rule, and know how to do without. Copious use of delay() tends to come back and bite you in your behind. See the Blink Without Delay example on how to use the millis() timer instead, plus numerous other tutorials explaining the technique in more detail.

The second technique allows you to play more complex tunes with more than one note at a time, where those notes are not starting/ending at the same time.

You guys give more homework than I do :) . Thanks for the suggestions, I've cleaned up some of it and will need to work on getting rid the delays.

If I use arrays to store my tunes where do I store my arrays? Do they stay in the main body of code or is there a simple way to have a function call them from another location?

Those tunes best go to PROGMEM. When that's not enough, external SD card.