Not enough Flash memory, Can I use PC to send commands instead?

Hello,
I have many servos hooked up to my arduino to play the piano.
The song I converted has many lines and it causes my UNO to run out of flash memory.

Here is what it looks like:

play(72,80);
delay(2);play(100,127);
delay(48);play(72,333);
delay(116);play(72,0);
delay(10);play(67,80);
delay(50);play(44,0);
delay(116);play(52,80);
delay(10);play(59,80);
delay(50);play(67,333);
delay(117);play(64,0);
delay(10);play(52,333);
delay(50);play(59,333);
delay(116);play(62,80);
delay(10);play(72,0);
delay(50);play(77,0);
delay(117);play(84,0);
delay(10);play(48,0);
delay(50);play(62,333);
delay(116);play(41,80);
delay(10);play(67,80);
delay(50);play(79,80);
delay(116);play(67,0);
delay(11);play(72,80);
delay(50);play(65,0);
delay(116);play(77,0);
delay(10);play(79,0);
and a few thousand more lines

The full code goes up to a few thousand lines.

Play(which pin to activate servo, speed servo activates)
Even at a hundred lines my memory is almost full.

Is it more feasable to just use my computer to send these commands live through the USB?
What is the terminology called to do this? I looked around google but I could not find what I am trying to do.

Thank you

Your code is actually pretty simple it's a loop executing

delay(value1);
play(value2, value3);

so if the values were stored somewhere, you would not run out of flash memory.

it would be pretty easy and cheap to add a SD card to your arduino and store the delay and play parameters in a file on the SD card. as a bonus you get to choose between many songs by selecting the right file.

You can alternatively add EEPROM extension or more RAM or get a different type of Arduino with more flash memory

The option from the PC would work too, you could write a small Processing program sending through the USB / Serial the right data to your Arduino Program

Hello!

Thank you for your insight. Your idea sounds awesome! How should I be storing my three values? In an array or a text file on the SD card?
What library would I be using to read from my file at the SD card?

For the serial to Arduino, will I need to use python or is there a program out there already that has an interface already set up for me?

Thank you -J-M-L-

for using a SD card yes there is the embedded SD library but a better one to use is the SdFat Library

if you are patient you can get one for ~$1 from the far east

the good thing with an SD card is that you can create that file on your main computer, and store it on the SD. it's easy to do. EEPROM would take a bit more efforts.

And yes storing 3 values per line - comma separated for example - makes it easy to create and read form the Arduino

For the Serial to Arduino, yes Python or anything knowing how to send data over Serial would do.

Your code will need a bit of intelligence as you don't want to blast all the data to your arduino, you should use the delay time to fetch the new data. For that don't use delay() as this is blocking, you will need to implement differently, using the Blink Without Delay approach to ask from the arduino new data to the computer or the SD card, this way you don't saturate the arduino input buffer

here is an example for connecting Python to Arduino but you'll find tons of articles on the web if you do a quick search

J-M-L:

delay(value1);

play(value2, value3);

Why not pass in value1, too, and make it part of play(), since it is needed to "play" a note. The first two parameters could be byte values which could save some space if he's using int's now. Other than the value 333, it seems that all of the values could be byte values. Without seeing the code, it's hard to tell if some kind of sentinel could be used.

Why not pass in value1, too, and make it part of play(), since it is needed to "play" a note.

well yes and no, the way it's written is easy to read and understand but indeed could be also the duration of the pause after a note is played. The fact that it's separated though makes it easier to get rid of it and use millis() to handle that

econjack:
Why not pass in value1, too, and make it part of play(), since it is needed to "play" a note. The first two parameters could be byte values which could save some space if he's using int's now. Other than the value 333, it seems that all of the values could be byte values. Without seeing the code, it's hard to tell if some kind of sentinel could be used.

The frequencies should be mapped to notes. Notes should always fit in a byte, for example there are only 88 keys on a piano.

aarg:
The frequencies should be mapped to notes. Notes should always fit in a byte, for example there are only 88 keys on a piano.

Yep, that's why it would have been nice to see what that 333 was all about.

@J-M-L: The fact that it's separated though makes it easier to get rid of it and use millis() to handle that

I'd rather put the millis() code in the play() function and pass it in than edit several thousand delay() calls, especially since millis() is not a one-to-one replacement for delay() and would require some supporting code. Given the way he is likely using delay(), it don't think keeping it out of play() would make it easier to read or understand.

econjack:
@J-M-L: The fact that it's separated though makes it easier to get rid of it and use millis() to handle that

I'd rather put the millis() code in the play() function and pass it in than edit several thousand delay() calls, especially since millis() is not a one-to-one replacement for delay() and would require some supporting code. Given the way he is likely using delay(), it don't think keeping it out of play() would make it easier to read or understand.

Sure !

My comment was in the context of keeping only 1 delay (actually its equivalent with millis() ) and one play() - in a loop reading the parameters from somewhere. so no thousands things to edit

Thanks for the suggestions so far,

The 333 is just a place holder I was using at the time to clearly differentiate to sustain the note for the piano. This was before I learned about keeping everything under 255 so I can use byte instead of int. The notes go from 1~ about 122, so adding another for the sustain and I will be well under 255. Another thing I forgot to mention is that 0 means turn off/release.

I am pretty free to play around with my values.
I have two sets of values I can use:

Timestamp, what time the note needs to be played. (time in ms, note, power)

0	72	80
2	100	127
50	72	333
166	72	0
176	67	80
226	67	333
342	67	0
352	72	80
402	72	333
519	72	0
529	79	80
579	79	333
695	79	0

And the one I had already posted:

0	72	80
2	100	127
48	72	333
116	72	0
10	67	80
50	44	0
116	52	80
10	59	80
50	67	333
117	64	0
10	52	333
50	59	333
116	62	80

The second one is the change in time to easily implement delay.

The first one seems like a better way to use millis() because everything is in order. However, sometimes multiple notes are played at the same time which might take some time to process, and sometimes notes that need to be played/turned off in the next millisecond. If the first notes take too long, will the next action be skipped? I need a way to be certain that no commands are skipped because my solenoids will melt if they are not turned off.

Do you guys have any ideas on how I should implement my millis loop? thank you.

[qupte]sometimes multiple notes are played at the same time which might take some time to process[/quote]

How do you describe multiple notes at the same time with that format ?

Hello,
Multiple notes will basically have the same timestamp, or a zero delay. On the right side, the script used to have delay(0) which I just delete. Here is the example:

13461	74	333		0(dly)	67	333
13745	62	0		284	79	333
13745	74	0		0	52	80
13764	60	80		19	64	80
13764	72	80		0	69	80
13814	60	333		50	72	80
13814	72	333		0	76	80
14080	31	0		266	41	80
14080	43	0		0	48	80
14098	60	0		18	57	80
14098	72	0		0	72	80
14117	33	80		19	100	0
14117	45	80		0	100	127
14117	72	80		0	72	333
14117	76	80		0	65	333
14117	81	80		0	72	80
14117	84	80		0	52	333
14119	100	0		2	64	333
14120	100	127		1	69	333
14167	33	333		47	72	333

It is basically a converted midi file which I farther processed with regex to get a nice list:

53,Min:Sec:Msec=0:03:176,NoteOn chan: 1 note: 72 vol: 80 dur: 113
54,Min:Sec:Msec=0:03:342,NoteOff chan: 1 note: 59
55,Min:Sec:Msec=0:03:342,NoteOff chan: 1 note: 72
56,Min:Sec:Msec=0:03:352,NoteOn chan: 1 note: 57 vol: 80 dur: 227
57,Min:Sec:Msec=0:03:352,NoteOn chan: 1 note: 79 vol: 80 dur: 113
58,Min:Sec:Msec=0:03:519,NoteOff chan: 1 note: 79
59,Min:Sec:Msec=0:03:529,NoteOn chan: 1 note: 72 vol: 80 dur: 113
60,Min:Sec:Msec=0:03:686,NoteOff chan: 1 note: 57
61,Min:Sec:Msec=0:03:695,NoteOff chan: 1 note: 72
62,Min:Sec:Msec=0:03:705,NoteOn chan: 1 note: 55 vol: 80 dur: 113
63,Min:Sec:Msec=0:03:705,NoteOn chan: 1 note: 67 vol: 80 dur: 113
64,Min:Sec:Msec=0:03:872,NoteOff chan: 1 note: 55
65,Min:Sec:Msec=0:03:872,NoteOff chan: 1 note: 67
66,Min:Sec:Msec=0:03:882,NoteOn chan: 1 note: 72 vol: 80 dur: 113
67,Min:Sec:Msec=0:03:995,NoteOff chan: 1 note: 53
68,Min:Sec:Msec=0:04:048,NoteOff chan: 1 note: 72
69,Min:Sec:Msec=0:04:058,NoteOn chan: 1 note: 55 vol: 80 dur: 227
70,Min:Sec:Msec=0:04:058,NoteOn chan: 1 note: 43 vol: 80 dur: 1025
71,Min:Sec:Msec=0:04:058,NoteOn chan: 1 note: 79 vol: 80 dur: 113
72,Min:Sec:Msec=0:04:224,NoteOff chan: 1 note: 79
73,Min:Sec:Msec=0:04:235,NoteOn chan: 1 note: 72 vol: 80 dur: 113
74,Min:Sec:Msec=0:04:392,NoteOff chan: 1 note: 55
75,Min:Sec:Msec=0:04:401,NoteOff chan: 1 note: 72
76,Min:Sec:Msec=0:04:411,NoteOn chan: 1 note: 50 vol: 80 dur: 227

A little more info on my project, I am actually powering solenoids and vary the duty cycle to achieve variable volume each key.
Since I need a lot of power to drive the solenoid on a loud note, I will need to super charge my solenoid for a short time (I set it as 50ms). After the note is pressed, I will need to hold down the piano key, which is fairly light. Therefore, you see the value 333, 50ms after every key stroke. I was going to do something like: If(power = 333) {output = 20%}.
Here is a little that may help:

During the time when the solenoid is super charged, it can literally melt itself in about 10 seconds.
The reason why I currently use “delay()” is because I did not figure out a way to reliably be sure every command is registered.
My current idea for millis() is to have a function looping until the correct timestamp and which will send the commands. if(millis() == timestamp) {play()} However, if the commands take too long, the important action of holding the note might be missed.
Time: 2000ms —if(millis() == timestamp) {play()}
play command takes 2ms to process
But the next function is: if millis = 2001, but it is already 2002ms.

While writing this, just thought of using a “<=” condition… Is there any problems with my plan?
I really apologize for my rambling. Let me know if you need anymore info or if I need to clarify. Thanks

delay(2);play(100,127);
delay(48);play(72,333);
delay(116);play(72,0);
delay(10);play(67,80);
delay(50);play(44,0);
delay(116);play(52,80);
delay(10);play(59,80);
delay(50);play(67,333);

Instead of that, implement a PROGMEM structure:

typedef struct note {
    byte delay;
    byte key;
    int idunno;
} note_t;

const note_t PROGMEM song[] = {
  {   2,  100, 127 },
  {  48,   72, 333 },
  { 116,   72,   0 },
  {  10,   67,  80 },
  {  50,   44,   0 },
  { 116,   52,  80 },
  {  10,   59,  80 },
  {  50;   67, 333 }
  :
};

play_song()
{
    for (int i=0; i<sizeof(song)/sizeof(note_t); i++) {
    byte d = pgm_read_byte(&song[i].delay);
    byte key = pgm_read_byte(&song[i].key);
    int val3 = pgm_read_byte(&song[i].idunno);
    delay(d);
    play(key, val3);
    }
}

How does play() work to manage multiple notes at the same time?

Hello, ill be gone for about a week without internet.
Thank you for your help. I may post a new thread that has full details about my functions.

No new thread please - keep everything here